diff options
Diffstat (limited to 'displayconfig/displayconfig.py')
-rwxr-xr-x | displayconfig/displayconfig.py | 1756 |
1 files changed, 1756 insertions, 0 deletions
diff --git a/displayconfig/displayconfig.py b/displayconfig/displayconfig.py new file mode 100755 index 0000000..b4535d4 --- /dev/null +++ b/displayconfig/displayconfig.py @@ -0,0 +1,1756 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +########################################################################### +# displayconfig.py - description # +# ------------------------------ # +# begin : Fri Mar 26 2004 # +# copyright : (C) 2004-2006 by Simon Edwards # +# email : [email protected] # +# # +########################################################################### +# # +# 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. # +# # +########################################################################### + +from qt import * +from kdecore import * +from kdeui import * +import xorgconfig +import xf86misc +import string +import os +import select +import sys +import csv +import time +import signal +import shutil +from ktimerdialog import * +from displayconfigwidgets import * +from displayconfigabstraction import * +from execwithcapture import * + +programname = "Display and Graphics Configuration" +version = "0.8.0" +DUAL_PREVIEW_SIZE = 240 + +# Are we running as a separate standalone application or in KControl? +standalone = __name__=='__main__' + +# Running as the root user or not? +isroot = os.getuid()==0 + +############################################################################ +class GfxCardDialog(KDialogBase): + video_ram_list = [256,512,1024,2048,4096,8192,16384,32768,65536] + + def __init__(self,parent): + KDialogBase.__init__(self,parent,None,True,"Choose Graphics Card", + KDialogBase.Ok|KDialogBase.Cancel, KDialogBase.Cancel) + + self.gfxcarddb = None + self.updatingGUI = True + self.card2listitem = {} + + topbox = QVBox(self) + topbox.setSpacing(KDialog.spacingHint()) + self.setMainWidget(topbox) + label = QLabel(topbox) + label.setText(i18n("Select Graphics Card:")) + self.listview = KListView(topbox) + self.listview.addColumn("") + self.listview.header().hide() + self.listview.setRootIsDecorated(True) + self.connect(self.listview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotListClicked) + topbox.setStretchFactor(self.listview,1) + + self.driver = KListViewItem(self.listview) + self.driver.setText(0,i18n("Drivers")) + self.driver.setSelectable(False) + + self.manufacturer = KListViewItem(self.listview) + self.manufacturer.setText(0,i18n("Manufacturers")) + self.manufacturer.setSelectable(False) + + hbox = QHBox(topbox) + topbox.setStretchFactor(hbox,0) + vbox = QVBox(hbox) + + self.detected_label = QLabel("",vbox) + + self.detected_button = KPushButton(vbox) + self.detected_button.setText(i18n("Select")) + self.connect(self.detected_button,SIGNAL("clicked()"),self.slotSelectDetectedClicked) + + spacer = QWidget(vbox) + vbox.setStretchFactor(self.detected_button,0) + vbox.setStretchFactor(spacer,1) + + hbox.setStretchFactor(vbox,0) + spacer = QWidget(hbox) + hbox.setStretchFactor(spacer,1) + + drivergrid = QGrid(2,hbox) + drivergrid.setSpacing(KDialog.spacingHint()) + QLabel(i18n("Driver:"),drivergrid) + self.standarddriverradio = QRadioButton(i18n("Standard"),drivergrid) + self.connect(self.standarddriverradio,SIGNAL("clicked()"),self.slotStandardDriverClicked) + QWidget(drivergrid) + self.proprietarydriverradio = QRadioButton(i18n("Proprietary"),drivergrid) + self.connect(self.proprietarydriverradio,SIGNAL("clicked()"),self.slotProprietaryDriverClicked) + + QLabel(i18n("Video RAM:"),drivergrid) + self.videoramcombo = QComboBox(drivergrid) + for s in [i18n("256 kB"), + i18n("512 kB"), + i18n("1 MB"), + i18n("2 MB"), + i18n("4 MB"), + i18n("8 MB"), + i18n("16 MB"), + i18n("32 MB"), + i18n("64 MB or more")]: + self.videoramcombo.insertItem(s) + + self.updatingGUI = False + self._setGfxCardDB(GetGfxCardModelDB()) + + def _setGfxCardDB(self,gfxcarddb): + self.updatingGUI = True + self.gfxcarddb = gfxcarddb + + # Add the GfxCards under the Manufacturer item. + keys = gfxcarddb.vendordb.keys() + keys.sort() + for key in keys: + cardkeys = self.gfxcarddb.vendordb[key].keys() + vendoritem = KListViewItem(self.manufacturer) + vendoritem.setText(0,key) + vendoritem.setSelectable(False) + for cardkey in cardkeys: + item = KListViewItem(vendoritem) + item.setText(0,cardkey) + self.card2listitem[self.gfxcarddb.vendordb[key][cardkey]] = item + + # Add the GfxCard _drivers_ under the Drivers item + drivers = gfxcarddb.driverdb.keys() + drivers.sort() + for driver in drivers: + driveritem = KListViewItem(self.driver) + driveritem.setText(0,driver) + self.card2listitem[gfxcarddb.driverdb[driver]] = driveritem + + self.updatingGUI = False + + def do(self,card,proprietarydriver,detected_card,video_ram): + self.updatingGUI = True + item = self.card2listitem[card] + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + + self.selected_video_ram = video_ram + + if detected_card is None: + self.detected_button.setEnabled(False) + self.detected_label.setText(i18n("Detected graphics card:\n(unknown)")) + else: + self.detected_button.setEnabled(True) + self.detected_label.setText(i18n("Detected graphics card:\n'%1'.").arg(detected_card.getName())) + + self.__syncDriver(card,proprietarydriver,video_ram) + + self.detected_card = detected_card + self.selected_card = card + self.updatingGUI = False + + if self.exec_loop()==QDialog.Accepted: + return (self.selected_card, + self.proprietarydriverradio.isChecked() and (self.selected_card is not None), + self.video_ram_list[self.videoramcombo.currentItem()]) + else: + return (card, proprietarydriver,video_ram) + + def __syncDriver(self,card,proprietarydriver,videoram): + if card.getProprietaryDriver() is None: + self.standarddriverradio.setChecked(True) + self.standarddriverradio.setEnabled(False) + self.proprietarydriverradio.setEnabled(False) + else: + self.standarddriverradio.setEnabled(True) + self.proprietarydriverradio.setEnabled(True) + self.standarddriverradio.setChecked(not proprietarydriver) + self.proprietarydriverradio.setChecked(proprietarydriver) + + self.videoramcombo.setEnabled(card.getNeedVideoRam()) + if card.getNeedVideoRam(): + self.videoramcombo.setCurrentItem(self.video_ram_list.index(videoram)) + + def slotSelectDetectedClicked(self): + self.updatingGUI = True + item = self.card2listitem[self.detected_card] + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + self.selected_card = self.detected_card + self.__syncDriver(self.selected_card, self.proprietarydriverradio.isChecked(), self.selected_video_ram) + self.updatingGUI = False + + def slotListClicked(self,item): + if self.updatingGUI: + return + + for key in self.card2listitem: + value = self.card2listitem[key] + if value is item: + self.selected_card = key + self.__syncDriver(self.selected_card, self.proprietarydriverradio.isChecked(), self.selected_video_ram) + + def slotStandardDriverClicked(self): + self.proprietarydriverradio.setChecked(False) + self.standarddriverradio.setChecked(True) + + def slotProprietaryDriverClicked(self): + self.standarddriverradio.setChecked(False) + self.proprietarydriverradio.setChecked(True) + +############################################################################ +class MonitorDialog(KDialogBase): + def __init__(self,parent): + KDialogBase.__init__(self,parent,None,True,"Choose Monitor", + KDialogBase.Ok|KDialogBase.Cancel, KDialogBase.Cancel) + + self.monitordb = None + self.selectedmonitor = None + self.aspect = ModeLine.ASPECT_4_3 + self.monitor2listitem = {} + self.updatingGUI = True + + topbox = QVBox(self) + topbox.setSpacing(KDialog.spacingHint()) + self.setMainWidget(topbox) + label = QLabel(topbox) + label.setText(i18n("Select Monitor:")) + self.listview = KListView(topbox) + self.listview.addColumn("") + self.listview.header().hide() + self.listview.setRootIsDecorated(True) + self.connect(self.listview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotListClicked) + + self.generic = KListViewItem(self.listview) + self.generic.setText(0,i18n("Generic")) + self.generic.setSelectable(False) + + self.manufacturer = KListViewItem(self.listview) + self.manufacturer.setText(0,i18n("Manufacturers")) + self.manufacturer.setSelectable(False) + + grid = QGroupBox(4,QGroupBox.Horizontal,topbox) + grid.setTitle(i18n("Details")) + + label = QLabel(grid) + label.setText(i18n("Horizontal Range:")) + + self.horizrange = KLineEdit(grid) + self.horizrange.setReadOnly(True) + + label = QLabel(grid) + label.setText(i18n("Vertical Refresh:")) + + self.vertrange = KLineEdit(grid) + self.vertrange.setReadOnly(True) + + hbox = QHBox(topbox) + + self.detectbutton = KPushButton(hbox) + self.detectbutton.setText(i18n("Detect Monitor")) # FIXME better label/text? + self.connect(self.detectbutton,SIGNAL("clicked()"),self.slotDetectClicked) + + spacer = QWidget(hbox) + hbox.setStretchFactor(self.detectbutton,0) + hbox.setStretchFactor(spacer,1) + + label = QLabel(hbox) + label.setText(i18n("Image format:")) + hbox.setStretchFactor(label,0) + + self.aspectcombobox = KComboBox(hbox) + self.aspectcombobox.insertItem(i18n("Standard 4:3")) + self.aspectcombobox.insertItem(i18n("Widescreen 16:9")) + hbox.setStretchFactor(self.aspectcombobox,0) + + self.updatingGUI = False + + def setMonitorDB(self,monitordb): + self.monitordb = monitordb + + # Add the Monitors + vendors = monitordb.vendordb.keys() + vendors.sort() + for vendor in vendors: + monitorkeys = self.monitordb.vendordb[vendor].keys() + vendoritem = KListViewItem(self.manufacturer) + vendoritem.setText(0,vendor) + vendoritem.setSelectable(False) + for monitorkey in monitorkeys: + item = KListViewItem(vendoritem) + item.setText(0,monitorkey) + self.monitor2listitem[self.monitordb.vendordb[vendor][monitorkey]] = item + + generics = monitordb.genericdb.keys() + generics.sort() + for generic in generics: + genericitem = KListViewItem(self.generic) + genericitem.setText(0,generic) + self.monitor2listitem[monitordb.genericdb[generic]] = genericitem + + customs = monitordb.getCustomMonitors().keys() + customs.sort() + for custom in customs: + customitem = KListViewItem(self.listview) + customitem.setText(0,custom) + self.monitor2listitem[monitordb.getCustomMonitors()[custom]] = customitem + + def do(self,monitor,aspect,is_primary_monitor=True): + """Run the monitor selection dialog. + + Parameters: + + monitor - Currently selected 'Monitor' object. + + Returns the newly selected monitor object and aspect ratio as a tuple. + """ + if monitor is not None: + self.selectedmonitor = monitor + item = self.monitor2listitem[monitor] + + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + + else: + self.selectedmonitor = None + self.listview.clearSelection() + self.aspect = aspect + + # Only the first/primary monitor can be detected. :-/ + self.detectbutton.setEnabled(is_primary_monitor) + + self.updatingGUI = True + self._syncGUI() + self.updatingGUI = False + + if self.exec_loop()!=QDialog.Accepted: + # Dialog was cancelled. Return the original monitor. + self.selectedmonitor = monitor + else: + self.aspect = [ModeLine.ASPECT_4_3,ModeLine.ASPECT_16_9][self.aspectcombobox.currentItem()] + + return (self.selectedmonitor,self.aspect) + + def slotDetectClicked(self): + detectedmonitor = self.monitordb.detect() + if detectedmonitor is not None: + self.selectedmonitor = detectedmonitor + self._syncGUI() + else: + KMessageBox.error(self, i18n("Sorry, the model and capabilities of your\nmonitor couldn't be detected."), + i18n("Monitor detection failed")) + + def slotListClicked(self,item): + if self.updatingGUI: + return + self.updatingGUI = True + for key in self.monitor2listitem: + value = self.monitor2listitem[key] + if value is item: + self.selectedmonitor = key + break + self._syncGUI() + self.updatingGUI = False + + def _syncGUI(self): + if self.selectedmonitor is not None: + item = self.monitor2listitem[self.selectedmonitor] + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + self.vertrange.setText(self.selectedmonitor.getVerticalSync()) + self.horizrange.setText(self.selectedmonitor.getHorizontalSync()) + else: + self.vertrange.setText("-") + self.horizrange.setText("-") + + self.aspectcombobox.setCurrentItem({ModeLine.ASPECT_4_3:0,ModeLine.ASPECT_16_9:1}[self.aspect]) + +############################################################################ +if standalone: + programbase = KDialogBase +else: + programbase = KCModule + +############################################################################ +class DisplayApp(programbase): + ######################################################################## + def __init__(self,parent=None,name=None): + global standalone,isroot,kapp + KGlobal.locale().insertCatalogue("guidance") + + if standalone: + KDialogBase.__init__(self,KJanusWidget.Tabbed,"Display Configuration",\ + KDialogBase.Apply|KDialogBase.User1|KDialogBase.User2|KDialogBase.Close, KDialogBase.Close) + self.setButtonText(KDialogBase.User1,i18n("Reset")) + self.setButtonText(KDialogBase.User2,i18n("About")) + else: + KCModule.__init__(self,parent,name) + self.setButtons(KCModule.Apply|KCModule.Reset) + self.aboutdata = MakeAboutData() + + # This line has the effect of hiding the "Admin only" message and also forcing + # the Apply/Reset buttons to be shown. Yippie! Only had to read the source + # to work that out. + self.setUseRootOnlyMsg(False) + + # Create a configuration object. + self.config = KConfig("displayconfigrc") + + # Compact mode means that we have to make the GUI + # much smaller to fit on low resolution screens. + self.compact_mode = kapp.desktop().height()<=600 + + KGlobal.iconLoader().addAppDir("guidance") + + global imagedir + imagedir = unicode(KGlobal.dirs().findDirs("data","guidance/pics/displayconfig")[0]) + + self.imagedir = imagedir + + self.xconfigchanged = False + self.xconfigtested = True + + self.availabletargetgammas = [unicode(i18n('1.4')),unicode(i18n('1.6')),unicode(i18n('1.8')),unicode(i18n('2.0')),unicode(i18n('2.2')),unicode(i18n('2.4'))] + self.lightimages = [] + self.mediumimages = [] + self.darkimages = [] + + # X Server stuff + self.xf86server = xf86misc.XF86Server() + + self.xconfigpath = self._findXorgConfig() + SetDataFileDir(unicode(KGlobal.dirs().findResourceDir("data","guidance/pcitable")) + "guidance/") + self.xsetup = XSetup(self.xconfigpath) + + self.updatingGUI = True + self.gfxcarddb = GfxCardModelDB() + self.monitordb = GetMonitorModelDB() + self.monitormodedb = GetMonitorModeDB() + + self._buildGUI() + + # Work out if the currently running Gfxdriver is safe enough that we + # can test other drivers at the same time. + self.badfbrestore = self._badFbRestore() + self.testbutton.setEnabled(isroot and not self._badFbRestore()) + if isroot and not self._badFbRestore(): + self.testunavailablelabel.hide() + else: + self.testunavailablelabel.show() + + # Load up some of our databases, and initialise our state variables. + if len(self.xsetup.getUsedScreens()): + self.currentsizescreen = self.xsetup.getUsedScreens()[0] + self.currentgammascreen = self.xsetup.getUsedScreens()[0] + else: + # FIXME + print "Houston, we have a problem: No screens found in configuration file, exiting. :(" + sys.exit(1) + + self.monitordialog.setMonitorDB(self.monitordb) + + self.aboutus = KAboutApplication(self) + + # For centering the timed Apply dialog. + self.applytimerdialog = None + self.connect(kapp.desktop(), SIGNAL("resized(int)"), self.slotDesktopResized) + self.applydialogscreenindex = 0 + + self.__loadImages() + self._loadConfig() + self._syncGUI() + + if standalone: + self.enableButton(KDialogBase.User1,False) # Reset button + self.enableButtonApply(False) # Apply button + + self.updatingGUI = False + + def _findXorgConfig(self): + # Lookup location of X configfile + for line in ExecWithCapture("xset", ["xset","q"],True).split('\n'): + if line.strip().startswith("Config file"): + return line.split(":")[1].strip() + # Sometimes, xset doesn't know about the configfile location, hence ... + if os.path.isfile("/etc/X11/xorg.conf"): + return "/etc/X11/xorg.conf" + return None + + def _buildGUI(self): + global standalone + if not standalone: + toplayout = QVBoxLayout( self, 0, KDialog.spacingHint() ) + tabcontrol = QTabWidget(self) + toplayout.addWidget(tabcontrol) + toplayout.setStretchFactor(tabcontrol,1) + + #--- Size, Orientation and Positioning --- + tabname = i18n("Size, Orientation && Positioning") + if standalone: + sopage = self.addGridPage(1,QGrid.Horizontal,tabname) + sopage.setSpacing(0) + self.SizePage = SizeOrientationPage(sopage,self.xsetup,self.compact_mode) + else: + self.SizePage = SizeOrientationPage(tabcontrol,self.xsetup,self.compact_mode) + self.SizePage.setMargin(KDialog.marginHint()) + + # Connect all PYSIGNALs from SizeOrientationPage Widget to appropriate actions. + self.connect(self.SizePage,PYSIGNAL("changedSignal()"),self._sendChangedSignal) + self.connect(self.SizePage,PYSIGNAL("resolutionChange(int)"),self.slotResolutionChange) + + if not standalone: + tabcontrol.addTab(self.SizePage,tabname) + + #--- Color & Gamma tab --- + tabname = i18n("Color && Gamma") + if standalone: + gammapage = self.addVBoxPage(tabname) + vbox = QVBox(gammapage) + else: + vbox = QVBox(tabcontrol) + vbox.setMargin(KDialog.marginHint()) + vbox.setSpacing(KDialog.spacingHint()) + + hbox = QHBox(vbox) + hbox.setSpacing(KDialog.spacingHint()) + vbox.setStretchFactor(hbox,0) + label = QLabel(hbox,"textLabel1") + label.setText(i18n("Screen:")) + hbox.setStretchFactor(label,0) + self.gammadisplaycombobox = QComboBox(0,hbox,"comboBox11") + hbox.setStretchFactor(self.gammadisplaycombobox,0) + spacer = QWidget(hbox) + hbox.setStretchFactor(spacer,1) + self.connect(self.gammadisplaycombobox,SIGNAL("activated(int)"),self.slotGammaScreenCombobox) + + # fill the combobox. + for screen in self.xsetup.getUsedScreens(): + self.gammadisplaycombobox.insertItem(screen.getName()) + + if not self.compact_mode: + # Create the colour matching pics + label = QLabel(vbox) + label.setText(i18n("Color calibration image:")) + vbox.setStretchFactor(label,0) + + hbox = QWidget(vbox) + hboxlayout = QHBoxLayout(hbox) + hboxlayout.setSpacing(KDialog.spacingHint()) + self.mediumpic = QLabel(hbox) + self.mediumpic.setFixedSize(305,105) + hboxlayout.addWidget(self.mediumpic,0,Qt.AlignTop) + + label = QLabel(hbox) + label.setPixmap(SmallIcon('info')) + hboxlayout.addWidget(label,0,Qt.AlignTop) + + label = QLabel(i18n("<qt><p>Gamma controls how your monitor displays colors.</p><p>For accurate color reproduction, adjust the gamma correction sliders until the squares blend into the background as much as possible.</p></qt>"),hbox) + label.setTextFormat(Qt.RichText) + hboxlayout.addWidget(label,1,Qt.AlignTop) + + sliderspace = QWidget(vbox) + + grid = QGridLayout(sliderspace, 9, 4, 0, KDialog.spacingHint()) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(1,0) + grid.setColStretch(2,0) + grid.setColStretch(3,1) + + label = QLabel(i18n("Gamma correction:"),sliderspace) + grid.addWidget(label, 0, 0) + + self.gammaradiogroup = QButtonGroup() + self.gammaradiogroup.setRadioButtonExclusive(True) + self.connect(self.gammaradiogroup,SIGNAL("clicked(int)"),self.slotGammaRadioClicked) + + self.allradio = QRadioButton(sliderspace) + grid.addWidget(self.allradio, 0, 1, Qt.AlignTop) + + label = QLabel(i18n("All:"),sliderspace) + grid.addWidget(label, 0, 2) + + self.gammaslider = KDoubleNumInput(0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'gammaslider') + grid.addMultiCellWidget(self.gammaslider,0,1,3,3) + self.gammaslider.setRange(0.5, 2.5, 0.05, True) + self.connect(self.gammaslider, SIGNAL("valueChanged(double)"), self.slotGammaChanged) + + self.componentradio = QRadioButton(sliderspace) + grid.addWidget(self.componentradio, 2, 1, Qt.AlignTop) + + label = QLabel(i18n("Red:"),sliderspace) + grid.addWidget(label, 2, 2) + + self.redslider = KDoubleNumInput(self.gammaslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'redslider') + grid.addMultiCellWidget(self.redslider,2,3,3,3) + self.redslider.setRange(0.5, 2.5, 0.05, True) + self.connect(self.redslider, SIGNAL("valueChanged(double)"), self.slotRedChanged) + + label = QLabel(i18n("Green:"),sliderspace) + grid.addWidget(label, 4, 2) + + self.greenslider = KDoubleNumInput(self.redslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'greenslider') + grid.addMultiCellWidget(self.greenslider,4,5,3,3) + self.greenslider.setRange(0.5, 2.5, 0.05, True) + self.connect(self.greenslider, SIGNAL("valueChanged(double)"), self.slotGreenChanged) + + label = QLabel(i18n("Blue:"),sliderspace) + grid.addWidget(label, 6, 2) + + self.blueslider = KDoubleNumInput(self.greenslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'blueslider') + grid.addMultiCellWidget(self.blueslider,6,7,3,3) + self.blueslider.setRange(0.5, 2.5, 0.05, True) + self.connect(self.blueslider, SIGNAL("valueChanged(double)"), self.slotBlueChanged) + + self.gammaradiogroup.insert(self.allradio,0) + self.gammaradiogroup.insert(self.componentradio,1) + + if not self.compact_mode: + label = QLabel(i18n("Target gamma:"),sliderspace) + grid.addWidget(label, 8, 0) + + hbox = QHBox(sliderspace) + self.targetgammacombo = KComboBox(False,hbox) + self.targetgammacombo.insertItem(i18n('1.4')) + self.targetgammacombo.insertItem(i18n('1.6')) + self.targetgammacombo.insertItem(i18n('1.8 Apple Macintosh standard')) + self.targetgammacombo.insertItem(i18n('2.0 Recommend')) + self.targetgammacombo.insertItem(i18n('2.2 PC standard, sRGB')) + self.targetgammacombo.insertItem(i18n('2.4')) + hbox.setStretchFactor(self.targetgammacombo,0) + spacer = QWidget(hbox) + hbox.setStretchFactor(spacer,1) + grid.addMultiCellWidget(hbox, 8, 8, 1, 3) + + self.connect(self.targetgammacombo,SIGNAL("activated(int)"),self.slotTargetGammaChanged) + + spacer = QWidget(vbox) + vbox.setStretchFactor(spacer,1) + + if not standalone: + tabcontrol.addTab(vbox,tabname) + + #--- Hardware tab --- + if standalone: + hardwarepage = self.addVBoxPage(i18n("Hardware")) + vbox = QVBox(hardwarepage) + else: + vbox = QVBox(tabcontrol) + vbox.setMargin(KDialog.marginHint()) + self.gfxcarddialog = GfxCardDialog(None) + self.monitordialog = MonitorDialog(None) + + self.xscreenwidgets = [] + + for gfxcard in self.xsetup.getGfxCards(): + w = GfxCardWidget(vbox,self.xsetup, gfxcard, self.gfxcarddialog, self.monitordialog) + self.xscreenwidgets.append(w) + self.connect(w,PYSIGNAL("configChanged"),self.slotConfigChanged) + + spacer = QWidget(vbox) + vbox.setStretchFactor(spacer,1) + + if not self.xsetup.mayModifyXorgConfig(): + QLabel(i18n("Changes on this tab require 'root' access."),vbox) + if not standalone: + QLabel(i18n("Click the \"Administrator Mode\" button to allow modifications on this tab."),vbox) + + hbox = QHBox(vbox) + hbox.setSpacing(KDialog.spacingHint()) + self.testbutton = KPushButton(i18n("Test"),hbox) + self.connect(self.testbutton,SIGNAL("clicked()"),self.slotTestClicked) + hbox.setStretchFactor(self.testbutton,0) + + self.testunavailablelabel = QHBox(hbox) + self.testunavailablelabel.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(self.testunavailablelabel) + self.testunavailablelabel.setStretchFactor(tmplabel,0) + tmplabel.setPixmap(SmallIcon('info')) + label = QLabel(i18n("This configuration cannot be safely tested."),self.testunavailablelabel) + self.testunavailablelabel.setStretchFactor(label,1) + self.testunavailablelabel.hide() + + spacer = QWidget(hbox) + hbox.setStretchFactor(spacer,1) + vbox.setStretchFactor(hbox,0) + + if not standalone: + tabcontrol.addTab(vbox,i18n("Hardware")) + + #--- Display Power Saving --- + tabname = i18n("Power saving") + if standalone: + powerpage = self.addGridPage(1,QGrid.Horizontal,tabname) + self.dpmspage = DpmsPage(powerpage) + else: + self.dpmspage = DpmsPage(tabcontrol) + self.dpmspage.setMargin(KDialog.marginHint()) + + #self.SizePage.setScreens(self.xsetup.getScreens()) + + # Connect all PYSIGNALs from SizeOrientationPage Widget to appropriate actions. + #self.connect(self.SizePage,PYSIGNAL("dualheadEnabled(bool)"),self.slotDualheadEnabled) + self.connect(self.dpmspage,PYSIGNAL("changedSignal()"),self._sendChangedSignal) + + if not standalone: + tabcontrol.addTab(self.dpmspage,tabname) + + def save(self): # KCModule + xorg_config_changed = self.xsetup.isXorgConfigChanged() + restart_recommended = self.xsetup.getRestartHint() + + # Check the Size & Orientation tab. + if self.applytimerdialog is None: + self.applytimerdialog = KTimerDialog(15000, KTimerDialog.CountDown, self, "mainKTimerDialog", + True, i18n("Confirm Display Setting Change"), KTimerDialog.Ok | KTimerDialog.Cancel, \ + KTimerDialog.Cancel) + self.applytimerdialog.setButtonOK(KGuiItem(i18n("&Keep"), "button_ok")) + self.applytimerdialog.setButtonCancel(KGuiItem(i18n("&Cancel"), "button_cancel")) + label = KActiveLabel(i18n("Trying new screen settings. Keep these new settings? (Automatically cancelling in 15 seconds.)"), + self.applytimerdialog, "userSpecifiedLabel") + self.applytimerdialog.setMainWidget(label) + + if self.xsetup.isLiveResolutionConfigChanged(): + if self.xsetup.applyLiveResolutionChanges(): + # running X server config has changed. Ask the user. + KDialog.centerOnScreen(self.applytimerdialog, 0) + if self.applytimerdialog.exec_loop(): + self.xsetup.acceptLiveResolutionChanges() + else: + try: + self.xsetup.rejectLiveResolutionChanges() + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + return + else: + # Nothing really changed, just accept the changes. + self.xsetup.acceptLiveResolutionChanges() + + + self.xsetup.acceptLiveGammaChanges() + self.dpmspage.apply() + + # Save the X server config. + if isroot and xorg_config_changed: + + if not self.xconfigtested: + if self.badfbrestore or self._badFbRestore(): + if KMessageBox.warningContinueCancel(self, \ + i18n("The selected driver and monitor configuration can not be safely tested on this computer.\nContinue with this configuration?"), + i18n("Configuration not tested"))!=KMessageBox.Continue: + return + else: + if KMessageBox.warningContinueCancel(self, + i18n("The selected driver and monitor configuration has not been successfully tested on this computer.\nContinue with this configuration?"), + i18n("Configuration not tested"))!=KMessageBox.Continue: + return + + try: + # Backup up the current config file. + i = 1 + while os.path.exists("%s.%i" % (self.xconfigpath,i)): + i += 1 + try: + shutil.copyfile(self.xconfigpath,"%s.%i" % (self.xconfigpath,i)) + except IOError, errmsg: + print "IOError", errmsg, " - while trying to save new xorg.conf - trying to fix" + self.xconfigpath = "/etc/X11/xorg.conf" + xorgfile = open(self.xconfigpath, 'a') + xorgfile.close() + shutil.copyfile(self.xconfigpath,"%s.%i" % (self.xconfigpath,i)) + + # Write out the new config + tmpfilename = self.xconfigpath + ".tmp" + self.xsetup.writeXorgConfig(tmpfilename) + + os.rename(tmpfilename,self.xconfigpath) + except (IOError,TypeError): + print "******* Bang" + raise + return + # FIXME error + + # FIXME the instructions in these messages are probably not quite right. + if restart_recommended==XSetup.RESTART_X: + KMessageBox.information(self, + i18n("Some changes require that the X server be restarted before they take effect. Log out and select \"Restart X server\" from the menu button."), + i18n("X Server restart recommend")) + + if restart_recommended==XSetup.RESTART_SYSTEM: + KMessageBox.information(self, + i18n("Some changes require that the entire system be restarted before they take effect. Log out and select \"Restart computer\" from the log in screen."), + i18n("System restart recommend")) + + self._saveConfig() + self._sendChangedSignal() + + # Called when the desktop is resized. Just center the confirm dialog. + def slotDesktopResized(self): + if self.applytimerdialog is not None: + KDialog.centerOnScreen(self.applytimerdialog, self.applydialogscreenindex) + + def slotApply(self): # KDialogBase + self.save() + + def slotClose(self): # KDialogBase + try: + self.xsetup.rejectLiveGammaChanges() + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + KDialogBase.slotClose(self) + + def load(self): # KCModule + self.__reset() + self._sendChangedSignal() + + def slotUser1(self): # Reset button, KDialogBase + self.load() + + def slotUser2(self): # About button, KDialogBase + self.aboutus.show() + + def slotResolutionChange(self,i): + self.currentsizescreen.setResolutionIndex(i) + self._sendChangedSignal() + + def slotTargetGammaChanged(self,i): + self.targetgamma = i + self._selectGamma(self.targetgamma) + self._sendChangedSignal() + + def slotGammaRadioClicked(self,i): + self.settingall = i==0 + self.gammaslider.setDisabled(not self.settingall) + self.redslider.setDisabled(self.settingall) + self.greenslider.setDisabled(self.settingall) + self.blueslider.setDisabled(self.settingall) + try: + if self.settingall: + self.currentgammascreen.setAllGamma(self.currentgammascreen.getAllGamma()) + else: + self.currentgammascreen.setRedGamma(self.currentgammascreen.getRedGamma()) + self.currentgammascreen.setGreenGamma(self.currentgammascreen.getGreenGamma()) + self.currentgammascreen.setBlueGamma(self.currentgammascreen.getBlueGamma()) + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + self._sendChangedSignal() + + def slotGammaChanged(self,value): + if self.updatingGUI: + return + try: + self.currentgammascreen.setAllGamma(value) + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + self._sendChangedSignal() + + def slotRedChanged(self,value): + if self.updatingGUI: + return + try: + self.currentgammascreen.setRedGamma(value) + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + self._sendChangedSignal() + + def slotGreenChanged(self,value): + if self.updatingGUI: + return + try: + self.currentgammascreen.setGreenGamma(value) + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + self._sendChangedSignal() + + def slotBlueChanged(self,value): + if self.updatingGUI: + return + try: + self.currentgammascreen.setBlueGamma(value) + except: + """Workaround! FIXME: Use isGammaLive function in displayconfigabstraction when this is implemented""" + print "Live gamma change not supported" + self._sendChangedSignal() + + def slotGammaScreenCombobox(self,i): + self.currentgammascreen = self.xsetup.getUsedScreens()[i] + self._syncGUI() + self._sendChangedSignal() + + def slotConfigChanged(self): + self.xconfigchanged = True + self.xconfigtested = False + + # Check if the current X config can be tested. + self.SizePage._syncGUI() + for widget in self.xscreenwidgets: + widget.syncConfig() + self._syncTestButton() + self._sendChangedSignal() + + def slotTestClicked(self): + self.xconfigtested = self.testX() + + def testX(self): + + self.xserverbin = "/usr/X11R6/bin/XFree86" + if not os.path.isfile(self.xserverbin): + self.xserverbin = "/usr/X11R6/bin/Xorg" + rc = False + + # Remove an stale X server lock + try: os.remove("/tmp/.X9-lock") + except OSError: pass + + # Try to find a safe tmp dir. + tmp_dir = None + if os.environ.get("TMPDIR") is not None: + tmp_dir = os.environ.get("TMPDIR") + if tmp_dir is None or not os.path.isdir(tmp_dir): + tmp_dir = os.path.join(os.environ.get("HOME"),"tmp") + if not os.path.isdir(tmp_dir): + tmp_dir = "/tmp" + working_tmp_dir = os.path.join(tmp_dir,"guidance."+str(os.getpid())) + error_filename = os.path.join(working_tmp_dir,"testserver.xoutput") + config_filename = os.path.join(working_tmp_dir,"testserver.config") + auth_filename = os.path.join(working_tmp_dir,"xauthority") + + # Start the Xserver up with the new config file. + try: + # Create our private dir. + os.mkdir(working_tmp_dir,0700) + + # Backup the XAUTHORITY environment variable. + old_xauthority = os.environ.get("XAUTHORITY",None) + + # Write out the new config file. + self.xsetup.writeXorgConfig(config_filename) + + os.system("xauth -f %s add :9 . `mcookie`" % (auth_filename,) ) + # FIXME:: -xf86config is nowhere in man X ?? + pid = os.spawnv(os.P_NOWAIT,"/bin/bash",\ + ["bash","-c","exec %s :9 -xf86config %s -auth %s &> %s" % \ + (self.xserverbin, config_filename, auth_filename, error_filename)]) + print "Got pid",pid + + # Wait for the server to show up. + print str(os.waitpid(pid,os.WNOHANG)) + + # Use our private xauthority file. + os.environ["XAUTHORITY"] = auth_filename + + time.sleep(1) # Wait a sec. + testserver = None + while True: + # Try connecting to the server. + try: + testserver = xf86misc.XF86Server(":9") + break + except xf86misc.XF86Error: + testserver = None + # Check if the server process is still alive. + if os.waitpid(pid,os.WNOHANG) != (0,0): + break + time.sleep(1) # Give the server some more time. + + print "checkpoint 1" + print str(testserver) + + if testserver is not None: + # Start the timed popup on the :9 display. + #servertestpy = str(KGlobal.dirs().findResource("data","guidance/servertestdialog.py")) + servertestpy = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])),"servertestdialog.py") + pythonexe = unicode(KStandardDirs.findExe("python")) + + testrc = os.system(pythonexe + " " + servertestpy + " '" + auth_filename+"' ") + rc = (rc >> 8) == 0 # Test is good if the return code was 0. + testserver = None + os.kill(pid,signal.SIGINT) + else: + # Server failed, read the error info. + msg = "" + try: + fhandle = open(error_filename,'r') + for line in fhandle.readlines(): + if (line.startswith("(EE)") and ("Disabling" not in line)) or line.startswith("Fatal"): + msg += line + msg = unicode(i18n("Messages from the X server:\n")) + msg + except IOError: + msg += unicode(i18n("Sorry, unable to capture the error messages from the X server.")) + KMessageBox.detailedSorry(self,i18n("Sorry, this configuration video card driver\nand monitor doesn't appear to work."),msg) + + finally: + # Attempt some cleanup before we go. + try: os.remove(error_filename) + except OSError: pass + try: os.remove(config_filename) + except OSError: pass + try: os.remove(auth_filename) + except OSError: pass + try: os.rmdir(working_tmp_dir) + except OSError: pass + + if old_xauthority is None: + del os.environ["XAUTHORITY"] + else: + os.environ["XAUTHORITY"] = old_xauthority + + return rc + + def _syncGUI(self): + self.SizePage._syncGUI() + + for gfxcard_widget in self.xscreenwidgets: + gfxcard_widget.syncConfig() + + # Sync the gamma tab. + if not self.compact_mode: + self.targetgammacombo.setCurrentItem(self.targetgamma) + self._selectGamma(self.targetgamma) + + if self.currentgammascreen.isGammaEqual(): + self.gammaradiogroup.setButton(0) + else: + self.gammaradiogroup.setButton(1) + + self.gammaslider.setValue(self.currentgammascreen.getAllGamma()) + self.redslider.setValue(self.currentgammascreen.getRedGamma()) + self.greenslider.setValue(self.currentgammascreen.getGreenGamma()) + self.blueslider.setValue(self.currentgammascreen.getBlueGamma()) + + self.settingall = self.currentgammascreen.isGammaEqual() + self.gammaslider.setDisabled(not self.settingall) + self.redslider.setDisabled(self.settingall) + self.greenslider.setDisabled(self.settingall) + self.blueslider.setDisabled(self.settingall) + self._syncTestButton() + + def _syncTestButton(self): + currentbadfbrestore = self._badFbRestore() + self.testbutton.setEnabled(self.xsetup.mayModifyXorgConfig() and not (self.badfbrestore or currentbadfbrestore)) + if not isroot or (self.xsetup.mayModifyXorgConfig() and not (self.badfbrestore or currentbadfbrestore)): + self.testunavailablelabel.hide() + else: + self.testunavailablelabel.show() + + def _loadConfig(self): + self.config.setGroup("General") + t = self.config.readEntry("targetgamma",unicode(i18n("2.0"))) + if t in self.availabletargetgammas: + t = unicode(i18n('2.0')) + self.targetgamma = self.availabletargetgammas.index(t) + + def _saveConfig(self): + global isroot + if isroot: + return + self.config.setGroup("General") + self.config.writeEntry("targetgamma",self.availabletargetgammas[self.targetgamma]) + for s in self.xsetup.getUsedScreens(): + self.config.setGroup("Screen"+str(s.getScreenIndex())) + self._saveRandRConfig(s) + + # Write out the gamma values. + if self.settingall: + self.config.writeEntry("redgamma", str(s.getAllGamma())) + self.config.writeEntry("greengamma", str(s.getAllGamma())) + self.config.writeEntry("bluegamma", str(s.getAllGamma())) + else: + self.config.writeEntry("redgamma", str(s.getRedGamma())) + self.config.writeEntry("greengamma", str(s.getGreenGamma())) + self.config.writeEntry("bluegamma", str(s.getBlueGamma())) + + self.config.writeEntry("dpmsSeconds", self.dpmspage.seconds) + self.config.writeEntry("dpmsEnabled", ("off","on")[self.dpmspage.enabled]) + self.config.sync() + + def _saveRandRConfig(self,screen): + w,h = screen.getAvailableResolutions()[screen.getResolutionIndex()] + self.config.writeEntry("width",w) + self.config.writeEntry("height",h) + self.config.writeEntry("reflectX", int( (screen.getReflection() & screen.RR_Reflect_X)!=0) ) + self.config.writeEntry("reflectY", int((screen.getReflection() & screen.RR_Reflect_Y)!=0) ) + self.config.writeEntry("refresh", screen.getAvailableRefreshRates()[screen.getRefreshRateIndex()]) + rotationmap = {screen.RR_Rotate_0: "0", screen.RR_Rotate_90: "90", + screen.RR_Rotate_180:"180", screen.RR_Rotate_270: "270"} + self.config.writeEntry("rotate", rotationmap[screen.getRotation()]) + + def _selectGamma(self,i): + self.mediumpic.setPixmap(self.mediumimages[i]) + + def __loadImages(self): + if not self.compact_mode: + for g in ['14','16','18','20','22','24']: + self.mediumimages.append( QPixmap(self.imagedir+'gammapics/MGam'+g+'.png') ) + + self.previewscreen = QPixmap(self.imagedir+'monitor_screen_1280x1024.png') + self.previewscreenportrait = QPixmap(self.imagedir+'monitor_screen_1024x1280.png') + + def __reset(self): + # Reset the screen settings. + self.xsetup.reset() + self.dpmspage.reset() + self._syncGUI() + + # Kcontrol expects updates about whether the contents have changed. + # Also we fake the Apply and Reset buttons here when running outside kcontrol. + def _sendChangedSignal(self): + global standalone + + changed = False + for s in self.xsetup.getUsedScreens(): + changed = changed or s.isResolutionSettingsChanged() + + changed = changed or self.xsetup.isXorgConfigChanged() + changed = changed or self.dpmspage.isChanged() + + if standalone: + self.enableButton(KDialogBase.User1,changed) # Reset button + self.enableButtonApply(changed) # Apply button + else: + self.emit(SIGNAL("changed(bool)"), (changed,) ) + + def _badFbRestore(self): + bad_fb_restore = False + for card in self.xsetup.getGfxCards(): + bad_fb_restore = bad_fb_restore or \ + ((card.getGfxCardModel() is not None) and card.getGfxCardModel().getBadFbRestore(card.isProprietaryDriver())) + return bad_fb_restore + +############################################################################ +class SizeOrientationPage(QWidget): + """ + A TabPage with all the settings for Size and Orientation of the screens, + also features Refreshrates and Dualheadsettings. + + Emits the following signals: + + changeSignal() + + ... + + TODO: + * Update __doc__ with emitted signals, connect these. + * Choose screen (more than one preview) + * Relative positioning. + * Call setRefreshCombo after switching screens. + """ + def __init__(self,parent,xsetup,compact): + QWidget.__init__(self,parent) + + global imagedir + self.xsetup = xsetup + self.imagedir = imagedir + self.parent = parent + self.current_screen = self.xsetup.getPrimaryScreen() + self.current_is_primary = True + self.compact_mode = compact + + self._buildGUI() + self._syncGUI() + + def _syncGUI(self): + if self.current_is_primary: + self.current_screen = self.xsetup.getPrimaryScreen() + else: + self.current_screen = self.xsetup.getSecondaryScreen() + + self._syncGUILayout() + self._syncGUIScreen() + + def _syncGUILayout(self): + # Secondary monitor radios. + available_layouts = self.xsetup.getAvailableLayouts() + + may = self.xsetup.mayModifyLayout() + + self.secondary_clone_radio.setEnabled(may and available_layouts & self.xsetup.LAYOUT_CLONE) + self.secondary_clone_radio.setShown(available_layouts & self.xsetup.LAYOUT_CLONE) + + self.secondary_dual_radio.setEnabled(may and available_layouts & self.xsetup.LAYOUT_DUAL) + self.secondary_dual_radio.setShown(available_layouts & self.xsetup.LAYOUT_DUAL) + + self.secondary_position_combo.setEnabled(may and self.xsetup.getLayout()==self.xsetup.LAYOUT_DUAL) + self.secondary_position_combo.setShown(available_layouts & self.xsetup.LAYOUT_DUAL) + + self.secondary_groupbox.setEnabled(may and available_layouts != self.xsetup.LAYOUT_SINGLE) + # If only the single layout is available, then we just hide the whole radio group + self.secondary_groupbox.setShown(available_layouts!=self.xsetup.LAYOUT_SINGLE) + + if self.xsetup.getLayout()!=self.xsetup.LAYOUT_SINGLE: + self.secondary_radios.setButton(self.secondary_option_ids[self.xsetup.getLayout()]) + else: + if available_layouts & XSetup.LAYOUT_CLONE: + self.secondary_radios.setButton(self.secondary_option_ids[XSetup.LAYOUT_CLONE]) + else: + self.secondary_radios.setButton(self.secondary_option_ids[XSetup.LAYOUT_DUAL]) + + self.secondary_groupbox.setChecked(self.xsetup.getLayout() != self.xsetup.LAYOUT_SINGLE) + + def _syncGUIScreen(self): + # Sync the size tab. + self.resize_slider.setScreen(self.current_screen) + + if self.xsetup.getLayout()!=self.xsetup.LAYOUT_DUAL: + self.resize_slider.setTitle(i18n("Screen size")) + else: + self.resize_slider.setTitle(i18n("Screen size #%1").arg(self.xsetup.getUsedScreens().index(self.current_screen)+1)) + + if self.xsetup.getLayout()==self.xsetup.LAYOUT_DUAL: + if not self.compact_mode: + self.monitor_preview_stack.raiseWidget(self.dual_monitor_preview) + else: + if not self.compact_mode: + self.monitor_preview_stack.raiseWidget(self.monitor_preview) + + # Sync the screen orientation. + width,height = self.current_screen.getAvailableResolutions()[self.current_screen.getResolutionIndex()] + + if not self.compact_mode: + self.monitor_preview.setResolution(width,height) + + if self.current_screen.getRotation()==Screen.RR_Rotate_0: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_0) + elif self.current_screen.getRotation()==Screen.RR_Rotate_90: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_90) + elif self.current_screen.getRotation()==Screen.RR_Rotate_270: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_270) + else: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_180) + + self.monitor_preview.setReflectX(self.current_screen.getReflection() & Screen.RR_Reflect_X) + self.monitor_preview.setReflectY(self.current_screen.getReflection() & Screen.RR_Reflect_Y) + + # Set the resolutions for the dual screen preview. + if self.xsetup.getAvailableLayouts() & XSetup.LAYOUT_DUAL: + for i in [0,1]: + screen = [self.xsetup.getPrimaryScreen(), self.xsetup.getSecondaryScreen()][i] + width,height = screen.getAvailableResolutions()[screen.getResolutionIndex()] + self.dual_monitor_preview.setScreenResolution(i,width,height) + self.dual_monitor_preview.setPosition(self.xsetup.getDualheadOrientation()) + + self._fillRefreshCombo() + + self.orientation_radio_group.setButton( \ + [Screen.RR_Rotate_0, Screen.RR_Rotate_90, Screen.RR_Rotate_270, + Screen.RR_Rotate_180].index(self.current_screen.getRotation())) + # This construct above just maps an rotation to a radiobutton index. + self.mirror_horizontal_checkbox.setChecked(self.current_screen.getReflection() & Screen.RR_Reflect_X) + self.mirror_vertical_checkbox.setChecked(self.current_screen.getReflection() & Screen.RR_Reflect_Y) + + width,height = self.current_screen.getAvailableResolutions()[self.current_screen.getResolutionIndex()] + if not self.compact_mode: + self.monitor_preview.setResolution(width,height) + + # Enable/disable the resolution/rotation/reflection widgets. + may_edit = self.xsetup.mayModifyResolution() + self.normal_orientation_radio.setEnabled(may_edit) + available_rotations = self.current_screen.getAvailableRotations() + + # Hide the whole group box if there is only one boring option. + self.orientation_group_box.setShown(available_rotations!=Screen.RR_Rotate_0) + + self.left_orientation_radio.setEnabled(available_rotations & Screen.RR_Rotate_90 and may_edit) + self.left_orientation_radio.setShown(available_rotations & Screen.RR_Rotate_90) + + self.right_orientation_radio.setEnabled(available_rotations & Screen.RR_Rotate_270 and may_edit) + self.right_orientation_radio.setShown(available_rotations & Screen.RR_Rotate_270) + + self.upside_orientation_radio.setEnabled(available_rotations & Screen.RR_Rotate_180 and may_edit) + self.upside_orientation_radio.setShown(available_rotations & Screen.RR_Rotate_180) + + self.mirror_horizontal_checkbox.setEnabled(available_rotations & Screen.RR_Reflect_X and may_edit) + self.mirror_horizontal_checkbox.setShown(available_rotations & Screen.RR_Reflect_X) + + self.mirror_vertical_checkbox.setEnabled(available_rotations & Screen.RR_Reflect_Y and may_edit) + self.mirror_vertical_checkbox.setShown(available_rotations & Screen.RR_Reflect_Y) + + self.resize_slider.setEnabled(may_edit) + self.size_refresh_combo.setEnabled(may_edit) + + # Set the dual orientation combo. + self.secondary_position_combo.setCurrentItem( + [XSetup.POSITION_LEFTOF, + XSetup.POSITION_RIGHTOF, + XSetup.POSITION_ABOVE, + XSetup.POSITION_BELOW].index(self.xsetup.getDualheadOrientation())) + + def _fillRefreshCombo(self): + # Update refresh combobox + self.size_refresh_combo.clear() + for rate in self.current_screen.getAvailableRefreshRates(): + self.size_refresh_combo.insertItem(i18n("%1 Hz").arg(rate)) + self.size_refresh_combo.setCurrentItem(self.current_screen.getRefreshRateIndex()) + self.current_screen.setRefreshRateIndex(self.size_refresh_combo.currentItem()) + + def slotMonitorFocussed(self,currentMonitor): + if currentMonitor==0: + self.current_screen = self.xsetup.getPrimaryScreen() + self.current_is_primary = True + else: + self.current_screen = self.xsetup.getSecondaryScreen() + self.current_is_primary = False + + self._syncGUIScreen() + + def _sendChangedSignal(self): + self.emit(PYSIGNAL("changedSignal()"),()) + + def _buildGUI(self): + """ Assemble all GUI elements """ + # Layout stuff. + top_layout = QHBoxLayout(self,0,KDialog.spacingHint()) + self.top_layout = top_layout + + # -- Left column with orientation and dualhead box. + vbox = QVBox(self) + top_layout.addWidget(vbox,0) + + # -- Orientation group + self.orientation_group_box = QVGroupBox(vbox) + self.orientation_group_box.setTitle(i18n("Monitor Orientation")) + self.orientation_group_box.setInsideSpacing(KDialog.spacingHint()) + self.orientation_group_box.setInsideMargin(KDialog.marginHint()) + self.orientation_radio_group = QButtonGroup() + self.orientation_radio_group.setRadioButtonExclusive(True) + + self.normal_orientation_radio = QRadioButton(self.orientation_group_box) + self.normal_orientation_radio.setText(i18n("Normal")) + self.left_orientation_radio = QRadioButton(self.orientation_group_box) + self.left_orientation_radio .setText(i18n("Left edge on top")) + self.right_orientation_radio = QRadioButton(self.orientation_group_box) + self.right_orientation_radio.setText(i18n("Right edge on top")) + self.upside_orientation_radio = QRadioButton(self.orientation_group_box) + self.upside_orientation_radio.setText(i18n("Upsidedown")) + + self.mirror_horizontal_checkbox = QCheckBox(self.orientation_group_box) + self.mirror_horizontal_checkbox.setText(i18n("Mirror horizontally")) + self.connect(self.mirror_horizontal_checkbox,SIGNAL("toggled(bool)"),self.slotMirrorHorizontallyToggled) + + self.mirror_vertical_checkbox = QCheckBox(self.orientation_group_box) + self.mirror_vertical_checkbox.setText(i18n("Mirror vertically")) + self.connect(self.mirror_vertical_checkbox,SIGNAL("toggled(bool)"),self.slotMirrorVerticallyToggled) + + self.orientation_radio_group.insert(self.normal_orientation_radio,0) + self.orientation_radio_group.insert(self.left_orientation_radio,1) + self.orientation_radio_group.insert(self.right_orientation_radio,2) + self.orientation_radio_group.insert(self.upside_orientation_radio,3) + self.connect(self.orientation_radio_group,SIGNAL("clicked(int)"),self.slotOrientationRadioClicked) + + # -- Dualhead Box. + self.secondary_groupbox = QVGroupBox(vbox) + self.secondary_groupbox.setCheckable(True) + self.secondary_groupbox.setTitle(i18n("Second screen")) + self.connect(self.secondary_groupbox,SIGNAL("toggled(bool)"),self.slotSecondMonitorToggled) + + self.secondary_radios = QVButtonGroup(None) # Invisible + self.connect(self.secondary_radios,SIGNAL("pressed(int)"),self.slotSecondMonitorRadioPressed) + + self.secondary_options = {} + self.secondary_option_ids = {} + + # Clone radio + self.secondary_clone_radio = QRadioButton(i18n("Clone primary screen"),self.secondary_groupbox) + radio_id = self.secondary_radios.insert(self.secondary_clone_radio) + self.secondary_options[radio_id] = self.xsetup.LAYOUT_CLONE + self.secondary_option_ids[self.xsetup.LAYOUT_CLONE] = radio_id + + # Dual radio + self.secondary_dual_radio = QRadioButton(i18n("Dual screen"),self.secondary_groupbox) + radio_id = self.secondary_radios.insert(self.secondary_dual_radio) + self.secondary_options[radio_id] = self.xsetup.LAYOUT_DUAL + self.secondary_option_ids[self.xsetup.LAYOUT_DUAL] = radio_id + + self.secondary_radios.setButton(radio_id) + + hbox = QHBox(self.secondary_groupbox) + spacer = QWidget(hbox) + spacer.setFixedSize(20,1) + hbox.setStretchFactor(spacer,0) + + self.secondary_position_combo = QComboBox(0,hbox,"") + self.secondary_position_combo.insertItem(i18n("1 left of 2")) + self.secondary_position_combo.insertItem(i18n("1 right of 2")) + self.secondary_position_combo.insertItem(i18n("1 above 2")) + self.secondary_position_combo.insertItem(i18n("1 below 2")) + self.connect(self.secondary_position_combo,SIGNAL("activated(int)"),self.slotSecondaryPositionChange) + + spacer = QWidget(vbox) + vbox.setStretchFactor(spacer,1) + + vbox = QVBox(self) + top_layout.addWidget(vbox,1) + + if not self.compact_mode: + # -- Right columns with preview, size and refresh widgets. + + # -- Preview Box. + self.monitor_preview_stack = QWidgetStack(vbox) + + self.monitor_preview = MonitorPreview(self.monitor_preview_stack,self.imagedir) + self.monitor_preview_stack.addWidget(self.monitor_preview) + self.connect(self.monitor_preview,PYSIGNAL("focussed()"),self.slotMonitorFocussed) + + self.dual_monitor_preview = DualMonitorPreview(self.monitor_preview_stack, DUAL_PREVIEW_SIZE, self.imagedir) + + self.monitor_preview_stack.addWidget(self.dual_monitor_preview) + self.connect(self.dual_monitor_preview,PYSIGNAL("pressed()"),self.slotMonitorFocussed) + self.connect(self.dual_monitor_preview,PYSIGNAL("positionChanged()"),self.slotDualheadPreviewPositionChanged) + + # -- Size & Refresh Box. + if not self.compact_mode: + hbox = QHBox(vbox) + else: + hbox = QVBox(vbox) + hbox.setSpacing(KDialog.spacingHint()) + + self.resize_slider = ResizeSlider(hbox) + self.connect(self.resize_slider,PYSIGNAL("resolutionChange(int)"),self.slotResolutionChange) + + hbox2 = QHBox(hbox) + self.refresh_label = QLabel(hbox2,"RefreshLabel") + self.refresh_label.setText(i18n("Refresh:")) + + self.size_refresh_combo = QComboBox(0,hbox2,"comboBox1") # gets filled in setRefreshRates() + self.connect(self.size_refresh_combo,SIGNAL("activated(int)"),self.slotRefreshRateChange) + if self.compact_mode: + spacer = QWidget(hbox2) + hbox2.setStretchFactor(spacer,1) + + spacer = QWidget(vbox) + vbox.setStretchFactor(spacer,1) + + self.clearWState(Qt.WState_Polished) + + def setNotification(self,text): + self.notify.setText(text) + + def slotOrientationRadioClicked(self,i): + self.current_screen.setRotation( + [Screen.RR_Rotate_0, Screen.RR_Rotate_90,Screen.RR_Rotate_270, Screen.RR_Rotate_180][i]) + + if self.current_screen.getRotation()==Screen.RR_Rotate_0: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_0) + elif self.current_screen.getRotation()==Screen.RR_Rotate_90: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_90) + elif self.current_screen.getRotation()==Screen.RR_Rotate_270: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_270) + else: + self.monitor_preview.setRotation(MonitorPreview.ROTATE_180) + + self._sendChangedSignal() + + def slotMirrorHorizontallyToggled(self,flag): + # Bit flippin' + if flag: + self.current_screen.setReflection(self.current_screen.getReflection() | Screen.RR_Reflect_X) + else: + self.current_screen.setReflection(self.current_screen.getReflection() & ~Screen.RR_Reflect_X) + self.monitor_preview.setReflectX(flag) + self._sendChangedSignal() + + def slotMirrorVerticallyToggled(self,flag): + # Bit flippin' + if flag: + self.current_screen.setReflection(self.current_screen.getReflection() | Screen.RR_Reflect_Y) + else: + self.current_screen.setReflection(self.current_screen.getReflection() & ~Screen.RR_Reflect_Y) + self.monitor_preview.setReflectY(flag) + self._sendChangedSignal() + + def slotResolutionChange(self,i): + self.current_screen.setResolutionIndex(i) + width,height = self.current_screen.getAvailableResolutions()[i] + + if not self.compact_mode: + self.monitor_preview.setResolution(width,height) + self.dual_monitor_preview.setScreenResolution( + self.xsetup.getUsedScreens().index(self.current_screen), + width,height) + + self._fillRefreshCombo() + self._sendChangedSignal() + + def slotRefreshRateChange(self,index): + self.current_screen.setRefreshRateIndex(index) + self._sendChangedSignal() + + def setScreen(self,screen): + self.current_screen = screen + self._syncGUI() + + def slotSecondMonitorToggled(self,enabled): + if enabled: + pressed_id = self.secondary_radios.selectedId() + self.xsetup.setLayout(self.secondary_options[pressed_id]) + else: + self.xsetup.setLayout(self.xsetup.LAYOUT_SINGLE) + + if self.xsetup.getLayout()!=self.xsetup.LAYOUT_DUAL: + self.current_screen = self.xsetup.getUsedScreens()[0] + + self.secondary_position_combo.setEnabled(self.xsetup.getLayout()==XSetup.LAYOUT_DUAL) + + self._syncGUIScreen() + self._sendChangedSignal() + + def slotSecondMonitorRadioPressed(self,pressedId): + self.xsetup.setLayout(self.secondary_options[pressedId]) + + if self.xsetup.getLayout()!=XSetup.LAYOUT_DUAL: + self.current_screen = self.xsetup.getUsedScreens()[0] + + self.secondary_position_combo.setEnabled(self.xsetup.getLayout()==XSetup.LAYOUT_DUAL) + + if self.xsetup.getLayout()==XSetup.LAYOUT_DUAL: + if not self.compact_mode: + self.monitor_preview_stack.raiseWidget(self.dual_monitor_preview) + else: + if not self.compact_mode: + self.monitor_preview_stack.raiseWidget(self.monitor_preview) + + self._syncGUIScreen() + self._sendChangedSignal() + + def slotSecondaryPositionChange(self,index): + position = [XSetup.POSITION_LEFTOF,XSetup.POSITION_RIGHTOF,XSetup.POSITION_ABOVE,XSetup.POSITION_BELOW][index] + self.xsetup.setDualheadOrientation(position) + self.dual_monitor_preview.setPosition(position) + self._sendChangedSignal() + + def slotDualheadPreviewPositionChanged(self,position): + self.xsetup.setDualheadOrientation(position) + index = { + XSetup.POSITION_LEFTOF:0, + XSetup.POSITION_RIGHTOF:1, + XSetup.POSITION_ABOVE:2, + XSetup.POSITION_BELOW:3 + }[position] + self.secondary_position_combo.setCurrentItem(index) + self._sendChangedSignal() + + def setMargin(self,margin): + self.top_layout.setMargin(margin) + + def setSpacing(self,spacing): + self.top_layout.setSpacing(spacing) + +############################################################################ +class DpmsPage(QWidget): + + # Mapping values in seconds to human-readable labels. + intervals = ( + (60,i18n("1 minute")), + (120,i18n("2 minutes")), + (180,i18n("3 minutes")), + (300,i18n("5 minutes")), + (600,i18n("10 minutes")), + (900,i18n("15 minutes")), + (1200,i18n("20 minutes")), + (1500,i18n("25 minutes")), + (1800,i18n("30 minutes")), + (2700,i18n("45 minutes")), + (3600,i18n("1 hour")), + (7200,i18n("2 hours")), + (10800,i18n("3 hours")), + (14400,i18n("4 hours")), + (18000,i18n("5 hours"))) + + def __init__(self,parent = None,name = None,modal = 0,fl = 0): + global imagedir + QWidget.__init__(self,parent) + + # Where to find xset. + self.xset_bin = os.popen('which xset').read()[:-1] + + if not name: + self.setName("DPMSTab") + + dpms_tab_layout = QVBoxLayout(self,0,0,"DPMSTabLayout") + self.top_layout = dpms_tab_layout + + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + dpms_tab_layout.addWidget(hbox) + + self.dpmsgroup = QHGroupBox(hbox,"dpmsgroup") + self.dpmsgroup.setInsideSpacing(KDialog.spacingHint()) + self.dpmsgroup.setInsideMargin(KDialog.marginHint()) + self.dpmsgroup.setTitle(i18n("Enable power saving")) + self.dpmsgroup.setCheckable(1) + + self.connect(self.dpmsgroup,SIGNAL("toggled(bool)"),self.slotDpmsToggled) + + hbox2 = QHBox(self.dpmsgroup) + hbox2.setSpacing(KDialog.spacingHint()) + + dpmstext = QLabel(hbox2,"dpmstext") + dpmstext.setText(i18n("Switch off monitor after:")) + + self.dpmscombo = QComboBox(0,hbox2,"dpmscombo") + self.fillCombo(self.dpmscombo) + self.connect(self.dpmscombo,SIGNAL("activated(int)"),self.slotDpmsActivated) + + spacer = QWidget(hbox2) + hbox2.setStretchFactor(spacer,1) + + self.energystarpix = QLabel(hbox,"energystarpix") + self.energystarpix.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.energystarpix.sizePolicy().hasHeightForWidth())) + self.energystarpix.setMinimumSize(QSize(150,77)) + self.energystarpix.setPixmap(QPixmap(imagedir+"../energystar.png")) + self.energystarpix.setScaledContents(1) + + bottomspacer = QSpacerItem(51,160,QSizePolicy.Minimum,QSizePolicy.Expanding) + dpms_tab_layout.addItem(bottomspacer) + + self.clearWState(Qt.WState_Polished) + + self.readDpms() + + def fillCombo(self,combo): + """ Fill the combobox with the values from our list """ + for interval in self.intervals: + combo.insertItem(interval[1]) + + def slotDpmsActivated(self,index): + """ Another dpms value has been chosen, update buttons at bottom. """ + self.emit(PYSIGNAL("changedSignal()"), ()) + + def slotDpmsToggled(self,bool): + """ Dpms checkbox has been toggled, update buttons at bottom. """ + self.emit(PYSIGNAL("changedSignal()"), ()) + + def readDpms(self): + # FIXME it is not the widget's job to read or change the values, just to present the GUI. + """ Read output from xset -q and parse DPMS settings from it. """ + # FIXME: localisation problem running this command. + lines = ExecWithCapture(self.xset_bin,[self.xset_bin,'-q']).split('\n') + + self.dpms_min = 1800 + self.dpms_enabled = False + + for line in lines: + if line.strip().startswith("Standby"): + self.dpms_min = int(line.strip().split()[5]) # TODO: More subtle exception handling. ;) + if line.strip().startswith("DPMS is"): + self.dpms_enabled = line.strip().split()[2]=="Enabled" + + if self.dpms_min==0: # 0 also means don't use Standby mode. + self.dpms_enabled = False + self.dpms_min = 1800 + + self.dpmsgroup.setChecked(self.dpms_enabled) + + for i in range(len(self.intervals)): + diff = abs(self.intervals[i][0] - self.dpms_min) + if i==0: + last_diff = diff + if (last_diff <= diff and i!=0) or (last_diff < diff): + i = i-1 + break + last_diff = diff + self.dpmscombo.setCurrentItem(i) + + def isChanged(self): + """ Check if something has changed since startup or last apply(). """ + if self.dpmsgroup.isChecked(): + if self.intervals[self.dpmscombo.currentItem()][0] != self.dpms_min: + return True + if self.dpmsgroup.isChecked() != self.dpms_enabled: + return True + return False + + else: + # self.dpmsgroup.isChecked() is False + return self.dpms_enabled # self.dpms_enabled != False + + def applyDpms(self): + """ Use xset to apply new dpms settings. """ + self.enabled = self.dpmsgroup.isChecked() + self.seconds = self.intervals[self.dpmscombo.currentItem()][0] + if self.enabled: + # Switch dpms on and set timeout interval. + cmd_on = "%s +dpms" % self.xset_bin + cmd_set = "%s dpms %i %i %i" % (self.xset_bin, self.seconds,self.seconds,self.seconds) + print cmd_set + if os.system(cmd_set) != 0: + print "DPMS command failed: ", cmd_set + else: + # Switch dpms off. + cmd_on = "%s -dpms" % self.xset_bin + if os.system(cmd_on) != 0: + print "DPMS command failed: ", cmd_on + self.readDpms() + self.emit(PYSIGNAL("changedSignal()"), ()) + + def apply(self): + self.applyDpms() + + def reset(self): + for i in range(len(self.intervals)): + if self.intervals[i][0] == self.dpms_min: + self.dpmscombo.setCurrentItem(i) + break + + self.dpmsgroup.setChecked(self.dpms_enabled) + + def setMargin(self,margin): + self.top_layout.setMargin(margin) + + def setSpacing(self,spacing): + self.top_layout.setSpacing(spacing) + +############################################################################ +def create_displayconfig(parent,name): + """ Factory function for KControl """ + global kapp + kapp = KApplication.kApplication() + return DisplayApp(parent, name) + +############################################################################ +def MakeAboutData(): + aboutdata = KAboutData("guidance",programname,version, \ + "Display and Graphics Configuration Tool", KAboutData.License_GPL, \ + "Copyright (C) 2003-2007 Simon Edwards", \ + "Thanks go to Phil Thompson, Jim Bublitz and David Boddie.") + aboutdata.addAuthor("Simon Edwards","Developer","[email protected]", \ + "http://www.simonzone.com/software/") + aboutdata.addAuthor("Sebastian Kügler","Developer","[email protected]", \ + "http://vizZzion.org"); + aboutdata.addCredit("Pete Andrews","Gamma calibration pictures/system",None, \ + "http://www.photoscientia.co.uk/Gamma.htm") + return aboutdata + +if standalone: + aboutdata = MakeAboutData() + KCmdLineArgs.init(sys.argv,aboutdata) + + kapp = KApplication() + + displayapp = DisplayApp() + displayapp.exec_loop() |