#!/usr/bin/python # -*- coding: UTF-8 -*- ########################################################################### # displayconfig.py - description # # ------------------------------ # # begin : Fri Mar 26 2004 # # copyright : (C) 2004-2006 by Simon Edwards # # email : simon@simonzone.com # # # ########################################################################### # # # 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 tdecore import * from tdeui 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("

Gamma controls how your monitor displays colors.

For accurate color reproduction, adjust the gamma correction sliders until the squares blend into the background as much as possible.

"),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 = TDEApplication.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","simon@simonzone.com", \ "http://www.simonzone.com/software/") aboutdata.addAuthor("Sebastian Kügler","Developer","sebas@kde.org", \ "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() TDECmdLineArgs.init(sys.argv,aboutdata) kapp = TDEApplication() displayapp = DisplayApp() displayapp.exec_loop()