From 3be8f38c8216094c12a7910ab09be94394a5e2db Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 28 Apr 2010 01:27:24 +0000 Subject: Added new central krandr library to handle everything related to dynamic screen configuration In the future this will probably handle ICC profiles as well This will allow easy integration of screen configuration into other KDE programs and control center modules git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1119941 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- Makefile.am.in | 2 +- kdecore/kdelibs_export.h | 1 + krandr/Makefile.am | 19 ++ krandr/configure.in.in | 21 ++ krandr/ktimerdialog.cpp | 205 ++++++++++++++ krandr/ktimerdialog.h | 170 ++++++++++++ krandr/libkrandr.cc | 126 +++++++++ krandr/libkrandr.h | 175 ++++++++++++ krandr/lowlevel_randr.c | 670 ++++++++++++++++++++++++++++++++++++++++++++ krandr/lowlevel_randr.h | 102 +++++++ krandr/randr.cpp | 703 +++++++++++++++++++++++++++++++++++++++++++++++ krandr/randr.h | 235 ++++++++++++++++ 12 files changed, 2428 insertions(+), 1 deletion(-) create mode 100644 krandr/Makefile.am create mode 100644 krandr/configure.in.in create mode 100644 krandr/ktimerdialog.cpp create mode 100644 krandr/ktimerdialog.h create mode 100644 krandr/libkrandr.cc create mode 100644 krandr/libkrandr.h create mode 100644 krandr/lowlevel_randr.c create mode 100644 krandr/lowlevel_randr.h create mode 100644 krandr/randr.cpp create mode 100644 krandr/randr.h diff --git a/Makefile.am.in b/Makefile.am.in index 2cca5003a..ba1149e5e 100644 --- a/Makefile.am.in +++ b/Makefile.am.in @@ -20,7 +20,7 @@ COMPILE_FIRST = dcop libltdl kdefx kdecore kunittest kdeui kdesu kjs kwallet kio kded kded_post COMPILE_BEFORE_doc = kdoctools -COMPILE_AFTER_kparts = kspell2 kmdi kdeprint kinit kate interfaces kcert khtml +COMPILE_AFTER_kparts = kspell2 kmdi kdeprint kinit kate interfaces kcert khtml krandr COMPILE_AFTER_kdeprint = kate khtml COMPILE_BEFORE_khtml = kutils COMPILE_BEFORE_kabc = kab kresources diff --git a/kdecore/kdelibs_export.h b/kdecore/kdelibs_export.h index 8f9afad71..8c80f3b6e 100644 --- a/kdecore/kdelibs_export.h +++ b/kdecore/kdelibs_export.h @@ -52,6 +52,7 @@ #define KATEPARTINTERFACES_EXPORT KDE_EXPORT #define KATEPART_EXPORT KDE_EXPORT #define KMID_EXPORT KDE_EXPORT +#define KRANDR_EXPORT KDE_EXPORT #define KIMPROXY_EXPORT KDE_EXPORT #define KDE_ARTS_EXPORT KDE_EXPORT #define KUNITTEST_EXPORT KDE_EXPORT diff --git a/krandr/Makefile.am b/krandr/Makefile.am new file mode 100644 index 000000000..4db082a62 --- /dev/null +++ b/krandr/Makefile.am @@ -0,0 +1,19 @@ + +INCLUDES = -I$(srcdir)/.. $(all_includes) + +# For the future: examine if condensing the tons of *_LDFLAGS variables +# into $(all_libraries) isn't better +AM_LDFLAGS = $(LDFLAGS_AS_NEEDED) $(LDFLAGS_NEW_DTAGS) + +libkrandrincludedir = $(includedir)/libkrandr +libkrandrinclude_HEADERS = randr.h lowlevel_randr.h ktimerdialog.h libkrandr.h + +lib_LTLIBRARIES = libkrandr.la +libkrandr_la_SOURCES = randr.cpp lowlevel_randr.c ktimerdialog.cpp libkrandr.cc +METASOURCES = AUTO + +libkrandr_la_LDFLAGS = $(KDE_MT_LDFLAGS) -version-info 0:95 -no-undefined +libkrandr_la_LIBADD = $(LIBASOUND) ../kdecore/libkdecore.la $(LIB_QT) $(LIB_XRANDR) + +DOXYGEN_REFERENCES = kdecore +include ../admin/Doxyfile.am diff --git a/krandr/configure.in.in b/krandr/configure.in.in new file mode 100644 index 000000000..6feca241b --- /dev/null +++ b/krandr/configure.in.in @@ -0,0 +1,21 @@ +dnl ----------------------------------------------------- +dnl X Resize and Rotate extension library check +dnl ----------------------------------------------------- + +KDE_CHECK_HEADERS(X11/extensions/Xrandr.h, [xrandr_h=yes], [xrandr_h=no], [#include ]) +if test "$xrandr_h" = yes; then + KDE_CHECK_LIB(Xrandr, XRRSetScreenConfigAndRate, [ + LIB_XRANDR=-lXrandr + AC_DEFINE_UNQUOTED(XRANDR_SUPPORT, 1, [Defined if your system has XRandR support]) + RANDR_SUBDIR="randr" + ], [ + RANDR_SUBDIR="" + ], -lXrender -lXext $X_EXTRA_LIBS) +else + LIB_XRANDR= +fi +AC_SUBST(LIB_XRANDR) +AM_CONDITIONAL(system_has_xrandr, test -n "$RANDR_SUBDIR") +if test $system_has_xrandr = no; then + DO_NOT_COMPILE="$DO_NOT_COMPILE libkrandr" +fi diff --git a/krandr/ktimerdialog.cpp b/krandr/ktimerdialog.cpp new file mode 100644 index 000000000..071088e9b --- /dev/null +++ b/krandr/ktimerdialog.cpp @@ -0,0 +1,205 @@ +/* + * This file is part of the KDE Libraries + * Copyright (C) 2002 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "ktimerdialog.h" +#include "ktimerdialog.moc" + +KTimerDialog::KTimerDialog( int msec, TimerStyle style, QWidget *parent, + const char *name, bool modal, + const QString &caption, + int buttonMask, ButtonCode defaultButton, + bool separator, + const KGuiItem &user1, + const KGuiItem &user2, + const KGuiItem &user3 ) + : KDialogBase(parent, name, modal, caption, buttonMask, defaultButton, + separator, user1, user2, user3 ) +{ + totalTimer = new QTimer( this ); + updateTimer = new QTimer( this ); + msecTotal = msecRemaining = msec; + updateInterval = 1000; + tStyle = style; + KWin::setIcons( winId(), DesktopIcon("randr"), SmallIcon("randr") ); + // default to cancelling the dialog on timeout + if ( buttonMask & Cancel ) + buttonOnTimeout = Cancel; + + connect( totalTimer, SIGNAL( timeout() ), SLOT( slotInternalTimeout() ) ); + connect( updateTimer, SIGNAL( timeout() ), SLOT( slotUpdateTime() ) ); + + // create the widgets + mainWidget = new QVBox( this, "mainWidget" ); + timerWidget = new QHBox( mainWidget, "timerWidget" ); + timerLabel = new QLabel( timerWidget ); + timerProgress = new QProgressBar( timerWidget ); + timerProgress->setTotalSteps( msecTotal ); + timerProgress->setPercentageVisible( false ); + + KDialogBase::setMainWidget( mainWidget ); + + slotUpdateTime( false ); +} + +KTimerDialog::~KTimerDialog() +{ +} + +void KTimerDialog::show() +{ + KDialogBase::show(); + totalTimer->start( msecTotal, true ); + updateTimer->start( updateInterval, false ); +} + +int KTimerDialog::exec() +{ + totalTimer->start( msecTotal, true ); + updateTimer->start( updateInterval, false ); + return KDialogBase::exec(); +} + +void KTimerDialog::setMainWidget( QWidget *widget ) +{ + // yuck, here goes. + QVBox *newWidget = new QVBox( this ); + + if ( widget->parentWidget() != mainWidget ) { + widget->reparent( newWidget, 0, QPoint(0,0) ); + } else { + newWidget->insertChild( widget ); + } + + timerWidget->reparent( newWidget, 0, QPoint(0, 0) ); + + delete mainWidget; + mainWidget = newWidget; + KDialogBase::setMainWidget( mainWidget ); +} + +void KTimerDialog::setRefreshInterval( int msec ) +{ + updateInterval = msec; + if ( updateTimer->isActive() ) + updateTimer->changeInterval( updateInterval ); +} + +int KTimerDialog::timeoutButton() const +{ + return buttonOnTimeout; +} + +void KTimerDialog::setTimeoutButton( const ButtonCode newButton ) +{ + buttonOnTimeout = newButton; +} + +int KTimerDialog::timerStyle() const +{ + return tStyle; +} + +void KTimerDialog::setTimerStyle( const TimerStyle newStyle ) +{ + tStyle = newStyle; +} + +void KTimerDialog::slotUpdateTime( bool update ) +{ + if ( update ) + switch ( tStyle ) { + case CountDown: + msecRemaining -= updateInterval; + break; + case CountUp: + msecRemaining += updateInterval; + break; + case Manual: + break; + } + + timerProgress->setProgress( msecRemaining ); + + timerLabel->setText( i18n("1 second remaining:","%n seconds remaining:",msecRemaining/1000) ); +} + +void KTimerDialog::slotInternalTimeout() +{ + emit timerTimeout(); + switch ( buttonOnTimeout ) { + case Help: + slotHelp(); + break; + case Default: + slotDefault(); + break; + case Ok: + slotOk(); + break; + case Apply: + applyPressed(); + break; + case Try: + slotTry(); + break; + case Cancel: + slotCancel(); + break; + case Close: + slotClose(); + break; + /*case User1: + slotUser1(); + break; + case User2: + slotUser2(); + break;*/ + case User3: + slotUser3(); + break; + case No: + slotNo(); + break; + case Yes: + slotCancel(); + break; + case Details: + slotDetails(); + break; + case Filler: + case Stretch: + kdDebug() << "Cannot execute button code " << buttonOnTimeout << endl; + break; + } +} diff --git a/krandr/ktimerdialog.h b/krandr/ktimerdialog.h new file mode 100644 index 000000000..23b4a92b0 --- /dev/null +++ b/krandr/ktimerdialog.h @@ -0,0 +1,170 @@ +/* + * This file is part of the KDE Libraries + * Copyright (C) 2002 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef _KTIMERDIALOG_H_ +#define _KTIMERDIALOG_H_ + +#include + +class QTimer; +class QHBox; +class QProgressBar; +class QLabel; + +/** + * Provides a dialog that is only available for a specified amount + * of time, and reports the time remaining to the user. + * + * The timer is capable of counting up or down, for any number of milliseconds. + * + * The button which is activated upon timeout can be specified, as can the + * update interval for the dialog box. + * + * In addition, this class retains all of the functionality of @see KDialogBase . + * + * @short A dialog with a time limit and corresponding UI features. + * @author Hamish Rodda + */ +class KTimerDialog : public KDialogBase +{ + Q_OBJECT + + public: + + /** + * @li @p CountDown - The timer counts downwards from the seconds given. + * @li @p CountUp - The timer counts up to the number of seconds given. + * @li @p Manual - The timer is not invoked; the caller must update the + * progress. + */ + enum TimerStyle + { + CountDown, + CountUp, + Manual + }; + + /** + * Constructor for the standard mode where you must specify the main + * widget with @ref setMainWidget() . See @see KDialogBase for further details. + * + * For the rest of the arguments, See @see KDialogBase . + */ + KTimerDialog( int msec, TimerStyle style=CountDown, QWidget *parent=0, + const char *name=0, bool modal=true, + const QString &caption=QString::null, + int buttonMask=Ok|Apply|Cancel, ButtonCode defaultButton=Ok, + bool separator=false, + const KGuiItem &user1=KGuiItem(), + const KGuiItem &user2=KGuiItem(), + const KGuiItem &user3=KGuiItem() ); + + /** + * Destructor. + */ + ~KTimerDialog(); + + /** + * Execute the dialog modelessly - see @see QDialog . + */ + virtual void show(); + + /** + * Set the refresh interval for the timer progress. Defaults to one second. + */ + void setRefreshInterval( int msec ); + + /** + * Retrieves the @ref ButtonCode which will be activated once the timer + * times out. @see setTimeoutButton + */ + int timeoutButton() const; + + /** + * Sets the @ref ButtonCode to determine which button will be activated + * once the timer times out. @see timeoutButton + */ + void setTimeoutButton( ButtonCode newButton ); + + /** + * Retrieves the current @ref TimerStyle. @see setTimerStyle + */ + int timerStyle() const; + + /** + * Sets the @ref TimerStyle. @see timerStyle + */ + void setTimerStyle( TimerStyle newStyle ); + + /** + * Overridden function which is used to set the main widget of the dialog. + * @see KDialogBase::setMainWidget. + */ + void setMainWidget( QWidget *widget ); + + signals: + /** + * Signal which is emitted once the timer has timed out. + */ + void timerTimeout(); + + public slots: + /** + * Execute the dialog modally - see @see QDialog . + */ + int exec(); + + private slots: + /** + * Updates the dialog with the current progress levels. + */ + void slotUpdateTime( bool update = true ); + + /** + * The internal + */ + void slotInternalTimeout(); + + private: + /** + * Prepares the layout that manages the widgets of the dialog + */ + void setupLayout(); + + QTimer *totalTimer; + QTimer *updateTimer; + int msecRemaining, updateInterval, msecTotal; + + ButtonCode buttonOnTimeout; + TimerStyle tStyle; + + QHBox *timerWidget; + QProgressBar *timerProgress; + QLabel *timerLabel; + QVBox *mainWidget; + + class KTimerDialogPrivate; + KTimerDialogPrivate *d; +}; + +#endif + + + diff --git a/krandr/libkrandr.cc b/krandr/libkrandr.cc new file mode 100644 index 000000000..214e7e7b0 --- /dev/null +++ b/krandr/libkrandr.cc @@ -0,0 +1,126 @@ +/* libkrandr.cc - class KRandr that makes it easy to use XRandr in KDE + This file is part of KRandr 0.9.5 + Copyright (C) 2010 Timothy Pearson + LibKMid's homepage : http://trinity.pearsoncomputing.net + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + Send comments and bug fixes to Timothy Pearson + +***************************************************************************/ + +#include "libkrandr.h" + +ScreenInfo* KRandrSimpleAPI::read_screen_info (Display *display) +{ + return read_screen_info(display); +} + +int KRandrSimpleAPI::set_screen_size (ScreenInfo *screen_info) +{ + return set_screen_size(screen_info); +} + +void KRandrSimpleAPI::output_auto (ScreenInfo *screen_info, OutputInfo *output_info) +{ + output_auto (screen_info, output_info); +} + +void KRandrSimpleAPI::output_off(ScreenInfo *screen_info, OutputInfo *output) +{ + output_off(screen_info, output); +} + +CrtcInfo* KRandrSimpleAPI::auto_find_crtc (ScreenInfo *screen_info, OutputInfo *output_info) +{ + return auto_find_crtc (screen_info, output_info); +} + +XRRModeInfo *KRandrSimpleAPI::find_mode_by_xid (ScreenInfo *screen_info, RRMode mode_id) +{ + return find_mode_by_xid (screen_info, mode_id); +} + +int KRandrSimpleAPI::mode_height (XRRModeInfo *mode_info, Rotation rotation) +{ + return mode_height (mode_info, rotation); +} + +int KRandrSimpleAPI::mode_width (XRRModeInfo *mode_info, Rotation rotation) +{ + return mode_width (mode_info, rotation); +} + +int KRandrSimpleAPI::get_width_by_output_id (ScreenInfo *screen_info, RROutput output_id) +{ + return get_width_by_output_id (screen_info, output_id); +} + +int KRandrSimpleAPI::get_height_by_output_id (ScreenInfo *screen_info, RROutput output_id) +{ + return get_height_by_output_id (screen_info, output_id); +} + +char *KRandrSimpleAPI::get_output_name (ScreenInfo *screen_info, RROutput id) +{ + return get_output_name (screen_info, id); +} + +Status KRandrSimpleAPI::crtc_apply (CrtcInfo *crtc_info) +{ + return crtc_apply (crtc_info); +} + +Status KRandrSimpleAPI::crtc_disable (CrtcInfo *crtc) +{ + return crtc_disable (crtc); +} + +int KRandrSimpleAPI::main_low_apply (ScreenInfo *screen_info) +{ + return main_low_apply (screen_info); +} + +bool KRandrSimpleAPI::kRandrHasRandr(void) +{ + return isValid(); +} + +const char *KRandrSimpleAPI::kRandrVersion(void) +{ + return "0.9.5"; +} + +const char *KRandrSimpleAPI::kRandrCopyright(void) +{ + return "LibKRandr 0.9.5 (C)2010 Timothy Pearson . U.S.A."; +} + +/* * * * * * + + Under this line (------) there's only a C wrapper for the KRandrSimpleAPI class + +* * * * * */ +const char *kRandrVersion(void) +{ + return KRandrSimpleAPI::kRandrVersion(); +} + +const char *kRandrCopyright(void) +{ + return KRandrSimpleAPI::kRandrCopyright(); +} + diff --git a/krandr/libkrandr.h b/krandr/libkrandr.h new file mode 100644 index 000000000..e9db64ae9 --- /dev/null +++ b/krandr/libkrandr.h @@ -0,0 +1,175 @@ +/* libkrandr.h - class KRandr that makes it easy to use XRandr in KDE + This file is part of KRandr 0.9.5 + Copyright (C) 2010 Timothy Pearson + LibKMid's homepage : http://trinity.pearsoncomputing.net + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + Send comments and bug fixes to Timothy Pearson + +***************************************************************************/ +#ifndef _LIBKRANDR_H +#define _LIBKRANDR_H + +#include "randr.h" +#include "lowlevel_randr.h" + +#ifdef __cplusplus + +#include + +/** + * Simple API covering most of the uses of libkrandr. + * + * You can use the members of this class in pure C applications, just by using + * the same name as the corresponding function member. + * + * @short A simple API around the rest of libkrandr. + * @version 0.9.5 27/04/2010 + * @author Timothy Pearson + */ +class KRANDR_EXPORT KRandrSimpleAPI : public RandRDisplay +{ + private: + + public: + /** + * Reads current screen information. + */ + ScreenInfo* read_screen_info(Display *display); + + /** + * Sets the screen size. + */ + int set_screen_size (ScreenInfo *screen_info); + + /** + * Automatically selects an output port. + */ + void output_auto (ScreenInfo *screen_info, OutputInfo *output_info); + + /** + * Turns off a specified output on a specified screen. + */ + void output_off(ScreenInfo *screen_info, OutputInfo *output); + + /** + * Automatically finds the CRTC structure. + */ + CrtcInfo* auto_find_crtc (ScreenInfo *screen_info, OutputInfo *output_info); + + /** + * Finds a mode by XID. + */ + XRRModeInfo *find_mode_by_xid (ScreenInfo *screen_info, RRMode mode_id); + + /** + * Returns specified mode height in pixels. + */ + int mode_height (XRRModeInfo *mode_info, Rotation rotation); + + /** + * Returns specified mode width in pixels. + */ + int mode_width (XRRModeInfo *mode_info, Rotation rotation); + + /** + * Returns specified output width in pixels. + */ + int get_width_by_output_id (ScreenInfo *screen_info, RROutput output_id); + + /** + * Returns specified output height in pixels. + */ + int get_height_by_output_id (ScreenInfo *screen_info, RROutput output_id); + + /** + * Returns output name. + */ + char *get_output_name (ScreenInfo *screen_info, RROutput id); + + /** + * Applies specified CRTC. + */ + Status crtc_apply (CrtcInfo *crtc_info); + + /** + * Disables specificed CRTC + */ + Status crtc_disable (CrtcInfo *crtc); + + /** + * Applies all previously configured settings to the specified screen. + */ + int main_low_apply (ScreenInfo *screen_info); + + /** + * Returns whether or not the system supports XRandR + */ + bool kRandrHasRandr(); + + /** + * Returns the version number of libkrandr, i.e. "0.9.5" or "1.0 Beta" + */ + static const char *kRandrVersion(void); + + /** + * Returns the copyright notice that applications using libkrandr should print + * to the user in an about box or somewhere visible. + * I.e. + * + * "LibKRandr 0.9.5 (C) 2010 Timothy Pearson . U.S.A." + */ + static const char *kRandrCopyright(void); + +}; + + + +extern "C" { + +#else +#define KRANDR_EXPORT +#endif + +// KRANDR_EXPORT ScreenInfo* read_screen_info(Display *); +// KRANDR_EXPORT int set_screen_size (ScreenInfo *screen_info); +// KRANDR_EXPORT void output_auto (ScreenInfo *screen_info, OutputInfo *output_info); +// KRANDR_EXPORT void output_off(ScreenInfo *screen_info, OutputInfo *output); +// KRANDR_EXPORT CrtcInfo* auto_find_crtc (ScreenInfo *screen_info, OutputInfo *output_info); +// KRANDR_EXPORT XRRModeInfo *find_mode_by_xid (ScreenInfo *screen_info, RRMode mode_id); +// KRANDR_EXPORT int mode_height (XRRModeInfo *mode_info, Rotation rotation); +// KRANDR_EXPORT int mode_width (XRRModeInfo *mode_info, Rotation rotation); +// KRANDR_EXPORT int get_width_by_output_id (ScreenInfo *screen_info, RROutput output_id); +// KRANDR_EXPORT int get_height_by_output_id (ScreenInfo *screen_info, RROutput output_id); +// KRANDR_EXPORT char *get_output_name (ScreenInfo *screen_info, RROutput id); +// KRANDR_EXPORT Status crtc_apply (CrtcInfo *crtc_info); +// KRANDR_EXPORT Status crtc_disable (CrtcInfo *crtc); +// KRANDR_EXPORT int main_low_apply (ScreenInfo *screen_info); +// KRANDR_EXPORT bool kRandrHasRandr(); + +KRANDR_EXPORT const char *kRandrVersion(void); +KRANDR_EXPORT const char *kRandrCopyright(void); + +#ifdef __cplusplus + +} + + +#endif + + +#endif diff --git a/krandr/lowlevel_randr.c b/krandr/lowlevel_randr.c new file mode 100644 index 000000000..7f3e890fd --- /dev/null +++ b/krandr/lowlevel_randr.c @@ -0,0 +1,670 @@ +/* + * Copyright © 2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "lowlevel_randr.h" +#include +#include +#include + +Status crtc_disable (struct CrtcInfo *crtc); + +char * get_output_name (struct ScreenInfo *screen_info, RROutput id) +{ + char *output_name = NULL; + int i; + + for (i = 0; i < screen_info->n_output; i++) { + if (id == screen_info->outputs[i]->id) { + output_name = screen_info->outputs[i]->info->name; + } + } + + if (!output_name) { + output_name = "Unknown"; + } + + return output_name; +} + +XRRModeInfo * find_mode_by_xid (struct ScreenInfo *screen_info, RRMode mode_id) +{ + XRRModeInfo *mode_info = NULL; + XRRScreenResources *res; + int i; + + res = screen_info->res; + for (i = 0; i < res->nmode; i++) { + if (mode_id == res->modes[i].id) { + mode_info = &res->modes[i]; + break; + } + } + + return mode_info; +} + +static XRRCrtcInfo * find_crtc_by_xid (struct ScreenInfo *screen_info, RRCrtc crtc_id) +{ + XRRCrtcInfo *crtc_info; + Display *dpy; + XRRScreenResources *res; + + dpy = screen_info->dpy; + res = screen_info->res; + + crtc_info = XRRGetCrtcInfo (dpy, res, crtc_id); + + return crtc_info; +} + +int get_width_by_output_id (struct ScreenInfo *screen_info, RROutput output_id) +{ + struct OutputInfo *output_info; + struct CrtcInfo *crtc_info; + RRMode mode_id; + XRRModeInfo *mode_info; + int i; + int width = -1; + + for (i = 0; i < screen_info->n_output; i++) { + if (output_id == screen_info->outputs[i]->id) { + crtc_info = screen_info->outputs[i]->cur_crtc; + if (!crtc_info) { + width = 0; + break; + } + mode_id = crtc_info->cur_mode_id; + mode_info = find_mode_by_xid (screen_info, mode_id); + + width = mode_width (mode_info, crtc_info->cur_rotation); + + break; + } + } + + return width; +} + +int get_height_by_output_id (struct ScreenInfo *screen_info, RROutput output_id) +{ + struct OutputInfo *output_info; + struct CrtcInfo *crtc_info; + RRMode mode_id; + XRRModeInfo *mode_info; + int i; + int height = -1; + + for (i = 0; i < screen_info->n_output; i++) { + if (output_id == screen_info->outputs[i]->id) { + crtc_info = screen_info->outputs[i]->cur_crtc; + if (!crtc_info) { + height = 0; + break; + } + mode_id = crtc_info->cur_mode_id; + mode_info = find_mode_by_xid (screen_info, mode_id); + + height = mode_height (mode_info, crtc_info->cur_rotation); + + break; + } + } + + return height; +} + +int mode_height (XRRModeInfo *mode_info, Rotation rotation) +{ + switch (rotation & 0xf) { + case RR_Rotate_0: + case RR_Rotate_180: + return mode_info->height; + case RR_Rotate_90: + case RR_Rotate_270: + return mode_info->width; + default: + return 0; + } +} + +int mode_width (XRRModeInfo *mode_info, Rotation rotation) +{ + switch (rotation & 0xf) { + case RR_Rotate_0: + case RR_Rotate_180: + return mode_info->width; + case RR_Rotate_90: + case RR_Rotate_270: + return mode_info->height; + default: + return 0; + } +} + + +static struct CrtcInfo * find_crtc (struct ScreenInfo *screen_info, XRROutputInfo *output) +{ + struct CrtcInfo *crtc_info = NULL; + int i; + + for (i = 0; i < screen_info->n_crtc; i++) { + if (screen_info->crtcs[i]->id == output->crtc) { + crtc_info = screen_info->crtcs[i]; + break; + } + } + + return crtc_info; +} + +struct CrtcInfo * auto_find_crtc (struct ScreenInfo *screen_info, struct OutputInfo *output_info) +{ + struct CrtcInfo *crtc_info = NULL; + int i; + + for (i = 0; i < screen_info->n_crtc; i++) { + if (0 == screen_info->crtcs[i]->cur_noutput) { + crtc_info = screen_info->crtcs[i]; + break; + } + } + + if (NULL == crtc_info) { + crtc_info = screen_info->crtcs[0]; + } + + return crtc_info; +} + +int set_screen_size (struct ScreenInfo *screen_info) +{ + Display *dpy; + int screen; + struct CrtcInfo *crtc; + XRRModeInfo *mode_info; + int cur_x = 0, cur_y = 0; + int w = 0, h = 0; + int mmW, mmH; + int max_width = 0, max_height = 0; + int i; + + dpy = screen_info->dpy; + screen = DefaultScreen (dpy); + + for (i = 0; i < screen_info->n_crtc; i++) { + crtc = screen_info->crtcs[i]; + if (!crtc->cur_mode_id) { + continue; + } + mode_info = find_mode_by_xid (screen_info, crtc->cur_mode_id); + cur_x = crtc->cur_x; + cur_y = crtc->cur_y; + + w = mode_width (mode_info, crtc->cur_rotation); + h = mode_height (mode_info, crtc->cur_rotation); + + if (cur_x + w > max_width) { + max_width = cur_x + w; + } + if (cur_y + h > max_height) { + max_height = cur_y + h; + } + } + + if (max_width > screen_info->max_width) { + #if RANDR_GUI_DEBUG + fprintf (stderr, "user set screen width %d, larger than max width %d, set to max width\n", + cur_x + w, screen_info->max_width); + #endif + return 0; + } else if (max_width < screen_info->min_width) { + screen_info->cur_width = screen_info->min_width; + } else { + screen_info->cur_width = max_width; + } + + if (max_height > screen_info->max_height) { + #if RANDR_GUI_DEBUG + fprintf (stderr, "user set screen height %d, larger than max height %d, set to max height\n", + cur_y + h, screen_info->max_height); + #endif + return 0; + } else if (max_height < screen_info->min_height) { + screen_info->cur_height = screen_info->min_height; + } else { + screen_info->cur_height = max_height; + } + + /* calculate mmWidth, mmHeight */ + if (screen_info->cur_width != DisplayWidth (dpy, screen) || + screen_info->cur_height != DisplayHeight (dpy, screen) ) { + double dpi; + + dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen); + mmW = (25.4 * screen_info->cur_width) / dpi; + mmH = (25.4 * screen_info->cur_height) / dpi; + } else { + mmW = DisplayWidthMM (dpy, screen); + mmH = DisplayHeightMM (dpy, screen); + } + + screen_info->cur_mmWidth = mmW; + screen_info->cur_mmHeight = mmH; + + return 1; +} + +void screen_apply (struct ScreenInfo *screen_info) +{ + int width, height; + int mmWidth, mmHeight; + Display *dpy, *cur_dpy; + Window window; + int screen; + static int first = 1; + + width = screen_info->cur_width; + height = screen_info->cur_height; + mmWidth = screen_info->cur_mmWidth; + mmHeight = screen_info->cur_mmHeight; + dpy = screen_info->dpy; + window = screen_info->window; + screen = DefaultScreen (dpy); + + cur_dpy = XOpenDisplay (NULL); + + if (width == DisplayWidth (cur_dpy, screen) && + height == DisplayHeight (cur_dpy, screen) && + mmWidth == DisplayWidthMM (cur_dpy, screen) && + mmHeight == DisplayHeightMM (cur_dpy, screen) ) { + return; + } else { + XRRSetScreenSize (dpy, window, width, height, mmWidth, mmHeight); + } +} + +Status crtc_apply (struct CrtcInfo *crtc_info) +{ + struct ScreenInfo *screen_info; + XRRCrtcInfo *rr_crtc_info; + Display *dpy; + XRRScreenResources *res; + RRCrtc crtc_id; + int x, y; + RRMode mode_id; + Rotation rotation; + RROutput *outputs; + int noutput; + Status s; + int i; + + /*if (!crtc_info->changed) { + return RRSetConfigSuccess; + }*/ + + screen_info = crtc_info->screen_info; + dpy = screen_info->dpy; + res = screen_info->res; + crtc_id = crtc_info->id; + x = crtc_info->cur_x; + y = crtc_info->cur_y; + + mode_id = crtc_info->cur_mode_id; + rotation = crtc_info->cur_rotation; + + noutput = crtc_info->cur_noutput; + + if (0 == noutput) { + return crtc_disable (crtc_info); + } + + outputs = malloc (sizeof (RROutput) * noutput); + noutput = 0; + for (i = 0; i < screen_info->n_output; i++) { + struct OutputInfo *output_info = screen_info->outputs[i]; + + if (output_info->cur_crtc && crtc_id == output_info->cur_crtc->id) { + outputs[noutput++] = output_info->id; + } + } + + + s = XRRSetCrtcConfig (dpy, res, crtc_id, CurrentTime, + x, y, mode_id, rotation, + outputs, noutput); + + if (RRSetConfigSuccess == s) { + crtc_info->changed = 0; + } + + free (outputs); + + return s; +} + +Status crtc_disable (struct CrtcInfo *crtc) +{ + struct ScreenInfo *screen_info; + + screen_info = crtc->screen_info; + + return XRRSetCrtcConfig (screen_info->dpy, screen_info->res, crtc->id, CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); +} + +struct ScreenInfo* read_screen_info (Display *display) +{ + struct ScreenInfo *screen_info; + int screen_num; + Window root_window; + XRRScreenResources *sr; + int i; + + screen_num = DefaultScreen (display); + root_window = RootWindow (display, screen_num); + + sr = XRRGetScreenResources (display, root_window); + + screen_info = malloc (sizeof (struct ScreenInfo)); + screen_info->dpy = display; + screen_info->window = root_window; + screen_info->res = sr; + screen_info->cur_width = DisplayWidth (display, screen_num); + screen_info->cur_height = DisplayHeight (display, screen_num); + screen_info->cur_mmWidth = DisplayWidthMM (display, screen_num); + screen_info->cur_mmHeight = DisplayHeightMM (display, screen_num); + screen_info->n_output = sr->noutput; + screen_info->n_crtc = sr->ncrtc; + screen_info->outputs = malloc (sizeof (struct OutputInfo *) * sr->noutput); + screen_info->crtcs = malloc (sizeof (struct CrtcInfo *) * sr->ncrtc); + screen_info->clone = 0; + + XRRGetScreenSizeRange (display, root_window, &screen_info->min_width, &screen_info->min_height, &screen_info->max_width, &screen_info->max_height); + + /* get crtc */ + for (i = 0; i < sr->ncrtc; i++) { + struct CrtcInfo *crtc_info; + screen_info->crtcs[i] = malloc (sizeof (struct CrtcInfo)); + crtc_info = screen_info->crtcs[i]; + XRRCrtcInfo *xrr_crtc_info = XRRGetCrtcInfo (display, sr, sr->crtcs[i]); + + crtc_info->id = sr->crtcs[i]; + crtc_info->info = xrr_crtc_info; + crtc_info->cur_x = xrr_crtc_info->x; + crtc_info->cur_y = xrr_crtc_info->y; + crtc_info->cur_mode_id = xrr_crtc_info->mode; + crtc_info->cur_rotation = xrr_crtc_info->rotation; + crtc_info->rotations = xrr_crtc_info->rotations; + crtc_info->cur_noutput = xrr_crtc_info->noutput; + + crtc_info->changed = 0; + crtc_info->screen_info = screen_info; + } + + + /* get output */ + for (i = 0; i < sr->noutput; i++) { + struct OutputInfo *output; + screen_info->outputs[i] = malloc (sizeof (struct OutputInfo)); + output = screen_info->outputs[i]; + + output->id = sr->outputs[i]; + output->info = XRRGetOutputInfo (display, sr, sr->outputs[i]); + output->cur_crtc = find_crtc (screen_info, output->info); + output->auto_set = 0; + if (output->cur_crtc) { + output->off_set = 0; + } else { + output->off_set = 1; + } + + } + + /* set current crtc */ + screen_info->cur_crtc = screen_info->outputs[0]->cur_crtc; + screen_info->primary_crtc = screen_info->cur_crtc; + screen_info->cur_output = screen_info->outputs[0]; + + return screen_info; +} + +void free_screen_info (struct ScreenInfo *screen_info) +{ + free (screen_info->outputs); + free (screen_info->crtcs); + free (screen_info); +} + + + +/*check if other outputs that connected to the same crtc support this mode*/ +static int check_mode (struct ScreenInfo *screen_info, struct OutputInfo *output, RRMode mode_id) +{ + XRRCrtcInfo *crtc_info; + /* XRR */ + int i, j; + int mode_ok = 1; + + if (!output->cur_crtc) { + return 1; + } + + crtc_info = output->cur_crtc->info; + for (i = 0; i < crtc_info->noutput; i++) { + XRROutputInfo *output_info; + int nmode; + + if (output->id == crtc_info->outputs[i]) { + continue; + } + + mode_ok = 0; + output_info = XRRGetOutputInfo (screen_info->dpy, screen_info->res, crtc_info->outputs[i]); + nmode = output_info->nmode; + for (j = 0; j < nmode; j++) { + if (mode_id == output_info->modes[j]) { + mode_ok = 1; + break; + } + } + if (!mode_ok) { + break; + } + } + + return mode_ok; +} + +static RRCrtc get_crtc_id_by_output_id (struct ScreenInfo *screen_info, RROutput output_id) +{ + int i; + RRCrtc crtc_id = -1; + + for (i = 0; i < screen_info->n_output; i++) { + if (output_id == screen_info->outputs[i]->id) { + if (screen_info->outputs[i]->cur_crtc) { + crtc_id = screen_info->outputs[i]->cur_crtc->id; + } else { + crtc_id = 0; /* this output is off */ + } + break; + } + } + + return crtc_id; +} + +static struct CrtcInfo * +get_crtc_info_by_xid (struct ScreenInfo *screen_info, RRCrtc crtc_id) +{ + struct CrtcInfo *crtc_info = NULL; + int i; + + for (i = 0; i < screen_info->n_crtc; i++) { + if (crtc_id == screen_info->crtcs[i]->id) { + crtc_info = screen_info->crtcs[i]; + break; + } + } + + return crtc_info; +} + +static XRRModeInfo * +preferred_mode (struct ScreenInfo *screen_info, struct OutputInfo *output) +{ + XRROutputInfo *output_info = output->info; + Display *dpy; + int screen; + int m; + XRRModeInfo *best; + int bestDist; + + dpy = screen_info->dpy; + screen = DefaultScreen (dpy); + best = NULL; + bestDist = 0; + for (m = 0; m < output_info->nmode; m++) { + XRRModeInfo *mode_info = find_mode_by_xid (screen_info, output_info->modes[m]); + int dist; + + if (m < output_info->npreferred) + dist = 0; + else if (output_info->mm_height) + dist = (1000 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) - + 1000 * mode_info->height / output_info->mm_height); + else + dist = DisplayHeight(dpy, screen) - mode_info->height; + + if (dist < 0) dist = -dist; + if (!best || dist < bestDist) { + best = mode_info; + bestDist = dist; + } + } + return best; +} + +int main_low_apply (struct ScreenInfo *screen_info) +{ + int i; + struct CrtcInfo *crtc_info; + + /* set_positions (screen_info); */ + + if (!set_screen_size (screen_info)) { + printf("Screen Size FAILURE\n\r"); + return 0; + } + + for (i = 0; i < screen_info->n_crtc; i++) { + int old_x, old_y, old_w, old_h; + + XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (screen_info->dpy, screen_info->res, screen_info->crtcs[i]->id); + XRRModeInfo *old_mode = find_mode_by_xid (screen_info, crtc_info->mode); + + if (crtc_info->mode == None) { + continue; + } + + old_x = crtc_info->x; + old_y = crtc_info->y; + old_w = mode_width (old_mode, crtc_info->rotation); + old_h = mode_height (old_mode, crtc_info->rotation); + + if (old_x + old_w <= screen_info->cur_width && + old_y + old_h <= screen_info->cur_height ) { + continue; + } else { + crtc_disable (screen_info->crtcs[i]); + } + } + + screen_apply (screen_info); + + for (i = 0; i < screen_info->n_crtc; i++) { + Status s; + crtc_info = screen_info->crtcs[i]; + + s = crtc_apply (crtc_info); + if (RRSetConfigSuccess != s) { + fprintf (stderr, "crtc apply error\n"); + } + } + + return 1; +} + +void output_auto (struct ScreenInfo *screen_info, struct OutputInfo *output_info) +{ + XRRModeInfo *mode_info; + RRMode mode_id; + struct CrtcInfo *crtc_info; + XRROutputInfo *probe_output_info; + + if (RR_Disconnected == output_info->info->connection) { + XRRScreenResources *cur_res; + + cur_res = XRRGetScreenResources (screen_info->dpy, screen_info->window); + probe_output_info = XRRGetOutputInfo (screen_info->dpy, cur_res, output_info->id); + if (RR_Disconnected != probe_output_info->connection) { + output_info->info = probe_output_info; + output_info->cur_crtc = auto_find_crtc (screen_info, output_info); + } + } + + mode_info = preferred_mode (screen_info, output_info); + if (!mode_info) { + return; + } + mode_id = mode_info->id; + + crtc_info = output_info->cur_crtc; + if (crtc_info) { + crtc_info->cur_mode_id = mode_id; + } else { + crtc_info = auto_find_crtc (screen_info, output_info); + if (!crtc_info) { +#if RANDR_GUI_DEBUG + fprintf (stderr, "Can not find usable CRTC\n"); +#endif + return; + } else { + screen_info->cur_output->cur_crtc = crtc_info; + screen_info->cur_crtc = crtc_info; + screen_info->cur_crtc->cur_noutput++; + fprintf (stderr, "n output: %d\n", screen_info->cur_crtc->cur_noutput); + screen_info->cur_crtc->cur_mode_id = mode_id; + screen_info->cur_crtc->changed = 1; + } + } + +} + +void output_off (struct ScreenInfo *screen_info, struct OutputInfo *output) +{ + if (output->cur_crtc) { + output->cur_crtc->cur_noutput--; + } + output->cur_crtc = NULL; + screen_info->cur_crtc = NULL; + output->off_set = 1; +} diff --git a/krandr/lowlevel_randr.h b/krandr/lowlevel_randr.h new file mode 100644 index 000000000..a9a519125 --- /dev/null +++ b/krandr/lowlevel_randr.h @@ -0,0 +1,102 @@ +/* + * Copyright © 2007 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +struct ScreenInfo; + +struct CrtcInfo { + RRCrtc id; + XRRCrtcInfo *info; + int cur_x; + int cur_y; + RRMode cur_mode_id; + Rotation cur_rotation; + Rotation rotations; + int cur_noutput; + + int changed; + + struct ScreenInfo *screen_info; +}; + +struct OutputInfo { + RROutput id; + XRROutputInfo *info; + struct CrtcInfo *cur_crtc; + + int auto_set; + int off_set; +}; + +struct ScreenInfo { + Display *dpy; + Window window; + XRRScreenResources *res; + int min_width, min_height; + int max_width, max_height; + int cur_width; + int cur_height; + int cur_mmWidth; + int cur_mmHeight; + + int n_output; + int n_crtc; + struct OutputInfo **outputs; + struct CrtcInfo **crtcs; + + int clone; + struct CrtcInfo *primary_crtc; + + struct CrtcInfo *cur_crtc; + struct OutputInfo *cur_output; +}; + +extern struct ScreenInfo *screen_info; +extern const uint big_pixbuf[], small_pixbuf[]; + +#ifdef __cplusplus +extern "C" { +#endif +void free_screen_info (struct ScreenInfo *screen_info); + +struct ScreenInfo* read_screen_info (Display *); + +int set_screen_size (struct ScreenInfo *screen_info); +void output_auto (struct ScreenInfo *screen_info, struct OutputInfo *output_info); +void output_off (struct ScreenInfo *screen_info, struct OutputInfo *output); +struct CrtcInfo* auto_find_crtc (struct ScreenInfo *screen_info, struct OutputInfo *output_info); + +XRRModeInfo *find_mode_by_xid (struct ScreenInfo *screen_info, RRMode mode_id); +int mode_height (XRRModeInfo *mode_info, Rotation rotation); +int mode_width (XRRModeInfo *mode_info, Rotation rotation); +int get_width_by_output_id (struct ScreenInfo *screen_info, RROutput output_id); +int get_height_by_output_id (struct ScreenInfo *screen_info, RROutput output_id); +char *get_output_name (struct ScreenInfo *screen_info, RROutput id); +Status crtc_apply (struct CrtcInfo *crtc_info); +Status crtc_disable (struct CrtcInfo *crtc); +int main_low_apply (struct ScreenInfo *screen_info); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/krandr/randr.cpp b/krandr/randr.cpp new file mode 100644 index 000000000..63c5c0450 --- /dev/null +++ b/krandr/randr.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2002,2003 Hamish Rodda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "randr.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ktimerdialog.h" + +#include +#define INT8 _X11INT8 +#define INT32 _X11INT32 +#include +#undef INT8 +#undef INT32 +#include + +class RandRScreenPrivate +{ +public: + RandRScreenPrivate() : config(0L) {}; + ~RandRScreenPrivate() + { + if (config) + XRRFreeScreenConfigInfo(config); + } + + XRRScreenConfiguration* config; +}; + +RandRScreen::RandRScreen(int screenIndex) + : d(new RandRScreenPrivate()) + , m_screen(screenIndex) + , m_shownDialog(NULL) +{ + loadSettings(); + setOriginal(); +} + +RandRScreen::~RandRScreen() +{ + delete d; +} + +void RandRScreen::loadSettings() +{ + if (d->config) + XRRFreeScreenConfigInfo(d->config); + + d->config = XRRGetScreenInfo(qt_xdisplay(), RootWindow(qt_xdisplay(), m_screen)); + Q_ASSERT(d->config); + + Rotation rotation; + m_currentSize = m_proposedSize = XRRConfigCurrentConfiguration(d->config, &rotation); + m_currentRotation = m_proposedRotation = rotation; + + m_pixelSizes.clear(); + m_mmSizes.clear(); + int numSizes; + XRRScreenSize* sizes = XRRSizes(qt_xdisplay(), m_screen, &numSizes); + for (int i = 0; i < numSizes; i++) { + m_pixelSizes.append(QSize(sizes[i].width, sizes[i].height)); + m_mmSizes.append(QSize(sizes[i].mwidth, sizes[i].mheight)); + } + + m_rotations = XRRRotations(qt_xdisplay(), m_screen, &rotation); + + m_currentRefreshRate = m_proposedRefreshRate = refreshRateHzToIndex(m_currentSize, XRRConfigCurrentRate(d->config)); +} + +void RandRScreen::setOriginal() +{ + m_originalSize = m_currentSize; + m_originalRotation = m_currentRotation; + m_originalRefreshRate = m_currentRefreshRate; +} + +bool RandRScreen::applyProposed() +{ + //kdDebug() << k_funcinfo << " size " << (SizeID)proposedSize() << ", rotation " << proposedRotation() << ", refresh " << refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) << endl; + + Status status; + + if (proposedRefreshRate() < 0) + status = XRRSetScreenConfig(qt_xdisplay(), d->config, DefaultRootWindow(qt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), CurrentTime); + else { + if( refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) <= 0 ) { + m_proposedRefreshRate = 0; + } + status = XRRSetScreenConfigAndRate(qt_xdisplay(), d->config, DefaultRootWindow(qt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), refreshRateIndexToHz(proposedSize(), proposedRefreshRate()), CurrentTime); + } + + //kdDebug() << "New size: " << WidthOfScreen(ScreenOfDisplay(QPaintDevice::x11AppDisplay(), screen)) << ", " << HeightOfScreen(ScreenOfDisplay(QPaintDevice::x11AppDisplay(), screen)) << endl; + + if (status == RRSetConfigSuccess) { + m_currentSize = m_proposedSize; + m_currentRotation = m_proposedRotation; + m_currentRefreshRate = m_proposedRefreshRate; + return true; + } + + return false; +} + +bool RandRScreen::applyProposedAndConfirm() +{ + if (proposedChanged()) { + setOriginal(); + + if (applyProposed()) { + if (!confirm()) { + proposeOriginal(); + applyProposed(); + return false; + } + } else { + return false; + } + } + + return true; +} + +bool RandRScreen::confirm() +{ + // uncomment the line below and edit out the KTimerDialog stuff to get + // a version which works on today's kdelibs (no accept dialog is presented) + + // FIXME remember to put the dialog on the right screen + + KTimerDialog acceptDialog ( 15000, KTimerDialog::CountDown, + KApplication::kApplication()->mainWidget(), + "mainKTimerDialog", + true, + i18n("Confirm Display Setting Change"), + KTimerDialog::Ok|KTimerDialog::Cancel, + KTimerDialog::Cancel); + + acceptDialog.setButtonOK(KGuiItem(i18n("&Accept Configuration"), "button_ok")); + acceptDialog.setButtonCancel(KGuiItem(i18n("&Return to Previous Configuration"), "button_cancel")); + + KActiveLabel *label = new KActiveLabel(i18n("Your screen orientation, size and refresh rate " + "have been changed to the requested settings. Please indicate whether you wish to " + "keep this configuration. In 15 seconds the display will revert to your previous " + "settings."), &acceptDialog, "userSpecifiedLabel"); + + acceptDialog.setMainWidget(label); + + KDialog::centerOnScreen(&acceptDialog, m_screen); + + m_shownDialog = &acceptDialog; + connect( m_shownDialog, SIGNAL( destroyed()), this, SLOT( shownDialogDestroyed())); + connect( kapp->desktop(), SIGNAL( resized(int)), this, SLOT( desktopResized())); + + return acceptDialog.exec(); +} + +void RandRScreen::shownDialogDestroyed() +{ + m_shownDialog = NULL; + disconnect( kapp->desktop(), SIGNAL( resized(int)), this, SLOT( desktopResized())); +} + +void RandRScreen::desktopResized() +{ + if( m_shownDialog != NULL ) + KDialog::centerOnScreen(m_shownDialog, m_screen); +} + +QString RandRScreen::changedMessage() const +{ + if (currentRefreshRate() == -1) + return i18n("New configuration:\nResolution: %1 x %2\nOrientation: %3") + .arg(currentPixelWidth()) + .arg(currentPixelHeight()) + .arg(currentRotationDescription()); + else + return i18n("New configuration:\nResolution: %1 x %2\nOrientation: %3\nRefresh rate: %4") + .arg(currentPixelWidth()) + .arg(currentPixelHeight()) + .arg(currentRotationDescription()) + .arg(currentRefreshRateDescription()); +} + +bool RandRScreen::changedFromOriginal() const +{ + return m_currentSize != m_originalSize || m_currentRotation != m_originalRotation || m_currentRefreshRate != m_originalRefreshRate; +} + +void RandRScreen::proposeOriginal() +{ + m_proposedSize = m_originalSize; + m_proposedRotation = m_originalRotation; + m_proposedRefreshRate = m_originalRefreshRate; +} + +bool RandRScreen::proposedChanged() const +{ + return m_currentSize != m_proposedSize || m_currentRotation != m_proposedRotation || m_currentRefreshRate != m_proposedRefreshRate; +} + +QString RandRScreen::rotationName(int rotation, bool pastTense, bool capitalised) +{ + if (!pastTense) + switch (rotation) { + case RR_Rotate_0: + return i18n("Normal"); + case RR_Rotate_90: + return i18n("Left (90 degrees)"); + case RR_Rotate_180: + return i18n("Upside-down (180 degrees)"); + case RR_Rotate_270: + return i18n("Right (270 degrees)"); + case RR_Reflect_X: + return i18n("Mirror horizontally"); + case RR_Reflect_Y: + return i18n("Mirror vertically"); + default: + return i18n("Unknown orientation"); + } + + switch (rotation) { + case RR_Rotate_0: + return i18n("Normal"); + case RR_Rotate_90: + return i18n("Rotated 90 degrees counterclockwise"); + case RR_Rotate_180: + return i18n("Rotated 180 degrees counterclockwise"); + case RR_Rotate_270: + return i18n("Rotated 270 degrees counterclockwise"); + default: + if (rotation & RR_Reflect_X) + if (rotation & RR_Reflect_Y) + if (capitalised) + return i18n("Mirrored horizontally and vertically"); + else + return i18n("mirrored horizontally and vertically"); + else + if (capitalised) + return i18n("Mirrored horizontally"); + else + return i18n("mirrored horizontally"); + else if (rotation & RR_Reflect_Y) + if (capitalised) + return i18n("Mirrored vertically"); + else + return i18n("mirrored vertically"); + else + if (capitalised) + return i18n("Unknown orientation"); + else + return i18n("unknown orientation"); + } +} + +QPixmap RandRScreen::rotationIcon(int rotation) const +{ + // Adjust icons for current screen orientation + if (!(m_currentRotation & RR_Rotate_0) && rotation & (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)) { + int currentAngle = m_currentRotation & (RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270); + switch (currentAngle) { + case RR_Rotate_90: + rotation <<= 3; + break; + case RR_Rotate_180: + rotation <<= 2; + break; + case RR_Rotate_270: + rotation <<= 1; + break; + } + + // Fix overflow + if (rotation > RR_Rotate_270) { + rotation >>= 4; + } + } + + switch (rotation) { + case RR_Rotate_0: + return SmallIcon("up"); + case RR_Rotate_90: + return SmallIcon("back"); + case RR_Rotate_180: + return SmallIcon("down"); + case RR_Rotate_270: + return SmallIcon("forward"); + case RR_Reflect_X: + case RR_Reflect_Y: + default: + return SmallIcon("stop"); + } +} + +QString RandRScreen::currentRotationDescription() const +{ + QString ret = rotationName(m_currentRotation & RotateMask); + + if (m_currentRotation != m_currentRotation & RotateMask) + if (m_currentRotation & RR_Rotate_0) + ret = rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X), true, true); + else + ret += ", " + rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X), true, false); + + return ret; +} + +int RandRScreen::rotationIndexToDegree(int rotation) const +{ + switch (rotation & RotateMask) { + case RR_Rotate_90: + return 90; + + case RR_Rotate_180: + return 180; + + case RR_Rotate_270: + return 270; + + default: + return 0; + } +} + +int RandRScreen::rotationDegreeToIndex(int degree) const +{ + switch (degree) { + case 90: + return RR_Rotate_90; + + case 180: + return RR_Rotate_180; + + case 270: + return RR_Rotate_270; + + default: + return RR_Rotate_0; + } +} + +int RandRScreen::currentPixelWidth() const +{ + return m_pixelSizes[m_currentSize].width(); +} + +int RandRScreen::currentPixelHeight() const +{ + return m_pixelSizes[m_currentSize].height(); +} + +int RandRScreen::currentMMWidth() const +{ + return m_pixelSizes[m_currentSize].width(); +} + +int RandRScreen::currentMMHeight() const +{ + return m_pixelSizes[m_currentSize].height(); +} + +QStringList RandRScreen::refreshRates(int size) const +{ + int nrates; + short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates); + + QStringList ret; + for (int i = 0; i < nrates; i++) + ret << refreshRateDirectDescription(rates[i]); + + return ret; +} + +QString RandRScreen::refreshRateDirectDescription(int rate) const +{ + return i18n("Refresh rate in Hertz (Hz)", "%1 Hz").arg(rate); +} + +QString RandRScreen::refreshRateIndirectDescription(int size, int index) const +{ + return i18n("Refresh rate in Hertz (Hz)", "%1 Hz").arg(refreshRateIndexToHz(size, index)); +} + +QString RandRScreen::refreshRateDescription(int size, int index) const +{ + return refreshRates(size)[index]; +} + +bool RandRScreen::proposeRefreshRate(int index) +{ + if (index >= 0 && (int)refreshRates(proposedSize()).count() > index) { + m_proposedRefreshRate = index; + return true; + } + + return false; +} + +int RandRScreen::currentRefreshRate() const +{ + return m_currentRefreshRate; +} + +QString RandRScreen::currentRefreshRateDescription() const +{ + return refreshRateIndirectDescription(m_currentSize, m_currentRefreshRate); +} + +int RandRScreen::proposedRefreshRate() const +{ + return m_proposedRefreshRate; +} + +int RandRScreen::refreshRateHzToIndex(int size, int hz) const +{ + int nrates; + short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates); + + for (int i = 0; i < nrates; i++) + if (hz == rates[i]) + return i; + + if (nrates != 0) + // Wrong input Hz! + Q_ASSERT(false); + + return -1; +} + +int RandRScreen::refreshRateIndexToHz(int size, int index) const +{ + int nrates; + short* rates = XRRRates(qt_xdisplay(), m_screen, (SizeID)size, &nrates); + + if (nrates == 0 || index < 0) + return 0; + + // Wrong input Hz! + if(index >= nrates) + return 0; + + return rates[index]; +} + +int RandRScreen::numSizes() const +{ + return m_pixelSizes.count(); +} + +const QSize& RandRScreen::pixelSize(int index) const +{ + return m_pixelSizes[index]; +} + +const QSize& RandRScreen::mmSize(int index) const +{ + return m_mmSizes[index]; +} + +int RandRScreen::sizeIndex(QSize pixelSize) const +{ + for (uint i = 0; i < m_pixelSizes.count(); i++) + if (m_pixelSizes[i] == pixelSize) + return i; + + return -1; +} + +int RandRScreen::rotations() const +{ + return m_rotations; +} + +int RandRScreen::currentRotation() const +{ + return m_currentRotation; +} + +int RandRScreen::currentSize() const +{ + return m_currentSize; +} + +int RandRScreen::proposedRotation() const +{ + return m_proposedRotation; +} + +void RandRScreen::proposeRotation(int newRotation) +{ + m_proposedRotation = newRotation & OrientationMask; +} + +int RandRScreen::proposedSize() const +{ + return m_proposedSize; +} + +bool RandRScreen::proposeSize(int newSize) +{ + if ((int)m_pixelSizes.count() > newSize) { + m_proposedSize = newSize; + return true; + } + + return false; +} + +void RandRScreen::load(KConfig& config) +{ + config.setGroup(QString("Screen%1").arg(m_screen)); + + if (proposeSize(sizeIndex(QSize(config.readNumEntry("width", currentPixelWidth()), config.readNumEntry("height", currentPixelHeight()))))) + proposeRefreshRate(refreshRateHzToIndex(proposedSize(), config.readNumEntry("refresh", currentRefreshRate()))); + + proposeRotation(rotationDegreeToIndex(config.readNumEntry("rotation", 0)) + (config.readBoolEntry("reflectX") ? ReflectX : 0) + (config.readBoolEntry("reflectY") ? ReflectY : 0)); +} + +void RandRScreen::save(KConfig& config) const +{ + config.setGroup(QString("Screen%1").arg(m_screen)); + config.writeEntry("width", currentPixelWidth()); + config.writeEntry("height", currentPixelHeight()); + config.writeEntry("refresh", refreshRateIndexToHz(currentSize(), currentRefreshRate())); + config.writeEntry("rotation", rotationIndexToDegree(currentRotation())); + config.writeEntry("reflectX", (bool)(currentRotation() & ReflectMask) == ReflectX); + config.writeEntry("reflectY", (bool)(currentRotation() & ReflectMask) == ReflectY); +} + +RandRDisplay::RandRDisplay() + : m_valid(true) +{ + // Check extension + Status s = XRRQueryExtension(qt_xdisplay(), &m_eventBase, &m_errorBase); + if (!s) { + m_errorCode = QString("%1, base %1").arg(s).arg(m_errorBase); + m_valid = false; + return; + } + + int major_version, minor_version; + XRRQueryVersion(qt_xdisplay(), &major_version, &minor_version); + + m_version = QString("X Resize and Rotate extension version %1.%1").arg(major_version).arg(minor_version); + + m_numScreens = ScreenCount(qt_xdisplay()); + + // This assumption is WRONG with Xinerama + // Q_ASSERT(QApplication::desktop()->numScreens() == ScreenCount(qt_xdisplay())); + + m_screens.setAutoDelete(true); + for (int i = 0; i < m_numScreens; i++) { + m_screens.append(new RandRScreen(i)); + } + + setCurrentScreen(QApplication::desktop()->primaryScreen()); +} + +bool RandRDisplay::isValid() const +{ + return m_valid; +} + +const QString& RandRDisplay::errorCode() const +{ + return m_errorCode; +} + +int RandRDisplay::eventBase() const +{ + return m_eventBase; +} + +int RandRDisplay::screenChangeNotifyEvent() const +{ + return m_eventBase + RRScreenChangeNotify; +} + +int RandRDisplay::errorBase() const +{ + return m_errorBase; +} + +const QString& RandRDisplay::version() const +{ + return m_version; +} + +void RandRDisplay::setCurrentScreen(int index) +{ + m_currentScreenIndex = index; + m_currentScreen = m_screens.at(m_currentScreenIndex); + Q_ASSERT(m_currentScreen); +} + +int RandRDisplay::screenIndexOfWidget(QWidget* widget) +{ + int ret = QApplication::desktop()->screenNumber(widget); + return ret != -1 ? ret : QApplication::desktop()->primaryScreen(); +} + +int RandRDisplay::currentScreenIndex() const +{ + return m_currentScreenIndex; +} + +void RandRDisplay::refresh() +{ + for (RandRScreen* s = m_screens.first(); s; s = m_screens.next()) + s->loadSettings(); +} + +int RandRDisplay::numScreens() const +{ + return m_numScreens; +} + +RandRScreen* RandRDisplay::screen(int index) +{ + return m_screens.at(index); +} + +RandRScreen* RandRDisplay::currentScreen() +{ + return m_currentScreen; +} + +bool RandRDisplay::loadDisplay(KConfig& config, bool loadScreens) +{ + if (loadScreens) + for (RandRScreen* s = m_screens.first(); s; s = m_screens.next()) + s->load(config); + + return applyOnStartup(config); +} + +bool RandRDisplay::applyOnStartup(KConfig& config) +{ + config.setGroup("Display"); + return config.readBoolEntry("ApplyOnStartup", false); +} + +bool RandRDisplay::syncTrayApp(KConfig& config) +{ + config.setGroup("Display"); + return config.readBoolEntry("SyncTrayApp", false); +} + +void RandRDisplay::saveDisplay(KConfig& config, bool applyOnStartup, bool syncTrayApp) +{ + Q_ASSERT(!config.isReadOnly()); + + config.setGroup("Display"); + config.writeEntry("ApplyOnStartup", applyOnStartup); + config.writeEntry("SyncTrayApp", syncTrayApp); + + for (RandRScreen* s = m_screens.first(); s; s = m_screens.next()) + s->save(config); +} + +void RandRDisplay::applyProposed(bool confirm) +{ + for (int screenIndex = 0; screenIndex < numScreens(); screenIndex++) { + if (screen(screenIndex)->proposedChanged()) { + if (confirm) + screen(screenIndex)->applyProposedAndConfirm(); + else + screen(screenIndex)->applyProposed(); + } + } +} + +int RandRScreen::pixelCount( int index ) const +{ + QSize sz = pixelSize(index); + return sz.width() * sz.height(); +} + +#include "randr.moc" diff --git a/krandr/randr.h b/krandr/randr.h new file mode 100644 index 000000000..c7eb240cf --- /dev/null +++ b/krandr/randr.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2002,2003 Hamish Rodda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __RANDR_H__ +#define __RANDR_H__ + +#include +#include +#include + +#include +#include + +class KTimerDialog; +class RandRScreenPrivate; + +class RandRScreen : public QObject +{ + Q_OBJECT + +public: + enum orientations { + Rotate0 = 0x1, + Rotate90 = 0x2, + Rotate180 = 0x4, + Rotate270 = 0x8, + RotateMask = 15, + RotationCount = 4, + ReflectX = 0x10, + ReflectY = 0x20, + ReflectMask = 48, + OrientationMask = 63, + OrientationCount = 6 + }; + + RandRScreen(int screenIndex); + ~RandRScreen(); + + void loadSettings(); + void setOriginal(); + + bool applyProposed(); + + /** + * @returns false if the user did not confirm in time, or cancelled, or the change failed + */ + bool applyProposedAndConfirm(); + +public slots: + bool confirm(); + +public: + QString changedMessage() const; + + bool changedFromOriginal() const; + void proposeOriginal(); + + bool proposedChanged() const; + + static QString rotationName(int rotation, bool pastTense = false, bool capitalised = true); + QPixmap rotationIcon(int rotation) const; + QString currentRotationDescription() const; + + int rotationIndexToDegree(int rotation) const; + int rotationDegreeToIndex(int degree) const; + + /** + * Refresh rate functions. + */ + QStringList refreshRates(int size) const; + + QString refreshRateDirectDescription(int rate) const; + QString refreshRateIndirectDescription(int size, int index) const; + QString refreshRateDescription(int size, int index) const; + + int currentRefreshRate() const; + QString currentRefreshRateDescription() const; + + // Refresh rate hz <==> index conversion + int refreshRateHzToIndex(int size, int hz) const; + int refreshRateIndexToHz(int size, int index) const; + + /** + * Screen size functions. + */ + int numSizes() const; + const QSize& pixelSize(int index) const; + const QSize& mmSize(int index) const; + int pixelCount(int index) const; + + /** + * Retrieve the index of a screen size with a specified pixel size. + * + * @param pixelSize dimensions of the screen in pixels + * @returns the index of the requested screen size + */ + int sizeIndex(QSize pixelSize) const; + + int rotations() const; + + /** + * Current setting functions. + */ + int currentPixelWidth() const; + int currentPixelHeight() const; + int currentMMWidth() const; + int currentMMHeight() const; + + int currentRotation() const; + int currentSize() const; + + /** + * Proposed setting functions. + */ + int proposedSize() const; + bool proposeSize(int newSize); + + int proposedRotation() const; + void proposeRotation(int newRotation); + + int proposedRefreshRate() const; + /** + * Propose a refresh rate. + * Please note that you must propose the target size first for this to work. + * + * @param index the index of the refresh rate (not a refresh rate in hz!) + * @returns true if successful, false otherwise. + */ + bool proposeRefreshRate(int index); + + /** + * Configuration functions. + */ + void load(KConfig& config); + void save(KConfig& config) const; + +private: + RandRScreenPrivate* d; + + int m_screen; + + QValueList m_pixelSizes; + QValueList m_mmSizes; + int m_rotations; + + int m_originalRotation; + int m_originalSize; + int m_originalRefreshRate; + + int m_currentRotation; + int m_currentSize; + int m_currentRefreshRate; + + int m_proposedRotation; + int m_proposedSize; + int m_proposedRefreshRate; + + KTimerDialog* m_shownDialog; + +private slots: + void desktopResized(); + void shownDialogDestroyed(); +}; + +typedef QPtrList ScreenList; + +class RandRDisplay +{ +public: + RandRDisplay(); + + bool isValid() const; + const QString& errorCode() const; + const QString& version() const; + + int eventBase() const; + int screenChangeNotifyEvent() const; + int errorBase() const; + + int screenIndexOfWidget(QWidget* widget); + + int numScreens() const; + RandRScreen* screen(int index); + + void setCurrentScreen(int index); + int currentScreenIndex() const; + RandRScreen* currentScreen(); + + void refresh(); + + /** + * Loads saved settings. + * + * @param config the KConfig object to load from + * @param loadScreens whether to call RandRScreen::load() for each screen + * @retuns true if the settings should be applied on KDE startup. + */ + bool loadDisplay(KConfig& config, bool loadScreens = true); + void saveDisplay(KConfig& config, bool applyOnStartup, bool syncTrayApp); + + static bool applyOnStartup(KConfig& config); + static bool syncTrayApp(KConfig& config); + + void applyProposed(bool confirm = true); + +private: + int m_numScreens; + int m_currentScreenIndex; + RandRScreen* m_currentScreen; + ScreenList m_screens; + + bool m_valid; + QString m_errorCode; + QString m_version; + + int m_eventBase; + int m_errorBase; +}; + +#endif -- cgit v1.2.1