diff options
Diffstat (limited to 'tderandr')
-rw-r--r-- | tderandr/CMakeLists.txt | 48 | ||||
-rw-r--r-- | tderandr/Mainpage.dox | 18 | ||||
-rw-r--r-- | tderandr/Makefile.am | 19 | ||||
-rw-r--r-- | tderandr/configure.in.in | 21 | ||||
-rw-r--r-- | tderandr/ktimerdialog.cpp | 205 | ||||
-rw-r--r-- | tderandr/ktimerdialog.h | 170 | ||||
-rw-r--r-- | tderandr/libtderandr.cc | 1634 | ||||
-rw-r--r-- | tderandr/libtderandr.h | 359 | ||||
-rw-r--r-- | tderandr/lowlevel_randr.c | 680 | ||||
-rw-r--r-- | tderandr/lowlevel_randr.h | 103 | ||||
-rw-r--r-- | tderandr/randr.cpp | 881 | ||||
-rw-r--r-- | tderandr/randr.h | 302 |
12 files changed, 4440 insertions, 0 deletions
diff --git a/tderandr/CMakeLists.txt b/tderandr/CMakeLists.txt new file mode 100644 index 000000000..efcd8519d --- /dev/null +++ b/tderandr/CMakeLists.txt @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2010 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${TQT_INCLUDE_DIRS} + ${XRANDR_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/dcop + ${CMAKE_SOURCE_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/tdeui +) + +link_directories( + ${TQT_LIBRARY_DIRS} + ${XRANDR_LIBRARY_DIRS} +) + + +##### headers ################################### + +install( FILES + randr.h lowlevel_randr.h ktimerdialog.h libtderandr.h + DESTINATION ${INCLUDE_INSTALL_DIR}/libtderandr ) + + +##### tderandr #################################### + +set( target tderandr ) + +set( ${target}_SRCS + randr.cpp lowlevel_randr.c ktimerdialog.cpp libtderandr.cc +) + +tde_add_library( ${target} SHARED AUTOMOC + SOURCES ${${target}_SRCS} + VERSION 0.0.95 + LINK tdeui-shared ${XRANDR_LIBRARIES} Xext + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/tderandr/Mainpage.dox b/tderandr/Mainpage.dox new file mode 100644 index 000000000..c173d52aa --- /dev/null +++ b/tderandr/Mainpage.dox @@ -0,0 +1,18 @@ +/** \mainpage XRandr Interface for Trinity Applications + +The \b KRandr library adds the ability for %Trinity applications to easily configure an Xorg display via XRandr. + +@authors +Timothy Pearson \<kb9vqf@pearsoncomputing.net\> + +@maintainers +Timothy Pearson \<kb9vqf@pearsoncomputing.net\> + +@licenses +@gpl + +*/ + +// DOXYGEN_REFERENCES = tdecore tdeui +// DOXYGEN_SET_PROJECT_NAME = KRandr +// vim:ts=4:sw=4:expandtab:filetype=doxygen diff --git a/tderandr/Makefile.am b/tderandr/Makefile.am new file mode 100644 index 000000000..7818014df --- /dev/null +++ b/tderandr/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) + +libtderandrincludedir = $(includedir)/libtderandr +libtderandrinclude_HEADERS = randr.h lowlevel_randr.h ktimerdialog.h libtderandr.h + +lib_LTLIBRARIES = libtderandr.la +libtderandr_la_SOURCES = randr.cpp lowlevel_randr.c ktimerdialog.cpp libtderandr.cc +METASOURCES = AUTO + +libtderandr_la_LDFLAGS = $(KDE_MT_LDFLAGS) -version-info 0:95 -no-undefined +libtderandr_la_LIBADD = $(LIBASOUND) ../tdecore/libtdecore.la $(LIB_QT) $(LIB_XRANDR) + +DOXYGEN_REFERENCES = tdecore +include ../admin/Doxyfile.am diff --git a/tderandr/configure.in.in b/tderandr/configure.in.in new file mode 100644 index 000000000..3dd4a9319 --- /dev/null +++ b/tderandr/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 <X11/Xlib.h>]) +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 libtderandr" +fi diff --git a/tderandr/ktimerdialog.cpp b/tderandr/ktimerdialog.cpp new file mode 100644 index 000000000..0be416be7 --- /dev/null +++ b/tderandr/ktimerdialog.cpp @@ -0,0 +1,205 @@ +/* + * This file is part of the KDE Libraries + * Copyright (C) 2002 Hamish Rodda <rodda@kde.org> + * + * 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 <tqhbox.h> +#include <tqlayout.h> +#include <tqvbox.h> +#include <tqtimer.h> +#include <tqprogressbar.h> +#include <tqlabel.h> + +#include <twin.h> +#include <kiconloader.h> + +#include <klocale.h> +#include <kdebug.h> + +#include "ktimerdialog.h" +#include "ktimerdialog.moc" + +KTimerDialog::KTimerDialog( int msec, TimerStyle style, TQWidget *parent, + const char *name, bool modal, + const TQString &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 TQTimer( this ); + updateTimer = new TQTimer( 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, TQT_SIGNAL( timeout() ), TQT_SLOT( slotInternalTimeout() ) ); + connect( updateTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotUpdateTime() ) ); + + // create the widgets + mainWidget = new TQVBox( this, "mainWidget" ); + timerWidget = new TQHBox( mainWidget, "timerWidget" ); + timerLabel = new TQLabel( timerWidget ); + timerProgress = new TQProgressBar( 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( TQWidget *widget ) +{ + // yuck, here goes. + TQVBox *newWidget = new TQVBox( this ); + + if ( widget->parentWidget() != mainWidget ) { + widget->reparent( newWidget, 0, TQPoint(0,0) ); + } else { + newWidget->insertChild( TQT_TQOBJECT(widget) ); + } + + timerWidget->reparent( newWidget, 0, TQPoint(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/tderandr/ktimerdialog.h b/tderandr/ktimerdialog.h new file mode 100644 index 000000000..965a72393 --- /dev/null +++ b/tderandr/ktimerdialog.h @@ -0,0 +1,170 @@ +/* + * This file is part of the KDE Libraries + * Copyright (C) 2002 Hamish Rodda <rodda@kde.org> + * + * 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 <kdialogbase.h> + +class TQTimer; +class TQHBox; +class TQProgressBar; +class TQLabel; + +/** + * 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 <rodda@kde.org> + */ +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, TQWidget *parent=0, + const char *name=0, bool modal=true, + const TQString &caption=TQString::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 TQDialog . + */ + 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( TQWidget *widget ); + + signals: + /** + * Signal which is emitted once the timer has timed out. + */ + void timerTimeout(); + + public slots: + /** + * Execute the dialog modally - see @see TQDialog . + */ + 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(); + + TQTimer *totalTimer; + TQTimer *updateTimer; + int msecRemaining, updateInterval, msecTotal; + + ButtonCode buttonOnTimeout; + TimerStyle tStyle; + + TQHBox *timerWidget; + TQProgressBar *timerProgress; + TQLabel *timerLabel; + TQVBox *mainWidget; + + class KTimerDialogPrivate; + KTimerDialogPrivate *d; +}; + +#endif + + + diff --git a/tderandr/libtderandr.cc b/tderandr/libtderandr.cc new file mode 100644 index 000000000..65005f91e --- /dev/null +++ b/tderandr/libtderandr.cc @@ -0,0 +1,1634 @@ +/* libtderandr.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 + LibKRandr's homepage : http://www.trinitydesktop.org + + 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 <kb9vqf@pearsoncomputing.net> + +***************************************************************************/ + +#include <tqdir.h> +#include <tqtimer.h> +#include <tqstringlist.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kapplication.h> + +#include <stdlib.h> +#include <unistd.h> +#include <cmath> + +#include "libtderandr.h" + +#include <X11/extensions/dpms.h> + +// FIXME +// For now, just use the standalone xrandr program to apply the display settings +#define USE_XRANDR_PROGRAM + +// This routine is courtsey of an answer on "Stack Overflow" +// It takes an LSB-first int and makes it an MSB-first int (or vice versa) +unsigned int reverse_bits(register unsigned int x) +{ + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + return((x >> 16) | (x << 16)); +} + +// This routine returns the output of an arbitrary Bash command +TQString exec(const char * cmd) { + TQString bashcommand = cmd; + bashcommand = bashcommand.replace("\"", "\\\""); + bashcommand = TQString("/bin/bash -c \"%1\" 2>&1").arg(bashcommand); + FILE* pipe = popen(bashcommand.ascii(), "r"); + if (!pipe) return "ERROR"; + char buffer[128]; + TQString result = ""; + while(!feof(pipe)) { + if(fgets(buffer, 128, pipe) != NULL) { + result += buffer; + } + } + pclose(pipe); + result.remove(result.length(), 1); + return result; +} + +TQString capitalizeString(TQString in) { + return in.left(1).upper() + in.right(in.length()-1); +} + +TQString KRandrSimpleAPI::getIccFileName(TQString profileName, TQString screenName, TQString kde_confdir) { + KSimpleConfig *t_config = NULL; + KSimpleConfig *t_systemconfig = NULL; + int t_numberOfProfiles; + TQStringList t_cfgProfiles; + TQString retval; + + if (profileName != NULL) { + t_config = new KSimpleConfig( TQString::fromLatin1( "kiccconfigrc" )); + } + else { + t_systemconfig = new KSimpleConfig( kde_confdir + TQString("/kicc/kiccconfigrc") ); + } + + if (profileName != NULL) { + t_config->setGroup(NULL); + if (t_config->readBoolEntry("EnableICC", false) == true) { + t_config->setGroup(profileName); + retval = t_config->readEntry(screenName); + } + else { + retval = ""; + } + } + else { + t_systemconfig->setGroup(NULL); + if (t_systemconfig->readBoolEntry("EnableICC", false) == true) { + retval = t_systemconfig->readEntry("ICCFile"); + } + else { + retval = ""; + } + } + + if (profileName != "") { + if (t_config) { + delete t_config; + } + } + else { + if (t_systemconfig) { + delete t_systemconfig; + } + } + + return retval; +} + +TQString KRandrSimpleAPI::applyIccFile(TQString screenName, TQString fileName) { + int i; + int j; + Display *randr_display; + ScreenInfo *randr_screen_info; + XRROutputInfo *output_info; + + int screenNumber = 0; + + if (fileName != "") { + // FIXME + // This should use the RRSetCrtcGamma function when available + // That is the only way to get proper setting when two output are active at the same time + // (otherwise in clone mode only one screen is available) + + // HACK + // For now, simply exit with no changes if screenName is not an active output + + if (isValid() == true) { + screenNumber = -1; + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + if (randr_screen_info == NULL) { + return ""; + } + j=0; + for (i = 0; i < randr_screen_info->n_output; i++) { + output_info = randr_screen_info->outputs[i]->info; + // Look for ON outputs... + if (!randr_screen_info->outputs[i]->cur_crtc) { + continue; + } + // ...that are connected + if (RR_Disconnected == randr_screen_info->outputs[i]->info->connection) { + continue; + } + if (output_info->name == screenName) { + screenNumber = j; + } + j++; + } + freeScreenInfoStructure(randr_screen_info); + } + + if (screenNumber >= 0) { + // Apply ICC settings with XCalib + TQString icc_command; + FILE *pipe_xcalib; + char xcalib_result[2048]; + int i; + xcalib_result[0]=0; + + icc_command = TQString("xcalib \"%1\"").arg(fileName); + if ((pipe_xcalib = popen(icc_command.ascii(), "r")) == NULL) + { + printf("Xcalib pipe error\n\r [xcalib apply]"); + } + else { + if (fgets(xcalib_result, 2048, pipe_xcalib)) { + pclose(pipe_xcalib); + for (i=1;i<2048;i++) { + if (xcalib_result[i] == 0) { + xcalib_result[i-1]=0; + i=2048; + } + } + if (strlen(xcalib_result) > 2) { + return xcalib_result; + } + } + else { + return ""; + } + } + } + } + else { + // Reset ICC profile on this screen + + // FIXME + // This should use the RRSetCrtcGamma function when available + // That is the only way to get proper setting when two output are active at the same time + // (otherwise in clone mode only one screen is available) + + // HACK + // For now, simply exit with no changes if screenName is not an active output + + if (isValid() == true) { + screenNumber = -1; + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + if (randr_screen_info == NULL) { + return ""; + } + j=0; + for (i = 0; i < randr_screen_info->n_output; i++) { + output_info = randr_screen_info->outputs[i]->info; + // Look for ON outputs... + if (!randr_screen_info->outputs[i]->cur_crtc) { + continue; + } + // ...that are connected + if (RR_Disconnected == randr_screen_info->outputs[i]->info->connection) { + continue; + } + if (output_info->name == screenName) { + screenNumber = j; + } + j++; + } + freeScreenInfoStructure(randr_screen_info); + } + + if (screenNumber >= 0) { + // Apply ICC settings with XCalib + TQString icc_command; + FILE *pipe_xcalib; + char xcalib_result[2048]; + int i; + xcalib_result[0]=0; + + icc_command = TQString("xcalib -c"); + if ((pipe_xcalib = popen(icc_command.ascii(), "r")) == NULL) + { + printf("Xcalib pipe error\n\r [xcalib clear]"); + } + else { + if (fgets(xcalib_result, 2048, pipe_xcalib)) { + pclose(pipe_xcalib); + for (i=1;i<2048;i++) { + if (xcalib_result[i] == 0) { + xcalib_result[i-1]=0; + i=2048; + } + } + if (strlen(xcalib_result) > 2) { + return xcalib_result; + } + } + else { + return ""; + } + } + } + } + return ""; +} + +TQString KRandrSimpleAPI::applyIccConfiguration(TQString profileName, TQString kde_confdir) { + int i; + Display *randr_display; + ScreenInfo *randr_screen_info; + XRROutputInfo *output_info; + KSimpleConfig *t_config; + + int screenNumber = 0; + TQString errorstr = ""; + + t_config = new KSimpleConfig( TQString::fromLatin1( "kiccconfigrc" )); + + // Find all screens + if (isValid() == true) { + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + if (randr_screen_info == NULL) { + return ""; + } + for (i = 0; i < randr_screen_info->n_output; i++) { + output_info = randr_screen_info->outputs[i]->info; + errorstr = applyIccFile(output_info->name, getIccFileName(profileName, output_info->name, kde_confdir)); + if (errorstr != "") { + return errorstr; + } + } + freeScreenInfoStructure(randr_screen_info); + } + else { + return applyIccFile(getIccFileName(profileName, "Default", kde_confdir), "Default"); + } + + t_config->writeEntry("CurrentProfile", profileName); + t_config->sync(); + delete t_config; + + return ""; +} + +TQString KRandrSimpleAPI::getEDIDMonitorName(int card, TQString displayname) { + TQString edid; + TQByteArray binaryedid = getEDID(card, displayname); + if (binaryedid.isNull()) + return TQString(); + + // Get the manufacturer ID + unsigned char letter_1 = ((binaryedid[8]>>2) & 0x1F) + 0x40; + unsigned char letter_2 = (((binaryedid[8] & 0x03) << 3) | ((binaryedid[9]>>5) & 0x07)) + 0x40; + unsigned char letter_3 = (binaryedid[9] & 0x1F) + 0x40; + TQChar qletter_1 = TQChar(letter_1); + TQChar qletter_2 = TQChar(letter_2); + TQChar qletter_3 = TQChar(letter_3); + TQString manufacturer_id = TQString("%1%2%3").arg(qletter_1).arg(qletter_2).arg(qletter_3); + + // Get the model ID + unsigned int raw_model_id = (((binaryedid[10] << 8) | binaryedid[11]) << 16) & 0xFFFF0000; + // Reverse the bit order + unsigned int model_id = reverse_bits(raw_model_id); + + // Try to get the model name + bool has_friendly_name = false; + unsigned char descriptor_block[18]; + int i; + for (i=72;i<90;i++) { + descriptor_block[i-72] = binaryedid[i] & 0xFF; + } + if ((descriptor_block[0] != 0) || (descriptor_block[1] != 0) || (descriptor_block[3] != 0xFC)) { + for (i=90;i<108;i++) { + descriptor_block[i-90] = binaryedid[i] & 0xFF; + } + if ((descriptor_block[0] != 0) || (descriptor_block[1] != 0) || (descriptor_block[3] != 0xFC)) { + for (i=108;i<126;i++) { + descriptor_block[i-108] = binaryedid[i] & 0xFF; + } + } + } + + TQString monitor_name; + if ((descriptor_block[0] == 0) && (descriptor_block[1] == 0) && (descriptor_block[3] == 0xFC)) { + char* pos = strchr((char *)(descriptor_block+5), '\n'); + if (pos) { + *pos = 0; + has_friendly_name = true; + monitor_name = TQString((char *)(descriptor_block+5)); + } + else { + has_friendly_name = false; + } + } + + // [FIXME] + // Look up manudacturer names if possible! + + if (has_friendly_name) + edid = TQString("%1 %2").arg(manufacturer_id).arg(monitor_name); + else + edid = TQString("%1 0x%2").arg(manufacturer_id).arg(model_id, 0, 16); + + return edid; +} + +TQByteArray KRandrSimpleAPI::getEDID(int card, TQString displayname) { + TQFile file(TQString("/sys/class/drm/card%1-%2/edid").arg(card).arg(displayname)); + if (!file.open (IO_ReadOnly)) + return TQByteArray(); + TQByteArray binaryedid = file.readAll(); + file.close(); + return binaryedid; +} + +TQString KRandrSimpleAPI::getCurrentProfile () { + TQString profileName; + KSimpleConfig *t_config; + + t_config = new KSimpleConfig( TQString::fromLatin1( "kiccconfigrc" )); + profileName = t_config->readEntry("CurrentProfile"); + delete t_config; + return profileName; +} + +TQString KRandrSimpleAPI::applySystemWideIccConfiguration(TQString kde_confdir) { + // Apply ICC settings with XCalib + TQString icc_command; + FILE *pipe_xcalib; + char xcalib_result[2048]; + int i; + xcalib_result[0]=0; + + icc_command = TQString("xcalib \"%1\"").arg(getIccFileName(NULL, "Default", kde_confdir)); + if ((pipe_xcalib = popen(icc_command.ascii(), "r")) == NULL) + { + printf("Xcalib pipe error [xcalib apply]\n\r"); + } + else { + if (fgets(xcalib_result, 2048, pipe_xcalib)) { + pclose(pipe_xcalib); + for (i=1;i<2048;i++) { + if (xcalib_result[i] == 0) { + xcalib_result[i-1]=0; + i=2048; + } + } + if (strlen(xcalib_result) > 2) { + return xcalib_result; + } + } + else { + return ""; + } + } + return ""; +} + +TQStringList KRandrSimpleAPI::getDisplayConfigurationProfiles(TQString kde_confdir) { + TQStringList ret; + + TQDir d(kde_confdir + "/displayconfig/"); + d.setFilter(TQDir::Files); + d.setSorting(TQDir::Name); + + const TQFileInfoList *list = d.entryInfoList(); + if (list) { + TQFileInfoListIterator it(*list); + TQFileInfo *fi; + + while ((fi = it.current()) != 0) { + if (fi->fileName() != "default") { + ret.append(fi->fileName()); + } + ++it; + } + } + + return ret; +} + +bool KRandrSimpleAPI::deleteDisplayConfiguration(TQString profilename, TQString kde_confdir) { + TQString fileName = kde_confdir + "/displayconfig/"; + fileName.append(profilename); + return (!unlink(fileName.ascii())); +} + +bool KRandrSimpleAPI::renameDisplayConfiguration(TQString profilename, TQString newprofilename, TQString kde_confdir) { + TQString fileName = kde_confdir + "/displayconfig/"; + TQString newFileName = fileName; + fileName.append(profilename); + newFileName.append(newprofilename); + TQDir d(kde_confdir + "/displayconfig/"); + return (d.rename(fileName, newFileName)); +} + +void KRandrSimpleAPI::saveDisplayConfiguration(bool enable, bool applyonstart, TQString profilename, TQString defaultprofilename, TQString kde_confdir, TQPtrList<SingleScreenData> screenInfoArray) { + int i; + + TQString filename; + + filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + display_config->setGroup("General"); + display_config->writeEntry("EnableDisplayControl", enable); + display_config->writeEntry("ApplySettingsOnStart", applyonstart); + display_config->writeEntry("StartupProfileName", defaultprofilename); + display_config->sync(); + delete display_config; + + filename = profilename; + if (filename == "") { + filename = "default"; + } + filename.prepend(kde_confdir.append("/displayconfig/")); + + display_config = new KSimpleConfig( filename ); + + i=0; + SingleScreenData *screendata; + for ( screendata=screenInfoArray.first(); screendata; screendata=screenInfoArray.next() ) { + display_config->setGroup(TQString("SCREEN %1").arg(i)); + display_config->writeEntry("ScreenUniqueName", screendata->screenUniqueName); + display_config->writeEntry("ScreenFriendlyName", screendata->screenFriendlyName); + display_config->writeEntry("GenericScreenDetected", screendata->generic_screen_detected); + display_config->writeEntry("ScreenConnected", screendata->screen_connected); + display_config->writeEntry("Resolutions", screendata->resolutions); + display_config->writeEntry("RefreshRates", screendata->refresh_rates); + display_config->writeEntry("ColorDepths", screendata->color_depths); + display_config->writeEntry("AvailableRotations", screendata->rotations); + display_config->writeEntry("CurrentResolution", screendata->current_resolution_index); + display_config->writeEntry("CurrentRefreshRate", screendata->current_refresh_rate_index); + display_config->writeEntry("CurrentColorDepth", screendata->current_color_depth_index); + display_config->writeEntry("CurrentRotation", screendata->current_rotation_index); + display_config->writeEntry("CurrentOrientiation", screendata->current_orientation_mask); + display_config->writeEntry("GammaRed", screendata->gamma_red); + display_config->writeEntry("GammaGreen", screendata->gamma_green); + display_config->writeEntry("GammaBlue", screendata->gamma_blue); + display_config->writeEntry("CurrentXFlip", screendata->has_x_flip); + display_config->writeEntry("CurrentYFlip", screendata->has_y_flip); + display_config->writeEntry("SupportsTransformation", screendata->supports_transformations); + display_config->writeEntry("IsPrimary", screendata->is_primary); + display_config->writeEntry("IsExtended", screendata->is_extended); + display_config->writeEntry("AbsXPos", screendata->absolute_x_position); + display_config->writeEntry("AbsYPos", screendata->absolute_y_position); + display_config->writeEntry("CurrentXPixelCount", screendata->current_x_pixel_count); + display_config->writeEntry("CurrentYPixelCount", screendata->current_y_pixel_count); + display_config->writeEntry("HasDPMS", screendata->has_dpms); + display_config->writeEntry("EnableDPMS", screendata->enable_dpms); + display_config->writeEntry("DPMSStandbyDelay", screendata->dpms_standby_delay); + display_config->writeEntry("DPMSSuspendDelay", screendata->dpms_suspend_delay); + display_config->writeEntry("DPMSPowerDownDelay", screendata->dpms_off_delay); + i++; + } + + display_config->sync(); + delete display_config; +} + +TQPoint KRandrSimpleAPI::applyStartupDisplayConfiguration(TQString kde_confdir) { + bool applyonstart = getDisplayConfigurationStartupAutoApplyEnabled(kde_confdir); + if (applyonstart) { + TQString profilename = getDisplayConfigurationStartupAutoApplyName(kde_confdir); + return applyDisplayConfiguration(profilename, kde_confdir); + } + else { + return TQPoint(); + } +} + +TQPoint KRandrSimpleAPI::applyDisplayConfiguration(TQString profilename, TQString kde_confdir) { + TQPoint ret; + + bool enabled = getDisplayConfigurationEnabled(kde_confdir); + if (profilename == "") { + profilename = "default"; + } + + if (enabled) { + TQPtrList<SingleScreenData> screenInfoArray; + screenInfoArray = loadDisplayConfiguration(profilename, kde_confdir); + if (screenInfoArray.count() > 0) { + applyDisplayConfiguration(screenInfoArray, FALSE, kde_confdir); + } + destroyScreenInformationObject(screenInfoArray); + screenInfoArray = readCurrentDisplayConfiguration(); + ensureMonitorDataConsistency(screenInfoArray); + ret = primaryScreenOffsetFromTLC(screenInfoArray); + destroyScreenInformationObject(screenInfoArray); + } + + return ret; +} + +TQPtrList<SingleScreenData> KRandrSimpleAPI::loadDisplayConfiguration(TQString profilename, TQString kde_confdir) { + int i; + + TQString filename; + filename = profilename; + if (filename == "") { + filename = "default"; + } + filename.prepend(kde_confdir.append("/displayconfig/")); + + KSimpleConfig* display_config = new KSimpleConfig( filename ); + + TQStringList grouplist = display_config->groupList(); + SingleScreenData *screendata; + TQPtrList<SingleScreenData> screenInfoArray; + for ( TQStringList::Iterator it = grouplist.begin(); it != grouplist.end(); ++it ) { + if ((*it).startsWith("SCREEN ")) { + display_config->setGroup(*it); + i = ((*it).remove("SCREEN ")).toInt(); + screendata = new SingleScreenData; + screenInfoArray.append(screendata); + screendata->screenUniqueName = display_config->readEntry("ScreenUniqueName"); + screendata->screenFriendlyName = display_config->readEntry("ScreenFriendlyName"); + screendata->generic_screen_detected = display_config->readBoolEntry("GenericScreenDetected"); + screendata->screen_connected = display_config->readBoolEntry("ScreenConnected"); + screendata->resolutions = display_config->readListEntry("Resolutions"); + screendata->refresh_rates = display_config->readListEntry("RefreshRates"); + screendata->color_depths = display_config->readListEntry("ColorDepths"); + screendata->rotations = display_config->readListEntry("AvailableRotations"); + screendata->current_resolution_index = display_config->readNumEntry("CurrentResolution"); + screendata->current_refresh_rate_index = display_config->readNumEntry("CurrentRefreshRate"); + screendata->current_color_depth_index = display_config->readNumEntry("CurrentColorDepth"); + screendata->current_rotation_index = display_config->readNumEntry("CurrentRotation"); + screendata->current_orientation_mask = display_config->readNumEntry("CurrentOrientiation"); + screendata->gamma_red = display_config->readDoubleNumEntry("GammaRed"); + screendata->gamma_green = display_config->readDoubleNumEntry("GammaGreen"); + screendata->gamma_blue = display_config->readDoubleNumEntry("GammaBlue"); + screendata->has_x_flip = display_config->readBoolEntry("CurrentXFlip"); + screendata->has_y_flip = display_config->readBoolEntry("CurrentYFlip"); + screendata->supports_transformations = display_config->readBoolEntry("SupportsTransformation"); + screendata->is_primary = display_config->readBoolEntry("IsPrimary"); + screendata->is_extended = display_config->readBoolEntry("IsExtended"); + screendata->absolute_x_position = display_config->readNumEntry("AbsXPos"); + screendata->absolute_y_position = display_config->readNumEntry("AbsYPos"); + screendata->current_x_pixel_count = display_config->readNumEntry("CurrentXPixelCount"); + screendata->current_y_pixel_count = display_config->readNumEntry("CurrentYPixelCount"); + screendata->has_dpms = display_config->readBoolEntry("HasDPMS"); + screendata->enable_dpms = display_config->readBoolEntry("EnableDPMS"); + screendata->dpms_standby_delay = display_config->readNumEntry("DPMSStandbyDelay"); + screendata->dpms_suspend_delay = display_config->readNumEntry("DPMSSuspendDelay"); + screendata->dpms_off_delay = display_config->readNumEntry("DPMSPowerDownDelay"); + } + } + + delete display_config; + + return screenInfoArray; +} + +int KRandrSimpleAPI::getHardwareRotationFlags(SingleScreenData* screendata) { + int rotationFlags = 0; + TQString rotationDesired = *screendata->rotations.at(screendata->current_rotation_index); + if (rotationDesired == ROTATION_0_DEGREES_STRING) { + rotationFlags = rotationFlags | RandRScreen::Rotate0; + } + else if (rotationDesired == ROTATION_90_DEGREES_STRING) { + rotationFlags = rotationFlags | RandRScreen::Rotate90; + } + else if (rotationDesired == ROTATION_180_DEGREES_STRING) { + rotationFlags = rotationFlags | RandRScreen::Rotate180; + } + else if (rotationDesired == ROTATION_270_DEGREES_STRING) { + rotationFlags = rotationFlags | RandRScreen::Rotate270; + } + if (screendata->has_x_flip) { + rotationFlags = rotationFlags | RandRScreen::ReflectX; + } + if (screendata->has_y_flip) { + rotationFlags = rotationFlags | RandRScreen::ReflectY; + } + return rotationFlags; +} + +#define USE_XRANDR_PROGRAM + +bool KRandrSimpleAPI::applyDisplayConfiguration(TQPtrList<SingleScreenData> screenInfoArray, bool test, TQString kde_confdir) { + int i; + int j; + bool accepted = true; + Display *randr_display; + XRROutputInfo *output_info; + ScreenInfo *randr_screen_info; + + SingleScreenData *screendata; + + TQPtrList<SingleScreenData> oldconfig; + if (test == TRUE) { + oldconfig = readCurrentDisplayConfiguration(); + } + + if (isValid() == true) { +#ifdef USE_XRANDR_PROGRAM + // Assemble the command string for xrandr + TQString command; + command = "xrandr"; + + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + if (screendata) { + output_info = randr_screen_info->outputs[i]->info; + command.append(" --output ").append(output_info->name); + if (screendata->is_primary || screendata->is_extended) { + command.append(TQString(" --mode %1x%2").arg(screendata->current_x_pixel_count).arg(screendata->current_y_pixel_count)); + command.append(TQString(" --pos %1x%2").arg(screendata->absolute_x_position).arg(screendata->absolute_y_position)); + command.append(TQString(" --refresh %1").arg((*screendata->refresh_rates.at(screendata->current_refresh_rate_index)).replace("Hz", ""))); + command.append(TQString(" --gamma %1:%2:%3").arg(screendata->gamma_red).arg(screendata->gamma_green).arg(screendata->gamma_blue)); + if (screendata->current_rotation_index == 0) command.append(" --rotate ").append("normal"); + if (screendata->current_rotation_index == 1) command.append(" --rotate ").append("left"); + if (screendata->current_rotation_index == 2) command.append(" --rotate ").append("inverted"); + if (screendata->current_rotation_index == 3) command.append(" --rotate ").append("right"); + if ((screendata->has_x_flip == 0) && (screendata->has_y_flip == 0)) command.append(" --reflect ").append("normal"); + if ((screendata->has_x_flip == 1) && (screendata->has_y_flip == 0)) command.append(" --reflect ").append("x"); + if ((screendata->has_x_flip == 0) && (screendata->has_y_flip == 1)) command.append(" --reflect ").append("y"); + if ((screendata->has_x_flip == 1) && (screendata->has_y_flip == 1)) command.append(" --reflect ").append("xy"); + if (screendata->is_primary) { + command.append(" --primary"); + } + } + else { + command.append(" --off"); + } + } + else { + printf("[WARNING] Unable to find configuration for monitor %d; settings may not be correctly applied...\n\r", i); fflush(stdout); + } + } + freeScreenInfoStructure(randr_screen_info); + + TQString xrandr_command_output = exec(command.ascii()); + xrandr_command_output = xrandr_command_output.stripWhiteSpace(); + if (test) { + if (xrandr_command_output != "") { + applyDisplayConfiguration(oldconfig, FALSE, kde_confdir); + accepted = false; + destroyScreenInformationObject(oldconfig); + KMessageBox::sorry(0, xrandr_command_output, i18n("XRandR encountered a problem")); + return accepted; + } + } +#else + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + // Turn off all displays + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + + randr_screen_info->cur_crtc = randr_screen_info->outputs[i]->cur_crtc; + randr_screen_info->cur_output = randr_screen_info->outputs[i]; + randr_screen_info->cur_output->auto_set = 0; + randr_screen_info->cur_output->off_set = 1; + output_off (randr_screen_info, randr_screen_info->cur_output); + j=main_low_apply(randr_screen_info); + } + freeScreenInfoStructure(randr_screen_info); + randr_screen_info = read_screen_info(randr_display); + // Turn on the primary display + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + + if (screendata->is_primary == true) { + randr_screen_info->cur_crtc = randr_screen_info->outputs[i]->cur_crtc; + randr_screen_info->cur_output = randr_screen_info->outputs[i]; + randr_screen_info->cur_output->auto_set = 1; + randr_screen_info->cur_output->off_set = 0; + output_auto (randr_screen_info, randr_screen_info->cur_output); + j=main_low_apply(randr_screen_info); + } + } + freeScreenInfoStructure(randr_screen_info); + // Handle the remaining displays + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + + // Activate or deactivate the screens as necessary + randr_screen_info->cur_crtc = randr_screen_info->outputs[i]->cur_crtc; + randr_screen_info->cur_output = randr_screen_info->outputs[i]; + if (screendata->is_primary == false) { + if (screendata->is_primary || screendata->is_extended) { + randr_screen_info->cur_output->auto_set = 1; + randr_screen_info->cur_output->off_set = 0; + output_auto (randr_screen_info, randr_screen_info->cur_output); + j=main_low_apply(randr_screen_info); + } + else { + randr_screen_info->cur_output->auto_set = 0; + randr_screen_info->cur_output->off_set = 1; + output_off (randr_screen_info, randr_screen_info->cur_output); + j=main_low_apply(randr_screen_info); + } + } + } + freeScreenInfoStructure(randr_screen_info); + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + + if (screendata->is_primary || screendata->is_extended) { + // Set rotation, refresh rate, and size + RandRScreen *cur_screen = new RandRScreen(i); + cur_screen->proposeSize(screendata->current_resolution_index); + cur_screen->proposeRefreshRate(screendata->current_refresh_rate_index); + cur_screen->proposeRotation(getHardwareRotationFlags(screendata)); + cur_screen->applyProposed(); + delete cur_screen; + + // Force data reload + randr_screen_info = read_screen_info(randr_display); + output_info = randr_screen_info->outputs[i]->info; + + // Finally, set the screen's position + randr_screen_info->cur_crtc = randr_screen_info->outputs[i]->cur_crtc; + if (randr_screen_info->cur_crtc) { + randr_screen_info->cur_crtc->cur_x = screendata->absolute_x_position; + randr_screen_info->cur_crtc->cur_y = screendata->absolute_y_position; + j=main_low_apply(randr_screen_info); + } + } + } + freeScreenInfoStructure(randr_screen_info); +#endif + } + + applyDisplayGamma(screenInfoArray); + applyDisplayDPMS(screenInfoArray); + TQString current_icc_profile = getCurrentProfile(); + applySystemWideIccConfiguration(kde_confdir); + applyIccConfiguration(current_icc_profile, kde_confdir); + + if (test == TRUE) { + int ret = showTestConfigurationDialog(); + if (!ret) { + applyDisplayConfiguration(oldconfig, FALSE, kde_confdir); + accepted = false; + } + destroyScreenInformationObject(oldconfig); + } + + return accepted; +} + +TQPtrList<SingleScreenData> KRandrSimpleAPI::copyScreenInformationObject(TQPtrList<SingleScreenData> screenInfoArray) { + SingleScreenData *origscreendata; + SingleScreenData *copyscreendata; + TQPtrList<SingleScreenData> retArray; + for ( origscreendata = screenInfoArray.first(); origscreendata; origscreendata = screenInfoArray.next() ) { + copyscreendata = new SingleScreenData; + *copyscreendata = *origscreendata; + retArray.append(copyscreendata); + } + return retArray; +} + +void KRandrSimpleAPI::destroyScreenInformationObject(TQPtrList<SingleScreenData> screenInfoArray) { + SingleScreenData *screendata; + for ( screendata = screenInfoArray.first(); screendata; screendata = screenInfoArray.next() ) { + screenInfoArray.remove(screendata); + delete screendata; + } +} + +void KRandrSimpleAPI::ensureMonitorDataConsistency(TQPtrList<SingleScreenData> screenInfoArray) { + int i; + SingleScreenData *screendata; + + int numberOfScreens = screenInfoArray.count(); + + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (!screendata->screen_connected) { + screendata->is_primary = false; + screendata->is_extended = false; + } + } + + bool has_primary_monitor = false; + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (screendata->is_primary) { + has_primary_monitor = true; + } + } + if (!has_primary_monitor) { + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (!has_primary_monitor) { + if (screendata->screen_connected && screendata->is_extended) { + screendata->is_primary = true; + screendata->is_extended = true; + has_primary_monitor = true; + } + } + } + } + if (!has_primary_monitor) { + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (!has_primary_monitor) { + if (screendata->screen_connected) { + screendata->is_primary = true; + screendata->is_extended = true; + has_primary_monitor = true; + } + } + } + } + + bool found_first_primary_monitor = false; + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (screendata->is_primary) { + if (!found_first_primary_monitor) { + found_first_primary_monitor = true; + } + else { + screendata->is_primary = false; + } + } + } + + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (screendata->is_primary) { + screendata->is_extended = true; + } + } + + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + TQString resolutionstring = screendata->resolutions[screendata->current_resolution_index]; + int separator_pos = resolutionstring.find(" x "); + TQString x_res_string = resolutionstring.left(separator_pos); + TQString y_res_string = resolutionstring.right(resolutionstring.length()-separator_pos-3); + screendata->current_x_pixel_count = x_res_string.toInt(); + screendata->current_y_pixel_count = y_res_string.toInt(); + screendata->current_orientation_mask = getHardwareRotationFlags(screendata); + } + + // Each screen's absolute position is given relative to the primary monitor + // Fix up the absolute positions + int primary_offset_x = 0; + int primary_offset_y = 0; + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (screendata->is_primary) { + primary_offset_x = screendata->absolute_x_position; + primary_offset_y = screendata->absolute_y_position; + primary_offset_x = primary_offset_x * (-1); + primary_offset_y = primary_offset_y * (-1); + } + } + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + screendata->absolute_x_position = screendata->absolute_x_position + primary_offset_x; + screendata->absolute_y_position = screendata->absolute_y_position + primary_offset_y; + } +} + +TQPoint KRandrSimpleAPI::primaryScreenOffsetFromTLC(TQPtrList<SingleScreenData> screenInfoArray) { + int i; + SingleScreenData *screendata; + int numberOfScreens = screenInfoArray.count(); + + int primary_offset_x = 0; + int primary_offset_y = 0; + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + if (screendata->absolute_x_position < primary_offset_x) { + primary_offset_x = screendata->absolute_x_position; + } + if (screendata->absolute_y_position < primary_offset_y) { + primary_offset_y = screendata->absolute_y_position; + } + } + primary_offset_x = primary_offset_x * (-1); + primary_offset_y = primary_offset_y * (-1); + + return TQPoint(primary_offset_x, primary_offset_y); +} + +HotPlugRulesList KRandrSimpleAPI::getHotplugRules(TQString kde_confdir) { + int i; + TQString filename; + HotPlugRulesList ret; + + filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + + TQStringList grouplist = display_config->groupList(); + for ( TQStringList::Iterator it = grouplist.begin(); it != grouplist.end(); ++it ) { + if (!(*it).startsWith("Hotplug-Rule")) { + continue; + } + HotPlugRule rule; + display_config->setGroup(*it); + rule.outputs = display_config->readListEntry("Outputs"); + rule.states = display_config->readIntListEntry("States"); + rule.profileName = display_config->readEntry("Profile"); + ret.append(rule); + } + delete display_config; + + return ret; +} + +void KRandrSimpleAPI::saveHotplugRules(HotPlugRulesList rules, TQString kde_confdir) { + int i; + TQString filename; + + filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + TQStringList grouplist = display_config->groupList(); + for ( TQStringList::Iterator it = grouplist.begin(); it != grouplist.end(); ++it ) { + if (!(*it).startsWith("Hotplug-Rule")) { + continue; + } + display_config->deleteGroup(*it, true, false); + } + HotPlugRulesList::Iterator it; + i=0; + for (it=rules.begin(); it != rules.end(); ++it) { + display_config->setGroup(TQString("Hotplug-Rule%1").arg(i)); + display_config->writeEntry("Outputs", (*it).outputs); + display_config->writeEntry("States", (*it).states); + display_config->writeEntry("Profile", (*it).profileName); + i++; + } + display_config->sync(); + delete display_config; +} + +bool KRandrSimpleAPI::getDisplayConfigurationEnabled(TQString kde_confdir) { + TQString filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + display_config->setGroup("General"); + bool enabled = display_config->readBoolEntry("EnableDisplayControl", false); + delete display_config; + + return enabled; +} + +bool KRandrSimpleAPI::getDisplayConfigurationStartupAutoApplyEnabled(TQString kde_confdir) { + TQString filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + display_config->setGroup("General"); + bool applyonstart = display_config->readBoolEntry("ApplySettingsOnStart", false); + delete display_config; + + return applyonstart; +} + +TQString KRandrSimpleAPI::getDisplayConfigurationStartupAutoApplyName(TQString kde_confdir) { + TQString filename = "displayglobals"; + filename.prepend(kde_confdir.append("/")); + KSimpleConfig* display_config = new KSimpleConfig( filename ); + display_config->setGroup("General"); + TQString profilename = display_config->readEntry("StartupProfileName", ""); + delete display_config; + + return profilename; +} + +void KRandrSimpleAPI::applyHotplugRules(TQString kde_confdir) { + bool enabled = getDisplayConfigurationEnabled(kde_confdir); + if (!enabled) { + return; + } + + HotPlugRulesList rules = getHotplugRules(kde_confdir); + TQPtrList<SingleScreenData> screenInfoArray = readCurrentDisplayConfiguration(); + + int i; + int j; + TQString bestRule; + int bestRuleMatchCount = 0; + SingleScreenData *screendata = NULL; + HotPlugRulesList::Iterator it; + for (it=rules.begin(); it != rules.end(); ++it) { + // Compare each rule against the current display configuration + // It an output matches the state given in the rule, increment matchCount + HotPlugRule rule = *it; + int matchCount = 0; + int numberOfScreens = screenInfoArray.count(); + for (i=0;i<numberOfScreens;i++) { + screendata = screenInfoArray.at(i); + for (j=0; j<(*it).outputs.count(); j++) { + if ((*it).outputs[j] != screendata->screenUniqueName) { + continue; + } + if ((*it).states[j] == HotPlugRule::Connected) { + if (screendata->screen_connected) { + matchCount++; + } + } + else if ((*it).states[j] == HotPlugRule::Disconnected) { + if (!screendata->screen_connected) { + matchCount++; + } + } + } + } + + if (matchCount > bestRuleMatchCount) { + bestRuleMatchCount = matchCount; + bestRule = rule.profileName; + } + } + + destroyScreenInformationObject(screenInfoArray); + + if (bestRuleMatchCount > 0) { + // At least one rule matched... + // Apply the profile name in bestRule to the display hardware + applyDisplayConfiguration(bestRule, kde_confdir); + } +} + +void KRandrSimpleAPI::applyDisplayGamma(TQPtrList<SingleScreenData> screenInfoArray) { + int i; + Display *randr_display; + XRROutputInfo *output_info; + ScreenInfo *randr_screen_info; + XRRCrtcGamma *gamma; + + SingleScreenData *screendata; + + if (isValid() == true) { + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + CrtcInfo *current_crtc = randr_screen_info->outputs[i]->cur_crtc; + if (!current_crtc) { + continue; + } + // vvvvvvvvv This chunk of code is borrowed from xrandr vvvvvvvvvv + int size = XRRGetCrtcGammaSize(randr_display, current_crtc->id); + if (!size) { + continue; + } + gamma = XRRAllocGamma(size); + if (!gamma) { + continue; + } + for (i = 0; i < size; i++) { + if (screendata->gamma_red == 1.0) + gamma->red[i] = i << 8; + else + gamma->red[i] = (pow((double)i/(double)(size-1), (double)screendata->gamma_red) * (double)(size-1)*256); + + if (screendata->gamma_green == 1.0) + gamma->green[i] = i << 8; + else + gamma->green[i] = (pow((double)i/(double)(size-1), (double)screendata->gamma_green) * (double)(size-1)*256); + + if (screendata->gamma_blue == 1.0) + gamma->blue[i] = i << 8; + else + gamma->blue[i] = (pow((double)i/(double)(size-1), (double)screendata->gamma_blue) * (double)(size-1)*256); + } + XRRSetCrtcGamma(randr_display, current_crtc->id, gamma); + free(gamma); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + } + freeScreenInfoStructure(randr_screen_info); + } +} + +void KRandrSimpleAPI::applyDisplayDPMS(TQPtrList<SingleScreenData> screenInfoArray) { + int i; + Display *randr_display; + XRROutputInfo *output_info; + ScreenInfo *randr_screen_info; + XRRCrtcGamma *gamma; + + SingleScreenData *screendata; + + if (isValid() == true) { + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < screenInfoArray.count(); i++) { + screendata = screenInfoArray.at(i); + output_info = randr_screen_info->outputs[i]->info; + CrtcInfo *current_crtc = randr_screen_info->outputs[i]->cur_crtc; + if (!current_crtc) { + continue; + } + if (!screendata->has_dpms) { + continue; + } + if (screendata->enable_dpms) { + DPMSSetTimeouts(randr_display, screendata->dpms_standby_delay, screendata->dpms_suspend_delay, screendata->dpms_off_delay); + DPMSEnable(randr_display); + } + else { + DPMSDisable(randr_display); + } + } + freeScreenInfoStructure(randr_screen_info); + } +} + +void KRandrSimpleAPI::freeScreenInfoStructure(ScreenInfo* screen_info) { + int i; + + for (i=0; i<screen_info->n_crtc; i++) { + free(screen_info->crtcs[i]); + } + for (i=0; i<screen_info->n_output; i++) { + free(screen_info->outputs[i]); + } + free(screen_info->outputs); + free(screen_info->crtcs); + free(screen_info); +} + +TQPtrList<SingleScreenData> KRandrSimpleAPI::readCurrentDisplayConfiguration() { + // Discover display information + int i; + int j; + + XRROutputInfo *output_info; + SingleScreenData *screendata; + TQPtrList<SingleScreenData> screenInfoArray; + + Display *randr_display; + ScreenInfo *randr_screen_info; + + // Clear existing info + destroyScreenInformationObject(screenInfoArray); + + int numberOfScreens = 0; + if (isValid() == true) { + randr_display = tqt_xdisplay(); + randr_screen_info = read_screen_info(randr_display); + for (i = 0; i < randr_screen_info->n_output; i++) { + output_info = randr_screen_info->outputs[i]->info; + CrtcInfo *current_crtc = randr_screen_info->outputs[i]->cur_crtc; + + // Create new data object + screendata = new SingleScreenData; + screenInfoArray.append(screendata); + screendata->screenUniqueName = TQString(i18n("%1:%2")).arg(":0").arg(capitalizeString(output_info->name)); // [FIXME] How can I get the name of the Xorg graphics driver currently in use? + screendata->screenFriendlyName = TQString(i18n("%1. %2 output on %3")).arg(i+1).arg(capitalizeString(output_info->name)).arg(":0"); // [FIXME] How can I get the name of the Xorg graphics driver currently in use? + screendata->generic_screen_detected = false; + + // Attempt to use KMS to find screen EDID and name + TQString edid = getEDIDMonitorName(0, output_info->name); // [FIXME] Don't hardwire to card 0! + if (!edid.isNull()) { + screendata->screenFriendlyName = TQString(i18n("%1. %2 on %3 on card %4")).arg(i+1).arg(edid).arg(capitalizeString(output_info->name)).arg("0"); // [FIXME] How can I get the name of the Xorg graphics driver currently in use? + } + + // Get resolutions + bool screen_active; + RandRScreen *cur_screen = 0; + if (RR_Disconnected == randr_screen_info->outputs[i]->info->connection) { + // Output DISCONNECTED + screen_active = false; + } + else { + if (randr_screen_info->outputs[i]->cur_crtc) { + // Output CONNECTED and ON + screen_active = true; + cur_screen = new RandRScreen(i); + } + else { + // Output CONNECTED and OFF + screen_active = false; + cur_screen = new RandRScreen(i); + } + } + + // Get DPMS information + screendata->has_dpms = 1; // [FIXME] Master Xorg check for global DPMS support should go here if possible + if (screendata->has_dpms) { + CARD16 dpms_standby_delay; + CARD16 dpms_suspend_delay; + CARD16 dpms_off_delay; + screendata->has_dpms = DPMSGetTimeouts(randr_display, &dpms_standby_delay, &dpms_suspend_delay, &dpms_off_delay); + screendata->dpms_standby_delay = dpms_standby_delay; + screendata->dpms_suspend_delay = dpms_suspend_delay; + screendata->dpms_off_delay = dpms_off_delay; + if (screendata->has_dpms) { + CARD16 power_level; + BOOL enable_dpms; + screendata->has_dpms = DPMSInfo(randr_display, &power_level, &enable_dpms); + screendata->enable_dpms = enable_dpms; + } + } + if (!screendata->has_dpms) { + screendata->enable_dpms = false; + screendata->dpms_standby_delay = 0; + screendata->dpms_suspend_delay = 0; + screendata->dpms_off_delay = 0; + } + + if (cur_screen) { + screendata->screen_connected = true; + for (int j = 0; j < cur_screen->numSizes(); j++) { + screendata->resolutions.append(i18n("%1 x %2").arg(cur_screen->pixelSize(j).width()).arg(cur_screen->pixelSize(j).height())); + } + screendata->current_resolution_index = 0; + if (current_crtc) { + screendata->current_resolution_index = screendata->resolutions.findIndex(i18n("%1 x %2").arg(current_crtc->info->width).arg(current_crtc->info->height)); + } + if (screendata->current_resolution_index < 0) { + screendata->current_resolution_index = cur_screen->proposedSize(); + } + + // Get refresh rates + TQStringList rr = cur_screen->refreshRates(screendata->current_resolution_index); + for (TQStringList::Iterator it = rr.begin(); it != rr.end(); ++it) { + screendata->refresh_rates.append(*it); + } + screendata->current_refresh_rate_index = cur_screen->proposedRefreshRate(); + + // Get color depths + // [FIXME] + screendata->color_depths.append(i18n("Default")); + screendata->current_color_depth_index = 0; + + // Get orientation flags + // RandRScreen::Rotate0 + // RandRScreen::Rotate90 + // RandRScreen::Rotate180 + // RandRScreen::Rotate270 + // RandRScreen::ReflectX + // RandRScreen::ReflectY + + screendata->rotations.append(i18n(ROTATION_0_DEGREES_STRING)); + screendata->rotations.append(i18n(ROTATION_90_DEGREES_STRING)); + screendata->rotations.append(i18n(ROTATION_180_DEGREES_STRING)); + screendata->rotations.append(i18n(ROTATION_270_DEGREES_STRING)); + screendata->supports_transformations = (cur_screen->rotations() != RandRScreen::Rotate0); + if (screendata->supports_transformations) { + screendata->current_orientation_mask = cur_screen->proposedRotation(); + switch (screendata->current_orientation_mask & RandRScreen::RotateMask) { + case RandRScreen::Rotate0: + screendata->current_rotation_index = 0; + break; + case RandRScreen::Rotate90: + screendata->current_rotation_index = 1; + break; + case RandRScreen::Rotate180: + screendata->current_rotation_index = 2; + break; + case RandRScreen::Rotate270: + screendata->current_rotation_index = 3; + break; + default: + // Shouldn't hit this one + Q_ASSERT(screendata->current_orientation_mask & RandRScreen::RotateMask); + screendata->current_rotation_index = 0; + break; + } + screendata->has_x_flip = (screendata->current_orientation_mask & RandRScreen::ReflectX); + screendata->has_y_flip = (screendata->current_orientation_mask & RandRScreen::ReflectY); + } + else { + screendata->has_x_flip = false; + screendata->has_y_flip = false; + screendata->current_rotation_index = 0; + } + + // Determine if this display is primary and/or extended + RROutput primaryoutput = XRRGetOutputPrimary(tqt_xdisplay(), DefaultRootWindow(tqt_xdisplay())); + if (primaryoutput == randr_screen_info->outputs[i]->id) { + screendata->is_primary = false; + } + else { + screendata->is_primary = true; + } + screendata->is_extended = screen_active; + if (!screendata->is_extended) { + screendata->is_primary = false; + } + + // Get this screen's absolute position + screendata->absolute_x_position = 0; + screendata->absolute_y_position = 0; + if (current_crtc) { + screendata->absolute_x_position = current_crtc->info->x; + screendata->absolute_y_position = current_crtc->info->y; + } + + // Get this screen's current resolution + screendata->current_x_pixel_count = cur_screen->pixelSize(screendata->current_resolution_index).width(); + screendata->current_y_pixel_count = cur_screen->pixelSize(screendata->current_resolution_index).height(); + + // Get this screen's current gamma values + // [FIXME] + // This attempts to guess a gamma value based on the LUT settings at 50% + // It may not always be 100% correct, or even anywhere close... + // Essentially it "undoes" the LUT gamma calculation from xrandr + // lut_gamma->green[i] = (pow(i/(size - 1), desired_gamma.green) * (size - 1) * 256); + if (current_crtc) { + //int slot = 127; + int slot = 7; + int size = XRRGetCrtcGammaSize(randr_display, current_crtc->id); + XRRCrtcGamma *gammastruct = XRRGetCrtcGamma (randr_display, current_crtc->id); + screendata->gamma_red = log(gammastruct->red[slot]/((size-1.0)*256.0))/log(slot/(size-1.0)); + screendata->gamma_green = log(gammastruct->green[slot]/((size-1.0)*256.0))/log(slot/(size-1.0)); + screendata->gamma_blue = log(gammastruct->blue[slot]/((size-1.0)*256.0))/log(slot/(size-1.0)); + } + else { + screendata->gamma_red = 2.2; + screendata->gamma_green = 2.2; + screendata->gamma_blue = 2.2; + } + // Round off the gamma to one decimal place + screendata->gamma_red = floorf(screendata->gamma_red * 10 + 0.5) / 10; + screendata->gamma_green = floorf(screendata->gamma_green * 10 + 0.5) / 10; + screendata->gamma_blue = floorf(screendata->gamma_blue * 10 + 0.5) / 10; + + delete cur_screen; + } + else { + // Fill in generic data for this disconnected output + screendata->screenFriendlyName = screendata->screenFriendlyName + TQString(" (") + i18n("disconnected") + TQString(")"); + screendata->screen_connected = false; + + screendata->resolutions = i18n("Default"); + screendata->refresh_rates = i18n("Default"); + screendata->color_depths = i18n("Default"); + screendata->rotations = i18n("N/A"); + + screendata->current_resolution_index = 0; + screendata->current_refresh_rate_index = 0; + screendata->current_color_depth_index = 0; + + screendata->gamma_red = 2.2; + screendata->gamma_green = 2.2; + screendata->gamma_blue = 2.2; + + screendata->current_rotation_index = 0; + screendata->current_orientation_mask = 0; + screendata->has_x_flip = false; + screendata->has_y_flip = false; + screendata->supports_transformations = false; + + screendata->is_primary = false; + screendata->is_extended = false; + screendata->absolute_x_position = 0; + screendata->absolute_y_position = 0; + screendata->current_x_pixel_count = 640; + screendata->current_y_pixel_count = 480; + } + + // Check for more screens... + numberOfScreens++; + } + + freeScreenInfoStructure(randr_screen_info); + } + else { + screendata = new SingleScreenData; + screenInfoArray.append(screendata); + + // Fill in a bunch of generic data + screendata->screenFriendlyName = i18n("Default output on generic video card"); + screendata->generic_screen_detected = true; + screendata->screen_connected = true; + + screendata->resolutions = i18n("Default"); + screendata->refresh_rates = i18n("Default"); + screendata->color_depths = i18n("Default"); + screendata->rotations = i18n("N/A"); + + screendata->current_resolution_index = 0; + screendata->current_refresh_rate_index = 0; + screendata->current_color_depth_index = 0; + + screendata->gamma_red = 2.2; + screendata->gamma_green = 2.2; + screendata->gamma_blue = 2.2; + + screendata->current_rotation_index = 0; + screendata->current_orientation_mask = 0; + screendata->has_x_flip = false; + screendata->has_y_flip = false; + screendata->supports_transformations = false; + + screendata->is_primary = true; + screendata->is_extended = true; + screendata->absolute_x_position = 0; + screendata->absolute_y_position = 0; + screendata->current_x_pixel_count = 640; + screendata->current_y_pixel_count = 480; + + numberOfScreens++; + } + + bool primary_set = false; + for ( screendata=screenInfoArray.first(); screendata; screendata=screenInfoArray.next() ) { + if (screendata->is_primary) { + primary_set = true; + break; + } + } + // If there is no primary monitor set, xrandr is probably not functioning correctly! + Q_ASSERT(primary_set); + if (!primary_set) { + // [FIXME] + // Set this on the real primary monitor only! + screendata = screenInfoArray.at(0); + screendata->is_primary = true; + } + + return screenInfoArray; +} + +TQString KRandrSimpleAPI::clearIccConfiguration() { + // Clear ICC settings with XCalib + TQString icc_command; + FILE *pipe_xcalib; + char xcalib_result[2048]; + int i; + xcalib_result[0]=0; + + icc_command = TQString("xcalib -c"); + if ((pipe_xcalib = popen(icc_command.ascii(), "r")) == NULL) + { + printf("Xcalib pipe error [xcalib clear]\n\r"); + } + else { + if (fgets(xcalib_result, 2048, pipe_xcalib)) { + pclose(pipe_xcalib); + for (i=1;i<2048;i++) { + if (xcalib_result[i] == 0) { + xcalib_result[i-1]=0; + i=2048; + } + } + if (strlen(xcalib_result) > 2) { + return xcalib_result; + } + } + else { + return ""; + } + } + return ""; +} + +ScreenInfo* KRandrSimpleAPI::read_screen_info (Display *display) +{ + return internal_read_screen_info(display); +} + +int KRandrSimpleAPI::set_screen_size (ScreenInfo *screen_info) +{ + return internal_set_screen_size(screen_info); +} + +void KRandrSimpleAPI::output_auto (ScreenInfo *screen_info, OutputInfo *output_info) +{ + internal_output_auto (screen_info, output_info); +} + +void KRandrSimpleAPI::output_off(ScreenInfo *screen_info, OutputInfo *output) +{ + internal_output_off(screen_info, output); +} + +CrtcInfo* KRandrSimpleAPI::auto_find_crtc (ScreenInfo *screen_info, OutputInfo *output_info) +{ + return internal_auto_find_crtc (screen_info, output_info); +} + +XRRModeInfo *KRandrSimpleAPI::find_mode_by_xid (ScreenInfo *screen_info, RRMode mode_id) +{ + return internal_find_mode_by_xid (screen_info, mode_id); +} + +int KRandrSimpleAPI::mode_height (XRRModeInfo *mode_info, Rotation rotation) +{ + return internal_mode_height (mode_info, rotation); +} + +int KRandrSimpleAPI::mode_width (XRRModeInfo *mode_info, Rotation rotation) +{ + return internal_mode_width (mode_info, rotation); +} + +int KRandrSimpleAPI::get_width_by_output_id (ScreenInfo *screen_info, RROutput output_id) +{ + return internal_get_width_by_output_id (screen_info, output_id); +} + +int KRandrSimpleAPI::get_height_by_output_id (ScreenInfo *screen_info, RROutput output_id) +{ + return internal_get_height_by_output_id (screen_info, output_id); +} + +char *KRandrSimpleAPI::get_output_name (ScreenInfo *screen_info, RROutput id) +{ + return internal_get_output_name (screen_info, id); +} + +Status KRandrSimpleAPI::crtc_apply (CrtcInfo *crtc_info) +{ + return internal_crtc_apply (crtc_info); +} + +Status KRandrSimpleAPI::crtc_disable (CrtcInfo *crtc) +{ + return internal_crtc_disable (crtc); +} + +int KRandrSimpleAPI::main_low_apply (ScreenInfo *screen_info) +{ + return internal_main_low_apply (screen_info); +} + +void KRandrSimpleAPI::set_primary_output (ScreenInfo *screen_info, RROutput output_id) +{ + internal_output_set_primary(screen_info, output_id); +} + +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 <kb9vqf@pearsoncomputing.net>. 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/tderandr/libtderandr.h b/tderandr/libtderandr.h new file mode 100644 index 000000000..b2b362a90 --- /dev/null +++ b/tderandr/libtderandr.h @@ -0,0 +1,359 @@ +/* libtderandr.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 + LibKRandr's homepage : http://www.trinitydesktop.org + + 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 <kb9vqf@pearsoncomputing.net> + +***************************************************************************/ +#ifndef _LIBKRANDR_H +#define _LIBKRANDR_H + +#include "randr.h" +#include "lowlevel_randr.h" + +#ifdef __cplusplus + +#include <tqfile.h> + +#include <kconfig.h> +#include <ksimpleconfig.h> +#include <tdelibs_export.h> + +#define ROTATION_0_DEGREES_STRING "0 degrees" +#define ROTATION_90_DEGREES_STRING "90 degrees" +#define ROTATION_180_DEGREES_STRING "180 degrees" +#define ROTATION_270_DEGREES_STRING "270 degrees" + +/** + * Simple API covering most of the uses of libtderandr. + * + * 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 libtderandr. + * @version 0.9.5 27/04/2010 + * @author Timothy Pearson <kb9vqf@pearsoncomputing.net> + */ +class KRANDR_EXPORT KRandrSimpleAPI : public RandRDisplay +{ + private: + + public: + /** + * Retrieves the specificed ICC profile filename from the configuration database + */ + TQString getIccFileName(TQString profileName, TQString screenName, TQString kde_confdir); + + /** + * Applies the specificed ICC profile filename to the specified RandR output + * If RandR is not available, the specified file is applied to the current display + */ + TQString applyIccFile(TQString screenName, TQString fileName); + + /** + * Applies all saved ICC profile settings to all RandR outputs + * If RandR is not available, the settings are applied to the current display + */ + TQString applyIccConfiguration(TQString profileName, TQString kde_confdir); + + /** + * Applies saved system wide settings to the current display + */ + TQString applySystemWideIccConfiguration(TQString kde_confdir); + + /** + * Resets the current display + */ + TQString clearIccConfiguration(void); + + /** + * Retrieves current profile name + */ + TQString getCurrentProfile(void); + + /** + * Reads current screen information. + * NOTE: The caller is responsible for calling freeScreenInfoStructure() when done + */ + ScreenInfo* read_screen_info(Display *display); + + /** + * Frees the ScreenInfo structure + */ + void freeScreenInfoStructure(ScreenInfo* screen_info); + + /** + * 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); + + /** + * Sets the primary output device to the specified output_id + */ + void set_primary_output (ScreenInfo *screen_info, RROutput output_id); + + /** + * Gets the binary monitor EDID for the specified card and display + */ + TQByteArray getEDID(int card, TQString displayname); + + /** + * Gets the monitor EDID name for the specified card and display + */ + TQString getEDIDMonitorName(int card, TQString displayname); + + /** + * Returns true if the specified configuration directory has enabled display configuration + */ + bool getDisplayConfigurationEnabled(TQString kde_confdir); + + /** + * Returns true if the specified configuration directory has enabled automatic profile application on startup + */ + bool getDisplayConfigurationStartupAutoApplyEnabled(TQString kde_confdir); + + /** + * Returns the name of the automatically applied startup profile in the specified configuration directory + */ + TQString getDisplayConfigurationStartupAutoApplyName(TQString kde_confdir); + + /** + * Returns a HotPlugRulesList containing all hotplug rules from the specified configuration directory + */ + HotPlugRulesList getHotplugRules(TQString kde_confdir); + + /** + * Saves a HotPlugRulesList containing all hotplug rules to the specified configuration directory + */ + void saveHotplugRules(HotPlugRulesList rules, TQString kde_confdir); + + /** + * Applies all hotplug rules in the specified configuration directory to the current display configuration + */ + void applyHotplugRules(TQString kde_confdir); + + /** + * Returns a list of all profiles available in the specified configuration directory + * This list does not include the default ("") profile + */ + TQStringList getDisplayConfigurationProfiles(TQString kde_confdir); + + /** + * Deletes the specified profile from the specified configuration directory + * Returns true on success, false on failure + */ + bool deleteDisplayConfiguration(TQString profilename, TQString kde_confdir); + + /** + * Renames the specified profile in the specified configuration directory + * Returns true on success, false on failure + */ + bool renameDisplayConfiguration(TQString profilename, TQString newprofilename, TQString kde_confdir); + + /** + * Saves the systemwide display configuration screenInfoArray to the specified profile + * If profilename is empty, the default profile is utilized + * If enable is set to true, the default profile will be applied at system startup + */ + void saveDisplayConfiguration(bool enable, bool applyonstart, TQString profilename, TQString defaultprofilename, TQString kde_confdir, TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Reads the systemwide display configuration screenInfoArray from the specified profile + * If profilename is empty, the default profile is utilized + * WARNING: The calling application must free the returned objects when it is done using them + */ + TQPtrList<SingleScreenData> loadDisplayConfiguration(TQString profilename, TQString kde_confdir); + + /** + * Applies the startup display configuration profile if enabled + * Returns the offset of the primary screen's top left corner + */ + TQPoint applyStartupDisplayConfiguration(TQString kde_confdir); + + /** + * Applies the systemwide display configuration screenInfoArray from the specified profile + * If profilename is empty, the default profile is utilized + * Returns the offset of the primary screen's top left corner + */ + TQPoint applyDisplayConfiguration(TQString profilename, TQString kde_confdir); + + /** + * Applies the systemwide display configuration screenInfoArray to the hardware + * If test is true, the new configuration will be loaded for a short period of time, then reverted automatically + * Returns true if configuration was accepted; false if not + */ + bool applyDisplayConfiguration(TQPtrList<SingleScreenData> screenInfoArray, bool test=TRUE, TQString kde_confdir=""); + + /** + * Applies the gamma contained within the systemwide display configuration screenInfoArray to the hardware + */ + void applyDisplayGamma(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Applies the DPMS settings contained within the systemwide display configuration screenInfoArray to the hardware + */ + void applyDisplayDPMS(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Copies a screen information object + */ + TQPtrList<SingleScreenData> copyScreenInformationObject(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Destroys a screen information object + */ + void destroyScreenInformationObject(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Returns the offset of the primary screen's Top Left Corner + */ + TQPoint primaryScreenOffsetFromTLC(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Ensures that the data contained within screenInfoArray is self consistent + */ + void ensureMonitorDataConsistency(TQPtrList<SingleScreenData> screenInfoArray); + + /** + * Reads the current display configuration screenInfoArray from the hardware + */ + TQPtrList<SingleScreenData> readCurrentDisplayConfiguration(); + + /** + * Returns the hardware rotation flags given a valid SingleScreenData structure + */ + int getHardwareRotationFlags(SingleScreenData*); + + /** + * Returns whether or not the system supports XRandR + */ + bool kRandrHasRandr(); + + /** + * Returns the version number of libtderandr, i.e. "0.9.5" or "1.0 Beta" + */ + static const char *kRandrVersion(void); + + /** + * Returns the copyright notice that applications using libtderandr should print + * to the user in an about box or somewhere visible. + * I.e. + * + * "LibKRandr 0.9.5 (C) 2010 Timothy Pearson <kb9vqf@pearsoncomputing.net>. 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/tderandr/lowlevel_randr.c b/tderandr/lowlevel_randr.c new file mode 100644 index 000000000..c7a2edc2a --- /dev/null +++ b/tderandr/lowlevel_randr.c @@ -0,0 +1,680 @@ +/* + * Copyright © 2010 Raptor Engineering + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +Status internal_crtc_disable (struct CrtcInfo *crtc); + +char * internal_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 * internal_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 * internal_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 internal_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 = internal_find_mode_by_xid (screen_info, mode_id); + + width = internal_mode_width (mode_info, crtc_info->cur_rotation); + + break; + } + } + + return width; +} + +int internal_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 = internal_find_mode_by_xid (screen_info, mode_id); + + height = internal_mode_height (mode_info, crtc_info->cur_rotation); + + break; + } + } + + return height; +} + +int internal_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 internal_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 * internal_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 * internal_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 internal_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 = internal_find_mode_by_xid (screen_info, crtc->cur_mode_id); + cur_x = crtc->cur_x; + cur_y = crtc->cur_y; + + w = internal_mode_width (mode_info, crtc->cur_rotation); + h = internal_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 internal_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 internal_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 internal_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 internal_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* internal_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); + + if (sr == NULL) { + return NULL; + } + + 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 = internal_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 internal_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 internal_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 internal_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 * +internal_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 * +internal_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 = internal_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 internal_main_low_apply (struct ScreenInfo *screen_info) +{ + int i; + struct CrtcInfo *crtc_info; + + /* set_positions (screen_info); */ + + if (!internal_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 = internal_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 = internal_mode_width (old_mode, crtc_info->rotation); + old_h = internal_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 { + internal_crtc_disable (screen_info->crtcs[i]); + } + } + + internal_screen_apply (screen_info); + + for (i = 0; i < screen_info->n_crtc; i++) { + Status s; + crtc_info = screen_info->crtcs[i]; + + s = internal_crtc_apply (crtc_info); + if (RRSetConfigSuccess != s) { + fprintf (stderr, "crtc apply error\n"); + } + } + + return 1; +} + +void internal_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 = internal_auto_find_crtc (screen_info, output_info); + } + } + + mode_info = internal_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 = internal_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 internal_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; +} + +void internal_output_set_primary (struct ScreenInfo *screen_info, RROutput output_id) +{ + XRRSetOutputPrimary(screen_info->dpy, screen_info->window, output_id); +}
\ No newline at end of file diff --git a/tderandr/lowlevel_randr.h b/tderandr/lowlevel_randr.h new file mode 100644 index 000000000..b5825e450 --- /dev/null +++ b/tderandr/lowlevel_randr.h @@ -0,0 +1,103 @@ +/* + * 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 <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> + +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* internal_read_screen_info (Display *); + +int internal_set_screen_size (struct ScreenInfo *screen_info); +void internal_output_auto (struct ScreenInfo *screen_info, struct OutputInfo *output_info); +void internal_output_off (struct ScreenInfo *screen_info, struct OutputInfo *output); +void internal_output_set_primary (struct ScreenInfo *screen_info, RROutput output_id); +struct CrtcInfo* internal_auto_find_crtc (struct ScreenInfo *screen_info, struct OutputInfo *output_info); + +XRRModeInfo *internal_find_mode_by_xid (struct ScreenInfo *screen_info, RRMode mode_id); +int internal_mode_height (XRRModeInfo *mode_info, Rotation rotation); +int internal_mode_width (XRRModeInfo *mode_info, Rotation rotation); +int internal_get_width_by_output_id (struct ScreenInfo *screen_info, RROutput output_id); +int internal_get_height_by_output_id (struct ScreenInfo *screen_info, RROutput output_id); +char *internal_get_output_name (struct ScreenInfo *screen_info, RROutput id); +Status internal_crtc_apply (struct CrtcInfo *crtc_info); +Status internal_crtc_disable (struct CrtcInfo *crtc); +int internal_main_low_apply (struct ScreenInfo *screen_info); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/tderandr/randr.cpp b/tderandr/randr.cpp new file mode 100644 index 000000000..334da5a79 --- /dev/null +++ b/tderandr/randr.cpp @@ -0,0 +1,881 @@ +/* + * Copyright (c) 2002,2003 Hamish Rodda <rodda@kde.org> + * + * 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 "lowlevel_randr.h" + +#include <tqtimer.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <dcopclient.h> +#include <kipc.h> +#include <kactivelabel.h> + +#include "ktimerdialog.h" + +#include <X11/Xlib.h> +#define INT8 _X11INT8 +#define INT32 _X11INT32 +#include <X11/Xproto.h> +#undef INT8 +#undef INT32 +#include <X11/extensions/Xrandr.h> + +HotPlugRule::HotPlugRule() +{ + // +} + +HotPlugRule::~HotPlugRule() +{ + // +} + +SingleScreenData::SingleScreenData() +{ + generic_screen_detected = false; + screen_connected = false; + + current_resolution_index = 0; + current_refresh_rate_index = 0; + current_color_depth_index = 0; + + gamma_red = 0.0; + gamma_green = 0.0; + gamma_blue = 0.0; + + current_rotation_index = 0; + current_orientation_mask = 0; + has_x_flip = false; + has_y_flip = false; + supports_transformations = false; + + is_primary = false; + is_extended = false; + absolute_x_position = 0; + absolute_y_position = 0; + current_x_pixel_count = 0; + current_y_pixel_count = 0; + + has_dpms = false; + enable_dpms = false; + dpms_standby_delay = 0; + dpms_suspend_delay = 0; + dpms_off_delay = 0; +} + +SingleScreenData::~SingleScreenData() +{ + // +} + +class RandRScreenPrivate +{ +public: + RandRScreenPrivate() : config(0L) {}; + ~RandRScreenPrivate() + { + if (config) { + XRRFreeScreenConfigInfo(config); + } + } + + XRRScreenConfiguration* config; +}; + +KDE_EXPORT RandRScreen::RandRScreen(int screenIndex) + : d(new RandRScreenPrivate()) + , m_screen(screenIndex) + , m_shownDialog(NULL) +{ + loadSettings(); + setOriginal(); +} + +KDE_EXPORT RandRScreen::~RandRScreen() +{ + delete d; +} + +KDE_EXPORT void RandRScreen::loadSettings() +{ + if (d->config) { + XRRFreeScreenConfigInfo(d->config); + } + + d->config = XRRGetScreenInfo(tqt_xdisplay(), RootWindow(tqt_xdisplay(), m_screen)); + + Rotation rotation; + if (d->config) { + m_currentSize = m_proposedSize = XRRConfigCurrentConfiguration(d->config, &rotation); + m_currentRotation = m_proposedRotation = rotation; + } + else { + m_currentSize = m_proposedSize = 0; + m_currentRotation = m_proposedRotation = 0; + } + + m_pixelSizes.clear(); + m_mmSizes.clear(); + + if (d->config) { + int numSizes; + XRRScreenSize* sizes = XRRSizes(tqt_xdisplay(), m_screen, &numSizes); + for (int i = 0; i < numSizes; i++) { + m_pixelSizes.append(TQSize(sizes[i].width, sizes[i].height)); + m_mmSizes.append(TQSize(sizes[i].mwidth, sizes[i].mheight)); + } + + m_rotations = XRRRotations(tqt_xdisplay(), m_screen, &rotation); + } + else { + // Great, now we have to go after the information manually. Ughh. + ScreenInfo *screeninfo = internal_read_screen_info(tqt_xdisplay()); + XRROutputInfo *output_info = screeninfo->outputs[m_screen]->info; + CrtcInfo *current_crtc = screeninfo->outputs[m_screen]->cur_crtc; + int numSizes = output_info->nmode; + for (int i = 0; i < numSizes; i++) { + XRRModeInfo *xrrmode; + xrrmode = internal_find_mode_by_xid (screeninfo, output_info->modes[i]); + TQSize newSize = TQSize(xrrmode->width, xrrmode->height); + if (!m_pixelSizes.contains(newSize)) { + m_pixelSizes.append(newSize); + m_mmSizes.append(TQSize(output_info->mm_width, output_info->mm_height)); + } + } + if (current_crtc) { + m_rotations = current_crtc->rotations; + m_currentRotation = m_proposedRotation = current_crtc->cur_rotation; + } + } + + if (d->config) { + m_currentRefreshRate = m_proposedRefreshRate = refreshRateHzToIndex(m_currentSize, XRRConfigCurrentRate(d->config)); + } + else { + m_currentRefreshRate = m_proposedRefreshRate = 0; + } +} + +KDE_EXPORT void RandRScreen::setOriginal() +{ + m_originalSize = m_currentSize; + m_originalRotation = m_currentRotation; + m_originalRefreshRate = m_currentRefreshRate; +} + +KDE_EXPORT bool RandRScreen::applyProposed() +{ + //kdDebug() << k_funcinfo << " size " << (SizeID)proposedSize() << ", rotation " << proposedRotation() << ", refresh " << refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) << endl; + + Status status; + + if (!d->config) { + d->config = XRRGetScreenInfo(tqt_xdisplay(), RootWindow(tqt_xdisplay(), m_screen)); + Q_ASSERT(d->config); + } + + if (d->config) { + if (proposedRefreshRate() < 0) + status = XRRSetScreenConfig(tqt_xdisplay(), d->config, DefaultRootWindow(tqt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), CurrentTime); + else { + if( refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) <= 0 ) { + m_proposedRefreshRate = 0; + } + status = XRRSetScreenConfigAndRate(tqt_xdisplay(), d->config, DefaultRootWindow(tqt_xdisplay()), (SizeID)proposedSize(), (Rotation)proposedRotation(), refreshRateIndexToHz(proposedSize(), proposedRefreshRate()), CurrentTime); + } + } + else { + // Great, now we have to set the information manually. Ughh. + // FIXME--this does not work! + ScreenInfo *screeninfo = internal_read_screen_info(tqt_xdisplay()); + screeninfo->cur_width = (*m_pixelSizes.at(proposedSize())).width(); + screeninfo->cur_height = (*m_pixelSizes.at(proposedSize())).height(); + internal_main_low_apply(screeninfo); + + status = RRSetConfigSuccess; + } + + //kdDebug() << "New size: " << WidthOfScreen(ScreenOfDisplay(TQPaintDevice::x11AppDisplay(), screen)) << ", " << HeightOfScreen(ScreenOfDisplay(TQPaintDevice::x11AppDisplay(), screen)) << endl; + + if (status == RRSetConfigSuccess) { + m_currentSize = m_proposedSize; + m_currentRotation = m_proposedRotation; + m_currentRefreshRate = m_proposedRefreshRate; + return true; + } + + return false; +} + +KDE_EXPORT bool RandRScreen::applyProposedAndConfirm() +{ + if (proposedChanged()) { + setOriginal(); + + if (applyProposed()) { + if (!confirm()) { + proposeOriginal(); + applyProposed(); + return false; + } + } else { + return false; + } + } + + return true; +} + +KDE_EXPORT bool RandRScreen::confirm() +{ + // uncomment the line below and edit out the KTimerDialog stuff to get + // a version which works on today's tdelibs (no accept dialog is presented) + + // FIXME remember to put the dialog on the right screen + + KTimerDialog acceptDialog ( 15000, KTimerDialog::CountDown, + TDEApplication::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, TQT_SIGNAL( destroyed()), this, TQT_SLOT( shownDialogDestroyed())); + connect( kapp->desktop(), TQT_SIGNAL( resized(int)), this, TQT_SLOT( desktopResized())); + + return acceptDialog.exec(); +} + +KDE_EXPORT void RandRScreen::shownDialogDestroyed() +{ + m_shownDialog = NULL; + disconnect( kapp->desktop(), TQT_SIGNAL( resized(int)), this, TQT_SLOT( desktopResized())); +} + +KDE_EXPORT void RandRScreen::desktopResized() +{ + if( m_shownDialog != NULL ) + KDialog::centerOnScreen(m_shownDialog, m_screen); +} + +KDE_EXPORT TQString 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()); +} + +KDE_EXPORT bool RandRScreen::changedFromOriginal() const +{ + return m_currentSize != m_originalSize || m_currentRotation != m_originalRotation || m_currentRefreshRate != m_originalRefreshRate; +} + +KDE_EXPORT void RandRScreen::proposeOriginal() +{ + m_proposedSize = m_originalSize; + m_proposedRotation = m_originalRotation; + m_proposedRefreshRate = m_originalRefreshRate; +} + +KDE_EXPORT bool RandRScreen::proposedChanged() const +{ + return m_currentSize != m_proposedSize || m_currentRotation != m_proposedRotation || m_currentRefreshRate != m_proposedRefreshRate; +} + +KDE_EXPORT TQString 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"); + } +} + +KDE_EXPORT TQPixmap 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"); + } +} + +KDE_EXPORT TQString RandRScreen::currentRotationDescription() const +{ + TQString 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; +} + +KDE_EXPORT 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; + } +} + +KDE_EXPORT 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; + } +} + +KDE_EXPORT int RandRScreen::currentPixelWidth() const +{ + return m_pixelSizes[m_currentSize].width(); +} + +KDE_EXPORT int RandRScreen::currentPixelHeight() const +{ + return m_pixelSizes[m_currentSize].height(); +} + +KDE_EXPORT int RandRScreen::currentMMWidth() const +{ + return m_pixelSizes[m_currentSize].width(); +} + +KDE_EXPORT int RandRScreen::currentMMHeight() const +{ + return m_pixelSizes[m_currentSize].height(); +} + +KDE_EXPORT TQStringList RandRScreen::refreshRates(int size) const +{ + int nrates; + TQStringList ret; + + if (d->config) { + short* rates = XRRRates(tqt_xdisplay(), m_screen, (SizeID)size, &nrates); + + for (int i = 0; i < nrates; i++) + ret << refreshRateDirectDescription(rates[i]); + } + else { + // Great, now we have to go after the information manually. Ughh. + ScreenInfo *screeninfo = internal_read_screen_info(tqt_xdisplay()); + int numSizes = screeninfo->res->nmode; + for (int i = 0; i < numSizes; i++) { + int refresh_rate = ((screeninfo->res->modes[i].dotClock*1.0)/((screeninfo->res->modes[i].hTotal)*(screeninfo->res->modes[i].vTotal)*1.0)); + TQString newRate = refreshRateDirectDescription(refresh_rate); + if (!ret.contains(newRate)) { + ret.append(newRate); + } + } + } + + return ret; +} + +KDE_EXPORT TQString RandRScreen::refreshRateDirectDescription(int rate) const +{ + return i18n("Refresh rate in Hertz (Hz)", "%1 Hz").arg(rate); +} + +KDE_EXPORT TQString RandRScreen::refreshRateIndirectDescription(int size, int index) const +{ + return i18n("Refresh rate in Hertz (Hz)", "%1 Hz").arg(refreshRateIndexToHz(size, index)); +} + +KDE_EXPORT TQString RandRScreen::refreshRateDescription(int size, int index) const +{ + return refreshRates(size)[index]; +} + +KDE_EXPORT bool RandRScreen::proposeRefreshRate(int index) +{ + if (index >= 0 && (int)refreshRates(proposedSize()).count() > index) { + m_proposedRefreshRate = index; + return true; + } + + return false; +} + +KDE_EXPORT int RandRScreen::currentRefreshRate() const +{ + return m_currentRefreshRate; +} + +KDE_EXPORT TQString RandRScreen::currentRefreshRateDescription() const +{ + return refreshRateIndirectDescription(m_currentSize, m_currentRefreshRate); +} + +KDE_EXPORT int RandRScreen::proposedRefreshRate() const +{ + return m_proposedRefreshRate; +} + +KDE_EXPORT int RandRScreen::refreshRateHzToIndex(int size, int hz) const +{ + int nrates; + short* rates = XRRRates(tqt_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; +} + +KDE_EXPORT int RandRScreen::refreshRateIndexToHz(int size, int index) const +{ + int nrates; + short* rates = XRRRates(tqt_xdisplay(), m_screen, (SizeID)size, &nrates); + + if (nrates == 0 || index < 0) + return 0; + + // Wrong input Hz! + if(index >= nrates) + return 0; + + return rates[index]; +} + +KDE_EXPORT int RandRScreen::numSizes() const +{ + return m_pixelSizes.count(); +} + +KDE_EXPORT const TQSize& RandRScreen::pixelSize(int index) const +{ + return m_pixelSizes[index]; +} + +KDE_EXPORT const TQSize& RandRScreen::mmSize(int index) const +{ + return m_mmSizes[index]; +} + +KDE_EXPORT int RandRScreen::sizeIndex(TQSize pixelSize) const +{ + for (uint i = 0; i < m_pixelSizes.count(); i++) + if (m_pixelSizes[i] == pixelSize) + return i; + + return -1; +} + +KDE_EXPORT int RandRScreen::rotations() const +{ + return m_rotations; +} + +KDE_EXPORT int RandRScreen::currentRotation() const +{ + return m_currentRotation; +} + +KDE_EXPORT int RandRScreen::currentSize() const +{ + return m_currentSize; +} + +KDE_EXPORT int RandRScreen::proposedRotation() const +{ + return m_proposedRotation; +} + +KDE_EXPORT void RandRScreen::proposeRotation(int newRotation) +{ + m_proposedRotation = newRotation & OrientationMask; +} + +KDE_EXPORT int RandRScreen::proposedSize() const +{ + return m_proposedSize; +} + +KDE_EXPORT bool RandRScreen::proposeSize(int newSize) +{ + if ((int)m_pixelSizes.count() > newSize) { + m_proposedSize = newSize; + return true; + } + + return false; +} + +KDE_EXPORT void RandRScreen::load(TDEConfig& config) +{ + config.setGroup(TQString("Screen%1").arg(m_screen)); + + if (proposeSize(sizeIndex(TQSize(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)); +} + +KDE_EXPORT void RandRScreen::save(TDEConfig& config) const +{ + config.setGroup(TQString("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); +} + +KDE_EXPORT RandRDisplay::RandRDisplay() + : m_valid(true) +{ + // Check extension + Status s = XRRQueryExtension(tqt_xdisplay(), &m_eventBase, &m_errorBase); + if (!s) { + m_errorCode = TQString("%1, base %1").arg(s).arg(m_errorBase); + m_valid = false; + return; + } + + // Sometimes the extension is available but does not return any screens (!) + // Check for that case + Display *randr_display = XOpenDisplay(NULL); + int screen_num; + Window root_window; + + screen_num = DefaultScreen (randr_display); + root_window = RootWindow (randr_display, screen_num); + if (XRRGetScreenResources (randr_display, root_window) == NULL) { + m_errorCode = i18n("No screens detected"); + m_valid = false; + return; + } + + int major_version, minor_version; + XRRQueryVersion(tqt_xdisplay(), &major_version, &minor_version); + + m_version = TQString("X Resize and Rotate extension version %1.%1").arg(major_version).arg(minor_version); + + m_numScreens = ScreenCount(tqt_xdisplay()); + + // This assumption is WRONG with Xinerama + // Q_ASSERT(TQApplication::desktop()->numScreens() == ScreenCount(tqt_xdisplay())); + + m_screens.setAutoDelete(true); + for (int i = 0; i < m_numScreens; i++) { + m_screens.append(new RandRScreen(i)); + } + + setCurrentScreen(TQApplication::desktop()->primaryScreen()); +} + +KDE_EXPORT bool RandRDisplay::isValid() const +{ + return m_valid; +} + +KDE_EXPORT const TQString& RandRDisplay::errorCode() const +{ + return m_errorCode; +} + +KDE_EXPORT int RandRDisplay::eventBase() const +{ + return m_eventBase; +} + +KDE_EXPORT int RandRDisplay::screenChangeNotifyEvent() const +{ + return m_eventBase + RRScreenChangeNotify; +} + +KDE_EXPORT int RandRDisplay::errorBase() const +{ + return m_errorBase; +} + +KDE_EXPORT const TQString& RandRDisplay::version() const +{ + return m_version; +} + +KDE_EXPORT void RandRDisplay::setCurrentScreen(int index) +{ + m_currentScreenIndex = index; + m_currentScreen = m_screens.at(m_currentScreenIndex); + Q_ASSERT(m_currentScreen); +} + +KDE_EXPORT int RandRDisplay::screenIndexOfWidget(TQWidget* widget) +{ + int ret = TQApplication::desktop()->screenNumber(widget); + return ret != -1 ? ret : TQApplication::desktop()->primaryScreen(); +} + +KDE_EXPORT int RandRDisplay::currentScreenIndex() const +{ + return m_currentScreenIndex; +} + +KDE_EXPORT void RandRDisplay::refresh() +{ + for (RandRScreen* s = m_screens.first(); s; s = m_screens.next()) + s->loadSettings(); +} + +KDE_EXPORT int RandRDisplay::numScreens() const +{ + return m_numScreens; +} + +KDE_EXPORT RandRScreen* RandRDisplay::screen(int index) +{ + return m_screens.at(index); +} + +KDE_EXPORT RandRScreen* RandRDisplay::currentScreen() +{ + return m_currentScreen; +} + +KDE_EXPORT bool RandRDisplay::loadDisplay(TDEConfig& config, bool loadScreens) +{ + if (loadScreens) + for (RandRScreen* s = m_screens.first(); s; s = m_screens.next()) + s->load(config); + + return applyOnStartup(config); +} + +KDE_EXPORT bool RandRDisplay::applyOnStartup(TDEConfig& config) +{ + config.setGroup("Display"); + return config.readBoolEntry("ApplyOnStartup", false); +} + +KDE_EXPORT bool RandRDisplay::syncTrayApp(TDEConfig& config) +{ + config.setGroup("Display"); + return config.readBoolEntry("SyncTrayApp", false); +} + +KDE_EXPORT void RandRDisplay::saveDisplay(TDEConfig& 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); +} + +KDE_EXPORT 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(); + } + } +} + +KDE_EXPORT bool RandRDisplay::showTestConfigurationDialog() +{ + RandRScreen* firstScreen = screen(0); + if (firstScreen) { + return firstScreen->showTestConfigurationDialog(); + } + else { + return false; + } +} + +KDE_EXPORT bool RandRScreen::showTestConfigurationDialog() +{ + // uncomment the line below and edit out the KTimerDialog stuff to get + // a version which works on today's tdelibs (no accept dialog is presented) + + // FIXME remember to put the dialog on the right screen + + KTimerDialog acceptDialog ( 15000, KTimerDialog::CountDown, + TDEApplication::kApplication()->mainWidget(), + "mainKTimerDialog", + true, + i18n("Confirm Display Settings"), + 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 display devices has been configured " + "to match the settings shown above. 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, 0); + + m_shownDialog = &acceptDialog; + connect( m_shownDialog, TQT_SIGNAL( destroyed()), this, TQT_SLOT( shownDialogDestroyed())); + connect( kapp->desktop(), TQT_SIGNAL( resized(int)), this, TQT_SLOT( desktopResized())); + + return acceptDialog.exec(); +} + +KDE_EXPORT int RandRScreen::pixelCount( int index ) const +{ + TQSize sz = pixelSize(index); + return sz.width() * sz.height(); +} + +#include "randr.moc" diff --git a/tderandr/randr.h b/tderandr/randr.h new file mode 100644 index 000000000..af3712e71 --- /dev/null +++ b/tderandr/randr.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2002,2003 Hamish Rodda <rodda@kde.org> + * + * 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 <tqobject.h> +#include <tqstringlist.h> +#include <tqptrlist.h> + +#include <tdecmodule.h> +#include <kconfig.h> + +class KTimerDialog; +class RandRScreenPrivate; + +class KRANDR_EXPORT HotPlugRule { + public: + enum states { + AnyState = 0, + Connected = 1, + Disconnected = 2 + }; + + public: + HotPlugRule(); + virtual ~HotPlugRule(); + + public: + TQStringList outputs; + TQValueList< int > states; + TQString profileName; +}; + +typedef TQValueList< HotPlugRule > HotPlugRulesList; + +class KRANDR_EXPORT SingleScreenData { + public: + SingleScreenData(); + virtual ~SingleScreenData(); + + public: + TQString screenUniqueName; + TQString screenFriendlyName; + bool generic_screen_detected; + bool screen_connected; + + TQStringList resolutions; + TQStringList refresh_rates; + TQStringList color_depths; + TQStringList rotations; + + int current_resolution_index; + int current_refresh_rate_index; + int current_color_depth_index; + + float gamma_red; + float gamma_green; + float gamma_blue; + + int current_rotation_index; + int current_orientation_mask; + bool has_x_flip; + bool has_y_flip; + bool supports_transformations; + + bool is_primary; + bool is_extended; + int absolute_x_position; + int absolute_y_position; + int current_x_pixel_count; + int current_y_pixel_count; + + bool has_dpms; + bool enable_dpms; + unsigned int dpms_standby_delay; + unsigned int dpms_suspend_delay; + unsigned int dpms_off_delay; +}; + +class RandRScreen : public TQObject +{ + 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(); + bool showTestConfigurationDialog(); + +public: + TQString changedMessage() const; + + bool changedFromOriginal() const; + void proposeOriginal(); + + bool proposedChanged() const; + + static TQString rotationName(int rotation, bool pastTense = false, bool capitalised = true); + TQPixmap rotationIcon(int rotation) const; + TQString currentRotationDescription() const; + + int rotationIndexToDegree(int rotation) const; + int rotationDegreeToIndex(int degree) const; + + /** + * Refresh rate functions. + */ + TQStringList refreshRates(int size) const; + + TQString refreshRateDirectDescription(int rate) const; + TQString refreshRateIndirectDescription(int size, int index) const; + TQString refreshRateDescription(int size, int index) const; + + int currentRefreshRate() const; + TQString 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 TQSize& pixelSize(int index) const; + const TQSize& 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(TQSize 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(TDEConfig& config); + void save(TDEConfig& config) const; + +private: + RandRScreenPrivate* d; + + int m_screen; + + TQValueList<TQSize> m_pixelSizes; + TQValueList<TQSize> 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 TQPtrList<RandRScreen> ScreenList; + +class RandRDisplay +{ +public: + RandRDisplay(); + + bool isValid() const; + const TQString& errorCode() const; + const TQString& version() const; + + int eventBase() const; + int screenChangeNotifyEvent() const; + int errorBase() const; + + int screenIndexOfWidget(TQWidget* 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 TDEConfig 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(TDEConfig& config, bool loadScreens = true); + void saveDisplay(TDEConfig& config, bool applyOnStartup, bool syncTrayApp); + + static bool applyOnStartup(TDEConfig& config); + static bool syncTrayApp(TDEConfig& config); + + void applyProposed(bool confirm = true); + + bool showTestConfigurationDialog(); + +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 |