From 19ae07d0d443ff8b777f46bcbe97119483356bfd Mon Sep 17 00:00:00 2001 From: tpearson Date: Sat, 13 Mar 2010 05:43:39 +0000 Subject: [PATCH] Added KDE3 version of KDE Guidance utilities git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kde-guidance@1102646 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- COPYING | 280 + ChangeLog | 334 + MANIFEST.in | 21 + Makefile.am | 15 + README | 189 + README.developers | 48 + TODO | 294 + displayconfig-TODO | 135 + .../40guidance-displayconfig_restore | 11 + displayconfig/ScanPCI.py | 340 + displayconfig/displayconfig-hwprobe.py | 132 + displayconfig/displayconfig-restore.py | 324 + displayconfig/displayconfig.desktop | 49 + displayconfig/displayconfig.py | 1756 ++++ displayconfig/displayconfigabstraction.py | 3230 +++++++ displayconfig/displayconfighardwaretab.py | 741 ++ displayconfig/displayconfigwidgets.py | 809 ++ displayconfig/driver-options.txt | 1054 +++ displayconfig/energy.py | 86 + displayconfig/energystar.png | Bin 0 -> 24160 bytes displayconfig/execwithcapture.py | 47 + displayconfig/extramodes | 39 + displayconfig/infimport.py | 297 + displayconfig/ktimerdialog.py | 155 + displayconfig/ldetect-lst/Cards+ | 2505 +++++ displayconfig/ldetect-lst/MonitorsDB | 5618 ++++++++++++ displayconfig/ldetect-lst/pcitable | 8017 +++++++++++++++++ displayconfig/monitor.png | Bin 0 -> 3601 bytes displayconfig/servertestdialog.py | 113 + displayconfig/vesamodes | 110 + displayconfig/videocard.png | Bin 0 -> 2083 bytes displayconfig/widescreenmodes | 66 + displayconfig/xconfig-test.py | 15 + displayconfig/xorgconfig.py | 903 ++ doc/en/index.docbook | 555 ++ grubconfig/grubconfig.desktop | 49 + grubconfig/grubconfig.py | 697 ++ .../pics/16x16/displayconfig.png | Bin 0 -> 1252 bytes kde/displayconfig/pics/colors.png | Bin 0 -> 274 bytes kde/displayconfig/pics/display_1280x1024.png | Bin 0 -> 4690 bytes kde/displayconfig/pics/dualhead/monitor_1.png | Bin 0 -> 2239 bytes kde/displayconfig/pics/dualhead/monitor_2.png | Bin 0 -> 3784 bytes kde/displayconfig/pics/energystar.png | Bin 0 -> 24160 bytes kde/displayconfig/pics/gammapics/MGam14.gif | Bin 0 -> 1878 bytes kde/displayconfig/pics/gammapics/MGam14.png | Bin 0 -> 489 bytes kde/displayconfig/pics/gammapics/MGam16.gif | Bin 0 -> 1877 bytes kde/displayconfig/pics/gammapics/MGam16.png | Bin 0 -> 485 bytes kde/displayconfig/pics/gammapics/MGam18.gif | Bin 0 -> 1877 bytes kde/displayconfig/pics/gammapics/MGam18.png | Bin 0 -> 480 bytes kde/displayconfig/pics/gammapics/MGam20.gif | Bin 0 -> 1877 bytes kde/displayconfig/pics/gammapics/MGam20.png | Bin 0 -> 487 bytes kde/displayconfig/pics/gammapics/MGam22.gif | Bin 0 -> 1875 bytes kde/displayconfig/pics/gammapics/MGam22.png | Bin 0 -> 492 bytes kde/displayconfig/pics/gammapics/MGam24.gif | Bin 0 -> 1883 bytes kde/displayconfig/pics/gammapics/MGam24.png | Bin 0 -> 488 bytes kde/displayconfig/pics/hi32-display.png | Bin 0 -> 1926 bytes kde/displayconfig/pics/hi32-gfxcard.png | Bin 0 -> 1618 bytes .../pics/monitor_resizable/background.png | Bin 0 -> 3978 bytes .../pics/monitor_resizable/background_r90.png | Bin 0 -> 2958 bytes .../monitor_resizable/background_wide.png | Bin 0 -> 4222 bytes .../monitor_resizable/background_wide_r90.png | Bin 0 -> 3104 bytes .../pics/monitor_resizable/monitor.png | Bin 0 -> 7568 bytes .../pics/monitor_resizable/monitor_r90.png | Bin 0 -> 7727 bytes .../pics/monitor_resizable/monitor_wide.png | Bin 0 -> 8017 bytes .../monitor_resizable/monitor_wide_r90.png | Bin 0 -> 7904 bytes .../pics/monitor_resizable/window_4th.png | Bin 0 -> 14592 bytes .../window_bottom_left_4th.png | Bin 0 -> 11400 bytes .../window_bottom_right_4th.png | Bin 0 -> 1528 bytes .../pics/16x16/disksfilesystems.png | Bin 0 -> 914 bytes kde/mountconfig/pics/exec.png | Bin 0 -> 1021 bytes kde/mountconfig/pics/file.png | Bin 0 -> 1007 bytes kde/mountconfig/pics/greenled.png | Bin 0 -> 466 bytes kde/mountconfig/pics/greyled.png | Bin 0 -> 389 bytes kde/mountconfig/pics/hi16-blockdevice.png | Bin 0 -> 793 bytes kde/mountconfig/pics/hi16-burner.png | Bin 0 -> 899 bytes kde/mountconfig/pics/hi16-cdrom.png | Bin 0 -> 833 bytes kde/mountconfig/pics/hi16-floppy.png | Bin 0 -> 729 bytes kde/mountconfig/pics/hi16-hdd.png | Bin 0 -> 724 bytes kde/mountconfig/pics/hi16-lock.png | Bin 0 -> 749 bytes kde/mountconfig/pics/hi16-memory.png | Bin 0 -> 674 bytes kde/mountconfig/pics/hi16-network.png | Bin 0 -> 893 bytes kde/mountconfig/pics/hi16-password.png | Bin 0 -> 706 bytes kde/mountconfig/pics/hi16-usbpen.png | Bin 0 -> 860 bytes kde/mountconfig/pics/hi32-samba.png | Bin 0 -> 2302 bytes kde/mountconfig/pics/important.png | Bin 0 -> 11391 bytes kde/mountconfig/pics/kcmpartitions.png | Bin 0 -> 2480 bytes kde/mountconfig/pics/kde1.png | Bin 0 -> 739 bytes kde/mountconfig/pics/kde2.png | Bin 0 -> 858 bytes kde/mountconfig/pics/kde3.png | Bin 0 -> 859 bytes kde/mountconfig/pics/kde4.png | Bin 0 -> 858 bytes kde/mountconfig/pics/kde5.png | Bin 0 -> 863 bytes kde/mountconfig/pics/kde6.png | Bin 0 -> 839 bytes kde/mountconfig/pics/laserwarn.png | Bin 0 -> 476 bytes kde/mountconfig/pics/tux.png | Bin 0 -> 686 bytes kde/mountconfig/pics/user.png | Bin 0 -> 756 bytes kde/powermanager/pics/ac-adapter.png | Bin 0 -> 563 bytes kde/powermanager/pics/ac-adapter.svg | 982 ++ .../pics/battery-charging-000.png | Bin 0 -> 829 bytes .../pics/battery-charging-010.png | Bin 0 -> 858 bytes .../pics/battery-charging-020.png | Bin 0 -> 840 bytes .../pics/battery-charging-030.png | Bin 0 -> 840 bytes .../pics/battery-charging-040.png | Bin 0 -> 834 bytes .../pics/battery-charging-050.png | Bin 0 -> 835 bytes .../pics/battery-charging-060.png | Bin 0 -> 830 bytes .../pics/battery-charging-070.png | Bin 0 -> 854 bytes .../pics/battery-charging-090.png | Bin 0 -> 866 bytes .../pics/battery-charging-100.png | Bin 0 -> 848 bytes .../pics/battery-discharging-000.png | Bin 0 -> 342 bytes .../pics/battery-discharging-010.png | Bin 0 -> 392 bytes .../pics/battery-discharging-020.png | Bin 0 -> 393 bytes .../pics/battery-discharging-020.svg | 662 ++ .../pics/battery-discharging-030.png | Bin 0 -> 398 bytes .../pics/battery-discharging-040.png | Bin 0 -> 398 bytes .../pics/battery-discharging-050.png | Bin 0 -> 399 bytes .../pics/battery-discharging-060.png | Bin 0 -> 399 bytes .../pics/battery-discharging-070.png | Bin 0 -> 397 bytes .../pics/battery-discharging-090.png | Bin 0 -> 395 bytes .../pics/battery-discharging-100.png | Bin 0 -> 374 bytes kde/powermanager/pics/battery_charging_0.svg | 1350 +++ kde/powermanager/pics/battery_charging_1.svg | 3551 ++++++++ kde/powermanager/pics/battery_charging_2.svg | 2822 ++++++ kde/powermanager/pics/battery_charging_3.svg | 2093 +++++ kde/powermanager/pics/battery_charging_4.svg | 1364 +++ kde/powermanager/pics/battery_charging_5.svg | 2822 ++++++ kde/powermanager/pics/battery_charging_6.svg | 3551 ++++++++ kde/powermanager/pics/battery_charging_7.svg | 1364 +++ kde/powermanager/pics/battery_charging_8.svg | 2093 +++++ kde/powermanager/pics/battery_charging_9.svg | 1358 +++ .../pics/battery_discharging_0.svg | 1941 ++++ .../pics/battery_discharging_1.svg | 3343 +++++++ .../pics/battery_discharging_2.svg | 4713 ++++++++++ .../pics/battery_discharging_3.svg | 4713 ++++++++++ .../pics/battery_discharging_4.svg | 1964 ++++ .../pics/battery_discharging_5.svg | 3334 +++++++ .../pics/battery_discharging_6.svg | 4704 ++++++++++ .../pics/battery_discharging_7.svg | 1964 ++++ .../pics/battery_discharging_8.svg | 3334 +++++++ .../pics/battery_discharging_9.svg | 4703 ++++++++++ kde/powermanager/pics/processor.png | Bin 0 -> 908 bytes kde/powermanager/pics/processor.svg | 272 + kde/serviceconfig/pics/16x16/daemons.png | Bin 0 -> 682 bytes kde/serviceconfig/pics/hi32-app-daemons.png | Bin 0 -> 1609 bytes kde/serviceconfig/pics/laserwarn.png | Bin 0 -> 476 bytes kde/userconfig/pics/16x16/userconfig.png | Bin 0 -> 756 bytes kde/userconfig/pics/hi16-encrypted.png | Bin 0 -> 927 bytes kde/userconfig/pics/hi32-group.png | Bin 0 -> 2303 bytes kde/userconfig/pics/hi32-identity.png | Bin 0 -> 3148 bytes kde/userconfig/pics/hi32-password.png | Bin 0 -> 1801 bytes kde/userconfig/pics/hi32-user.png | Bin 0 -> 1806 bytes kde/wineconfig/pics/16x16/wineconfig.png | Bin 0 -> 441 bytes kde/wineconfig/pics/16x16/wineconfig.svg | 56 + kde/wineconfig/pics/32-wine.png | Bin 0 -> 904 bytes kde/wineconfig/pics/48-wine.png | Bin 0 -> 1192 bytes kde/wineconfig/pics/kdewinewizard.png | Bin 0 -> 18467 bytes modules/ixf86misc.c | 516 ++ modules/xf86misc.py | 197 + mountconfig/MicroHAL.py | 884 ++ mountconfig/SMBShareSelectDialog.py | 573 ++ mountconfig/SimpleCommandRunner.py | 69 + mountconfig/fuser.py | 299 + mountconfig/fuser_ui.ui | 352 + mountconfig/mountconfig.desktop | 51 + mountconfig/mountconfig.py | 3303 +++++++ mountconfig/sizeview.py | 504 ++ package/mandrake/guidance-kcmdisplayconfig | 1 + package/mandrake/guidance-kcmmountconfig | 1 + package/mandrake/guidance-kcmserviceconfig | 1 + package/mandrake/guidance-kcmuserconfig | 1 + package/mandrake/guidance.spec | 79 + powermanager/TODO | 49 + powermanager/g-p-m-restart | 6 + powermanager/gpmhelper.py | 147 + powermanager/guidance-power-manager.desktop | 35 + powermanager/guidance-power-manager.py | 1134 +++ powermanager/guidance_power_manager_ui.py | 241 + powermanager/guidance_power_manager_ui.ui | 530 ++ powermanager/hal-test.py | 35 + powermanager/notify.py | 68 + powermanager/notify.ui | 75 + powermanager/powermanage.py | 606 ++ powermanager/powermanager_ui.ui | 924 ++ powermanager/recompile-ui-files | 6 + powermanager/tooltip.py | 57 + powermanager/tooltip.ui | 53 + review_guidance_malaga.pdf | Bin 0 -> 393561 bytes serviceconfig/serviceconfig.desktop | 50 + serviceconfig/serviceconfig.py | 1481 +++ setup.cfg | 2 + setup.py | 208 + uninstall_rude.py | 5 + userconfig/unixauthdb.py | 1154 +++ userconfig/userconfig.desktop | 49 + userconfig/userconfig.py | 1753 ++++ wineconfig/drivedetect.py | 122 + wineconfig/firstrunwizard.py | 326 + wineconfig/kcm_wineconfig.cpp | 156 + wineconfig/wineconfig.desktop | 57 + wineconfig/wineconfig.py | 3552 ++++++++ wineconfig/wineread.py | 543 ++ wineconfig/winewrite.py | 489 + 200 files changed, 108831 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 MANIFEST.in create mode 100644 Makefile.am create mode 100644 README create mode 100644 README.developers create mode 100644 TODO create mode 100644 displayconfig-TODO create mode 100644 displayconfig/40guidance-displayconfig_restore create mode 100644 displayconfig/ScanPCI.py create mode 100755 displayconfig/displayconfig-hwprobe.py create mode 100755 displayconfig/displayconfig-restore.py create mode 100644 displayconfig/displayconfig.desktop create mode 100755 displayconfig/displayconfig.py create mode 100644 displayconfig/displayconfigabstraction.py create mode 100644 displayconfig/displayconfighardwaretab.py create mode 100644 displayconfig/displayconfigwidgets.py create mode 100644 displayconfig/driver-options.txt create mode 100644 displayconfig/energy.py create mode 100644 displayconfig/energystar.png create mode 100644 displayconfig/execwithcapture.py create mode 100644 displayconfig/extramodes create mode 100755 displayconfig/infimport.py create mode 100644 displayconfig/ktimerdialog.py create mode 100644 displayconfig/ldetect-lst/Cards+ create mode 100644 displayconfig/ldetect-lst/MonitorsDB create mode 100644 displayconfig/ldetect-lst/pcitable create mode 100644 displayconfig/monitor.png create mode 100755 displayconfig/servertestdialog.py create mode 100644 displayconfig/vesamodes create mode 100644 displayconfig/videocard.png create mode 100644 displayconfig/widescreenmodes create mode 100644 displayconfig/xconfig-test.py create mode 100755 displayconfig/xorgconfig.py create mode 100644 doc/en/index.docbook create mode 100644 grubconfig/grubconfig.desktop create mode 100644 grubconfig/grubconfig.py create mode 100644 kde/displayconfig/pics/16x16/displayconfig.png create mode 100644 kde/displayconfig/pics/colors.png create mode 100644 kde/displayconfig/pics/display_1280x1024.png create mode 100644 kde/displayconfig/pics/dualhead/monitor_1.png create mode 100644 kde/displayconfig/pics/dualhead/monitor_2.png create mode 100644 kde/displayconfig/pics/energystar.png create mode 100644 kde/displayconfig/pics/gammapics/MGam14.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam14.png create mode 100644 kde/displayconfig/pics/gammapics/MGam16.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam16.png create mode 100644 kde/displayconfig/pics/gammapics/MGam18.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam18.png create mode 100644 kde/displayconfig/pics/gammapics/MGam20.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam20.png create mode 100644 kde/displayconfig/pics/gammapics/MGam22.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam22.png create mode 100644 kde/displayconfig/pics/gammapics/MGam24.gif create mode 100644 kde/displayconfig/pics/gammapics/MGam24.png create mode 100644 kde/displayconfig/pics/hi32-display.png create mode 100644 kde/displayconfig/pics/hi32-gfxcard.png create mode 100644 kde/displayconfig/pics/monitor_resizable/background.png create mode 100644 kde/displayconfig/pics/monitor_resizable/background_r90.png create mode 100644 kde/displayconfig/pics/monitor_resizable/background_wide.png create mode 100644 kde/displayconfig/pics/monitor_resizable/background_wide_r90.png create mode 100644 kde/displayconfig/pics/monitor_resizable/monitor.png create mode 100644 kde/displayconfig/pics/monitor_resizable/monitor_r90.png create mode 100644 kde/displayconfig/pics/monitor_resizable/monitor_wide.png create mode 100644 kde/displayconfig/pics/monitor_resizable/monitor_wide_r90.png create mode 100644 kde/displayconfig/pics/monitor_resizable/window_4th.png create mode 100644 kde/displayconfig/pics/monitor_resizable/window_bottom_left_4th.png create mode 100644 kde/displayconfig/pics/monitor_resizable/window_bottom_right_4th.png create mode 100644 kde/mountconfig/pics/16x16/disksfilesystems.png create mode 100644 kde/mountconfig/pics/exec.png create mode 100644 kde/mountconfig/pics/file.png create mode 100644 kde/mountconfig/pics/greenled.png create mode 100644 kde/mountconfig/pics/greyled.png create mode 100644 kde/mountconfig/pics/hi16-blockdevice.png create mode 100644 kde/mountconfig/pics/hi16-burner.png create mode 100644 kde/mountconfig/pics/hi16-cdrom.png create mode 100644 kde/mountconfig/pics/hi16-floppy.png create mode 100644 kde/mountconfig/pics/hi16-hdd.png create mode 100644 kde/mountconfig/pics/hi16-lock.png create mode 100644 kde/mountconfig/pics/hi16-memory.png create mode 100644 kde/mountconfig/pics/hi16-network.png create mode 100644 kde/mountconfig/pics/hi16-password.png create mode 100644 kde/mountconfig/pics/hi16-usbpen.png create mode 100644 kde/mountconfig/pics/hi32-samba.png create mode 100644 kde/mountconfig/pics/important.png create mode 100644 kde/mountconfig/pics/kcmpartitions.png create mode 100644 kde/mountconfig/pics/kde1.png create mode 100644 kde/mountconfig/pics/kde2.png create mode 100644 kde/mountconfig/pics/kde3.png create mode 100644 kde/mountconfig/pics/kde4.png create mode 100644 kde/mountconfig/pics/kde5.png create mode 100644 kde/mountconfig/pics/kde6.png create mode 100644 kde/mountconfig/pics/laserwarn.png create mode 100644 kde/mountconfig/pics/tux.png create mode 100644 kde/mountconfig/pics/user.png create mode 100644 kde/powermanager/pics/ac-adapter.png create mode 100644 kde/powermanager/pics/ac-adapter.svg create mode 100644 kde/powermanager/pics/battery-charging-000.png create mode 100644 kde/powermanager/pics/battery-charging-010.png create mode 100644 kde/powermanager/pics/battery-charging-020.png create mode 100644 kde/powermanager/pics/battery-charging-030.png create mode 100644 kde/powermanager/pics/battery-charging-040.png create mode 100644 kde/powermanager/pics/battery-charging-050.png create mode 100644 kde/powermanager/pics/battery-charging-060.png create mode 100644 kde/powermanager/pics/battery-charging-070.png create mode 100644 kde/powermanager/pics/battery-charging-090.png create mode 100644 kde/powermanager/pics/battery-charging-100.png create mode 100644 kde/powermanager/pics/battery-discharging-000.png create mode 100644 kde/powermanager/pics/battery-discharging-010.png create mode 100644 kde/powermanager/pics/battery-discharging-020.png create mode 100644 kde/powermanager/pics/battery-discharging-020.svg create mode 100644 kde/powermanager/pics/battery-discharging-030.png create mode 100644 kde/powermanager/pics/battery-discharging-040.png create mode 100644 kde/powermanager/pics/battery-discharging-050.png create mode 100644 kde/powermanager/pics/battery-discharging-060.png create mode 100644 kde/powermanager/pics/battery-discharging-070.png create mode 100644 kde/powermanager/pics/battery-discharging-090.png create mode 100644 kde/powermanager/pics/battery-discharging-100.png create mode 100644 kde/powermanager/pics/battery_charging_0.svg create mode 100644 kde/powermanager/pics/battery_charging_1.svg create mode 100644 kde/powermanager/pics/battery_charging_2.svg create mode 100644 kde/powermanager/pics/battery_charging_3.svg create mode 100644 kde/powermanager/pics/battery_charging_4.svg create mode 100644 kde/powermanager/pics/battery_charging_5.svg create mode 100644 kde/powermanager/pics/battery_charging_6.svg create mode 100644 kde/powermanager/pics/battery_charging_7.svg create mode 100644 kde/powermanager/pics/battery_charging_8.svg create mode 100644 kde/powermanager/pics/battery_charging_9.svg create mode 100644 kde/powermanager/pics/battery_discharging_0.svg create mode 100644 kde/powermanager/pics/battery_discharging_1.svg create mode 100644 kde/powermanager/pics/battery_discharging_2.svg create mode 100644 kde/powermanager/pics/battery_discharging_3.svg create mode 100644 kde/powermanager/pics/battery_discharging_4.svg create mode 100644 kde/powermanager/pics/battery_discharging_5.svg create mode 100644 kde/powermanager/pics/battery_discharging_6.svg create mode 100644 kde/powermanager/pics/battery_discharging_7.svg create mode 100644 kde/powermanager/pics/battery_discharging_8.svg create mode 100644 kde/powermanager/pics/battery_discharging_9.svg create mode 100644 kde/powermanager/pics/processor.png create mode 100644 kde/powermanager/pics/processor.svg create mode 100644 kde/serviceconfig/pics/16x16/daemons.png create mode 100644 kde/serviceconfig/pics/hi32-app-daemons.png create mode 100644 kde/serviceconfig/pics/laserwarn.png create mode 100644 kde/userconfig/pics/16x16/userconfig.png create mode 100644 kde/userconfig/pics/hi16-encrypted.png create mode 100644 kde/userconfig/pics/hi32-group.png create mode 100644 kde/userconfig/pics/hi32-identity.png create mode 100644 kde/userconfig/pics/hi32-password.png create mode 100644 kde/userconfig/pics/hi32-user.png create mode 100644 kde/wineconfig/pics/16x16/wineconfig.png create mode 100644 kde/wineconfig/pics/16x16/wineconfig.svg create mode 100644 kde/wineconfig/pics/32-wine.png create mode 100644 kde/wineconfig/pics/48-wine.png create mode 100644 kde/wineconfig/pics/kdewinewizard.png create mode 100644 modules/ixf86misc.c create mode 100755 modules/xf86misc.py create mode 100755 mountconfig/MicroHAL.py create mode 100644 mountconfig/SMBShareSelectDialog.py create mode 100644 mountconfig/SimpleCommandRunner.py create mode 100644 mountconfig/fuser.py create mode 100644 mountconfig/fuser_ui.ui create mode 100644 mountconfig/mountconfig.desktop create mode 100755 mountconfig/mountconfig.py create mode 100644 mountconfig/sizeview.py create mode 100644 package/mandrake/guidance-kcmdisplayconfig create mode 100644 package/mandrake/guidance-kcmmountconfig create mode 100644 package/mandrake/guidance-kcmserviceconfig create mode 100644 package/mandrake/guidance-kcmuserconfig create mode 100644 package/mandrake/guidance.spec create mode 100644 powermanager/TODO create mode 100755 powermanager/g-p-m-restart create mode 100644 powermanager/gpmhelper.py create mode 100644 powermanager/guidance-power-manager.desktop create mode 100755 powermanager/guidance-power-manager.py create mode 100644 powermanager/guidance_power_manager_ui.py create mode 100644 powermanager/guidance_power_manager_ui.ui create mode 100644 powermanager/hal-test.py create mode 100644 powermanager/notify.py create mode 100644 powermanager/notify.ui create mode 100644 powermanager/powermanage.py create mode 100644 powermanager/powermanager_ui.ui create mode 100644 powermanager/recompile-ui-files create mode 100644 powermanager/tooltip.py create mode 100644 powermanager/tooltip.ui create mode 100644 review_guidance_malaga.pdf create mode 100644 serviceconfig/serviceconfig.desktop create mode 100755 serviceconfig/serviceconfig.py create mode 100644 setup.cfg create mode 100755 setup.py create mode 100644 uninstall_rude.py create mode 100755 userconfig/unixauthdb.py create mode 100644 userconfig/userconfig.desktop create mode 100755 userconfig/userconfig.py create mode 100644 wineconfig/drivedetect.py create mode 100755 wineconfig/firstrunwizard.py create mode 100644 wineconfig/kcm_wineconfig.cpp create mode 100644 wineconfig/wineconfig.desktop create mode 100755 wineconfig/wineconfig.py create mode 100644 wineconfig/wineread.py create mode 100644 wineconfig/winewrite.py diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..c7aea18 --- /dev/null +++ b/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..76f7ee0 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,334 @@ +- Handle "ain't got no cpufreq" more gracefully (Malone bug # 99198) +- Fix typo in mountconfig that lead to a crash (Malone bug # 87861) +- BUG: Fix detection of the nvidia proprietary driver (Malone bug #104860) +- BUG: Displayconfig and displayconfig-restore.py would crash in combination + with later versions Xorg if the xrandr extension was not available. + (Malone bug #91545) + +* Wednesday 29 March 2007 Simon Edwards +- version 0.8 +- Deal with AttributeError in displayconfigabstraction (Malone bug #94108) +- Add menu to choose CPU frequency policy manually +- Handle problems not being able to read the filesystem label gracefully +- Make displayconfig-restore not crash on incorrect modelines (Malone bug # 76393) +- Make displayconfig a little smarter when the preferred resolution can't be found +- Make wineconfig not crash on empty fstab lines +- Fix the fuser frontend in mountconfig to actually work again. +- Handle ZeroDivisionError in displayconfig gracefully (Malone bug #77844) +- Support for LABEL in fstab added to mountconfig, improved support for + UUID. +- Support for changing CPU frequency policy with HAL added to powermanager. +- Suspend after N minutes idle added to powermanager +- Added an option to not lock screen on resume (Malone bug # 64650) +- Handle crash in userconfig due to problems with locale (Malone bug #65739) +- Support for UUIDs in fstab added to mountconfig. +- grubconfig added. (Martin Böhm) + +* Tuesday 6 March 2007 Simon Edwards +- version 0.7.1 +- BUG: Fixed typo in userconfig.py. (thanks to Rocco Stanzione) +- BUG: Fix double hibernate call on lid close. (Malone bug #65885) +- BUG: Change CHARGE_LEVEL_THRESHOLD to 10% (was 50%) to better workaround + acpi issue. when remaining_time is not reported correctly. Also use + threshold for battery low notification warning. (Malone bug #64752, + #67081) +- BUG: Handle error in locale.getpreferredencoding gracefully. (Malone bug + #65739) +- BUG: DPMS settings had not been saved on Apply, do that. +- BUG: Fix crash where cpu frequency scaling is not readable. +- BUG: Fix unicode and localisation handling in targetgamma, do a better job + choosing the right resolutions for example for nvidia twinview and other + unusual resolutions. +- BUG: Powermanager, first read config, then adapt the state of the UI from + those values. +- BUG: Don't crash displayconfig-restore when screenwidth and height are + bogus, use a sensible value of 96 instead. (Malone bug #77844) +- BUG: Make displayconfig-restore not crash on unknown modelines. +- BUG: Make tempfile handling more robust by using Python's tempfile. +- BUG: Check for None groupids in userconfig. +- BUG: Clear password edit after exiting the edit dialog in userconfig. +- BUG: Make AC adapter detection more robust to potential failure of + actions. (Malone bug #77091) +- BUG: permissions on groupfile should've been read, before they can be set. +- BUG: Check for valid groupid when selecting in userconfig. +- BUG: gamma settings were not being restored after logging in. +- BUG: A rounding error was causing problems in displayconfig when filtering + resolutions by the selected monitor. +- BUG: Displayconfig, userconfig and wineconfig didn't handle RTL desktops + correctly. (Diego lastrubni) +- BUG: Displayconfig would sometimes consider some widescreen modes as being + standand aspect ratio. +- BUG: Displayconfig would sometimes fail to set Display virtual size in + xorg.conf. +- BUG: Stopped displayconfig from stacktracing when it encounters a + degenerate gfx card + monitor combination that has no valid resolutions. + +* Thursday 12 October 2006 Simon Edwards +- version 0.7 +- BUG: Don't bail out of laptop-detect is not there (Malone bug #60309) +- BUG: mountconfig.py didn't handle USB disks correctly. (KDE bug #132390) +- BUG: Disable double click opening items in mountconfig if the user is not + root. +- BUG: Don't bail out if the device section is already there (Malone bug #50411) +- BUG: Don't show new_user in the secondary groups (Malone bug #44203) +- BUG: userconfig set wrong uid for new users (Malone bug #56275) +- BUG: serviceconfig.py would sometimes fail on non-English systems. + (Malone bug #43313). +- BUG: userconfig would sometimes fail when writing the shadow password file. + (Malone bug #47090) +- BUG: userconfig would fail if the password warning field in /etc/shadow was + empty. (Malone bug #47317) +- Changed a couple of labels in order to match the new system-settings spec: + https://wiki.kubuntu.org/KubuntuSystemSettingsUsability +- mountconfig now uses CIFS for Windows Shares instead of obsolete smbfs. + (Martin Böhm) +- Displayconfig now hides useless Monitor Orientation and Second Screen group + box options which can never be activated without changing hardware or the + X driver. +- Simplified the color and gamma tab in displayconfig. +- BUG: Better detection for dualhead intel chips in displayconfig. +- Updated the data files for displayconfig from + http://cvs.mandriva.com/cgi-bin/viewvc.cgi/soft/ldetect-lst/trunk/lst/?root=svn +- wineconfig added for configuring Wine. (Yuriy Kozlov) +- powermanager applet added for monitoring laptop power levels (Sebastian + Kügler) + +* Saturday 13 May 2006 Simon Edwards +- version 0.6.7 +- Dutch translation added (Rinse de Vries) +- BUG: Displayconfig would fail if the monitor frequency settings in xorg.conf + contained extra spaces. (Malong bug #38692) +- BUG: Displayconfig would not add the lowest screen resolution available to + the modes list in the xorg.conf's Screen section/Display subsection. +- French translation added from Launchpad Rossetta. + +* Friday 28 April 2006 Simon Edwards +- version 0.6.6 +- BUG: Serviceconfig didn't correctly remove links in runlevel directories. + (Malone bug #39404) +- BUG: In Serviceconfig, toggling the "Start during boot" checkbox using the + context menu would fail. (Malone bug #34252) +- BUG: displayconfig-restore.py was would fail with "global name 'syslog' + undefined". (Malone bug #40683) +- BUG: Displayconfig had trouble picking a driver gfxcard model entry instead + of the detected default (e.g. VESA). (Malone bug #41127) +- BUG: Displayconfig would fail when writing out a xorg.conf that contained + non-ascii characters. (Malone bug #41474) +- Work around for an annoying bug in PyQt/PyKDE that causes the tools to + crash on exit if a dialog window has been used. +- BUG: The file paths used in displayconfig for checking for the proprietary + nvidia driver were wrong or out of date. + +* Tuesday 18 April 2006 Simon Edwards +- version 0.6.5 +- BUG: services that have not been installed via apt would cause long loops + a lot of dpkg queries. Only query dpkg db once. +- BUG: Serviceconfig's Apply button doesn't work. It should actually be Close + (Malone #38582) +- BUG: Changing an user's password can change another password for a different + user (Malone bug #39444) +- BUG: Displayconfig would fail at startup on systems with an nVidia 7800 GTX. + (Malone bug #32915 for Rob Hughes) +- BUG: Serviceconfig would crash if the windows is closed quickly after + serviceconfig appears. +- BUG: userconfig would fail at startup if an entry in /etc/passwd refered to + a group that is not defined in /etc/group. (Malone bug #34311) +- BUG: Userconifg. Manually typing in or editing the list of secondary groups + for a user had no effect. (Malone bug #37212) +- BUG: Displayconfig fails to detect the presence of the proprietary ATI and + nVidia drivers. (The location of some of the driver files had been recently + changed). +- BUG: displayconfig-restore.py would calculated the needed DPI at login using + stale screen information which would sometimes result in the wrong DPI. +- BUG: Numerious small bugs and compatibility problems in mountconfig. +- BUG: When browsing for a SMB share, mountconfig now correctly catches the + authentication information entered by the user into the smaller popup from + kio. +- Displayconfig now assumes that dualhead/clone mode is supported if the + laptop-detect script detects a laptop. +- Displayconfig now uses the clone mode support in the i810 driver. +- Displayconfig now only offers resolutions that both monitors support when + using clone mode. +- Displayconfig now supports clone mode on any setup that also supports + xinerama. +- BUG: Mountconfig failed to take into account that the order of the + user/users, exec/noexc, suid/nosuid etc options in /etc/fstab is significant. + (thanks Christoph Wiesen) +- French translations added to the desktop files. (Anthony Mercatante) + +* Sunday 2 April 2006 Simon Edwards +- version 0.6.4 +- BUG: All of the tools no longer write out config files under ~/.kde when + running as root. This should stop the annoying creation of config files + that can't be overwritten by the normal user. +- Userconfig is now by default not quite as tall. This should help stop + it from appearing too big in systemsettings. +- Displayconfig updated to also recognise late model nVidia chipsets. +- BUG: Displayconfig didn't support Clone mode for the proprietary nVidia + driver. +- BUG: The tools now correctly specify which translation catalogue to use for + translations. +- The screen images in the dualhead widget make better use of available widget + space. +- Displayconfig: The 40guidance-displayconfig_restore script which is used by + the Xsession script during login via KDM/xdm, has now been fixed to not stop + the login in case of failure. +- BUG: Displayconfig was getting confused by unknown graphics cards and + crashing. (Malone bug #32915) +- BUG: Displayconfig still can't handling unicode in xorg.conf. (Malone bug + #34437). +- BUG: Displayconfig is now more forgiving when xorg.conf contains characters + that are illegal with respect to the system character encoding. (Malone bug + #36590). +- BUG: Displayconfig would have trouble detecting hardware on the PCI bus on + big endian architectures. (raphink) +- BUG: Displayconfig did not correctly handle situations where the X RandR + extension is missing. +- BUG: Displayconfig would fail when loading some xorg.conf files containing + multiple graphics card specifications. (Malone bug #37275, patch applied) +- BUG: Userconfig didn't respect the entered UID when creating a account. + (Malone bug #37722). +- Displayconfig: Added 1280x960 modes (60 & 75Hz). + +* Friday 17 March 2006 Simon Edwards +- version 0.6.3 +- BUG: userconfig and unixauthdb didn't respect the ownership of system files + when update /etc/passwd and friends. (Malone bug #26175). +- BUG: userconfig and unixauthdb would fail if /etc/passwd, /etc/groups or + /etc/shadow contained blank lines. +- BUG: displayconfig now does a better job of detecting graphics PCI devices + and handling non-detected graphics cards. Instead of crashing, "generic + VESA" is used when the type of card can't be found. (Malone bug #32915) +- The DPI that displayconfig-restore.py uses at login time can now be + controlled by adding a line to ~/.kde/share/config/displayconfigrc + in the [General] section. Add "dpi=xserver" to use the default DPI + from the X server, or "dpi=100" to use 100 DPI for example. +- BUG: Widescreen modes were missing in displayconfig. +- Displayconfig: Monitors can now be specified as being standard aspect ratio + or widescreen. +- BUG: Using the xresprobe command in displayconfig would crash some people's + machines. The much more safer ddcprobe command is now used. + (Malone bug #33943) +- BUG: A bug is displayconfig stopped monitor model detection. The xresprobe + command didn't actually return the eisa ID of the connected monitor. + ddcprobe does though (see above). +- BUG: Displayconfig. Changes to the monitor model or image format are + shown immediately on the "Size & Orientation" tab, even if the screen is + currently being used. +- BUG: When userconfig asks about whether the home directory should be created + when creating a new account, sometimes the wrong directory name was shown in + the dialog. +- BUG: Powerbook screen mode "1280x854" add to displayconfig. (Malone bug + #34383). +- BUG: Displayconfig would throw an exception if the current display didn't + support DPMS. (Malone bug #34316). +- BUG: Most utilities would fail if they came across UTF-8 or unicode + characters. (Malone bug #34194). +- BUG: Displayconfig wouldn't correctly detect the presence of installed + proprietary drivers. (OculusAquilae) +- BUG: Displayconfig had trouble handling BusID rows in xorg.conf. This would + cause the xorg.conf to be incorrectly read. (Tonio) +- Added some extra methods to ScanPCI.py to aid debugging. +- BUG: Displayconfig would not save the user's display settings when running + in kcontrol or systemsettings. (Malone bug #35257) + +* Wednesday 1 March 2006 Simon Edwards +- version 0.6.2 +- BUG: Small bug in displayconfig that caused it to bug out around the + newCustomMonitor() method. +- BUG: userconfig had space character just before the she-bang which really + caused some trouble the for the shell. +- BUG: Displayconfig: Selecting the "Plug n Play" monitor directly without + clicking on "Detect" meant that only a very small set of resolutions would + be written to xorg.conf. +- BUG: The DPI calculations in displayconfig-restore.py where broken. This + resulted in the wrong DPI being used. +- BUG: Displayconfig: The clone mode option is now correctly disabled for + Matrox cards (mga driver). +- BUG: Displayconfig would bug out if the monitor model in the xorg.conf was + anything other than Plug n Play. +- BUG: Displayconfig would bug out if the DPMS Standby setting was 0 and DPMS + enabled. +- Displayconfig is now shown in kcontrol under settings/hardware instead of + settings/system. +- BUG: Small bug in displayconfig that will causes displayconfig to bug out + when trying to detect the monitor and no EDID info is available. +- BUG: Small cosmetic bug in displayconfig where the some tabs were missing + margins when shown in kcontrol/system settings. +- BUG: displayconfig would bug out when detecting ATI dualhead cards. + (pci_device.text was None). + +* Monday 20 February 2006 Simon Edwards +- version 0.6.1 +- displayconfig-hwprobe.py now saves its scan info in + /var/lib/guidance/guidance-gfxhardware-snapshot. +- "Details..." button added to mountconfig in addition to the context menu for + opening the disk info dialog. +- BUG: serviceconfig: When starting and stopping daemons, the scripts expect a + terminal that understands colours. The command runner dialog doesn't, and + you end up seeing garbage characters. TERM is now set to vt100 when running + commands. +- BUG: displayconfig: Reset button didn't reset all of the parts/fields in the + GUI. +- Detect dualhead Matrox cards. +- displayconfig-restore.py now at login time also chooses and sets a 'sane' + DPI setting used by applications for fonts. More info is in the + displayconfig-restore.py source file. +- BUG: displayconfig-restore.py wasn't restoring the user's display resolution + at login. +- Plug N Play monitors are handled much better and are automatically probed + when neccessary. + +* Tuesday 14 February 2006 Simon Edwards +- version 0.6.0 +- Right mousebutton action for most listviews. +- mountconfig can now handle multi-fs entries. +- i18n() all over the place. +- Dualhead support added to displayconfig. +- displayconfig's hardware database files updated from Mandriva. +- numerous bug fixes. +- displayconfig-hwprobe.py add. This is Ubuntu specific right now, but + what it does is detect hardware changes at boottime and automatically + run "dpkg-reconfigure" to generate a xorg.conf file that will get Xorg + running. + +* Tuesday 29 November 2005 Simon Edwards +- version 0.5.0 +- displayconfig should now work for single head configurations. +- displayconfig has a shiney new preview. +- displayconfig is now usable on low resolution screens. +- DPMS tab cleaned up in displayconfig. +- displayconfig's hardware database files updated from Mandriva. + +* Monday 12 September 2005 Simon Edwards +- version 0.4.0 +- displayconfig has been massively restructured internally. +- numerous bug fixes. +- start of dualhead support in displayconfig. +- When umount fails in mountconfig the option to killing blocking processes + has been added. +- better Debian support in serviceconfig. It now uses apt and dpkg to get + service descriptions. +- ext3 added to mountconfig. :) +- userconfig now respects /etc/useradd.conf +- Now uses PyKDE Extensions for building and installation. + http://www.simonzone.com/software/pykdeextensions/ +- DPMS tab added to displayconfig. + +* Tuesday 5 April 2005 Simon Edwards +- version 0.3.0 +- Displayconfig working and also feature complete. +- xf86config C module has been removed and replaced with pure Python. +- Numerous little bugs fixed in the userconfig, mountconfig and serviceconfig. + +* Thursday 9 December 2004 Simon Edwards +- version 0.2.0 +- mountconfig added, beta quality, feature complete. +- displayconfig added, alpha quality, not feature complete. +- userconfig, beta quality, feature complete. +- serviceconfig, beta quality, feature complete. + +* Thursday 20 November 2003 Simon Edwards + +- version 0.1.0 +- Initial release. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b1b40ad --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,21 @@ +recursive-include kde *.png *.svg +recursive-include doc *.docbook *.png +graft package +prune package/.svn +graft package/mandrake +graft debian +prune package/mandrake/.svn +include README COPYING ChangeLog MANIFEST.in TODO +global-include *.desktop *.py *.pot *.po *.ui +global-exclude *~ .svn +include displayconfig/40guidance-displayconfig_restore +include displayconfig/vesamodes +include displayconfig/extramodes +include displayconfig/widescreenmodes +include displayconfig/ldetect-lst/Cards+ +include displayconfig/ldetect-lst/MonitorsDB +include displayconfig/ldetect-lst/pcitable +exclude displayconfig/test.py +exclude displayconfig/xconfig-test.py +exclude displayconfig/popentest.py +exclude displayconfig/guidance_bug_reporter.py diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..ddb0573 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,15 @@ +messagesold: + LIST=`find . -name \*.py`; \ + if test -n "$$LIST"; then \ + xgettext -ki18n -LPython $$LIST -o po/guidance.pot; \ + fi + + sh /usr/lib/kubuntu-desktop-i18n/findfiles LIST + perl /usr/lib/kubuntu-desktop-i18n/createdesktop.pl --file-list=LIST --base-dir=. > desktop.guidance.tmp + msguniq --to-code=UTF-8 --no-wrap -o desktop.guidance desktop.guidance.tmp 2>/dev/null + python /usr/lib/kubuntu-desktop-i18n/msgsplit desktop.guidance + mv desktop.guidance po/desktop_guidance.pot + rm -f desktop.guidance desktop.guidance.tmp + +messages: + true diff --git a/README b/README new file mode 100644 index 0000000..89ebc0d --- /dev/null +++ b/README @@ -0,0 +1,189 @@ +!!! Warning: Read this through to the end. These tools can be dangerous. !!! + +Guidance 0.8.0 +~~~~~~~~~~~~~~ +by Simon Edwards , Sebastian Kügler +& Yuriy Kozlov , Martin Böhm + + +Introduction +------------ +Guidance is a collection of system administration tools for Linux/KDE systems +that is designed to be: + + * Update to date + * High quality, designed with user friendliness as high priority. (That is + to say that the tools should get the job done quickly, accurately and with + as little effort as possible.) + * Use KDE and "fit in" with KDE. + * Maintainable. + * Free Software + + +Warning +------- +These tools often require root access and modify important system files in +order to do their job. There is a very real chance that these tools can +destroy your computer and your data. Backup your data! Also especially +backup up /etc/passwd, /etc/shadow and /etc/fstab. If you don't know what +I'm talking about now then you probably shouldn't try using Guidance yet. +You should have some idea how to fix + +We accept no responsibility in the event that something goes terribly wrong. +You have been warned. + + +Status +------ +The tools that make up Guidance are in different stages of development and +have only been tested on Mandrake. + +* userconfig - User & Group configuration. Supports /etc/passwd and + /etc/shadow right now. + +* serviceconfig - System services configuration utility. Feature complete. + Works on Mandrake, Debian, (K)ubuntu Gentoo and maybe Red Hat out of the box. + +* mountconfig - Mount point configuration utility. Feature complete and + stable. + +* displayconfig - Display and graphics card configuration utility. Feature + complete but it only tested on Kubuntu right now. + +* wineconfig - Wine configuration utility. + +* grubconfig - Grub boot loader configuration utility. beta quality now. + + +Requirements +------------ +* Linux. + +* Python. 2.4 recommended. You probably already have Python installed, and if + not then it will most definately be available for you distribution of choice. + +* PyQt / PyKDE. Version 3.7 or higher. PyQt and PyKDE are distributed with + KDE as part of kde-bindings since version 3.3. + + For versions of KDE before 3.3 it is possible to install PyQt/PyKDE + directly. The homepage for PyQt and PyKDE is: + + http://www.riverbankcomputing.co.uk/pykde/index.php + + Packages for most popular distributions are available here: + + http://sourceforge.net/project/showfiles.php?group_id=61057 + +* KDE 3. + +* "PyKDE Extensions" is needed for installation. It is available here: + http://www.simonzone.com/software/pykdeextensions/ + This is also a runtime requirement! + +* libpythonize. If you don't want the tools to appear on the KDE Control + Center of you just don't want to bother with libpythonize, then you can + build Guidance with this command: + + python setup.py install build_kcm --no-kcontrol + + libpythonize is only needed for the KDE Control Center. + +* You also need to have the development files from your X-server installed, + XFree86 or Xorg. Most distributions package these files up in a package + called XFree86-devel, or Xorg-devel, or libxorg-X11-devel or some similar. + +* Specifically, the following packages are needed on Kubuntu: + + build-essential + python-sip4-dev + libxxf86vm-dev + libxrender-dev + libxrandr-dev + libpythonize0-dev + pyqt-tools + libtool + kde-devel + + +Installation +------------ +As root run: + + ./setup.py install + +This will test for a working installation of PyQt/PyKDE and automatically +install the files using the same installation prefix as KDE. You will need +to have a working Python install before you can even run the setup.py script. + +displayconfig-hwprobe installation +---------------------------------- +displayconfig-hwprobe.py is a small program that should be run at boottime +before Xorg is started. It scans the PCI bus looking for graphics cards and +compares the list it finds to the previous time it was run. If the two lists +of hardware are different then "dpkg-reconfigure xserver-xorg" is automatically +run in non-interactive mode to generate a new xorg.conf based on the new +hardware. + +The philosophy is that it is better to have a system with a raw but working +xorg.conf and X server, than to keep the old configuration and a Xorg that +won't startup. Swapping a graphics card should not "break" the OS. + +The setup.py script currently does not install displayconfig-hwprobe.py by +itself. For now this must be done by the packager. displayconfig-hwprobe.py +should be put in /etc/init.d and installed to run at boot time with a command +like this: + + update-rc.d displayconfig-hwprobe.py start 18 3 . + +displayconfig-hwprobe.py only supports Kubuntu right now. Perhaps in the +future displayconfig-hwprobe will be expanded to detect hardware and generate +an xorg.conf by itself. + +More information about displayconfig-hwprobe.py and the files it uses is +inside displayconfig-hwprobe.py. Currently the hardware data is written to +/var/lib/guidance/guidance-gfxhardware-snapshot. IMPORTANT: The directory +/var/lib/guidance/ should be created before using displayconfig-hwprobe.py. + + +Running +------- +Installation should add a couple of entries to the KDE Control Center in the +System section (displayconfig will show up in Peripherals). It is also possible +to run the commands outside of the KDE Control Center from the shell as root: + + * serviceconfig + * userconfig + * mountconfig + * displayconfig + * wineconfig + * grubconfig + +Reporting Bugs +-------------- +Bug reports and feedback can be sent to simon@simonzone.com . Do make sure +that say which version of Guidance you are using and also what Linux +distribution you are using, and also how you installed SIP, PyQt, PyKDE. Also +if any error messages are printing to the console, email those too. By +running these tools from the command line you can often get useful (to me) +debug information. That kind of information is valuable. + + +Deinstallation +-------------- +As root run: + + ./setup.py uninstall + + +Thanks go to +------------ +Jim Bublitz +David Boddie +Sebastian Kügler +Theo Houtman +Pete Andrews (gamma correction system) + + +-- +Simon Edwards + diff --git a/README.developers b/README.developers new file mode 100644 index 0000000..82b98da --- /dev/null +++ b/README.developers @@ -0,0 +1,48 @@ + +Intro +~~~~~ +In this file I want to try to explain some practical things about Guidance +the "project" and some basic (and hopefully not too heavy) policies about how +things work organisationally. + +-- 11 Feb 2007, Simon Edwards + + +Developers +~~~~~~~~~~ +The list of developers as of 11 Feb 2007: + +Simon Edwards , SVN username: sedwards, IRC: sime (unregistered) +Sebastian Kügler , SVN username: sebas, IRC: sebas +Yuriy Kozlov , SVN username: ykozlov, IRC: yuriy +Martin Böhm , SVN username: martinbohm, IRC: ??? + +"Lure" on #kubuntu-devel will join this list whether he likes it or not if he keeps +on committing stuff to powermanager. ;-) + + +Subversion +~~~~~~~~~~ +Main development occurs in KDE's subversion repository in +/trunk/kdereview/guidance. Branches of the stable releases can be found +in svn under /kde/branches/guidance/. 0.6 was used in the Kubuntu Dapper, 0.7 +was in Edgy. + + +Python source code +~~~~~~~~~~~~~~~~~~ +Use 4 spaces for indentation, for the simple reason that it is very common +and mixing indentation styles is a PITA. + +I (Simon) use Qt/KDE style naming conventions for methods. For variable +names I'm a bit inconsistent but it is usually lower case of lower case +with underscores. All I ask is that variable names be descriptive and +understandable. + +TIP: When dealing with translated strings, use uncide() and not str(), +otherwise things will break on translated desktops. + + +Release procedure +~~~~~~~~~~~~~~~~~ +[TODO: explain how a release tarball is created.] diff --git a/TODO b/TODO new file mode 100644 index 0000000..4124684 --- /dev/null +++ b/TODO @@ -0,0 +1,294 @@ +TODO +==== + +BUG: Mountconfig has no "enable/disable" in the context menu. + +BUG: Mountconfig: "enable/disable" can sometime be available for normal users. + +BUG: mountconfig: The disk details dialog (sizeview.py) doesn't show *unpartioned* free space. + +BUG: Live gamma changes didn't seem to work on one of the S3s. + +BUG: mountconfig. Authentication details in the SMSShareSelectDialog are sometimes not +correctly used when browsing. + + + +TODO: Some S3 cards need to have the video ram specified, and some later model don't. + It would be good if we could tell the difference and only offer the ram pulldown + when strictly needed. + + +TODO: Monitor type selection (CRT, lcd etc). needed for clone mode on ATI at least. + +TODO: When using the proprietary nVidia driver, choose between the nVidia AGP +and kernel agpgart based on what is best the of the machine's chipset. + +TODO: The proprietary ATI drivers have a 'Option "BusType" "PCI"' thing which may +of may not need to be set for PCI based ATI cards. + +TODO: 3D accel on the 9250 with open source drivers. + + +Future TODO +=========== + +setup.py +~~~~~~~~ +. + +userconfig +~~~~~~~~~~ +. + +unixauthdb.py +~~~~~~~~~~~~~ +* LDAP (post-1.0) in-progress +* (others?) (post-1.0) + +serviceconfig +~~~~~~~~~~~~~ +* Change os.system() to the calls Simon uses in some cases. +* Some services are running, but not in /var/run, implement special treatment. :> +* Remove commented line from /etc/shells in userconfig -> Modify -> Shell. + +mountconfig +~~~~~~~~~~~ +* Add 'proper' GUIs for editing some of the more common FS types. + - NFS. +* AttributeError when mounting Samba volume +* Handle the 'managed' mount entry options. See http://www.die.net/doc/linux/man/man8/fstab-sync.8.html + This is stand on Mandriva 2005. +* Implement "real" HAL backend. + +displayconfig +~~~~~~~~~~~~~ +* Use HAL for fetching PCI and card info? alongside existing systems (ldetect)? + + + +Extra? +~~~~~~ +* Swap/kernel config: + http://kerneltrap.org/node/view/3000 + "To tune, simply echo a value from 0 to 100 onto /proc/sys/vm/swappiness." + +* Hardware detection info: + + http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&threadm=lyit5wc14g.fsf%40leia.mandrakesoft.com&rnum=1&prev=/groups%3Fq%3Ddebian%2520ldetect%2520hardware%2520detection%26hl%3Den%26lr%3D%26ie%3DUTF-8%26sa%3DN%26tab%3Dwg + +---------------------------------------------------------------------------- +From: Pixel (pixel@mandrakesoft.com) +Subject: Re: Why so many HW detection packages? +Newsgroups: linux.debian.devel +Date: 2002-05-09 17:20:07 PST + +On Fre, 26 Apr 2002, Petter Reinholdtsen wrote: + +[...] + +> The reson is that there are 3 hardware detection system: +> - Mandrake (libdetect, old) + +truly libdetect is old and deprecated +(harddrake (was lothar) used to use it) + +we (mandrake) are now mostly using ldetect & ldetect-lst + +AFAIK here are the various free software hardware databases: + +-------------------------------------------------------------------------------- +- pci ---------- + + - pci.ids (in pciutils) + maps vendor+device -> description + and vendor+device+subvendor+subdevice -> description + also has device classes names + + - modules.pcimap (in kernel /lib/modules/2.4*/) + maps vendor+device -> module + and vendor+device+subvendor+subdevice -> module + + - XFree's xf86PciInfo.h (in XFree's source: xc/programs/Xserver/hw/xfree86/common/xf86PciInfo.h) + maps vendor+device -> description + + - RedHat's pcitable (in hwdata) + maps vendor+device -> module+description + and a few vendor+device+subvendor+subdevice -> module+description + when needed + module can also be "Card:xxxx" for XFree (using Cards, see below) + + - Mandrake's pcitable (in ldetect-lst) + same format as RedHat's (except for a few syntactical changes since mandrake kept old RedHat's format) + module can also be + "Card:xxxx" for XFree (using Cards+, see below) + "Server:xxxx" for XFree3 + "ISDN:xxxx" for hisax special parameters + "Bad:xxxx" for warning about unhandled devices (mainly winmodems) + + - Mandrake old detect's pci.lst (in detect-lst) + maps vendor+device -> class+module+description + + - Progeny's pci.lst (in discover-data) + same format as detect + maps vendor+device -> class+module+description + module can also be + "Server:XFree86(module)" for XFree4 + "Server:XF86_xxx" for XFree3 + +some comments: + + - the "class" in pci.lst is not useful when "module" is given since + from the module name, one can have the "class" + + - the subvendor+subdevice distinction is sometimes useful + (not very often though) + + - hopefully one day modules.pcimap will be the reference :) + (except for XFree of course) + +tools using those databases: + + - kudzu and anaconda are using pcitable from hwdata + + - library ldetect accesses pcitable from ldetect-lst, + this library is used by DrakX and drakxtools. + Mandrake's patched kudzu uses pcitable from ldetect-lst + + - discover uses pci.ids from discover-data + + - i don't know if tools are using modules.pcimap + +- XFree -------------------- + + - XFree comes with Cards + + - Redhat has its own version (in hwdata) + + - Mandrake has its own version Cards+ & CardsNames (in ldetect-lst) + (mainly a merge of XF3 Cards and XF4 Cards) + + - discover doesn't need it since it's precising the server name + (XF3) or the module name (XF4) (?) + this is usually enough (except if you want to propose the choice, + but who wants XF3 nowadays :) + + +- usb -------------------- + + - usbutils's usb.ids + maps vendor+device -> description + also has device classes names and some more stuff + + - modules.usbmap (in kernel /lib/modules/2.4*/) + maps vendor+device -> module (?) + + - Mandrake's usbtable (in ldetect-lst) + maps vendor+device -> module+description + module can also be + "Mouse:xxxx" for mouse configuration (fed to mousedrake) + "Tablet:wacom" for wacom tablet configuration + "Flag:xxxx" for DrakX package choosing + "Floppy:normal" + + - Progeny's usb.lst (in discover-data) + maps vendor+device -> class+module+description + (but current's version only have module=unknown, so what's it + for, why not usb.ids?) + + +tools using those databases: + + - library ldetect accesses usbtable from ldetect-lst + this library is used by DrakX and drakxtools + + - i don't know if tools are using modules.usbmap + +- scanner -------------------- + + - ScannerDB (in ldetect-lst) + maps name -> driver+kind(usb,scsi,serial,parallel)+options+various + (i don't know much about it, i don't know if yves made it from + scratch or what. ask yduret@mandrakesoft.com for more) + +- isdn -------------------- + + - isdn.db (in ldetect-lst) + list of internet providers by country + -> phone number + domainname + dns1 (ip) + dns2 + +- old or small databases ---------- + + - isa.lst (detect), isatable (ldetect-lst), modules.isapnpmap (kernel) + - pcmcia.lst (detect, discover-data), pcmciatable (ldetect-lst) + - modules.parportmap (kernel but empty?) + - modules.ieee1394map (kernel but empty?) + +-------------------------------------------------------------------------------- + +There may be some errors, or some missing stuff, please correct me! + +I've written a tool to keep in sync with as many databases as +possible. see merge2pcitable.pl in +http://www.mandrakelinux.com/cgi-bin/cvsweb.cgi/soft/ldetect-lst/convert/ + +Maybe some common mailing list could be set up to deal with this? + +*but* note that the database is quite kernel dependent. +- our pcitable doesn't handle this nicely +- redhat has "upgradelist" in hwdata to partly handle this +- i know we handle some pbs via /lib/modutils/macros with things like +"if `kernelversion` = 2.4", debian seems to have it in +/etc/modutils/arch + + +Once again, hopefully one day modules.pcimap and modules.usbmap will +be the reference! :) + + + +[...] + +> Mandrake switched from libdetect to kudzu, afaik +> (latest mandrake (8.2) version is using kudzu for HW detection). + +well, mandrake has many tools doing more or less the same thing (and +alas, not exactly always the same thing): DrakX (during install), +drakxtools (when called, after install), kudzu (at boot, usually +calling a drakxtools) + +---------------------------------------------------------------------------- + + +List of hardware probing tools to use for displayconfig: +-------------------------------------------------------- + + +[1] xvinfo - Print out X-Video extension adaptor information + + xvinfo prints out the capabilities of any video adaptors + associated with the display + that are accesible through the X-Video extension. + +[2] xresprobe - Prints out resolutions, frequency and displaytype. + Doesn't work in all cases. Works via ddc, I guess. + +[3] ddcprobe - Uses VESA BIOS Extension + Detects VGA + OEM, modes (only set up modes?), vid mem (kudzu) + +[3] read-edid + get-edid|parse-edid prints out a "good-looking" Monitor Section for + xorg.conf, not reliable (failed on notebook) + +[4] ddcxinfo - prints out modelines, hsync and vsync (kudzu) + +[5] svgamodes - prints out supported video modes (kudzu) + + + + +-- +Pixel +programming languages addict http://merd.net/pixel/language-study/ + +---------------------------------------------------------------------------- diff --git a/displayconfig-TODO b/displayconfig-TODO new file mode 100644 index 0000000..926f7d9 --- /dev/null +++ b/displayconfig-TODO @@ -0,0 +1,135 @@ +* How many cards do we have? autodetection? +* Is our card a dualhead card? +* How many monitors are connected? + +* difference between one and two card is different device sections +* twinview has one device, one screen, one serverlayout +* xinerama has two devices, two screens, two monitors + +twinview <> xinerama: + all sections double + +one or two cards: + different device sections + +one or two monitors + ?? one or two cards + ?? twinview or xinerama (driver? different resolutions?) + + +class XSetup(Object): + Screens[] getScreens() + bool maySetDualhead() + getUseDualhead() + setUseDualhead(bool) + (xoff,yoff) getDualheadPosition() # offset from screen 1 top left corner. + setDualheadOrientation(xoff,yoff) + + bool is3DAccelerated() + + GFXCard[] getGFXCards() + + +class Screen(Object): + maySetResolution() + Resolution[] getAvailableResolutions() + getResolution() + setResolution(Resolution) + + maySetRefresh() + int getRefreshRate() + setRefreshRate(int) + + maySetRotation() + getRotation() + getAvailableRotations() + setRotation() + + bool isAvailableMirrorHorizontal() + getMirrorHorizontal() + setMirrorHorizontal() + bool isAvailableMirrorVertical() + getMirrorVertical() + setMirrorVertical() + +class Resolution(Object): + int getWidth() + int getHeight() + int[] getRefreshRates(): + + + +* What is the current setup? (Xinerama? Singlehead? Twinview?) + +- Warning: Xinerama vs. DRI + * "unlinking" the sliders will yield a warning "Using different + resolutions on the screens will disable 3D hardware acceleration on + the second head", which is a Xinerama deficiency). + + Note: Probably only Ati and nvidia do support mergedFB properly, other + drivers might lose xinerama features like placement and maximize + +- Drag and drop widget + * The fun part: implementing a Drag and Drop widget where the (resizing) + screens can be dragged into their respective relative position, + "snapping" in the more obvious ones, i.e. plain "left of" and "right + of", "above" and "under" (without offset). + + +- displayconfig.MonitorPreview: + Singleheadpreview: Monitor keeps size + DualheadPreview: like now, monitor resizes to show relative screen size + +Displayconfig; Notes abstractielaag +------------------------------------- +* een XSetup heeft N gfxcards. +* een GfxCard heeft N Monitors +* XSetup is een container voor alle objecten uit de abstractielaag. +* XSetup.addMonitor(monitor,gfxcard) waarbij gfxcard al in XSetup zit en een + referentie naar de kaart vormt waarop monitor is aangesloten. +* Een Adaptor heeft een 1:1 relatie met een monitor, en is 'onderdeel' van + een "Screen" (in de zin van Screen section uit xorg.conf) +* Controls zoals resize en de rotatie / mirroring widgets manipuleren een + Adaptor, die intern de Screen manipuleert +* Singlehead resolutie verandert via xrandr en slaat settings op via + displayconfig-restore.py +* Dualhead resolutie aanpassen pakt intern een passende metamode (bij twinview) + en checkt of alle zinvolle metamodes beschikbaar zijn, anders wordt de user + gewaarschuwd dat dit eerst moet gebeuren. +* Adaptor in dualhead / twinview modus worden dus naar een screen vertaald + metamodes heeft. +* Screen moet dan nog support voor metamodes krijgen. + + + +displayconfig: +=============== +* Current resolution doesn't get recognized if current != highest resolution +* new tab display powermanagement, just like the existing tab, we need to be + able to at least replace existing functionality + + +New Widgets: +------------- +* DualheadPreview +* RadioButtons instead of dropdown for Positioning + +* Detect if we're already running on dualhead, (en|dis)able widgets accordingly +* Nvidia MergedFB + - Make preview of dualhead more clear (only xinerama or also mergedFB?) + - Generate list of Metamodes from resolutions available + - write dualhead settings to xorg.conf +* Compare dualhead with different drivers WRT xorg.conf + - generic Xinerama (Matrox, others?) + - dualhead for fglrx, SiS ... + - MergedFB as special case for nvidia (sis?) +* Preview should be rotatable (and|or) resizable (and|or) DualHeadpreview? +* Add missing elements to DualHeadTab + - [ ] Use MergedFB (binds two resolution sliders) + - label with warning "You can only use DRI on one head with different resolutions blahblah" +FIXME: enabling Dualhead should: + - Update resolutions with "metamodes" + - disable Rotation/mirroring stuff + - Add ServerLayout to xorg.conf + - Add second Screen to xorg.conf + - Alternatively (if both resolutions are the same enable MergedFB diff --git a/displayconfig/40guidance-displayconfig_restore b/displayconfig/40guidance-displayconfig_restore new file mode 100644 index 0000000..27f35a1 --- /dev/null +++ b/displayconfig/40guidance-displayconfig_restore @@ -0,0 +1,11 @@ +# Set the X server resolution to that selected by the user. +# +# This needs to be done before windows managers or X clients start, +# otherwise the DPI and fonts sizes get all screwed up. +# +# http://www.simonzone.com/software/guidance +# +# This file is sourced by Xsession(5), not executed. +# The "|| true" is to ensure that the Xsession script does not terminate +# and stop the login if something fails in the Python program. +/opt/kde3/bin/displayconfig-restore || true diff --git a/displayconfig/ScanPCI.py b/displayconfig/ScanPCI.py new file mode 100644 index 0000000..ec63b55 --- /dev/null +++ b/displayconfig/ScanPCI.py @@ -0,0 +1,340 @@ +########################################################################### +# ScanPCI.py - # +# ------------------------------ # +# copyright : (C) 2005 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. # +# # +########################################################################### +"""Provides information about the devices attached to the PCI bus. +""" +import struct +import csv +import os.path +import sys + +########################################################################### +class PCIDevice(object): + def __init__(self,line=None): + self.vendor = None # PCI vendor id + self.device = None + + self.subvendor = None # 0xffff if not probe_type'd or no subid + self.subdevice = None # 0xffff if not probe_type'd or no subid + self.pci_class = None # 'None' if not probe_type'd + + self.pci_bus = None # pci bus id 8 bits wide + self.pci_device = None # pci device id 5 bits wide + self.pci_function = None# pci function id 3 bits wide + + self.module = None + self.text = None + self.already_found = False + + if line is not None: + self.loadFromString(line) + + def isGfxCard(self): + if self.module is not None and \ + (self.module.startswith("Card:") or self.module.startswith("Server:XFree86(")): + return True + + return (self.pci_class & PCIBus.PCI_BASE_CLASS_MASK)==PCIBus.PCI_BASE_CLASS_DISPLAY + + def getModule(self): + if self.module is not None: + if self.module.startswith("Server:XFree86("): + return self.module[15:-1] + elif self.module.startswith("Card:"): + return self.module[5:] + return self.module + + def isModuleXorgDriver(self): + return self.module is not None and \ + (self.module.startswith("Server:XFree86(") or self.module.startswith("Card:")) + + def __str__(self): + s = "PCI:%i:%i:%i, " % (self.pci_bus,self.pci_device,self.pci_function) + s += "Vendor:%x, Device:%x," % (self.vendor,self.device) + if self.subvendor is not None: + s += " Subvendor:%x," % self.subvendor + if self.subdevice is not None: + s += " Subdevice:%x," % self.subdevice + if self.pci_class is not None: + s += " Class:%x," % self.pci_class + if self.module is not None: + s += " Module:%s," % self.module + if self.text is not None: + s += " Text:%s" % self.text + return s + + def loadFromString(self,line): + parts = line.split(",") + for i in range(len(parts)): + bit = parts[i].strip() + if bit.startswith("PCI:"): + pci_code = bit[4:].split(":") + self.pci_bus = int(pci_code[0]) + self.pci_device = int(pci_code[1]) + self.pci_function = int(pci_code[2]) + elif bit.startswith("Vendor:"): + self.vendor = int(bit[7:],16) + elif bit.startswith("Device:"): + self.device = int(bit[7:],16) + elif bit.startswith("Subvendor:"): + self.subvendor = int(bit[10:],16) + elif bit.startswith("Subdevice:"): + self.subdevice = int(bit[10:],16) + elif bit.startswith("Class:"): + self.pci_class = int(bit[6:],16) + elif bit.startswith("Module:"): + self.module = bit[7:] + elif bit.startswith("Text:"): + self.text = " ".join(parts[i:]).strip()[5:] + break + +############################################################################ +class PCIBus(object): + PCI_CLASS_SERIAL_USB = 0x0c03 + PCI_CLASS_SERIAL_FIREWIRE = 0x0c00 + PCI_BASE_CLASS_MASK = 0xff00 + PCI_BASE_CLASS_DISPLAY = 0x0300 + + def __init__(self, data_file_dir="."): + self.devices = [] + self.data_file_dir = data_file_dir + + def detect(self,device_data="/proc/bus/pci/devices"): + # Shamelessly translated from ldetect's pci.c. + fhandle = open(device_data) + for line in fhandle.readlines(): + #print "L:",line + entry = PCIDevice() + self.devices.append(entry) + parts = line.split() + + devbusfn = int(parts[0],16) + idbits = int(parts[1],16) + entry.vendor = idbits >> 16 + entry.device = idbits & 0xffff + entry.pci_bus = devbusfn >> 8 + entry.pci_device = (devbusfn & 0xff) >> 3 + entry.pci_function = (devbusfn & 0xff) & 0x07 + + try: + infohandle = open("/proc/bus/pci/%02x/%02x.%d" % ( + entry.pci_bus, entry.pci_device, entry.pci_function),"r") + # these files are 256 bytes but we only need first 48 bytes + buf = infohandle.read(48) + (class_prog, entry.pci_class, entry.subvendor, entry.subdevice) = \ + struct.unpack("= 1 and row[0] != '': + # Skip manufacturer info lines. + continue + + vendor = int(row[1][:4],16) + device = int(row[1][4:],16) + module = row[3] + text = ' '.join(row[4:]).strip() + + i = 0 + while i1: + if sys.argv[1]=="--help" or sys.argv[1]=="-h": + print "Usage:\n ScanPCI.py " + sys.exit(0) + bus.detect(sys.argv[1]) + else: + bus.detect() + print bus + +if __name__=='__main__': + main() diff --git a/displayconfig/displayconfig-hwprobe.py b/displayconfig/displayconfig-hwprobe.py new file mode 100755 index 0000000..7ba5c69 --- /dev/null +++ b/displayconfig/displayconfig-hwprobe.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +########################################################################### +# displayconfig-hwprobe.py - description # +# ------------------------------ # +# begin : Sun Jan 22 2006 # +# copyright : (C) 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. # +# # +########################################################################### + +# This program should be run during boot time. It quickly examines the +# graphics cards (read: PCI devices) in the computer and compares they to +# the list in the file $hardware_info_filename. If the two lists differ +# then the Debian package manager is automatically called to regenerate +# /etc/X11/xorg.conf. This hopefully should mean that people can swap gfx +# cards in and out and always have a system that will run Xorg. (even +# though the config will be most likely be suboptimal. Suboptimal is better +# than no X server). + +import ScanPCI +import os +import syslog +import select + +hardware_info_filename = "/var/lib/guidance/guidance-gfxhardware-snapshot" +data_file_dir = "/usr/share/apps/guidance/" + +def main(): + # Scan the PCI bus. + pci_bus = ScanPCI.PCIBus(data_file_dir) + pci_bus.detect() + + # Stuff our device info in to a string. + hardware_config = "" + for pci_device in pci_bus.devices: + if pci_device.isGfxCard(): + hardware_config += "PCI:%i:%i:%i Vendor:%x Device:%x Subvendor:%x Subdevice:%x\n" % \ + (pci_device.pci_bus, pci_device.pci_device, pci_device.pci_function, + pci_device.vendor, pci_device.device, + pci_device.subvendor, pci_device.subdevice) + + # Read in the old gfx hardware info in. + previous_hardware = None + try: + fhandle = open(hardware_info_filename) + previous_hardware = fhandle.read() + fhandle.close() + except IOError: + previous_hardware = None + + if previous_hardware is not None and previous_hardware!=hardware_config: + # Run dpkg and configure the new hardware. + syslog.syslog(syslog.LOG_INFO, "Graphics card hardware has changed. Reconfiguring xorg.conf using 'dpkg-reconfigure xserver-xorg'.") + cmd = ['dpkg-reconfigure','xserver-xorg'] + environ = os.environ.copy() + environ['DEBIAN_FRONTEND'] = 'noninteractive' + #os.spawnvpe(os.P_WAIT, 'dpkg-reconfigure', cmd, environ) + result = ExecWithCapture('/usr/sbin/dpkg-reconfigure', cmd, 0, '/', 0,1, -1, environ) + for line in result.split('\n'): + syslog.syslog(syslog.LOG_INFO,"dpkg-reconfigure:"+line) + + # [21:18] you are brave indeed + # [21:21] I figured some kind of non-interactive "dpkg-reconfigure xorg" might be enough. + # [21:22] yep + + if previous_hardware is None or previous_hardware!=hardware_config: + syslog.syslog(syslog.LOG_INFO, "Writing graphics card hardware list to "+hardware_info_filename) + # Write out the gfx hardware info + tmp_filename = hardware_info_filename + ".tmp" + fhandle = open(tmp_filename,'w') + fhandle.write(hardware_config) + fhandle.close() + os.rename(tmp_filename, hardware_info_filename) + + +############################################################################ +def ExecWithCapture(command, argv, searchPath = 0, root = '/', stdin = 0, + catchfd = 1, closefd = -1, environ = None): + + if not os.access(root + command, os.X_OK) and not searchPath: + raise RuntimeError, command + " can not be run" + + (read, write) = os.pipe() + childpid = os.fork() + if (not childpid): + if (root and root != '/'): os.chroot(root) + os.dup2(write, catchfd) + os.close(write) + os.close(read) + + if closefd != -1: + os.close(closefd) + if stdin: + os.dup2(stdin, 0) + os.close(stdin) + + # Replace the environment + if environ is not None: + os.environ.clear() + os.environ.update(environ) + + if searchPath: + os.execvp(command, argv) + else: + os.execv(command, argv) + sys.exit(1) + os.close(write) + + rc = "" + s = "1" + while s: + select.select([read], [], []) + s = os.read(read, 1000) + rc = rc + s + + os.close(read) + + try: + os.waitpid(childpid, 0) + except OSError, (errno, msg): + print __name__, "waitpid:", msg + + return rc + +main() diff --git a/displayconfig/displayconfig-restore.py b/displayconfig/displayconfig-restore.py new file mode 100755 index 0000000..8c44a48 --- /dev/null +++ b/displayconfig/displayconfig-restore.py @@ -0,0 +1,324 @@ +#!/usr/bin/python +########################################################################### +# displayconfig-restore.py - description # +# ------------------------------ # +# begin : Wed Dec 15 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. # +# # +########################################################################### +import os +import os.path +import subprocess +import ixf86misc +import xf86misc + +from execwithcapture import * + +############################################################################ +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 + +############################################################################ +# FixXorgDPI +# ========== +# The idea here is to ensure that applications use a sensible DPI setting +# for fonts. When Xorg starts up it tries to detect the size of the attached +# monitor and calculate the real DPI from there and use that. Problems are: +# +# * if the monitor size can not be detect then Xorg uses 75dpi. This is +# usually far too low. +# +# * if the monitor size is not accurately detected then you get bad a DPI. +# +# * most fonts are optimised to work at a handful of standard DPIs. 96dpi, +# 120dpi and printer resolution 300dpi and 600dpi. Fonts rendered in +# non-standard DPIs often look bad and jagged. This is a real problem +# when rendering fonts on low resolution devices. (i.e. a computer +# monitor). +# +# Although it is desirable in theory to use the real DPI of the monitor, in +# practice it is more important to ensure that fonts are well rendered even +# if the DPI in use is not correct. +# +# What this function does is read the display size from the X server and +# if it is lower than 140dpi then 'round' it to either 96dpi or 120dpi. +# (A dpi greater or equal to 140 is assumed to be high enough to render fonts +# well.) The new dpi is then loaded with the xrdb command into the X server +# resource database. Most X applications (Qt and GTK apps at least) will then +# use this DPI for font rendering. +# +def FixXorgDPI(desiredDPI): + # dpi is: + # None - round the DPI. + # xserver - Use the X server's DPI. + # - DPI to use. + if desiredDPI=="xserver": + return + + dpi = 96 + try: + if desiredDPI is not None: + dpi = int(desiredDPI) + except ValueError: + desiredDPI = None + + if desiredDPI is None: + xserver = xf86misc.XF86Server() + if len(xserver.getScreens())!=0: + (width,height,width_mm,height_mm) = xserver.getScreens()[0].getDimensions() + if not float(width_mm) == 0: + w_dpi = float(width)/(float(width_mm)/25.4) + else: + w_dpi = 96 + if not float(height_mm) == 0: + h_dpi = float(height)/(float(height_mm)/25.4) + else: + h_dpi = 96 + dpi = (w_dpi+h_dpi)/2.0 # Average the two possible DPIs. + + if dpi >= 140: # Anything above 140 is ok. + dpi = int(dpi) + else: + if abs(96-dpi) < abs(120-dpi): # Rounding to 96 is best. + dpi = 96 + else: + dpi = 120 + + # work around for LP beastie 151311 + if ((w_dpi < 200) and (h_dpi > 900)): + dpi = 96 + + try: + xrdb = subprocess.Popen(["xrdb","-nocpp","-merge"],stdin=subprocess.PIPE) + xrdb.communicate("Xft.dpi: %i\n" % dpi) + xrdb.wait() + except OSError: + pass + + # Other common, but now used settingsfor xrdb: + # Xft.antialias: + # Xft.hinting: + # Xft.hintstyle: + # Xft.rgba: + +############################################################################ +def ReadDisplayConfigRC(): + screens = None + dpi = None + dpms_seconds = None + dpms_enabled = None + + configpath = ExecWithCapture("kde-config",['kde-config','--path','config'],True) + + # Hunt down the user's displayconfigrc file and adjust the resolution + # on the fly to match. (Non-root Users can independantly specify their own settings.) + dirs = configpath.strip().split(":") + for dir in dirs: + if dir!="": + configpath = os.path.join(dir,"displayconfigrc") + if os.path.exists(configpath): + # Parse the config file. + fhandle = open(configpath) + screens = [] + currentscreen = None + for line in fhandle.readlines(): + line = line.strip() + if line.startswith("[Screen"): + # Screen, width, height, refresh, reflectx, reflecty, rotate, redgamma, greengamma,bluegamma + currentscreen = [int(line[7:-1]), None, None, None, False, False, "0", None, None, None] + screens.append(currentscreen) + elif line.startswith("["): + currentscreen = None + elif line.startswith("dpi="): + dpi = line[4:] + elif currentscreen is not None: + if line.startswith("width="): + currentscreen[1] = int(line[6:]) + elif line.startswith("height="): + currentscreen[2] = int(line[7:]) + elif line.startswith("refresh="): + currentscreen[3] = int(line[8:]) + elif line.startswith("reflectX="): + currentscreen[4] = line[9:]=="1" + elif line.startswith("reflectY="): + currentscreen[5] = line[9:]=="1" + elif line.startswith("rotate="): + currentscreen[6] = line[7:] + elif line.startswith("redgamma="): + currentscreen[7] = line[9:] + elif line.startswith("greengamma="): + currentscreen[8] = line[11:] + elif line.startswith("bluegamma="): + currentscreen[9] = line[10:] + elif line.startswith("dpmsEnabled"): + dpms_enabled = line.split("=")[1] + elif line.startswith("dpmsSeconds"): + dpms_seconds = int(line.split("=")[1]) + fhandle.close() + break + + return (screens,dpi,dpms_enabled,dpms_seconds) + +############################################################################ +def main(): + (screens,dpi,dpms_enabled,dpms_seconds) = ReadDisplayConfigRC() + + if dpms_enabled: + if dpms_enabled == "on": + if not dpms_seconds: + dpms_seconds = 900 + cmd = "xset dpms %i %i %i" % (dpms_seconds,dpms_seconds,dpms_seconds) + os.system(cmd) + else: + cmd = "xset -dpms" + os.system(cmd) + + if screens is not None: + # Set the X server. + try: + xserver = xf86misc.XF86Server() + if len(screens)!=0: + + for screen in screens: + (id,width,height,refresh,reflectx,reflecty,rotate,redgamma,greengamma,bluegamma) = screen + + # Convert the stuff into RandR's rotation bitfield thingy. + if rotate=="0": + rotation = xf86misc.XF86Screen.RR_Rotate_0 + elif rotate=="90": + rotation = xf86misc.XF86Screen.RR_Rotate_90 + elif rotate=="180": + rotation = xf86misc.XF86Screen.RR_Rotate_180 + elif rotate=="270": + rotation = xf86misc.XF86Screen.RR_Rotate_270 + if reflectx: + rotation |= xf86misc.XF86Screen.RR_Reflect_X + if reflecty: + rotation |= xf86misc.XF86Screen.RR_Reflect_Y + + if id

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 = 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","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() + KCmdLineArgs.init(sys.argv,aboutdata) + + kapp = KApplication() + + displayapp = DisplayApp() + displayapp.exec_loop() diff --git a/displayconfig/displayconfigabstraction.py b/displayconfig/displayconfigabstraction.py new file mode 100644 index 0000000..f59b2ff --- /dev/null +++ b/displayconfig/displayconfigabstraction.py @@ -0,0 +1,3230 @@ +#!/usr/bin/env python + +import os +import sys +import string +import math +import subprocess +import xf86misc +import xorgconfig +import ScanPCI +import csv +import re +from execwithcapture import * + +"""Classes for dealing with X.org configuration in a sane way. + +The object model used here is fairly simple. An XSetup object represents +the complete configuration of the server. The XSetup object contains one or +more GfxCard objects. One for each graphics card present in the machine. +Each GfxCard has one or more Screen objects with each Screen representing +one 'output' on the graphics card. + +Each GfxCard object is also associated with a GfxCardModel object which +describes the model of graphics card. + +Each Screen object is associated with a MonitorModel object which +describes the model of monitor attached. + +""" + +FALLBACK_RESOLUTION = (800,600) + +# FIXME updating /etc/modules for fglrx. +data_file_dir = "." +def SetDataFileDir(dir_name): + global data_file_dir + data_file_dir = dir_name + +var_data_dir = "/var/lib/guidance-backends" +def SetVarDataDir(dir_name): + global var_data_dir + var_data_dir = dir_name + +############################################################################ +class XSetup(object): + """Represents the current configuration of the X.org X11 server. + + + """ + # Map positions + ABOVE = 0 + UNDER = 1 + LEFTOF = 2 + RIGHTOF = 3 + + RESTART_NONE = 0 + RESTART_X = 1 + RESTART_SYSTEM = 2 + + LAYOUT_SINGLE = 1 # These are bit flags. + LAYOUT_CLONE = 2 + LAYOUT_DUAL = 4 + LAYOUT_SINGLE_XINERAMA = 256 # For internal use. + LAYOUT_CLONE_XINERAMA = 512 # For internal use. + + POSITION_LEFTOF = 0 + POSITION_RIGHTOF = 1 + POSITION_ABOVE = 2 + POSITION_BELOW = 3 + + ROLE_UNUSED = 0 + ROLE_PRIMARY = 1 + ROLE_SECONDARY = 2 + + def __init__(self,xorg_config_filename='/etc/X11/xorg.conf',debug_scan_pci_filename=None,secondtry=False): + self.screens = [] + self.gfxcards = [] + self.xorg_config, self.hasxorg = xorgconfig.readConfig(xorg_config_filename, check_exists=True) + if not secondtry: + self.xorg_config_filename = xorg_config_filename + if self.xorg_config_filename == None: + self.xorg_config_filename = '/etc/X11/xorg.conf'; + self.x_live_info = xf86misc.XF86Server() + + self.primary_screen = None + self.secondary_screen = None + + pci_bus = ScanPCI.PCIBus(data_file_dir) + if debug_scan_pci_filename is None: + pci_bus.detect() + else: + pci_bus.loadFromFile(debug_scan_pci_filename) + + # First thing. Scan the PCI bus and find out how many Gfx cards we have. + found_list = self._detectGfxCards(pci_bus) + # list of (PCI_ID, PCIDevice, GfxCard) tuples + + found_list.sort() + + # Prepare some useful data structures. + self.layout = self.LAYOUT_SINGLE + self.xinerama = False + self.orientation = self.POSITION_LEFTOF + + # Maps screen section names to xorg screens section objects. + xorg_screen_name_dict = {} + xorg_unused_screen_sections = self.xorg_config.getSections("Screen") + for screen in xorg_unused_screen_sections: + xorg_screen_name_dict[screen.identifier] = screen + + # Maps device sections names to xorg device sections + xorg_device_name_dict = {} + xorg_unused_device_sections = self.xorg_config.getSections("Device") + for device in xorg_unused_device_sections: + xorg_device_name_dict[device.identifier] = device + + # Maps device sections names to xorg device sections + xorg_monitor_name_dict = {} + xorg_monitor_sections = self.xorg_config.getSections("Monitor") + for monitor in xorg_monitor_sections: + xorg_monitor_name_dict[monitor.identifier] = monitor + + # Maps GfxCard objects to ScanPCI.PCIDevice objects. + gfx_card_pcidevice_dict = {} + + #------------------------------------------------------------------- + # Decode the server layout. + server_layouts = self.xorg_config.getSections("ServerLayout") + if len(server_layouts)==0: + print "*** Error: couldn't find any ServerLayout sections" + return + layout = server_layouts[0] # Grab the first ServerLayout + + if len(layout.screen)==0: + print "*** Error: no screens were specified in the ServerLayout section" + + # Handle leftof rightof below and above. + (primary_name, secondary_name, layout, self.orientation) = self._decodeServerLayoutScreens(layout.screen) + + screen_list = [primary_name] + if secondary_name is not None: + screen_list.append(secondary_name) + + for screen_name in screen_list: + if screen_name in xorg_screen_name_dict: + screen_section = xorg_screen_name_dict[screen_name] + if screen_section.device in xorg_device_name_dict: + + device_section = xorg_device_name_dict[screen_section.device] + + # Ok, we've now got a screen section and its device. + gfx_card = None + + if device_section.busid is not None: + # Try to match this device to a gfxcard by using the PCI bus ID. + bus_id = self._canonicalPCIBusID(device_section.busid) + + # See if there is already a known gfxcard at this PCI ID. + gfx_card = self.getGfxCardByPCIBusID(bus_id) + if gfx_card is not None: + # Let the gfxcard know that we have another device section for it to manage. + gfx_card._addXDevice(device_section) + try: + xorg_unused_device_sections.remove(device_section) + except ValueError: + pass + else: + # Not known, look for a matching pci device instead. + for pci_device_tuple in found_list: + if pci_device_tuple[0]==bus_id: + # Got a hit, create a gfxcard object. + gfx_card = GfxCard( self, pci_id=bus_id, \ + x_device=device_section, \ + detected_model=pci_device_tuple[2]) + + self.gfxcards.append(gfx_card) + gfx_card_pcidevice_dict[gfx_card] = pci_device_tuple[1] + xorg_unused_device_sections.remove(device_section) + found_list.remove(pci_device_tuple) + break + + else: + + # OK, no PCI ID, try matching to a PCI device by X driver name, + # or if there is one only gfx card then just grab it. + driver_name = device_section.driver + for pci_device_tuple in found_list: + if pci_device_tuple[2].getDriver()==driver_name \ + or pci_device_tuple[2].getProprietaryDriver()==driver_name \ + or len(found_list)==1: + # Got a hit, create a gfxcard object. + gfx_card = GfxCard( self, pci_id=pci_device_tuple[0], \ + x_device=device_section, \ + detected_model=pci_device_tuple[2]) + + self.gfxcards.append(gfx_card) + gfx_card_pcidevice_dict[gfx_card] = pci_device_tuple[1] + xorg_unused_device_sections.remove(device_section) + found_list.remove(pci_device_tuple) + break + + if gfx_card is not None: + # Look up the monitor section from the monitor name. + monitor_section = None + monitor_model = None + if screen_section.monitor in xorg_monitor_name_dict: + monitor_section = xorg_monitor_name_dict[screen_section.monitor] + monitor_model = self._matchMonitor(monitor_section) + + screen = Screen(x_config_screen=screen_section, gfx_card=gfx_card, \ + x_config_monitor=monitor_section, monitor_model=monitor_model, \ + x_config=self.xorg_config) + gfx_card._addScreen(screen) + + if self.primary_screen is None: + self.primary_screen = screen + elif self.secondary_screen is None: + self.secondary_screen = screen + + xorg_unused_screen_sections.remove(screen_section) + + if self.primary_screen is not None and self.secondary_screen is not None: + self.layout = layout + + #------------------------------------------------------------------- + # Dualhead hardware detection. + gfx_cards_needing_second_heads = [] + + # Detect dualhead ATI cards + for pci_device in pci_bus.devices: + if pci_device.text is not None and pci_device.text.find("Secondary")!=-1: + + pci_device_id = "PCI:%i:%i:%i" % (pci_device.pci_bus, pci_device.pci_device, pci_device.pci_function) + + for gfx_card in self.gfxcards: + if gfx_card.getPCIBusID() != pci_device_id: + # Compare the first two numbers that make up a PCI bus id (e.g. middle part of PCI:1:0:0) + if gfx_card.getPCIBusID().split(":")[1:-1] == [str(pci_device.pci_bus),str(pci_device.pci_device)]: + if len(gfx_card.getScreens())<2: + gfx_cards_needing_second_heads.append(gfx_card) + found_list = [x for x in found_list if x[0]!=pci_device_id] + break + + # Detect dualhead Intel cards + for gfx_card in self.gfxcards: + if gfx_card._getDetectedGfxCardModel().getDriver() in ['i740','i810', 'intel']: + gfx_card_pci_id = gfx_card.getPCIBusID().split(":")[1:] + base_pci_id = gfx_card_pci_id[:-1] + + for pci_device in pci_bus.devices: + if gfx_card_pci_id != [str(pci_device.pci_bus),str(pci_device.pci_device),str(pci_device.pci_function)]: + if base_pci_id == [str(pci_device.pci_bus),str(pci_device.pci_device)]: + pci_device_id = "PCI:%i:%i:%i" % (pci_device.pci_bus, pci_device.pci_device, pci_device.pci_function) + found_list = [x for x in found_list if x[0]!=pci_device_id] + # Try to configure a second head later if not yet available + if len(gfx_card.getScreens()) < 2: + gfx_cards_needing_second_heads.append(gfx_card) + break + + # Detect dualhead nVidia cards + for gfx_card in self.gfxcards: + if gfx_card._getDetectedGfxCardModel().getDriver() in ['nv','nvidia']: + if self._isNVidiaCardDualhead(gfx_card_pcidevice_dict[gfx_card]): + if len(gfx_card.getScreens())<2: + if gfx_card not in gfx_cards_needing_second_heads: + gfx_cards_needing_second_heads.append(gfx_card) + continue + + # Detect dualhead Matrox cards. This info is from the Cards+ db. + for gfx_card in self.gfxcards: + if (gfx_card._getDetectedGfxCardModel().getMultiHead()>1) and (len(gfx_card.getScreens())<2): + if gfx_card not in gfx_cards_needing_second_heads: + gfx_cards_needing_second_heads.append(gfx_card) + + # Detect laptops. Dualhead/clone mode is standard functionality on laptops. + # (but can be hard to detect). + if os.path.isfile('/usr/sbin/laptop-detect'): + if subprocess.call(['/usr/sbin/laptop-detect'])==0: + if len(self.gfxcards)!=0: + gfx_card = self.gfxcards[0] + if gfx_card not in gfx_cards_needing_second_heads and \ + len(gfx_card.getScreens())<2: + gfx_cards_needing_second_heads.append(gfx_card) + + # Match up the second heads with any loose sections in xorg.conf. + for gfx_card in gfx_cards_needing_second_heads: + screens = gfx_card.getScreens() + # Try to find a loose xorg.conf Screen section that also + # references this gfx card. That is probably the config + # for the second screen. + for screen_section in xorg_unused_screen_sections: + if screen_section.device in xorg_device_name_dict: + device_section = xorg_device_name_dict[screen_section.device] + + # Is this the second screen for the same PCI device aka gfxcard? + + # Note: even though the second head shows up as a separate PCI ID, the screen + # section in xorg.conf still uses the primary PCI ID. + if str(device_section.screen)=="1" and \ + self._canonicalPCIBusID(device_section.busid)==gfx_card.getPCIBusID(): + + # Look up the monitor section from the monitor name. + monitor_section = None + monitor_model = None + if screen_section.monitor in xorg_monitor_name_dict: + monitor_section = xorg_monitor_name_dict[screen_section.monitor] + monitor_model = self._matchMonitor(monitor_section) + + gfx_card._addXDevice(device_section) + xorg_unused_device_sections.remove(device_section) + + screen = Screen(x_config_screen=screen_section, gfx_card=gfx_card, \ + x_config_monitor=monitor_section, monitor_model=monitor_model, \ + x_config=self.xorg_config) + gfx_card._addScreen(screen) + self.secondary_screen = screen + xorg_unused_screen_sections.remove(screen_section) + break + else: + # Couldn't anything in xorg.conf, just make an empty screen + screen = Screen(gfx_card=gfx_card, x_config=self.xorg_config) + gfx_card._addScreen(screen) + + #------------------------------------------------------------------- + # Handle loose gfx card devices. Check that all PCI gfxcards are accounted for. + for pci_device_tuple in found_list: + + bus_id = pci_device_tuple[0] + for device_section in xorg_unused_device_sections: + if bus_id == self._canonicalPCIBusID(device_section.busid): + xorg_unused_device_sections.remove(device_section) + break + else: + device_section = None + + # Got a hit, create a gfxcard object. + gfx_card = GfxCard( self, pci_id=pci_device_tuple[0], \ + x_device=device_section, \ + detected_model=pci_device_tuple[2]) + + gfx_card_pcidevice_dict[gfx_card] = pci_device_tuple[1] + self.gfxcards.append(gfx_card) + + screen = None + # See if this device section is referenced by a screen section. + # if so, then we grab the screen section. + if device_section is not None: + for screen_section in xorg_unused_screen_sections: + if screen_section.device==device_section.identifier: + + # Ok, we have found the screen section, monitor? + monitor_section = None + monitor_model = None + if screen_section.monitor in xorg_monitor_name_dict: + monitor_section = xorg_monitor_name_dict[screen_section.monitor] + monitor_model = self._matchMonitor(monitor_section) + + screen = Screen(x_config_screen=screen_section, gfx_card=gfx_card, \ + x_config_monitor=monitor_section, monitor_model=monitor_model, \ + x_config=self.xorg_config) + gfx_card._addScreen(screen) + xorg_unused_screen_sections.remove(screen_section) + break + + if screen is None: + # Manually add a screen. + screen = Screen(gfx_card=gfx_card, x_config=self.xorg_config) + gfx_card._addScreen(screen) + + #------------------------------------------------------------------- + # Sort the gfx cards by PCI id. + def gfxcard_pci_sort(a,b): return cmp(a.getPCIBusID(),b.getPCIBusID()) + self.gfxcards.sort(gfxcard_pci_sort) + + # Hand out some randr live screens + x_live_screens = self.x_live_info.getScreens() + i = 0 + for gfx_card in self.gfxcards: + for screen in gfx_card.getScreens(): + if itwoHeads = " + # + NV_ARCH_04 = 0x4 + NV_ARCH_10 = 0x10 + NV_ARCH_20 = 0x20 + NV_ARCH_30 = 0x30 + NV_ARCH_40 = 0x40 + + pci_device = PCIDeviceObject.device + + if pci_device & 0xfff0 == 0x00f0: + return True # FIXME PCIXpress chipsets + + # These IDs come from the Xorg source. + # xc/programs/Xserver/hw/xfree86/drivers/nv/nv_driver.c + # And should be periodically updated. + chipset = pci_device & 0x0ff0 + if chipset in [ + 0x0100, # GeForce 256 + 0x0110, # GeForce2 MX + 0x0150, # GeForce2 + 0x0170, # GeForce4 MX + 0x0180, # GeForce4 MX (8x AGP) + 0x01A0, # nForce + 0x01F0]:# nForce2 + architecture = NV_ARCH_10 + elif chipset in [ + 0x0200, # GeForce3 + 0x0250, # GeForce4 Ti + 0x0280]:# GeForce4 Ti (8x AGP) + architecture = NV_ARCH_20 + elif chipset in [ + 0x0300, # GeForceFX 5800 + 0x0310, # GeForceFX 5600 + 0x0320, # GeForceFX 5200 + 0x0330, # GeForceFX 5900 + 0x0340]:# GeForceFX 5700 + architecture = NV_ARCH_30 + elif chipset in [ + 0x0040, + 0x00C0, + 0x0120, + 0x0130, + 0x0140, + 0x0160, + 0x01D0, + 0x0090, + 0x0210, + 0x0220, + 0x0230, + 0x0290, + 0x0390]: + architecture = NV_ARCH_40 + else: + architecture = NV_ARCH_04 + + return (architecture >= NV_ARCH_10) and \ + (chipset != 0x0100) and \ + (chipset != 0x0150) and \ + (chipset != 0x01A0) and \ + (chipset != 0x0200) + + def _syncXorgConfig(self): + + xinerama_clone = (self.layout==XSetup.LAYOUT_CLONE) and \ + not ((self.secondary_screen._getGfxCard() is self.primary_screen._getGfxCard()) \ + and (self.primary_screen._getGfxCard()._getAvailableLayouts() & XSetup.LAYOUT_CLONE)) + + if xinerama_clone: + # For clone mode with xinerama we copy the screen settings from the primary screen + # to the secondary screen. + primary_screen = self.getPrimaryScreen() + secondary_screen = self.getSecondaryScreen() + + resolution = primary_screen.getAvailableResolutions()[primary_screen.getResolutionIndex()] + secondary_resolution_index = secondary_screen.getAvailableResolutions().index(resolution) + secondary_screen.setResolutionIndex(secondary_resolution_index) + secondary_rates = secondary_screen.getAvailableRefreshRatesForResolution(secondary_resolution_index) + primary_rate = primary_screen.getAvailableRefreshRates()[primary_screen.getRefreshRateIndex()] + + best_rate_index = 0 + best_score = 1000000 + for i in range(len(secondary_rates)): + rate = secondary_rates[i] + score = abs(rate-primary_rate) + if score < best_score: + best_score = score + best_rate_index = i + + secondary_screen.setRefreshRateIndex(best_rate_index) + + # Sync up the graphics cards. + for gfxcard in self.gfxcards: + gfxcard._syncXorgConfig() + + server_flags_sections = self.xorg_config.getSections("ServerFlags") + if len(server_flags_sections)!=0: + server_flags = server_flags_sections[0] + server_flags.option.removeOptionByName("Xinerama") + else: + server_flags = self.xorg_config.makeSection(None,["Section","ServerFlags"]) + self.xorg_config.append(server_flags) + + # Delete any old screen entries in the serverlayout section. + server_layout = self.xorg_config.getSections("ServerLayout")[0] + for screen in server_layout.screen[:]: + server_layout.screen.remove(screen) + + # Add the first Screen row + screen_id_1 = self.primary_screen._getXorgScreenSection().identifier + server_layout.screen.append(server_layout.screen.makeLine(None, + ["0",screen_id_1,"0","0"])) + + # FIXME server_flags -> Option "DefaultServerLayout" "???" + if self.layout==XSetup.LAYOUT_DUAL or xinerama_clone: + server_flags.option.append( server_flags.option.makeLine(None,["Xinerama","true"]) ) + + # Add the second screen row. This one also has the dual screen + # orientation info. + screen_id_2 = self.secondary_screen._getXorgScreenSection().identifier + + if not xinerama_clone: + + position = {XSetup.POSITION_LEFTOF:"RightOf", + XSetup.POSITION_RIGHTOF:"LeftOf", + XSetup.POSITION_ABOVE:"Below", + XSetup.POSITION_BELOW:"Above"}[self.orientation] + + server_layout.screen.append(server_layout.screen.makeLine(None, + ["1",screen_id_2,position,screen_id_1])) + else: + # Xinerama clone mode. Place the second screen directly on top of the + # primary screen. + server_layout.screen.append(server_layout.screen.makeLine(None, + ["1",screen_id_2,"0","0"])) + + self.original_layout = self.layout + self.original_orientation = self.orientation + self.original_primary_screen = self.primary_screen + self.original_secondary_screen = self.secondary_screen + + def writeXorgConfig(self,filename): + self._syncXorgConfig() + self.xorg_config.writeConfig(filename) + + def xorgConfigToString(self): + return self.xorg_config.toString() + + def getUsedScreens(self): + """Returns the list of Screen objects that the current setup is using.""" + if self.layout==XSetup.LAYOUT_SINGLE: + return [self.primary_screen] + else: + return [self.primary_screen, self.secondary_screen] + + def getAllScreens(self): + """Returns a list of all Screen object.""" + screens = [] + for card in self.gfxcards: + for screen in card.getScreens(): + screens.append(screen) + return screens + + def getScreen(self,screenindex): + return self.getUsedScreens()[screenindex] + + def getGfxCards(self): + return self.gfxcards[:] # No messin' with the gfx card list. + + def getGfxCardByPCIBusID(self,bus_id): + for gfxcard in self.gfxcards: + if gfxcard.getPCIBusID()==bus_id: + return gfxcard + return None + + def getPrimaryScreen(self): + return self.primary_screen + + def getSecondaryScreen(self): + return self.secondary_screen + + def getScreenRole(self,screen): + if screen is self.primary_screen: + return XSetup.ROLE_PRIMARY + if screen is self.secondary_screen: + return XSetup.ROLE_SECONDARY + return XSetup.ROLE_UNUSED + + def setScreenRole(self,screen,role): + if role==XSetup.ROLE_PRIMARY: + if screen is self.secondary_screen: + # Swap the roles around. + self.secondary_screen = self.primary_screen + self.primary_screen = screen + else: + self.primary_screen = screen + + elif role==XSetup.ROLE_SECONDARY: + if screen is self.primary_screen: + # Swap the roles around. + self.primary_screen = self.secondary_screen + self.secondary_screen = screen + else: + self.secondary_screen = screen + else: + # ROLE_UNUSED + if screen is not self.primary_screen and screen is not self.secondary_screen: + return + + # Find the first screen unused. + for unused_screen in self.getAllScreens(): + if screen is not self.primary_screen and screen is not self.secondary_screen: + if screen is self.primary_screen: + self.primary_screen = unused_screen + else: + self.secondary_screen = unused_screen + return + + def mayModifyXorgConfig(self): + """Check if the current user may modify the xorg.conf file + + Returns True or False + """ + return os.access(self.xorg_config_filename, os.W_OK|os.R_OK) + + def mayModifyGamma(self): + """Check if the current user may modify the gamma settings. + + Returns True or False. + """ + return self.isGammaLive() or self.mayModifyXorgConfig() + + def mayModifyResolution(self): + """Check if the current user may modify the screen resolution. + + Returns True or False. + """ + for screen in self.x_live_info.getScreens(): + if screen.resolutionSupportAvailable(): + return True + + return self.mayModifyXorgConfig() + + def isGammaLive(self): + """Check if gamma changes are done immediately. + + Returns True or False. + """ + return True # FIXME depends on the xvid extension and if86misc. + + def isLiveResolutionConfigChanged(self): + """Check if the live server configuration is changed + + Checks if the configuration has been modified with changes that can be + pushed to the running X server. + + Returns True or False. + """ + # XRandR tends to break Xinerama + if self.primary_screen._getGfxCard().getLayout() in \ + (XSetup.LAYOUT_SINGLE_XINERAMA, XSetup.LAYOUT_DUAL): + return False + for screen in self.getAllScreens(): + if screen.isLive() and screen.isResolutionSettingsChanged(): + return True + + return False + + def applyLiveResolutionChanges(self): + """Apply any changes that can be done live + + Returns True if running server resolution has been changed. + """ + rc = False + for s in self.getUsedScreens(): + if s.isResolutionSettingsChanged(): + s.applyResolutionSettings() + rc = rc or s.isResolutionLive() + return rc + + def acceptLiveResolutionChanges(self): + """ + + + """ + for s in self.getUsedScreens(): + s.acceptResolutionSettings() + + def rejectLiveResolutionChanges(self): + """Rejects and reverts the last live server resolution changes + + Rejects the last resolution changes that were made to the live server + and reverts it back to the previous configuration. + """ + for s in self.getUsedScreens(): + s.revertResolutionSettings() + + def isLiveGammaConfigChanged(self): + """Check if the live server gamma configuration is changed + + Checks if the configuration has been modified with changes that can be + pushed to the running X server. + + Returns True or False. + """ + for screen in self.getAllScreens(): + if screen.isLive() and screen.isGammaSettingsChanged(): + return True + + return False + + def applyLiveGammaChanges(self): + """Apply any changes that can be done live + + Returns True if running server gamma has been changed. + """ + rc = False + for s in self.getUsedScreens(): + if s.isGammaSettingsChanged(): + s.applyGammaSettings() + rc = rc or s.isGammaLive() + return rc + + def acceptLiveGammaChanges(self): + """ + + + """ + for s in self.getUsedScreens(): + s.acceptGammaSettings() + + def rejectLiveGammaChanges(self): + """Rejects and reverts the last live server gamma changes + + Rejects the last gamma changes that were made to the live server + and reverts it back to the previous configuration. + """ + for s in self.getUsedScreens(): + s.revertGammaSettings() + + def isXorgConfigChanged(self): + """Check if the xorg.config needs to updated + + Returns True if the xorg.config file needs to updated to reflect new changes. + """ + changed = self.original_layout!=self.layout or \ + self.original_orientation!=self.orientation or \ + self.original_primary_screen!=self.primary_screen or \ + self.original_secondary_screen!=self.secondary_screen + + for gfxcard in self.gfxcards: + changed = changed or gfxcard.isXorgConfigChanged() + for screen in self.getAllScreens(): + changed = changed or screen.isXorgConfigChanged() + return changed + + def getRestartHint(self): + hint = XSetup.RESTART_NONE + if self.original_layout!= self.layout or self.original_orientation != self.orientation: + hint = XSetup.RESTART_X + return max(hint,max( [gfxcard.getRestartHint() for gfxcard in self.gfxcards] )) + + def reset(self): + for card in self.gfxcards: + card.reset() + + self.layout = self.original_layout + self.orientation = self.original_orientation + self.primary_screen = self.original_primary_screen + self.secondary_screen = self.original_secondary_screen + + # Dualhead and secondary monitor support ---------- + def getLayout(self): + return self.layout + + def setLayout(self,layout): + """ + + Keyword arguments: + layout - XSetup.LAYOUT_SINGLE, XSetup.LAYOUT_CLONE or XSetup.LAYOUT_DUAL. + """ + self.layout = layout + + if self.layout==XSetup.LAYOUT_SINGLE: + for gfxcard in self.gfxcards: + gfxcard.setLayout(XSetup.LAYOUT_SINGLE) + self.xinerama = False + elif self.layout==XSetup.LAYOUT_DUAL: + # 'xinerama' screens can be combined by the ServerLayout xorg.conf + # sections into a multihead configurations. Gfxcard objects just + # have to output xinerama friendly xorg.conf device and screen + # sections. + self.xinerama = True + for gfxcard in self.gfxcards: + gfxcard.setLayout(XSetup.LAYOUT_SINGLE_XINERAMA) + + # Check if the primary and secondary screen are on the same gfx card. + # If so then see if the gfxcard can directly (read: accelarated) support + # the layout we want. + if self.primary_screen._getGfxCard() is self.secondary_screen._getGfxCard(): + if self.primary_screen._getGfxCard().getAvailableLayouts() & self.layout: + self.primary_screen._getGfxCard().setLayout(self.layout) + self.xinerama = False + + elif self.layout==XSetup.LAYOUT_CLONE: + + # If the graphics card itself has both heads and it can offer a better clone + # mode, then we use that instead of faking it with xinerama. + if (self.secondary_screen._getGfxCard() is self.primary_screen._getGfxCard()) \ + and (self.primary_screen._getGfxCard()._getAvailableLayouts() & XSetup.LAYOUT_CLONE): + self.xinerama = False + for gfxcard in self.gfxcards: + gfxcard.setLayout(XSetup.LAYOUT_CLONE) + else: + self.xinerama = True + for gfxcard in self.gfxcards: + gfxcard.setLayout(XSetup.LAYOUT_SINGLE_XINERAMA) + + def mayModifyLayout(self): + return self.mayModifyXorgConfig() + + def getAvailableLayouts(self): + if self.secondary_screen is not None: + return XSetup.LAYOUT_SINGLE | XSetup.LAYOUT_DUAL | XSetup.LAYOUT_CLONE + else: + return XSetup.LAYOUT_SINGLE + + def setDualheadOrientation(self,orientation): + """ Sets orientation of monitor to one of + XSetup.ABOVE, XSetup.UNDER, XSetup.LEFTOF, XSetup.RIGHTOF + """ + self.orientation = orientation + + def getDualheadOrientation(self): + """ Returns the current orientation, one of + XSetup.ABOVE, XSetup.UNDER, XSetup.LEFTOF, XSetup.RIGHTOF + """ + return self.orientation + + def isHWAccelerated(self): + # FIXME: + # if twinview-alike and screen[0].res = screen[1].res + # else: if primary screen + return True + + # Internal ---------- + def _addScreen(self,screen): + self.screens.append(screen) + + def _addGfxCard(self,gfxcard): + self.gfxcards.append(gfxcard) + + def _getColorDepth(self): + return min([s._getColorDepth() for s in self.getUsedScreens()]) + + def __str__(self): + string = "XSetup:\n" + string += " Layout: %s\n" % ({self.LAYOUT_SINGLE: "Single", + self.LAYOUT_CLONE: "Clone", + self.LAYOUT_DUAL: "Dual" + }[self.getLayout()]) + + i = 1 + for gfxcard in self.gfxcards: + string += " Gfxcard %i: %s\n" % (i,str(gfxcard)) + i += 1 + return string + +############################################################################ +class GfxCard(object): + """Represents a graphics card that is present in this computer.""" + + def __init__(self, setup, pci_id=None, x_device=None, detected_model=None, proprietary_driver=False): + self.setup = setup + self.x_config = self.setup.xorg_config + self.layout = XSetup.LAYOUT_SINGLE + self.pci_id = pci_id + self.screens = [] + + self.detected_model = detected_model + self.proprietary_driver = proprietary_driver + + self.video_ram = 1024 + + # The (optimised) layout that was detected in xorg.conf on device level. + self.detected_layout = XSetup.LAYOUT_SINGLE + self.detected_orientation = XSetup.POSITION_LEFTOF + + self.x_device = [] # This can be a list of xorg device sections + if x_device is not None: + self.x_device.append(x_device) + + def _addScreen(self,screen): + self.screens.append(screen) + + def _addXDevice(self,x_device): + self.x_device.append(x_device) + + def _finalizeInit(self): + # Finish initalisation. + + if len(self.x_device)!=0: + + # Try to find a gfx card model. + self.gfxcard_model = None + if self.x_device[0].boardname is not None: + # Look up the model by boardname. + try: + self.gfxcard_model = GetGfxCardModelDB().getGfxCardModelByName(self.x_device[0].boardname) + except KeyError: + pass + + if self.gfxcard_model is None: + # OK, try looking it up by driver. + try: + self.gfxcard_model = GetGfxCardModelDB().getGfxCardModelByDriverName(self.x_device[0].driver) + except KeyError: + self.gfxcard_model = self.detected_model + + # Write the current driver in the model + if self.x_device[0].driver: + self.gfxcard_model.setDriver(self.x_device[0].driver) + + self.proprietary_driver = self.gfxcard_model.getProprietaryDriver()==self.x_device[0].driver + + if self.x_device[0].videoram is not None: + self.video_ram = int(self.x_device[0].videoram) + + # Detect layout + if len(self.screens)>=2: + # Xorg ATI driver. + if self._getCurrentDriver() in ['ati','r128','radeon']: + merged = self.x_device[0].option.getOptionByName('mergedfb') + if merged is not None and xorgconfig.toBoolean(merged._row[2]): + self.detected_layout = XSetup.LAYOUT_CLONE + # ATI proprietary driver + elif self._getCurrentDriver()=='fglrx': + desktopsetup = self.x_device[0].option.getOptionByName('desktopsetup') + if desktopsetup is not None and desktopsetup._row[2]=='c': + self.detected_layout = XSetup.LAYOUT_CLONE + # nVidia proprietary driver + elif self._getCurrentDriver()=='nvidia': + twinview = self.x_device[0].option.getOptionByName('twinview') + if twinview is not None: + desktopsetup = self.x_device[0].option.getOptionByName('twinvieworientation') + if desktopsetup is not None and desktopsetup._row[2].lower()=='clone': + self.detected_layout = XSetup.LAYOUT_CLONE + # i810 driver + elif self._getCurrentDriver() in ['i810', 'intel']: + clone = self.x_device[0].option.getOptionByName('clone') + if clone is not None: + if xorgconfig.toBoolean(clone._row[2]): + self.detected_layout = XSetup.LAYOUT_CLONE + + else: + self.gfxcard_model = self.detected_model + + self.original_gfxcard_model = self.gfxcard_model + self.original_proprietary_driver = self.proprietary_driver + self.original_layout = self.layout + + # Give the screens a chance to finish initialisation. + for screen in self.screens: + screen._finalizeInit() + for screen in self.screens: + screen._resyncResolution() + + def _getDetectedLayout(self): + return self.detected_layout + + def _getDetectedDualheadOrientation(self): + return self.detected_orientation + + def _getDetectedGfxCardModel(self): + return self.detected_model + + def getScreens(self): + return self.screens[:] + + def getGfxCardModel(self): + return self.gfxcard_model + + def setGfxCardModel(self,gfx_card_model): + self.gfxcard_model = gfx_card_model + + for screen in self.screens: + screen._resyncResolution() + + def getXorgDeviceSection(self,index): + return self.x_device[index] + + def isProprietaryDriver(self): + return self.proprietary_driver + + def setProprietaryDriver(self,proprietary_driver): + self.proprietary_driver = proprietary_driver + + def getDetectedGfxCardModel(self): + return self.detected_model + + def getPCIBusID(self): + return self.pci_id + + def isXorgConfigChanged(self): + return self.original_gfxcard_model is not self.gfxcard_model or \ + self.original_proprietary_driver != self.proprietary_driver or \ + self.original_layout != self.layout + + def reset(self): + for screen in self.screens: + screen.reset() + + self.gfxcard_model = self.original_gfxcard_model + self.proprietary_driver = self.original_proprietary_driver + + def isAGP(self): + return self.pci_id=='PCI:1:0:0' # FIXME this might not be accurate. + + def getLayout(self): + return self.layout + + def setLayout(self,layout): + self.layout = layout + for screen in self.screens: + screen._resyncResolution() + + def getAvailableLayouts(self): + layouts = XSetup.LAYOUT_SINGLE + if len(self.screens)==2: + if self._getCurrentDriver() in ['fglrx', 'nvidia', 'i810']: + layouts |= XSetup.LAYOUT_CLONE | XSetup.LAYOUT_DUAL + return layouts + + def getVideoRam(self): + """ + Get the amount of video ram that this card has. + + The values returned have the following meanings: + 256 => 256 kB + 512 => 512 kB + 1024 => 1 MB + 2048 => 2 MB + 4096 => 4 MB + 8192 => 8 MB + 16384 => 16 MB + 32768 => 32 MB + 65536 => 64 MB or more + + The video ram setting is only used if the selected graphics card model requires + that the amount of video ram be explicitly specified. That is to say that + gfxcard.getGfxCardModel().getNeedVideoRam() returns true. + + Returns an integer from the list above. + """ + return self.video_ram + + def setVideoRam(self,ram): + self.video_ram = ram + + for screen in self.screens: + screen._resyncResolution() + + def _getCurrentDriver(self): + if self.proprietary_driver: + return self.gfxcard_model.getProprietaryDriver() + else: + return self.gfxcard_model.getDriver() + + def getRestartHint(self): + # The ATI propreitary drivers need a special AGP kernel module to be loaded. + # The best, and often only way to get this module loaded is to reboot. + # The same applies for removing the module. + hint = XSetup.RESTART_NONE + + if self.original_proprietary_driver: + original_gfx_driver = self.original_gfxcard_model.getProprietaryDriver() + else: + original_gfx_driver = self.original_gfxcard_model.getDriver() + + current_gfx_driver = self._getCurrentDriver() + + if current_gfx_driver!=original_gfx_driver: + # Restart X if the driver is different. + hint = XSetup.RESTART_X + + # Layout changes also require an X server restart. + if self.original_layout!=self.layout: + hint = XSetup.RESTART_X + + if original_gfx_driver=='fglrx' and current_gfx_driver in ['ati','r128','radeon']: + hint = XSetup.RESTART_SYSTEM + + # If a different kernel module is needed, then restart the system. + kernel_module_list = self._getLoadedKernelModules() + if self._needATIFglrx() and 'fglrx' not in kernel_module_list: + hint = XSetup.RESTART_SYSTEM + else: + if 'fglrx' in kernel_module_list: + hint = XSetup.RESTART_SYSTEM + + hintlist = [hint] + hintlist.extend([screen.getRestartHint() for screen in self.screens]) + return max(hintlist) + + def _needATIFglrx(self): + """Work out if the current configuration require the ATI fglrx kernel module.""" + return self.isAGP() and self.getGfxCardModel() is not None and \ + self.getGfxCardModel().getProprietaryDriver()=='fglrx' and self.isProprietaryDriver() + + def _getLoadedKernelModules(self): + return [line.split()[0] for line in open('/proc/modules')] + + def _getAvailableLayouts(self): + if len(self.screens)>=2: + drv = self._getCurrentDriver() + layouts = XSetup.LAYOUT_SINGLE | XSetup.LAYOUT_DUAL + if drv in ['fglrx', 'nvidia', 'i810']: + layouts |= XSetup.LAYOUT_CLONE + elif drv in ['ati', 'radeon', 'r128', 'intel']: + layouts = XSetup.LAYOUT_SINGLE + return layouts + else: + return XSetup.LAYOUT_SINGLE + + def __str__(self): + screen_string = ",".join([str(s) for s in self.screens]) + driver = self._getCurrentDriver() + return "GfxCard: {model:"+str(self.gfxcard_model)+", driver:"+driver+", screens:"+screen_string+"}" + + def _syncXorgConfig(self): + if self.proprietary_driver and self.gfxcard_model.getProprietaryDriver() is not None: + driver = self.gfxcard_model.getProprietaryDriver() + else: + driver = self.gfxcard_model.getDriver() + + # FIXME maybe this module stuff should migrate into XSetup. + + # --- Fix the module section --- + + # $raw_X->set_devices($card, @{$card->{cards} || []}); + # $raw_X->get_ServerLayout->{Xinerama} = { commented => !$card->{Xinerama}, Option => 1 } + #if defined $card->{Xinerama}; + module_sections = self.x_config.getSections("Module") + if len(module_sections) > 0: + module = module_sections[0] + else: + module = self.x_config.makeSection(None, ["Section", "Module"]) + self.x_config.append(module) + + module.removeModule('GLcore') + module.removeModule('glx') + module.removeModule('dbe') + + # Mandriva + #module.removeModule("/usr/X11R6/lib/modules/extensions/libglx.so") + + if driver=='nvidia': + module.addModule("glx") + + # Mandriva + # This loads the NVIDIA GLX extension module. + # IT IS IMPORTANT TO KEEP NAME AS FULL PATH TO libglx.so ELSE + # IT WILL LOAD XFree86 glx module and the server will crash. + + # module.addModule("/usr/X11R6/lib/modules/extensions/libglx.so") + # FIXME lib64 + elif self.gfxcard_model.getProprietaryDriver()!='fglrx': + module.addModule('glx') + module.addModule('GLcore') + + #module.removeModule("/usr/X11R6/lib/modules/extensions/libglx.a") + if driver=='fglrx': + module.addModule("glx") + module.addModule("dbe") + #elif driver!='nvidia': + # module.addModule("/usr/X11R6/lib/modules/extensions/libglx.a") + + # DRI + module.removeModule('dri') + if self.gfxcard_model.getDriGlx(): + module.addModule('dri') + + module.removeModule('v4l') + if not (self.gfxcard_model.getDriGlx() and self.gfxcard_model.getDriver()=='r128'): + module.addModule('v4l') + + # --- Fix all of the Device sections --- + for i in range(len(self.screens)): + + if i==len(self.x_device): + new_device = self.x_config.makeSection('',['section','device']) + self.x_config.append(new_device) + self.x_device.append(new_device) + new_device.identifier = self.x_config.createUniqueIdentifier("device") + + identifier = self.x_device[i].identifier + busid = self.x_device[i].busid + + self.x_device[i].clear() + + # Create a new Device section in the Xorg config file. + self.x_device[i].identifier = identifier + self.x_device[i].boardname = self.gfxcard_model.getName() + self.x_device[i].busid = self.pci_id + self.x_device[i].driver = driver + self.x_device[i].screen = str(i) + + if self.gfxcard_model.getVendor() is not None: + self.x_device[i].vendorname = self.gfxcard_model.getVendor() + + if self.gfxcard_model.getNeedVideoRam(): + self.x_device[i].videoram = self.video_ram + + # Setup Clone mode for second heads. + if driver in ["ati","r128","radeon"]: # Xorg ATI driver + merged_value = { + XSetup.LAYOUT_CLONE: "on", + XSetup.LAYOUT_SINGLE: "off", + XSetup.LAYOUT_DUAL: "on", + XSetup.LAYOUT_SINGLE_XINERAMA: "off" + }[self.layout] + + merged_option = self.x_device[i].option.makeLine(None,["MergedFB",merged_value]) + self.x_device[i].option.append(merged_option) + + if self.layout==XSetup.LAYOUT_CLONE: + monitor_model = self.setup.getSecondaryScreen().getMonitorModel() + if monitor_model is not None: + if monitor_model.getHorizontalSync() is not None: + hsyncline = self.x_device[i].option.makeLine(None,['CRT2HSync',monitor_model.getHorizontalSync()]) + self.x_device[i].option.append(hsyncline) + + if monitor_model.getVerticalSync() is not None: + vsyncline = self.x_device[i].option.makeLine(None,['CRT2VRefresh',monitor_model.getVerticalSync()]) + self.x_device[i].option.append(vsyncline) + + # FIXME option "CloneMode" "off" + + if driver=='fglrx': # ATI proprietary driver. + if self.layout==XSetup.LAYOUT_CLONE: + new_option = self.x_device[i].option.makeLine(None,["DesktopSetup","c"]) + self.x_device[i].option.append(new_option) + + # FIXME this probably won't work on laptops and DVI. The user will probably + # have to manually select the monitor types. + + # We do this to make sure that the driver starts up in clone mode even + # if it can't detect the second monitor. + new_option = self.x_device[i].option.makeLine(None,["ForceMonitors","crt1,crt2"]) + self.x_device[i].option.append(new_option) + + monitor_model = self.setup.getSecondaryScreen().getMonitorModel() + if monitor_model is not None: + if monitor_model.getHorizontalSync() is not None: + hsyncline = self.x_device[i].option.makeLine(None,['HSync2',monitor_model.getHorizontalSync()]) + self.x_device[i].option.append(hsyncline) + + if monitor_model.getVerticalSync() is not None: + vsyncline = self.x_device[i].option.makeLine(None,['VRefresh2',monitor_model.getVerticalSync()]) + self.x_device[i].option.append(vsyncline) + + if driver=='nvidia': # nVidia proprietary driver. + if self.layout==XSetup.LAYOUT_CLONE: + new_option = self.x_device[i].option.makeLine(None,["TwinView","on"]) + self.x_device[i].option.append(new_option) + new_option = self.x_device[i].option.makeLine(None,["TwinViewOrientation","clone"]) + self.x_device[i].option.append(new_option) + + monitor_model = self.setup.getSecondaryScreen().getMonitorModel() + if monitor_model is not None: + if monitor_model.getHorizontalSync() is not None: + hsyncline = self.x_device[i].option.makeLine(None,['SecondMonitorHorizSync',monitor_model.getHorizontalSync()]) + self.x_device[i].option.append(hsyncline) + + if monitor_model.getVerticalSync() is not None: + vsyncline = self.x_device[i].option.makeLine(None,['SecondMonitorVertRefresh',monitor_model.getVerticalSync()]) + self.x_device[i].option.append(vsyncline) + + if driver in ['i810']: # i810 driver + if self.layout in (XSetup.LAYOUT_SINGLE_XINERAMA, + XSetup.LAYOUT_DUAL, + XSetup.LAYOUT_CLONE): + new_option = self.x_device[i].option.makeLine(None,["MonitorLayout", "CRT,LFP"]) + self.x_device[i].option.append(new_option) + if self.layout==XSetup.LAYOUT_CLONE: + new_option = self.x_device[i].option.makeLine(None,["Clone","on"]) + self.x_device[i].option.append(new_option) + + # Find the closest matching refresh rate for the second monitor. + primary_screen = self.setup.getPrimaryScreen() + secondary_screen = self.setup.getSecondaryScreen() + resolution = primary_screen.getAvailableResolutions()[primary_screen.getResolutionIndex()] + secondary_resolution_index = secondary_screen.getAvailableResolutions().index(resolution) + secondary_rates = secondary_screen.getAvailableRefreshRatesForResolution(secondary_resolution_index) + primary_rate = primary_screen.getAvailableRefreshRates()[primary_screen.getRefreshRateIndex()] + + best_rate = 50 + best_score = 1000000 + for rate in secondary_rates: + score = abs(rate-primary_rate) + if score < best_score: + best_score = score + best_rate = rate + + # Specify a working refresh rate for the second monitor. + new_option = self.x_device[i].option.makeLine(None,["CloneRefresh",str(best_rate)]) + self.x_device[i].option.append(new_option) + + self._insertRawLinesIntoConfig(self.x_device[i], self.gfxcard_model.getLines()) + + self.screens[i]._syncXorgConfig(self.x_device[i]) + + self.original_gfxcard_model = self.gfxcard_model + self.original_proprietary_driver = self.proprietary_driver + self.original_layout = self.layout + + def _insertRawLinesIntoConfig(self,section,lines): + reader = csv.reader(lines,delimiter=' ') + for row in reader: + if len(row)>=2: + if row[0].lower()=="option": + option = section.option.makeLine(None,row[1:]) + section.option.append(option) + +############################################################################ +class Screen(object): + """Represents a single output/screen/monitor on a graphics card. + + Changes to the screen resolution, refresh rate, rotation and reflection + settings are not made active until the method applyResolutionSettings() is + called. After calling applyResolutionSettings(), changes can be backed out + of with the revertResolutionSettings() method. If you, should I say the user, + is satisfied with the new settings then call the acceptResolutionSettings() + method. + + Gamma correction settings take effect immediately, and don't take part in the + apply, revert and accept mechanism above. + """ + + RR_Rotate_0 = xf86misc.XF86Screen.RR_Rotate_0 + RR_Rotate_90 = xf86misc.XF86Screen.RR_Rotate_90 + RR_Rotate_180 = xf86misc.XF86Screen.RR_Rotate_180 + RR_Rotate_270 = xf86misc.XF86Screen.RR_Rotate_270 + RR_Reflect_X = xf86misc.XF86Screen.RR_Reflect_X + RR_Reflect_Y = xf86misc.XF86Screen.RR_Reflect_Y + + def __init__(self, gfx_card=None, x_config_screen=None, x_config_monitor=None, \ + monitor_model=None, x_config=None): + """Create a Screen object. + + This method is private to this module. + """ + self.gfx_card = gfx_card + self.x_config_screen = x_config_screen + + self.x_config_monitor = x_config_monitor + self.monitor_model = monitor_model + self.monitor_aspect = ModeLine.ASPECT_4_3 + self.original_monitor_model = monitor_model + self.x_config = x_config + + # Cookup some sensible screen sizes. + self.standard_sizes = GetMonitorModeDB().getAllResolutions() + + self.x_live_screen = None + + # Intialise the gamma settings with defaults. + self.redgamma = 1.0 + self.greengamma = 1.0 + self.bluegamma = 1.0 + self.allgamma = 1.0 + self.settingall = True + + # If there is a monitor xorg.conf section then look there for gamma info. + if self.x_config_monitor is not None: + gamma_row = self.x_config_monitor.getRow('gamma') + if gamma_row is not None: + # Read the gamma info out of xorg.conf + try: + if len(gamma_row)==3: + self.redgamma = float(gamma_row[0]) + self.greengamma = float(gamma_row[1]) + self.bluegamma = float(gamma_row[2]) + self.allgamma = self.redgamma + elif len(gamma_row)==1: + self.allgamma = float(gamma_row[0]) + self.redgamma = self.allgamma + self.greengamma = self.allgamma + self.bluegamma = self.allgamma + except ValueError: + pass + + # Try to work out if this monitor is setup for 4:3 modes or 16:9. + aspect_43_count = 0 + aspect_169_count = 0 + # Count the number of 4:3 modelines compared to 16:9 modes. + for mode in self.x_config_monitor.modeline: + try: + # Don't count the fallback resolution. It is also present + # if the monitor is widescreen. Just ignore it. + if (mode._row[2],mode._row[6])!=FALLBACK_RESOLUTION: + if MonitorModeDB.aspectRatio(mode._row[2],mode._row[6])==ModeLine.ASPECT_4_3: + aspect_43_count += 1 + else: + aspect_169_count += 1 + except IndexError: + pass + + if aspect_43_count >= aspect_169_count: + self.monitor_aspect = ModeLine.ASPECT_4_3 + else: + self.monitor_aspect = ModeLine.ASPECT_16_9 + + # Backup the settings + (self.originalredgamma, self.originalgreengamma, self.originalbluegamma) = ( + self.redgamma, self.greengamma, self.bluegamma) + self.originalallgamma = self.allgamma + self.originalsettingall = self.settingall + self.original_monitor_aspect = self.monitor_aspect + + def _setXLiveScreen(self,x_live_screen): + self.x_live_screen = x_live_screen + + def _finalizeInit(self): + + if self.x_live_screen is not None and self.x_live_screen.resolutionSupportAvailable(): + self._computeSizesFromXorg() + + (cw,ch,x,x) = self.x_live_screen.getSize() + i = 0 + self.currentsizeindex = 0 + for size in self.available_sizes: + if (cw,ch)==size: + self.currentsizeindex = i + break + i += 1 + + self.currentrefreshrate = self.x_live_screen.getRefreshRate() + self.currentrotation = self.x_live_screen.getRotation() & ( + Screen.RR_Rotate_0 | Screen.RR_Rotate_90 | Screen.RR_Rotate_180 | Screen.RR_Rotate_270) + self.currentreflection = self.x_live_screen.getRotation() & ( + Screen.RR_Reflect_X | Screen.RR_Reflect_Y) + + else: + # There is no live info, so try to collect some info out + # of xorg.conf itself. + + # Cookup some reasonable screen resolutions based on what we know about the monitor. + self._computeSizesFromMonitor() + + (cw,ch) = self.available_sizes[0] + self.currentrefreshrate = None + + # Dig through the Display sections in the xorg.conf Screen section + # and try to find the first resolution/mode. + if self.x_config_screen is not None: + default_depth = self.x_config_screen.defaultdepth + + current_mode_name = None + + for display_section in self.x_config_screen.getSections('display'): + if default_depth is None or display_section.depth==default_depth: + modes_row = display_section.getRow('modes') + if modes_row is not None and len(modes_row)>=1: + current_mode_name = modes_row[0] + break + + if current_mode_name is not None: + for mode in self.mode_list: + if mode.getName()==current_mode_name: + cw = mode.getWidth() + ch = mode.getHeight() + self.currentrefreshrate = mode.getVRefresh() + break + + # Work out the index of the current resolution + i = 0 + for size in self.available_sizes: + if (cw,ch)==size: + self.currentsizeindex = i + break + i += 1 + + if self.currentrefreshrate is None: + self.currentrefreshrate = self.getAvailableRefreshRates()[0] + + self.currentrotation = Screen.RR_Rotate_0 # FIXME + self.currentreflection = 0 # FIXME + + # Gamma settings + if self.x_live_screen is not None: + try: + (self.redgamma, self.greengamma, self.bluegamma, self.allgama, + self.settingall) = self._getGammaFromLiveScreen() + except: + (self.redgamma, self.greengamma, self.bluegamma, self.allgama, + self.settingall) = self._getGammaFromXorg() + else: + (self.redgamma, self.greengamma, self.bluegamma, self.allgama, + self.settingall) = self._getGammaFromXorg() + + self.originalsizeindex = self.currentsizeindex + self.original_size = self.getAvailableResolutions()[self.currentsizeindex] + self.originalrefreshrate = self.currentrefreshrate + self.originalrotation = self.currentrotation + self.originalreflection = self.currentreflection + + self.originalredgamma = self.redgamma + self.originalgreengamma = self.greengamma + self.originalbluegamma = self.bluegamma + + self.originalallgamma = self.allgamma + self.originalsettingall = self.settingall + + def _getGammaFromLiveScreen(self): + """Reads the gamma information from the x server""" + # Read the current gamma settings directly from X. + (redgamma, greengamma, bluegamma) = self.x_live_screen.getGamma() + + # Round the values off to 2 decimal places. + redgamma = round(self.redgamma,2) + greengamma = round(self.greengamma,2) + bluegamma = round(self.bluegamma,2) + + allgamma = redgamma + settingall = redgamma==greengamma==bluegamma + return (redgamma, greengamma, bluegamma, allgamma, settingall) + + def _getGammaFromXorg(self): + """Extracts the gamma information from the xorg configuration""" + # Set some gamma defaults + redgamma = greengamma = bluegamma = allgamma = 1.0 + settingall = True + + # Look for gamma information in xorg.conf + if self.x_config_monitor is not None: + gamma_row = self.x_config_monitor.getRow('gamma') + if gamma_row is not None: + try: + if len(gamma_row)==1: + allgamma = float(gamma_row[0]) + redgamma = greengamma = bluegamma = allgamma + self.settingall = True + elif len(gamma_row.row)==3: + redgamma = float(gamma_row[0]) + greengamma = float(gamma_row[1]) + bluegamma = float(gamma_row[2]) + allgamma = self.redgamma + settingall = False + except ValueError: + pass + return (redgamma, greengamma, bluegamma, allgamma, settingall) + + def _computeSizesFromXorg(self): + all_sizes = self.x_live_screen.getAvailableSizes() + self.available_sizes = [] + + # Some dualhead setups repolonger sizelists, those are unlikely + # to be standard zes, we still want to be able to use them.s + for i in range(len(all_sizes)): + if len(all_sizes[i]) > 2: + self.available_sizes.append(all_sizes[i][:2]) + elif len(alls_sizes[i]) == 2: + if (size[0],size[1]) in self.standard_sizes: + self.available_sizes.append(all_sizes[i]) + self.available_sizes.sort() + + def _computeSizesFromMonitor(self): + monitor_model = self.monitor_model + + if monitor_model is None: + # If there is no monitor model selected, then just use a default + # model so that we at least get some fairly safe resolutions. + monitor_model = GetMonitorModelDB().getMonitorByName("Monitor 800x600") + + self.mode_list = GetMonitorModeDB().getAvailableModes(monitor_model,self.monitor_aspect) + resolutions = set() + for mode in self.mode_list: + pw = mode.getWidth() + ph = mode.getHeight() + if (pw,ph) in self.standard_sizes and pw>=640 and ph>=480: + resolutions.add( (pw,ph) ) + + # Filter the sizes by the amount of video ram that we have. + color_bytes = self._getColorDepth()/8 + if self.gfx_card.getGfxCardModel().getNeedVideoRam(): + video_memory = self.gfx_card.getVideoRam() + else: + video_memory = 65536 # Big enough. + video_memory *= 1024 # Convert to bytes. + + # Filter the list of modes according to available memory. + self.available_sizes = [mode for mode in resolutions if mode[0]*mode[1]*color_bytes <= video_memory] + + self.available_sizes.sort() + + def _getColorDepth(self): + if self.gfx_card.getGfxCardModel().getNeedVideoRam(): + # If this card has limited memory then we fall back to 16bit colour. + if self.gfx_card.getVideoRam() <= 4096: + return 16 # 16bit colour + else: + return 24 # 24bit colour + else: + return 24 + + def getName(self): + try: + return "Screen %i" % (self.gfx_card.setup.getUsedScreens().index(self)+1) + except ValueError: + return "Screen ?" + + def isLive(self): + """Returns True if this screen is currently being used by the X server. + """ + return self.x_live_screen is not None + + def getMonitorModel(self): + """ + + Returns a MonitorModel object or None. + """ + return self.monitor_model + + def setMonitorModel(self,monitor_model): + """ + + Setting the monitor also changes the resolutions that are available. + + """ + self.monitor_model = monitor_model + self._resyncResolution() + + def getMonitorAspect(self): + """ + Get the aspect ratio for the monitor + + Returns one of ModeLine.ASPECT_4_3 or ModeLine.ASPECT_16_9. + """ + return self.monitor_aspect + + def setMonitorAspect(self,aspect): + """Specify the aspect ratio of the monitor. + + Keyword arguments: + aspect -- Aspect ratio. Either the constant ModeLine.ASPECT_4_3 or ModeLine.ASPECT_16_9. + + Setting this also changes the resolutions that are available. + """ + self.monitor_aspect = aspect + self._resyncResolution() + + def _resyncResolution(self): + try: + (preferred_width,preferred_height) = self.getAvailableResolutions()[self.getResolutionIndex()] + except IndexError: + print self.getAvailableResolutions() + (preferred_width,preferred_height) = self.getAvailableResolutions()[-1] + + if self.isResolutionLive(): + self._computeSizesFromXorg() + else: + # The list of resolutions needs to be updated. + self._computeSizesFromMonitor() + + if self.gfx_card.setup.getLayout()==XSetup.LAYOUT_CLONE: + if self.gfx_card.setup.getPrimaryScreen() is self: + # Filter the list of resolutions based on what the secondary screen can show. + secondary_screen = self.gfx_card.setup.getSecondaryScreen() + primary_set = set(self.available_sizes) + secondary_set = set(secondary_screen.available_sizes) + + common_set = primary_set.intersection(secondary_set) + + suitable_resolutions = [] + # Make sure that each resolution also has a common refresh rate. + for resolution in common_set: + primary_rates = self.getAvailableRefreshRatesForResolution(self.available_sizes.index(resolution)) + secondary_rates = secondary_screen.getAvailableRefreshRatesForResolution(secondary_screen.available_sizes.index(resolution)) + + if len(set(primary_rates).intersection(set(secondary_rates)))!=0: + suitable_resolutions.append(resolution) + + suitable_resolutions.sort() + self.available_sizes = suitable_resolutions + + # Now we select a resolution that closely matches the previous resolution. + best_score = 2000000 # big number. + best_index = 0 + resolution_list = self.getAvailableResolutions() + for i in range(len(resolution_list)): + (width,height) = resolution_list[i] + new_score = abs(width-preferred_width) + abs(height-preferred_height) + + if new_score < best_score: + best_index = i + best_score = new_score + self.setResolutionIndex(best_index) + + if self.gfx_card.setup.getLayout()==XSetup.LAYOUT_CLONE: + if self.gfx_card.setup.getSecondaryScreen() is self: + self.gfx_card.setup.getPrimaryScreen()._resyncResolution() + + def isXorgConfigChanged(self): + isroot = os.getuid()==0 + return self.original_monitor_model is not self.monitor_model \ + or self.original_monitor_aspect != self.monitor_aspect \ + or self.isGammaSettingsChanged() \ + or (self.isResolutionSettingsChanged() and (not self.isResolutionLive() or isroot)) + + def getRedGamma(self): + """Get the current red gamma value. + """ + return self.redgamma + + def setRedGamma(self,value): + """Set gamma correction value for red + + This method takes effect immediately on the X server if possible. + + Keyword arguments: + value -- gamma correction value (float) + """ + self.redgamma = value + if self.x_live_screen is not None: + self.x_live_screen.setGamma( (self.redgamma,self.greengamma,self.bluegamma) ) + self.settingall = False + + def getGreenGamma(self): + """Get the current green gamma value + """ + return self.greengamma + + def setGreenGamma(self,value): + """Set gamma correction value for green + + This method takes effect immediately on the X server if possible. + + Keyword arguments: + value -- gamma correction value (float) + """ + self.greengamma = value + if self.x_live_screen is not None: + self.x_live_screen.setGamma( (self.redgamma,self.greengamma,self.bluegamma) ) + self.settingall = False + + def getBlueGamma(self): + """Get the current blue gamma value + """ + return self.bluegamma + + def setBlueGamma(self,value): + """Set gamma correction value for blue + + This method takes effect immediately on the X server if possible. + + Keyword arguments: + value -- gamma correction value (float) + """ + self.bluegamma = value + if self.x_live_screen is not None: + self.x_live_screen.setGamma( (self.redgamma,self.greengamma,self.bluegamma) ) + self.settingall = False + + def getAllGamma(self): + """Get the gamma correction value for all colours. + + Returns a float. + + See isGammaEqual() + """ + return self.allgamma + + def setAllGamma(self,value): + """Set the gamma correction value for all colours. + + Keyword arguments: + value -- gamma correction value (float) + """ + self.allgamma = value + if self.x_live_screen is not None: + self.x_live_screen.setGamma( (self.allgamma,self.allgamma,self.allgamma) ) + self.settingall = True + + def isGammaLive(self): + """Returns true if modifications to the gamma are immediately visible. + """ + return self.x_live_screen is not None + + def isGammaEqual(self): + """Test whether each colour is using the same gamma correction value. + + Returns True if the gamma value is the same for all colours + """ + return self.getRedGamma()==self.getGreenGamma()==self.getBlueGamma() + + def getScreenIndex(self): + return self.gfx_card.getScreens().index(self) + + # Size and resolution + def getResolutionIndex(self): + """Get the current resolution of this screen. + + Returns an index into the list of available resolutions. See + getAvailableResolutions(). + """ + return self.currentsizeindex + + def setResolutionIndex(self,index): + """Set the resolution for this screen. + + This method does not take effect immediately, only after applyResolutionSetttings() + has been called. + + Keyword arguments: + index -- The index of the resolution to use. See getAvailableResolutions(). + """ + self.currentsizeindex = index + + def getAvailableResolutions(self): + """Get the list of available resolutions. + + Returns a list of screen (width,height) tuples. width and height are in + pixels. + """ + return self.available_sizes[:] + + # Rotation + def getRotation(self): + """Get the current rotation settings for this screen. + + Returns one of Screen.RR_Rotate_0, Screen.RR_Rotate_90, + Screen.RR_Rotate_180 or Screen.RR_Rotate_270 + """ + return self.currentrotation + + def setRotation(self,rotation): + """Set the rotation for this screen + + This method does not take effect immediately, only after + applyResolutionSetttings() has been called. See getAvailableRotations() + for how to find out which rotations are supported. + + Keyword arguments: + rotation -- One of Screen.RR_Rotate_0, Screen.RR_Rotate_90, + Screen.RR_Rotate_180 or Screen.RR_Rotate_270 + """ + self.currentrotation = rotation + + def getAvailableRotations(self): + """Get the supported rotations for this screen. + + Returns a bitmask of support rotations for this screen. The returned + integer is the bitwise OR of one or more of the constants here below. + * Screen.RR_Rotate_0 + * Screen.RR_Rotate_90 + * Screen.RR_Rotate_180 + * Screen.RR_Rotate_270 + """ + if self.x_live_screen is not None and self.x_live_screen.resolutionSupportAvailable(): + return self.x_live_screen.getAvailableRotations() & \ + (self.RR_Rotate_0 | self.RR_Rotate_90 | self.RR_Rotate_180 | self.RR_Rotate_270) + else: + return self.RR_Rotate_0 # FIXME + + + # Reflection + def getReflection(self): + """Get the current reflection settings for this screen. + + Returns the reflection settings as a bit string. Use Screen.RR_Reflect_X + and Screen.RR_Reflect_Y as bitmasks to determine which reflections are + in use. + """ + return self.currentreflection + + def setReflection(self,reflection): + """Set the reflection settings for this screen. + + This method does not take effect immediately, only after + applyResolutionSetttings() has been called. See getAvailableReflections() + for how to find out which rotations are supported. + + Keyword arguments: + reflection -- Bit string (Python integer) of desired reflections. + Bitwise OR Screen.RR_Reflect_X and Screen.RR_Reflect_Y + to construct the string. + """ + self.currentreflection = reflection + + def getAvailableReflections(self): + """Get the supported reflections for this screen. + + Returns a bit string (Python integer) of supported reflections. Use + Screen.RR_Reflect_X and Screen.RR_Reflect_Y as bitmasks to determine + which reflections are available. + """ + if self.x_live_screen is not None and self.x_live_screen.resolutionSupportAvailable(): + return self.x_live_screen.getAvailableRotations() & (self.RR_Reflect_X | self.RR_Reflect_Y) + else: + return 0 # FIXME + + # Refresh rates + def getRefreshRateIndex(self): + """Get the current refresh rate index for this screen. + + Returns an index into the list of refresh rates. See getAvailableRefreshRates(). + """ + rates = self.getAvailableRefreshRates() + i = 0 + for r in rates: + if r>=self.currentrefreshrate: + return i + i += 1 + return len(rates)-1 + + def setRefreshRateIndex(self,index): + """Set the refresh rate for this screen. + + Keyword arguments: + index -- Index into the list of refresh rates. See getAvailableRefreshRates(). + """ + self.currentrefreshrate = self.getAvailableRefreshRates()[index] + + def getAvailableRefreshRates(self): + """Get the list of available refresh rates + + Get the list of available refresh rates for the currently selected + resolution. See setResolutionIndex() and getAvailableRefreshRatesForResolution() + + Returns a list of integers in Hz. + """ + return self.getAvailableRefreshRatesForResolution(self.currentsizeindex) + + def getAvailableRefreshRatesForResolution(self,resolution_index): + """Get the list of available refresh rates for the given resolution + + Get the list of available refresh rates for the given resolution. + + Keyword arguments: + resolution_index -- Index into the list of resolutions. + + Returns a list of integers in Hz. + """ + isize = self.available_sizes[resolution_index] + if self.isResolutionLive(): + j = 0 + for size in self.x_live_screen.getAvailableSizes(): + (sw,sh,wm,hm) = size + if isize==(sw,sh): + rates = self.x_live_screen.getAvailableRefreshRates(j) + rates.sort() + return rates + j += 1 + assert False,"Can't find matching screen resolution" + else: + # + rates = [] + for mode in self.mode_list: + if isize==(mode.getWidth(),mode.getHeight()): + rates.append(mode.getVRefresh()) + + rates.sort() + return rates + + # Applying changes. + + def isResolutionLive(self): + return self.x_live_screen is not None and \ + self.x_live_screen.resolutionSupportAvailable() and \ + self.original_monitor_model is self.monitor_model and \ + self.original_monitor_aspect==self.monitor_aspect + + def isResolutionSettingsChanged(self): + try: + current_size = self.getAvailableResolutions()[self.currentsizeindex] + except IndexError: + #FIXME: why does this happen? + return False + return current_size != self.original_size or \ + self.currentrefreshrate != self.originalrefreshrate or \ + self.currentrotation != self.originalrotation or \ + self.currentreflection != self.originalreflection + + def applyResolutionSettings(self): + """Apply any tending resolution changes on the X server if possible. + + + """ + if self.isResolutionSettingsChanged() and self.isResolutionLive(): + # Work out what the correct index is for randr. + (width,height) = self.available_sizes[self.currentsizeindex] + sizeindex = 0 + for size in self.x_live_screen.getAvailableSizes(): + (pw,ph,wm,hm) = size + if pw==width and ph==height: + break + sizeindex += 1 + + rc = self.x_live_screen.setScreenConfigAndRate(sizeindex, \ + self.currentrotation | self.currentreflection, self.currentrefreshrate) + + # FIXME this can fail if the config on the server has been updated. + + def acceptResolutionSettings(self): + """Accept the last resolution change + """ + self.originalsizeindex = self.currentsizeindex + self.original_size = self.getAvailableResolutions()[self.currentsizeindex] + self.originalrefreshrate = self.currentrefreshrate + self.originalrotation = self.currentrotation + self.originalreflection = self.currentreflection + + def revertResolutionSettings(self): + """Revert the last resolution change on the X server + + """ + if self.x_live_screen is not None and self.x_live_screen.resolutionSupportAvailable(): + # Work out what the correct index is for randr. + (width,height) = self.available_sizes[self.originalsizeindex] + sizeindex = 0 + for size in self.x_live_screen.getAvailableSizes(): + (pw,ph,wm,hm) = size + if pw==width and ph==height: + break + sizeindex += 1 + + self.x_live_screen.setScreenConfigAndRate(sizeindex, \ + self.originalrotation | self.originalreflection, self.originalrefreshrate) + # FIXME this can fail if the config on the server has been updated. + + def resetResolutionSettings(self): + """Reset the resolution settings to the last accepted state + + """ + # Restore the resolution settings to their original state. + self.currentsizeindex = self.originalsizeindex + self.currentrefreshrate = self.originalrefreshrate + self.currentrotation = self.originalrotation + self.currentreflection = self.originalreflection + + def isGammaSettingsChanged(self): + return self.redgamma != self.originalredgamma or \ + self.greengamma != self.originalgreengamma or \ + self.bluegamma != self.originalbluegamma or \ + self.allgamma != self.originalallgamma or \ + self.settingall != self.originalsettingall + + def acceptGammaSettings(self): + (self.originalredgamma, self.originalgreengamma, self.originalbluegamma) = ( + self.redgamma, self.greengamma, self.bluegamma) + self.originalallgamma = self.allgamma + self.originalsettingall = self.settingall + + def revertGammaSettings(self): + (self.redgamma, self.greengamma, self.bluegamma) = ( + self.originalredgamma, self.originalgreengamma, self.originalbluegamma) + self.allgamma = self.originalallgamma + self.settingall = self.originalsettingall + + if self.x_live_screen is not None: + if self.settingall: + self.x_live_screen.setGamma( (self.allgamma,self.allgamma,self.allgamma) ) + else: + self.x_live_screen.setGamma( (self.redgamma,self.greengamma,self.bluegamma) ) + + def isGammaSettingsChanged(self): + if self.settingall: + return self.originalallgamma != self.allgamma + else: + return self.originalredgamma != self.redgamma or \ + self.originalgreengamma != self.greengamma or \ + self.originalbluegamma != self.bluegamma + + def reset(self): + if self.isLive(): + self.revertGammaSettings() + self.resetResolutionSettings() + + self.monitor_model = self.original_monitor_model + self.monitor_aspect = self.original_monitor_aspect + + def getRestartHint(self): + if self.original_monitor_model is not self.monitor_model \ + or self.original_monitor_aspect != self.monitor_aspect \ + or (self.isResolutionSettingsChanged() and not self.isResolutionLive()): + return XSetup.RESTART_X + return XSetup.RESTART_NONE + + def _syncXorgConfig(self,x_device): + layout = self.gfx_card.getLayout() + + if self.x_config_screen is None: + self.x_config_screen = self.x_config.makeSection('',['section','screen']) + self.x_config.append(self.x_config_screen) + self.x_config_screen.identifier = self.x_config.createUniqueIdentifier("screen") + self.x_config_screen.device = x_device.identifier + + bit_depth = self.gfx_card.setup._getColorDepth() + self.x_config_screen.defaultdepth = bit_depth + + # Maybe we don't have a X config monitor section. + if self.x_config_monitor is None: + # Make a monitor section. + self.x_config_monitor = self.x_config.makeSection('',['section','monitor']) + self.x_config.append(self.x_config_monitor) + self.x_config_monitor.identifier = self.x_config.createUniqueIdentifier("monitor") + self.x_config_screen.monitor = self.x_config_monitor.identifier + + # Empty the monitor section and fill it in again. + monitor_identifier = self.x_config_monitor.identifier + self.x_config_monitor.clear() + self.x_config_monitor.identifier = monitor_identifier + + if self.monitor_model is not None: + if self.monitor_model.getManufacturer() is not None: + self.x_config_monitor.vendorname = self.monitor_model.getManufacturer() + + self.x_config_monitor.modelname = self.monitor_model.getName() + + if self.monitor_model.getType()!=MonitorModel.TYPE_PLUGNPLAY: + if self.monitor_model.getHorizontalSync() is not None: + hsyncline = self.x_config_monitor.makeLine(None,['HorizSync',self.monitor_model.getHorizontalSync()]) + self.x_config_monitor.append(hsyncline) + + if self.monitor_model.getVerticalSync() is not None: + vsyncline = self.x_config_monitor.makeLine(None,['VertRefresh',self.monitor_model.getVerticalSync()]) + self.x_config_monitor.append(vsyncline) + + # Add a bunch of standard mode lines. + mode_list = GetMonitorModeDB().getAvailableModes(self.monitor_model,self.monitor_aspect) + + if mode_list is not None: + + # Filter the mode list by video memory. + color_bytes = bit_depth/8 + if self.gfx_card.getGfxCardModel().getNeedVideoRam(): + video_memory = self.gfx_card.getVideoRam() + else: + video_memory = 65536 # Big enough. + video_memory *= 1024 # Convert to bytes. + mode_list = [mode for mode in mode_list if mode.getWidth()*mode.getHeight()*color_bytes <= video_memory] + + def mode_cmp(a,b): return cmp(a.getWidth(),b.getWidth()) + mode_list.sort(mode_cmp) + + for mode in mode_list: + modeline = self.x_config_monitor.modeline.makeLine(None,mode.getXorgModeLineList()) + self.x_config_monitor.modeline.append(modeline) + + # Specify the preferred resolution. + + # Get rid of any old display subsections. + for display_section in self.x_config_screen.getSections('display'): + self.x_config_screen.remove(display_section) + + try: + (preferred_width, preferred_height) = self.getAvailableResolutions()[self.currentsizeindex] + preferred_rate = self.getAvailableRefreshRates()[self.getRefreshRateIndex()] + except IndexError, errmsg: + # This is presumed to be better than a crash: + print "Failed to get preferred width, height, or rate - Assuming none. IndexError: ", errmsg + preferred_width = 0 + preferred_height = 0 + preferred_rate = 0 + + # Find the monitor supported mode that best matches what the user has selected. + best_score = 2000000 # big number. + best_index = 0 + for i in range(len(mode_list)): + mode = mode_list[i] + new_score = abs(mode.getWidth()-preferred_width) + \ + abs(mode.getHeight()-preferred_height) + \ + abs(mode.getVRefresh()-preferred_rate) + if new_score < best_score: + best_index = i + best_score = new_score + + # This is all about putting the list of resolutions into a + # sensible preferred order starting with what the user has chosen + # and then the rest of the resolutions. + lower = best_index - 1 + higher = best_index + 1 + len_modes = len(mode_list) + + mode_indexes = [] + mode_indexes.append(best_index) + while lower>=0 or higher=0: + mode_indexes.append(lower) + lower -= 1 + + # Convert the list of resolution indexes into monitor mode names and a modes line for xorg.conf. + mode_list_line = ['modes'] + mode_list_line.extend([ mode_list[mode_index].getName() for mode_index in mode_indexes ]) + + # Create the Display subsections in the Screen section. + display_section = self.x_config_screen.makeSection(None,['SubSection','Display']) + display_section.depth = bit_depth + + # The virtual screen size hack should not be used in combination + # with Xinerama (RandR doesn't work with Xinerama). + if layout!=XSetup.LAYOUT_SINGLE_XINERAMA and self.isLive(): + # Find the largest monitor supported mode. We need this info + # to set the size of the virtual screen. See the big comment + # in displayconfig-restore.py. + virtual_width = max([mode_list[mode_index].getWidth() for mode_index in mode_indexes]) + virtual_height = max([mode_list[mode_index].getHeight() for mode_index in mode_indexes]) + display_section.append(display_section.makeLine(None,["virtual",virtual_width,virtual_height])) + + display_section.append(display_section.makeLine(None,mode_list_line)) + + self.x_config_screen.append(display_section) + + # Set the gamma info too. + if self.settingall: + gamma_row = self.x_config_monitor.makeLine(None,['gamma',str(self.allgamma)]) + else: + gamma_row = self.x_config_monitor.makeLine(None,['gamma',str(self.redgamma),str(self.greengamma),str(self.bluegamma)]) + self.x_config_monitor.append(gamma_row) + + # If resolution changes were not live because the monitor has been changed + # then we also stop them from being live from now on too. + if not self.isResolutionLive(): + self.x_live_screen = None + self.acceptResolutionSettings() + + # The orignal monitor model is now the selected one. => no changes need to be applied now. + self.original_monitor_model = self.monitor_model + self.original_monitor_aspect = self.monitor_aspect + + def _getXorgScreenSection(self): + return self.x_config_screen + + def _getGfxCard(self): + return self.gfx_card + + def __str__(self): + # FIXME string = str(self.getIdentifier()) + " {" + string = " {" + if self.isLive(): + string += "Size: " + string += str(self.getAvailableResolutions()[self.getResolutionIndex()]) + string += " " + else: + string += "Not live, " + if self.monitor_model is not None: + string += "Monitor:" + str(self.monitor_model) + string += "}" + return string + +############################################################################ +class GfxCardModel(object): + """Describes the properties of a particular model of graphics card. + + """ + def __init__(self,name): + self.name = name + self.vendor = None + self.server = None + self.driver = None + self.proprietarydriver = None + self.lines = [] + self.seeobj = None + self.noclockprobe = None + self.unsupported = None + self.driglx = None + self.utahglx = None + self.driglxexperimental = None + self.utahglxexperimental = None + self.badfbrestore = None + self.badfbrestoreexf3 = None + self.multihead = None + self.fbtvout = None + self.needvideoram = None + + def getName(self): return self.name + + def setVendor(self,vendor): self.vendor = vendor + def getVendor(self): return self._get(self.vendor,"getVendor",None) + def setServer(self,server): self.server = server + def getServer(self): return self._get(self.server,"getServer",None) + def setDriver(self,driver): self.driver = driver + def getDriver(self): return self._get(self.driver,"getDriver",None) + def setProprietaryDriver(self,proprietarydriver): self.proprietarydriver = proprietarydriver + def getProprietaryDriver(self): return self._get(self.proprietarydriver,"getProprietaryDriver",None) + def addLine(self,line): self.lines.append(line) + def getLines(self): + if (len(self.lines)==0) and (self.seeobj is not None): + return self.seeobj.getLines() + else: + return self.lines[:] # Copy + + def setNoClockProbe(self,noprobe): self.noclockprobe = noprobe + def getNoClockProbe(self): return self._get(self.noclockprobe,"getNoClockProbe",False) + def setUnsupported(self,unsupported): self.unsupported = unsupported + def getUnsupported(self): return self._get(self.unsupported,"getUnsupported",False) + def setDriGlx(self,on): self.driglx = on + def getDriGlx(self): return self._get(self.driglx,"getDriGlx",False) + def setUtahGlx(self,on): self.utahglx = on + def getUtahGlx(self): return self._get(self.utahglx,"getUtahGlx",False) + def setDriGlxExperimental(self,on): self.driglxexperimental = on + def getDriGlxExperimental(self): return self._get(self.driglxexperimental,"getDriGlxExperimental",False) + def setUtahGlxExperimental(self,on): self.utahglxexperimental = on + def getUtahGlxExperimental(self): return self._get(self.utahglxexperimental,"getUtahGlxExperimental",False) + def setBadFbRestore(self,on): self.badfbrestore = on + def getBadFbRestore(self,proprietary=False): + if proprietary: + driver = self.getProprietaryDriver() + else: + driver = self.getDriver() + if driver in ['i810','intel','fbdev','nvidia','vmware']: + return True + if self.badfbrestore is not None: + return self.badfbrestore + if self.seeobj is not None: + return self.seeobj.getBadFbRestore(proprietary) + return False + def setBadFbRestoreXF3(self,on): self.badfbrestoreexf3 = on + def getBadFbRestoreXF3(self): return self._get(self.badfbrestoreexf3,"getBadFbRestoreXF3",False) + def setMultiHead(self,n): self.multihead = n + def getMultiHead(self): return self._get(self.multihead,"getMultiHead",1) + def setFbTvOut(self,on): self.fbtvout = on + def getFbTvOut(self): return self._get(self.fbtvout,"getFbTvOut",False) + def setNeedVideoRam(self,on): self.needvideoram = on + def getNeedVideoRam(self): return self._get(self.needvideoram,"getNeedVideoRam",False) + def setSee(self,seeobj): self.seeobj = seeobj + + # If the seeobj is set, then all attributes that are not filled in for this + # instance are inheritted from seeobj. + def _get(self,attr,meth,default): + if attr is not None: + return attr + if self.seeobj is not None: + return getattr(self.seeobj,meth)() + else: + return default + + def __str__(self): + return self.getName() + +############################################################################ +gfxcard_model_db_instance = None # Singleton. + +def GetGfxCardModelDB(): + """Returns a GfxCardModelDB instance. + """ + global gfxcard_model_db_instance + # Lazy instantiation. + if gfxcard_model_db_instance is None: + gfxcard_model_db_instance = GfxCardModelDB() + return gfxcard_model_db_instance + +############################################################################ +class GfxCardModelDB(object): + def __init__(self): + # List of gfx card databases, if order of preference. + filename = '/usr/share/ldetect-lst/Cards+' + if not os.path.exists(filename): + filename = os.path.join(data_file_dir,"Cards+") + + # The card DB. A dict mapping card names to card objects. + self.db = {} + # The keys in this dict will be vendor names, values are dicts mapping card names to objects. + self.vendordb = {} + self.driverdb = {} + + self.drivers = self._getAvailableDrivers() + + self.proprietary_drivers = [] + + self._checkProprietaryDrivers() + self._loadDrivers(self.drivers, self.proprietary_drivers) + self._loadDB(filename) + + def getGfxCardModelByName(self,name): + return self.db[name] + + def getGfxCardModelByDriverName(self,driver_name): + return self.driverdb[driver_name] + + def getAllGfxCardModelNames(self): + return self.db.keys() + + def _getDriverDirs(self): + "Returns a list of directories where X driver files may be located" + + # Fallback dir: + defaultDirs = ["/usr/lib/xorg/modules/drivers/"] + + # Get display number: + display_number = 0 + if "DISPLAY" in os.environ: + display_name = os.environ["DISPLAY"] + displayRE = re.compile("^.*:(\d+)\.\d+$") + m = displayRE.match(display_name) + if m: + display_number = int(m.group(1)) + else: + print "failed to parse display number from '%s' - falling back to default (%d)" % (display_name, display_number) + else: + print "$DISPLAY not set - falling back to default number (%d)" % display_number + + # Get the list of module paths from the Xorg log file: + XLogfile = "/var/log/Xorg.%d.log" % display_number + cmd = "awk -F \" ModulePath set to \" '/^\(..\) ModulePath set to (.*)/ {print $2}' %s" % XLogfile + + baseList = os.popen(cmd).readline().strip().strip('"') + if baseList == "": + print "warning: failed to get module paths from '%s' - falling back to default" % XLogfile + return defaultDirs + + pathList = [] + for basePath in baseList.split(","): + pathList.append("%s/drivers/" % basePath) + + return pathList + + def _getAvailableDrivers(self): + """ + Returns the list of available X graphics drivers. + Algorithm taken from Xorg source (see GenerateDriverlist() in xf86Config.C). + """ + + # These are drivers that cannot actually be used in xorg.conf, hence they are hidden: + hiddenDrivers = ( + "atimisc", # seems to be just the internal implementation for ati driver + "dummy", # dummy driver without any output + "v4l", # not an actual video device driver, but just the v4l module + "ztv" # seems to be the TV output module for AMD Geode + ) + + drivers = [] + driverDirectories = self._getDriverDirs() + + driverNameRE = re.compile("^(.+)_drv.(s)?o$") + for ddir in driverDirectories: + try: + driverFiles = os.listdir(ddir) + except OSError: + print "error reading directory '%s'" % ddir + continue + for f in driverFiles: + m = driverNameRE.match(f) + if m: + driverName = m.group(1) + if driverName in drivers: + print "ignoring duplicate driver '%s/%s'" % (ddir, f) + else: + if driverName in hiddenDrivers: + #print "ignoring hidden driver '%s'" % driverName + pass + else: + drivers.append(driverName) + else: + #print "ignoring driver file with invalid name '%s'" % f + pass + #print "found %d drivers" % len(drivers) + return drivers + + def _checkProprietaryDrivers(self): + # Check for the NVidia driver. + # FIXME x86_64 => 'lib64' + + if (os.path.exists("/usr/X11R6/lib/modules/drivers/nvidia_drv.o") and \ + os.path.exists("/usr/X11R6/lib/modules/extensions/libglx.so")) \ + or \ + (os.path.exists("/usr/lib/xorg/modules/drivers/nvidia_drv.o") and \ + os.path.exists("/usr/lib/xorg/modules/libglx.so")) \ + or \ + (os.path.exists("/usr/lib/xorg/modules/drivers/nvidia_drv.so") and \ + os.path.exists("/usr/lib/xorg/modules/extensions/libglx.so")): + self.proprietary_drivers.append("nvidia") + + # Check for the ATI driver + if (os.path.exists("/usr/X11R6/lib/modules/dri/fglrx_dri.so") and \ + os.path.exists("/usr/X11R6/lib/modules/drivers/fglrx_drv.o")) or \ + (os.path.exists("/usr/lib/dri/fglrx_dri.so") and \ + os.path.exists("/usr/lib/xorg/modules/drivers/fglrx_drv.so")): + self.proprietary_drivers.append("fglrx") + + # FIXME MATROX_HAL? + + def _loadDrivers(self, drivers, proprietary_drivers): + # Insert the Driver entries. + for drivername in drivers: + cardobj = GfxCardModel(drivername) + cardobj.setDriver(drivername) + self.db[drivername] = cardobj + self.driverdb[drivername] = cardobj + + if drivername=="nv" and "nvidia" in proprietary_drivers: + cardobj.setProprietaryDriver("nvidia") + self.driverdb["nvidia"] = cardobj + elif drivername=="ati" and "fglrx" in proprietary_drivers: + cardobj.setProprietaryDriver("fglrx") + self.driverdb["fglrx"] = cardobj + + def _loadDB(self,filename): + vendors = ['3Dlabs', 'AOpen', 'ASUS', 'ATI', 'Ark Logic', 'Avance Logic', + 'Cardex', 'Chaintech', 'Chips & Technologies', 'Cirrus Logic', 'Compaq', + 'Creative Labs', 'Dell', 'Diamond', 'Digital', 'ET', 'Elsa', 'Genoa', + 'Guillemot', 'Hercules', 'Intel', 'Leadtek', 'Matrox', 'Miro', 'NVIDIA', + 'NeoMagic', 'Number Nine', 'Oak', 'Orchid', 'RIVA', 'Rendition Verite', + 'S3', 'Silicon Motion', 'STB', 'SiS', 'Sun', 'Toshiba', 'Trident', + 'VideoLogic'] + + cardobj = None + # FIXME the file might be compressed. + fhandle = open(filename,'r') + for line in fhandle.readlines(): + line = line.strip() + if len(line)!=0: + if not line.startswith("#"): + if line.startswith("NAME"): + cardobj = GfxCardModel(line[4:].strip()) + cardname = cardobj.getName() + self.db[cardname] = cardobj + + # Try to extract a vendor name from the card's name. + for vendor in vendors: + if vendor in cardname: + cardobj.setVendor(vendor) + if vendor not in self.vendordb: + self.vendordb[vendor] = {} + self.vendordb[vendor][cardname] = cardobj + break + else: + if "Other" not in self.vendordb: + self.vendordb["Other"] = {} + self.vendordb["Other"][cardname] = cardobj + + elif line.startswith("SERVER"): + cardobj.setServer(line[6:].strip()) + elif line.startswith("DRIVER2"): + driver = line[7:].strip() + if driver in self.proprietary_drivers: + cardobj.setProprietaryDriver(driver) + elif line.startswith("DRIVER"): + cardobj.setDriver(line[6:].strip()) + elif line.startswith("LINE"): + cardobj.addLine(line[4:].strip()) + elif line.startswith("SEE"): + try: + cardobj.setSee(self.db[line[3:].strip()]) + except KeyError: + pass + elif line.startswith("NOCLOCKPROBE"): + cardobj.setNoClockProbe(True) + elif line.startswith("UNSUPPORTED"): + cardobj.setUnsupported(True) + elif line.startswith("DRI_GLX"): + cardobj.setDriGlx(True) + elif line.startswith("UTAH_GLX"): + cardobj.setUtahGlx(True) + elif line.startswith("DRI_GLX_EXPERIMENTAL"): + cardobj.setDriGlxExperimental(True) + elif line.startswith("UTAH_GLX_EXPERIMENTAL"): + cardobj.setUtahGlxExperimental(True) + elif line.startswith("BAD_FB_RESTORE"): + cardobj.setBadFbRestore(True) + elif line.startswith("BAD_FB_RESTORE_XF3"): + cardobj.setBadFbRestoreXF3(True) + elif line.startswith("MULTI_HEAD"): + cardobj.setMultiHead(int(line[10:].strip())) + elif line.startswith("FB_TVOUT"): + cardobj.setFbTvOut(True) + elif line.startswith("NEEDVIDEORAM"): + cardobj.setNeedVideoRam(True) + fhandle.close() + +############################################################################ +class MonitorModel(object): + TYPE_NORMAL = 0 + TYPE_PLUGNPLAY = 1 + TYPE_CUSTOM = 2 + + def __init__(self): + self.name = None + self.manufacturer = None + self.eisaid = None + self.horizontalsync = None + self.verticalsync = None + self.dpms = False + self.type = MonitorModel.TYPE_NORMAL + + def copy(self): + newmonitor = MonitorModel() + newmonitor.name = self.name + newmonitor.manufacturer = self.manufacturer + newmonitor.eisaid = self.eisaid + newmonitor.horizontalsync = self.horizontalsync + newmonitor.verticalsync = self.verticalsync + newmonitor.dpms = self.dpms + return newmonitor + + def getName(self): return self.name + def setName(self,name): self.name = name + def getManufacturer(self): return self.manufacturer + def setManufacturer(self,manufacturer): self.manufacturer = manufacturer + def setEisaId(self,eisaid): self.eisaid = eisaid + def getEisaId(self): return self.eisaid + def setDpms(self,on): self.dpms = on + def getDpms(self): return self.dpms + def getHorizontalSync(self): return self.horizontalsync + def setHorizontalSync(self,horizontalsync): self.horizontalsync = horizontalsync + def getVerticalSync(self): return self.verticalsync + def setVerticalSync(self,verticalsync): self.verticalsync = verticalsync + def setType(self,flag): self.type = flag + def getType(self): return self.type + def __str__(self): + return "{Name:"+self.name+"}" + +############################################################################ +class PlugNPlayMonitorModel(MonitorModel): + def __init__(self,monitor_model_db): + MonitorModel.__init__(self) + self.monitor_detected = False + self.monitor_model_db = monitor_model_db + + def getEisaId(self): + self._detectMonitor() + return self.eisaid + + def getHorizontalSync(self): + self._detectMonitor() + return self.horizontalsync + + def getVerticalSync(self): + self._detectMonitor() + return self.verticalsync + + def _detectMonitor(self): + if not self.monitor_detected: + (eisaid, horizontalsync, verticalsync) = self.monitor_model_db._detectMonitor() + if eisaid is not None: + self.eisaid = eisaid + if horizontalsync is not None: + self.horizontalsync = horizontalsync + if verticalsync is not None: + self.verticalsync = verticalsync + + self.monitor_detected = True + +############################################################################ +monitor_model_db_instance = None # Singleton + +def GetMonitorModelDB(force=False): + """Returns a GetMonitorModelDB instance. + """ + global monitor_model_db_instance + if monitor_model_db_instance is None or force == True: + monitor_model_db_instance = MonitorModelDB() + return monitor_model_db_instance + +############################################################################ +class MonitorModelDB(object): + def __init__(self): + self.db = {} + self.vendordb = {} + self.genericdb = {} + self.customdb = {} + self.custom_counter = 1 + self.monitor_detect_run = False + + # Plug'n Play is a kind of fake entry for monitors that are detected but unknown. + # It's frequency info is filled in by hardware detection or from the X server config. + self._plugnplay = PlugNPlayMonitorModel(self) + self._plugnplay.setName("Plug 'n' Play") + self._plugnplay.setManufacturer(self._plugnplay.getName()) + self._plugnplay.setType(MonitorModel.TYPE_PLUGNPLAY) + # This default is what Xorg claims to use when there is no + # horizontal sync info in the a monitor section. + self._plugnplay.setHorizontalSync("28.0-33.0") + # This default is what Xorg claims to use when there is no + # vertical sync info in the a monitor section. + self._plugnplay.setVerticalSync("43-72") + self.customdb[self._plugnplay.getName()] = self._plugnplay + self.db[self._plugnplay.getName()] = self._plugnplay + + # Load monitors from the shipped database + filename = "/usr/share/ldetect-lst/MonitorsDB" + if not os.path.exists(filename): + filename = os.path.join(data_file_dir,"MonitorsDB") + self.load(filename) + # Load monitors from the custom database + filename = os.path.join(var_data_dir, "CustomMonitorsDB") + if os.path.exists(filename): + self.load(filename) + + def load(self,filename,split=";"): + fhandle = open(filename,'r') + for line in fhandle.readlines(): + line = line.strip() + if len(line)!=0: + if not line.startswith("#"): + try: + parts = line.split(split) + monitorobj = MonitorModel() + monitorobj.setManufacturer(parts[0].strip()) + monitorobj.setName(parts[1].strip()) + monitorobj.setEisaId(parts[2].strip().upper()) + monitorobj.setHorizontalSync(parts[3].strip()) + monitorobj.setVerticalSync(parts[4].strip()) + if len(parts)>=6: + monitorobj.setDpms(parts[5].strip()=='1') + self.db[monitorobj.getName()] = monitorobj + + if monitorobj.getManufacturer() in \ + ["Generic LCD Display", "Generic CRT Display"]: + self.genericdb[monitorobj.getName()] = monitorobj + else: + if monitorobj.getManufacturer() not in self.vendordb: + self.vendordb[monitorobj.getManufacturer()] = {} + self.vendordb[monitorobj.getManufacturer()]\ + [monitorobj.getName()] = monitorobj + + except IndexError: + print "Bad monitor line:",line + fhandle.close() + + def getMonitorByName(self,name): + return self.db.get(name,None) + + def newCustomMonitor(self,name=None): + custom_model = MonitorModel() + if name is None: + name = "Custom %i" % self.custom_counter + custom_model.setName(name) + self.db[custom_model.getName()] = custom_model + self.customdb[name] = custom_model + self.custom_counter += 1 + return custom_model + + def getCustomMonitors(self): + return self.customdb + + def detect(self): + """Detect the attached monitor. + + Returns a 'monitor' object on success, else None. + """ + (eisaid,hrange,vrange) = self._detectMonitor() + + # Look up the EISAID in our database. + if eisaid is not None: + for monitor in self.db: + if eisaid==self.db[monitor].getEisaId(): + return self.db[monitor] + + return self._plugnplay + + def _detectMonitor(self): + if not self.monitor_detect_run: + eisaid = None + hrange = None + vrange = None + + if os.path.isfile("/usr/sbin/monitor-edid"): + # This utility appeared in Mandriva 2005 LE + output = ExecWithCapture("/usr/sbin/monitor-edid",["monitor-edid","-v"]) + for line in output.split("\n"): + if "HorizSync" in line: + hrange = line.split()[1] + elif "VertRefresh" in line: + vrange = line.split()[1] + elif line.startswith("EISA ID:"): + eisaid = line[9:].upper() + + elif os.path.isfile("/usr/sbin/ddcxinfos"): + # This utility _was_ standard on Mandrake 10.1 and earlier. + output = ExecWithCapture("/usr/sbin/ddcxinfos",["ddcxinfos"]) + for line in output.split("\n"): + if "HorizSync" in line: + hrange = line.split()[0] + elif "VertRefresh" in line: + vrange = line.split()[0] + elif "EISA ID=" in line: + eisaid = line[line.find("EISA ID=")+8:].upper() + + elif os.path.isfile("/usr/sbin/ddcprobe"): + # on Debian + """ + ddcprobe's output looks like this: + + ... + eisa: SAM00b1 + ... + monitorrange: 30-81, 56-75 + ... + """ + output = ExecWithCapture("/usr/sbin/ddcprobe",["ddcprobe"]) + for line in output.split("\n"): + if line.startswith("eisa:"): + parts = line.split(":") + if len(parts)>=2: + eisaid = parts[1].strip().upper() + elif line.startswith("monitorrange:"): + parts = line.replace(',','').split() + if len(parts)==3: + hrange = parts[1].strip() + vrange = parts[2].strip() + + self.detected_eisa_id = eisaid + self.detected_h_range = hrange + self.detected_v_range = vrange + self.monitor_detect_run = True + + return (self.detected_eisa_id, self.detected_h_range, self.detected_v_range) + +############################################################################ + +SYNC_TOLERANCE = 0.01 # 1 percent +class ModeLine(object): + ASPECT_4_3 = 0 + ASPECT_16_9 = 1 + + XF86CONF_PHSYNC = 0x0001 + XF86CONF_NHSYNC = 0x0002 + XF86CONF_PVSYNC = 0x0004 + XF86CONF_NVSYNC = 0x0008 + XF86CONF_INTERLACE = 0x0010 + XF86CONF_DBLSCAN = 0x0020 + XF86CONF_CSYNC = 0x0040 + XF86CONF_PCSYNC = 0x0080 + XF86CONF_NCSYNC = 0x0100 + XF86CONF_HSKEW = 0x0200 # hskew provided + XF86CONF_BCAST = 0x0400 + XF86CONF_CUSTOM = 0x0800 # timing numbers customized by editor + XF86CONF_VSCAN = 0x1000 + flags = {"interlace": XF86CONF_INTERLACE, + "doublescan": XF86CONF_DBLSCAN, + "+hsync": XF86CONF_PHSYNC, + "-hsync": XF86CONF_NHSYNC, + "+vsync": XF86CONF_PVSYNC, + "-vsync": XF86CONF_NVSYNC, + "composite": XF86CONF_CSYNC, + "+csync": XF86CONF_PCSYNC, + "-csync": XF86CONF_NCSYNC } + + # Thanks go out to Redhat for this code donation. =) + def __init__(self, elements): + self.name = elements[1].strip('"') + self.clock = float(elements[2]) + self.hdisp = int(elements[3]) + self.hsyncstart = int(elements[4]) + self.hsyncend = int(elements[5]) + self.htotal = int(elements[6]) + self.vdisp = int(elements[7]) + self.vsyncstart = int(elements[8]) + self.vsyncend = int(elements[9]) + self.vtotal = int(elements[10]) + + self.flags = 0 + for i in range(11, len(elements)): + try: + self.flags |= ModeLine.flags[string.lower(elements[i])] + except KeyError: + pass + + def getWidth(self): + return self.hdisp + + def getHeight(self): + return self.vdisp + + def getName(self): + return self.name + + def getVRefresh(self): + vrefresh = self.clock * 1000000.0 / float(self.htotal * self.vtotal) + if self.flags & ModeLine.XF86CONF_INTERLACE: + vrefresh = vrefresh * 2.0 + if self.flags & ModeLine.XF86CONF_DBLSCAN: + vrefresh = vrefresh / 2.0 + return int(round(vrefresh)) + + # Basically copied from xf86CheckModeForMonitor + def supports(self, monitor_hsync, monitor_vsync): + hsync = self.clock * 1000 / self.htotal + for freq in monitor_hsync: + if hsync > freq[0] * (1.0 - SYNC_TOLERANCE) and hsync < freq[1] * (1.0 + SYNC_TOLERANCE): + break + else: + return False + + vrefresh = self.getVRefresh() + for freq in monitor_vsync: + if vrefresh > freq[0] * (1.0 - SYNC_TOLERANCE) and vrefresh < freq[1] * (1.0 + SYNC_TOLERANCE): + return True + return False + + def getXorgModeLineList(self): + row = [self.name, str(self.clock), str(self.hdisp), str(self.hsyncstart), str(self.hsyncend), + str(self.htotal), str(self.vdisp), str(self.vsyncstart), str(self.vsyncend), str(self.vtotal)] + + for (flag_name,flag_bit) in ModeLine.flags.iteritems(): + if self.flags & flag_bit: + row.append(flag_name) + return row + + def __str__(self): + return "ModeLine:"+self.name + +############################################################################ +monitor_mode_db_instance = None # Singleton + + +def GetMonitorModeDB(): + """Returns a GetMonitorModeDB instance. + """ + global monitor_mode_db_instance + if monitor_mode_db_instance is None: + monitor_mode_db_instance = MonitorModeDB() + return monitor_mode_db_instance + +############################################################################ +class MonitorModeDB(object): + def __init__(self): + self.db = {} + self.db169 = {} + + module_dir = os.path.dirname(os.path.join(os.getcwd(),__file__)) + self.load(os.path.join(data_file_dir,"vesamodes")) + self.load(os.path.join(data_file_dir,"extramodes")) + self.load(os.path.join(data_file_dir,"widescreenmodes")) + + # Make a list of screen sizes for the getAllResolutions() method. + self.all_resolutions = [] + for mode in self.db.values()+self.db169.values(): + size = (mode.getWidth(),mode.getHeight()) + if size not in self.all_resolutions: + self.all_resolutions.append(size) + + self.all_resolutions.sort() + + def load(self,filename): + fd = open(filename, 'r') + lines = fd.readlines() + fd.close() + + for line in lines: + if line[0] != "#" and line[0] != '/': + line = line.strip() + elements = line.split() + if line!="": + if len(elements) < 11 or string.lower(elements[0]) != "modeline": + print "Bad modeline found:",line + continue + name = elements[1][1:-1] + new_mode = ModeLine(elements) + + width = new_mode.getWidth() + height = new_mode.getHeight() + if self.aspectRatio(width, height)==ModeLine.ASPECT_4_3: + self.db[name] = new_mode + else: + self.db169[name] = new_mode + + if (width,height)==FALLBACK_RESOLUTION: + # We grab these modes and use them a fallbacks in the widescreen list. + self.db169[name] = new_mode + + @staticmethod + def aspectRatio(width,height): + ratio = float(width)/float(height) + # 4/3 is 1.333333 + # 16/9 is 1.777777 + # We will just consider anything below 1.45 to be standard. + if ratio < 1.45: + return ModeLine.ASPECT_4_3 + else: + return ModeLine.ASPECT_16_9 + + def getAvailableModes(self,monitor,aspect): + """ + Get the list of video modes that this monitor supports. + + Returns a list of modeline objects or None if the available modes for this monitor are unknown. + """ + if monitor.horizontalsync is None or monitor.verticalsync is None: + return None + + result = [] + + hsync_list = self._list_from_string(monitor.getHorizontalSync()) + vsync_list = self._list_from_string(monitor.getVerticalSync()) + + if aspect==ModeLine.ASPECT_4_3: + db = self.db + else: + db = self.db169 + + for modeline in db.values(): + if modeline.supports(hsync_list, vsync_list): + result.append(modeline) + return result + + def getAllResolutions(self): + return self.all_resolutions + + def _list_from_string(self,src): + l = [] + pieces = src.split(",") + for piece in pieces: + tmp = string.split(piece, "-") + if len(tmp) == 1: + l.append( (float(tmp[0].strip()), float(tmp[0].strip())) ) + else: + l.append( (float(tmp[0].strip()), float(tmp[1].strip())) ) + return l + +############################################################################ + +def ranges_to_string(array, length): + stringobj = "" + for i in range(length): + r = array[i] + if stringobj != "": + stringobj = stringobj + "," + if r[0] == r[1]: + stringobj = stringobj + repr(r[0]) + else: + stringobj = stringobj + repr(r[0]) + "-" + repr(r[1]) + return stringobj + + +def main(): + # FIXME: turns this into a real set of unit tests. + SetDataFileDir("ldetect-lst") + + #xs = XSetup() + #xs = XSetup('xorg.conf.test') + xs = XSetup(xorg_config_filename='bug_data/tonio_intel/xorg.conf', + debug_scan_pci_filename="bug_data/tonio_intel/PCIbus.txt") + print str(xs) + return + + #screen1 = xs.getGfxCards()[0].getScreens()[0] + #monitor_db = GetMonitorModelDB() + #new_model = monitor_db.getMonitorByName('Samsung SyncMaster 15GL') + #print new_model + #screen1.setMonitorModel(new_model) + + #screen2 = xs.getGfxCards()[0].getScreens()[1] + #screen2.setMonitorModel(new_model) + + print "getAvailableLayouts(): ",xs.getAvailableLayouts() + xs.getGfxCards()[0].setProprietaryDriver(True) + print str(xs) + xs.setLayout(XSetup.LAYOUT_CLONE) # XSetup.LAYOUT_DUAL. + print "getAvailableLayouts(): ",xs.getAvailableLayouts() + print str(xs) + + #gfxcard_db = GetGfxCardModelDB() + #new_gfxcard_model = gfxcard_db.getGfxCardModelByName('NVIDIA GeForce FX (generic)') + ##'ATI Radeon 8500' + ##'NVIDIA GeForce FX (generic)' + #print new_gfxcard_model + #gfx_card = xs.getGfxCards()[0] + #gfx_card.setProprietaryDriver(False) + #gfx_card.setGfxCardModel(new_gfxcard_model) + xs.writeXorgConfig('xorg.conf.test') + +if __name__=='__main__': + main() diff --git a/displayconfig/displayconfighardwaretab.py b/displayconfig/displayconfighardwaretab.py new file mode 100644 index 0000000..90c019e --- /dev/null +++ b/displayconfig/displayconfighardwaretab.py @@ -0,0 +1,741 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file '/home/sebas/dev/guidance/trunk/displayconfig/displayconfighardwaretab.ui' +# +# Created: Sat Apr 23 14:39:39 2005 +# by: The PyQt User Interface Compiler (pyuic) 3.13 +# +# WARNING! All changes made in this file will be lost! + + +import sys +from qt import * + +image0_data = [ +"32 32 522 2", +"Qt c None", +".i c #000000", +"bA c #0037a6", +"bU c #0038a6", +"ch c #0038a7", +"bo c #003ba8", +"cA c #003ba9", +"bT c #003ca8", +"bG c #003ca9", +"fp c #020202", +"cS c #0242ae", +"fq c #030303", +"bH c #0341ab", +"cg c #0441ab", +"b1 c #0443ae", +"fo c #050505", +"fB c #060606", +"bS c #0642ad", +"a7 c #0740a5", +"cf c #0744ad", +"fC c #080808", +".5 c #083fa5", +"bn c #0841a5", +"d# c #0848b2", +"bF c #0942a6", +"bh c #0949b1", +".h c #0a0a0a", +"cz c #0a47b0", +"ce c #0a48b0", +"dY c #0a4fb7", +"fI c #0b0b0b", +"b0 c #0b44a9", +"cm c #0b4ab2", +"b2 c #0c4ab1", +"fA c #0e0e0e", +"dk c #0e4eb5", +"bz c #0f4db2", +"b3 c #0f4db3", +"bI c #0f4db4", +".A c #111111", +"cl c #114bad", +"cR c #114eb4", +"cd c #114fb4", +"b4 c #1250b5", +"cG c #1250b6", +"fr c #131313", +"cy c #1351b5", +"dA c #1353b8", +"cn c #1452b6", +"aJ c #1550af", +"bR c #1552b7", +"eR c #161616", +"cQ c #1653b7", +"bp c #1654b7", +"d. c #1654b8", +"a8 c #1656b9", +"cx c #1756b8", +"cF c #1852b2", +"b5 c #1857b9", +"cZ c #1857ba", +".2 c #191919", +"dX c #195cbf", +"cP c #1a58ba", +"cc c #1a59ba", +"#p c #1a5dbf", +"cH c #1b59bb", +"co c #1b5abb", +"ag c #1c1c1c", +"c9 c #1c5abb", +"dH c #1c60c2", +"dg c #1d5ebf", +"a3 c #1e1e1e", +"cY c #1e58b6", +"bJ c #1e5dbd", +"dG c #1f5ebc", +"cp c #1f5ebd", +"aY c #1f5fbf", +"cI c #205ebd", +"b6 c #205fbe", +"dW c #2063c3", +"bX c #212121", +"c8 c #215fbe", +"c0 c #2160bf", +"cb c #2161bf", +"#v c #225db7", +"cq c #2261c0", +"cw c #2262c0", +"df c #235db9", +"by c #2362c0", +"ds c #2364c2", +"cD c #242424", +"bQ c #2463c0", +"cr c #2464c1", +"aj c #2560b9", +"dp c #262626", +"cv c #2665c1", +"c1 c #2665c2", +"aB c #2667c4", +"dI c #266ac7", +".# c #272727", +"dr c #2763bc", +"b7 c #2766c2", +"cJ c #2766c3", +"cs c #2767c2", +"ca c #2767c3", +"eq c #282828", +"cO c #2867c3", +"bg c #2968c3", +"ct c #2968c4", +"cu c #2969c4", +"ab c #296bc7", +"bK c #2a69c5", +"b8 c #2a6ac5", +".6 c #2a6eca", +"#V c #2b66bc", +"cK c #2b6ac6", +"bq c #2b6bc5", +"c7 c #2b6bc6", +".o c #2c2c2c", +"#q c #2c5cb7", +"bi c #2c5eb7", +"bx c #2c6bc6", +"bP c #2c6cc6", +"aK c #2c6cc7", +"#P c #2c6ec8", +"g. c #2d2d2d", +"bB c #2d60b9", +"c# c #2d6dc7", +"cL c #2d6ec7", +"dJ c #2d71cb", +"dV c #2d71cc", +"aX c #2e6dc7", +"b9 c #2e6ec7", +"c. c #2e6ec8", +"fb c #2f2f2f", +"c2 c #2f6ec8", +"a9 c #2f6fc8", +"cT c #3063bb", +"cM c #3070c8", +"bw c #3070c9", +"ak c #3072cb", +"bf c #3171c9", +"br c #3171ca", +"aA c #3271c9", +"cN c #3272c9", +"aW c #3272ca", +"c3 c #3372ca", +"dt c #3372cb", +"dz c #3373ca", +"b. c #3373cb", +"bL c #3374cb", +"dK c #3377cf", +"dU c #3379d0", +"aZ c #3467be", +"aL c #3474cb", +"#o c #3478d0", +"da c #3567bf", +"dZ c #356cc3", +"aa c #3575cc", +"bO c #3576cc", +"#W c #3576ce", +".B c #363636", +"bM c #3676cc", +"be c #3676cd", +"c6 c #3677cd", +"fJ c #373737", +"az c #3777cd", +"bN c #3778cd", +"#T c #383838", +"bv c #3878cd", +"bs c #3878ce", +"#O c #3879ce", +"fZ c #393939", +"dl c #396cc1", +"aM c #3979ce", +"#w c #397bd1", +"dL c #397dd3", +"#n c #397ed3", +"fz c #3a3a3a", +"c4 c #3a7bcf", +"bu c #3a7bd0", +"dT c #3a7fd4", +"aG c #3b3b3b", +"c5 c #3b7bcf", +"bd c #3b7bd0", +"a# c #3b7cd0", +".7 c #3b80d5", +"gh c #3c3c3c", +"dB c #3c70c3", +"ay c #3c7cd1", +"aV c #3c7dd1", +"a4 c #3d3d3d", +"#X c #3d7dd1", +"aN c #3d7ed1", +"dy c #3d7ed2", +"bt c #3e7fd1", +"dh c #3e7fd2", +"dM c #3e83d7", +"bk c #3f3f3f", +"#Q c #3f73c5", +"al c #3f7fd2", +"#N c #3f80d2", +"b# c #3f80d3", +"dS c #3f85d7", +"#x c #4081d3", +"#m c #4084d7", +"f5 c #414141", +"a. c #4182d3", +"aU c #4182d4", +"bY c #424242", +"aC c #4276c6", +"aO c #4282d4", +"ax c #4283d4", +"bc c #4283d5", +"di c #4284d4", +".8 c #4287d9", +"#Y c #4384d5", +"dN c #4389da", +"cW c #444444", +"dj c #4484d5", +"dR c #4489db", +"g# c #454545", +"#M c #4586d6", +"bb c #4587d6", +"dd c #464646", +"ac c #467ac9", +"aT c #4687d7", +"aP c #4788d7", +"#y c #4788d8", +"#l c #478ddc", +"dO c #478ddd", +"er c #484848", +"ba c #4889d7", +"aw c #4889d8", +"am c #488ad8", +".9 c #488edd", +"dE c #494949", +"#Z c #498ad8", +"dx c #498bd9", +"d2 c #4a4a4a", +"aS c #4a8bd9", +"dP c #4a90de", +"aQ c #4b8cda", +"du c #4b8dda", +"#L c #4b8ddb", +"fU c #4c4c4c", +"dw c #4c8eda", +"dv c #4c8edb", +"dQ c #4c92df", +"av c #4d8edb", +"#z c #4d8fdb", +"an c #4d8fdc", +"#9 c #4e8fdc", +"#0 c #4e90dc", +"#k c #4f94e1", +"#. c #4f95e2", +"aR c #5092dd", +"au c #5193de", +"ao c #5294de", +"#K c #5294df", +"#A c #5395df", +"#1 c #5395e0", +"ap c #5597e0", +"at c #5597e1", +"#j c #559ce6", +"## c #579de6", +"#8 c #589ae2", +"aq c #589be2", +"fs c #595959", +"#B c #599be3", +"as c #599ce3", +"ar c #5a9ce3", +"#7 c #5c9fe6", +"#2 c #5d9fe5", +"#i c #5da3ea", +"fH c #5e5e5e", +"#C c #5ea2e7", +"#a c #5ea4eb", +"#J c #5fa1e6", +"gg c #606060", +"#6 c #60a3e7", +"#3 c #60a3e8", +"#5 c #62a4e9", +"#4 c #62a5e9", +"#I c #63a7ea", +"#h c #63aaef", +"#D c #64a7ea", +"#b c #64abef", +".g c #666666", +"f4 c #686868", +"#E c #68abed", +"#g c #69b1f2", +"#H c #6aaeee", +"#F c #6aaeef", +"#c c #6ab1f3", +"#G c #6bafef", +"#f c #6db4f5", +"#d c #6eb5f5", +"#e c #6eb6f6", +".E c #7087ae", +".n c #717171", +"f9 c #757575", +".Y c #758fb7", +"fO c #787878", +"el c #7ba0d7", +".F c #7d98be", +"gf c #7e7e7e", +"f0 c #808080", +"ek c #83a7dc", +"ga c #848484", +".X c #85a2c7", +".a c #868686", +"d5 c #86abdf", +"fy c #878787", +".W c #87a5c9", +"ej c #87abdd", +"d4 c #88aadc", +"f6 c #898989", +".Z c #899cc0", +".G c #8aa7ca", +"ei c #8aafe0", +"fD c #8b8b8b", +".V c #8ba8ca", +".H c #8ca9cb", +"d6 c #8cb1e2", +".U c #8eaccd", +"eh c #8eb3e3", +".I c #8faccd", +"d7 c #90b5e4", +".T c #92afcf", +"em c #92afdd", +".J c #92b0d0", +"eg c #92b7e5", +"d8 c #93b8e6", +".j c #949494", +".S c #95b3d1", +".K c #95b3d2", +"d9 c #96bbe8", +"ge c #979797", +".R c #98b6d3", +".L c #98b6d4", +"e. c #99bfea", +".f c #9a9a9a", +".e c #9b9b9b", +".Q c #9bb9d4", +".M c #9bb9d6", +".d c #9c9c9c", +"ef c #9cc2ec", +".c c #9d9d9d", +"e# c #9dc2eb", +".b c #9e9e9e", +".N c #9ebcd7", +"ee c #9ec4ed", +"ea c #9fc4ee", +".O c #a0bed8", +".P c #a0bfd8", +"ed c #a0c5ee", +"eb c #a0c6ee", +"ec c #a1c6ef", +"gd c #a3a3a3", +"gb c #a4a4a4", +"fa c #a5a5a5", +"gc c #a6a6a6", +"fN c #a8a8a8", +"fc c #acacac", +"fi c #b4b4b4", +"f8 c #b5b5b5", +"fm c #b8b8b8", +"fj c #b9b9b9", +"fl c #bababa", +"fk c #bbbbbb", +"fn c #bcbcbc", +"fx c #bebebe", +"fw c #bfbfbf", +"fh c #c1c1c1", +"fv c #c2c2c2", +"fu c #c3c3c3", +"eQ c #c4c4c4", +"eo c #c6c6c5", +"fE c #c6c6c6", +".4 c #c6c9d0", +"fe c #c7c7c7", +".z c #c8c8c8", +"#u c #c8ccd3", +"fd c #c9c9c9", +"d1 c #cac9c8", +"aF c #cacaca", +"f# c #cbcac9", +"ep c #cbcbcb", +"a2 c #cccccc", +"dD c #cdccca", +"do c #cdcdcd", +"#U c #cdd0d7", +"f. c #cecccc", +"af c #cecece", +"ai c #ced1d8", +"aI c #ced2d9", +"dn c #cfcecd", +"eP c #cfcfcf", +"e9 c #d0cfcf", +"ft c #d0d0d0", +"eO c #d0d0d1", +"dc c #d1d1cf", +"fg c #d1d1d1", +"e8 c #d2d2d1", +"#s c #d2d2d2", +"a6 c #d2d6dc", +".1 c #d3d3d3", +"cV c #d4d3d2", +"eN c #d4d3d3", +"e7 c #d4d4d3", +"f7 c #d4d4d4", +"bm c #d4d7de", +"ff c #d5d5d5", +"eM c #d5d6d6", +"d0 c #d5d7da", +"cC c #d6d5d4", +"e6 c #d6d6d5", +"f3 c #d6d6d6", +"en c #d6d7d9", +"dC c #d6d8db", +"bE c #d6d9e0", +"fY c #d7d7d7", +"eL c #d7d8d7", +".D c #d7d8db", +"bZ c #d7dbe2", +"fX c #d8d8d8", +"e5 c #d8d9d8", +"dm c #d8d9dc", +"cj c #d9d8d7", +"eK c #d9d9d9", +"db c #d9dbde", +"ck c #d9dde4", +"fM c #dadada", +"cU c #dadcdf", +"e4 c #dbdbda", +"eJ c #dbdbdb", +"cB c #dbdde0", +"dF c #dbdfe5", +"bW c #dcdbda", +"eI c #dcdcdc", +"cE c #dce0e6", +"fQ c #dddddd", +"cX c #dde1e8", +"e3 c #dedddc", +"fT c #dedede", +"ci c #dedfe2", +"dq c #dee1e7", +"de c #dee2e8", +"bD c #dfdedc", +"eH c #dfdfdf", +"bV c #dfe1e4", +"e2 c #e0dfdd", +"bC c #e0e2e5", +"bj c #e1e0df", +"eG c #e1e1e1", +"a0 c #e1e3e6", +"aD c #e1e3e7", +"e1 c #e2e1e0", +"eF c #e2e2e2", +"a1 c #e3e2e1", +"eE c #e3e3e3", +"e0 c #e4e2e3", +"aE c #e4e3e1", +"fS c #e4e4e4", +"fR c #e5e5e5", +"eZ c #e6e5e5", +"eD c #e6e6e6", +"fF c #e7e7e7", +"eY c #e8e7e7", +"eC c #e8e8e8", +"eX c #e9e9e8", +"eB c #e9eaea", +"d3 c #e9ecef", +".p c #eaeaea", +"ad c #eaebef", +"eA c #ebebeb", +"eW c #ecebea", +"fP c #ececec", +"ez c #ededec", +"fW c #ededed", +"f2 c #eeeeee", +"ey c #efeeef", +"f1 c #efefef", +"ex c #f0f0f0", +"#R c #f0f2f6", +"ae c #f1f0ef", +".m c #f1f1f1", +"ew c #f1f2f2", +"fL c #f2f2f2", +"fG c #f3f3f3", +"#r c #f3f5f8", +"ev c #f4f3f3", +"eV c #f4f4f3", +".3 c #f4f4f4", +"et c #f4f6f8", +".C c #f5f5f5", +"eU c #f6f6f4", +"#t c #f6f6f6", +"eu c #f6f7f8", +"fV c #f7f7f7", +"ah c #f8f8f8", +".0 c #f8f9fa", +"eT c #f9f7f7", +"aH c #f9f9f9", +"fK c #fafafa", +"eS c #fbfafa", +"a5 c #fbfbfb", +"bl c #fcfcfc", +"es c #fdfdfc", +".k c #fdfdfd", +".y c #fefefe", +".x c #fffcf8", +".w c #fffcf9", +".v c #fffdf9", +".u c #fffef9", +".t c #fffefa", +"#S c #fffefd", +".s c #fffffa", +".r c #fffffc", +".q c #fffffd", +".l c #ffffff", +"Qt.#.a.b.c.c.c.c.c.c.c.c.c.d.d.d.d.d.d.d.d.d.e.e.e.f.f.f.e.g.hQt", +".i.j.k.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.m.n.i", +".o.p.l.l.q.r.s.s.s.t.u.v.v.w.x.x.x.v.v.v.v.u.u.u.u.u.s.r.y.l.z.A", +".B.C.l.D.E.F.G.H.I.J.K.L.M.N.O.P.O.N.Q.R.S.T.U.V.W.X.Y.Z.0.l.1.2", +".B.3.l.4.5.6.7.8.9#.###a#b#c#d#e#f#g#h#i#j#k#l#m#n#o#p#q#r.l#s.2", +".B#t.l#u#v#w#x#y#z#A#B#C#D#E#F#G#H#E#I#J#B#K#L#M#N#O#P#Q#R#S#s.2", +"#T#t.l#U#V#W#X#Y#Z#0#1#B#2#3#4#4#5#6#7#8#A#9#ya.a#aaabacadaeafag", +"#Tah.laiajak#Oal#Yamanaoapaqararas#8atauavawaxayazaAaBaCaDaEaFag", +"aGaH.laIaJaKaLaMaNaOaPaQanaRauauauaR#zaSaTaUaVazaWaXaYaZa0a1a2a3", +"a4a5.la6a7a8a9b.aza#b##Y#M#y#Z#Z#Zbabbbc#NbdbebfaXbgbhbia0bja2a3", +"bkbl.lbmbnbobpbqbraabsa#bt#N#x#x#x#NaNbubvaLbwbxbybzbAbBbCbDa2a3", +"bkbl.lbEbFbGbHbIbJbKbwbLbMbNbsbsbvazbOb.bwbPbQbRbSbTbUbBbVbWa2bX", +"bY.k.lbZb0b1b2b3b4b5b6b7b8aXb9c.c.c#bqcacbcccdcecfcgchbBcicja2bX", +"bY.y.lckclcmcnb5cocpcqcrcsctcucuctcacvcwcpcocxcyb3czcAbBcBcCa2cD", +"bY.l.lcEcFcGcHcIbycJcKcLcMbfcNcNbfcMaXbqcObQcpcPcQcRcScTcUcVa2cD", +"cW.l.lcXcYcZc0c1b8c2c3bMaMc4c5c5c4aMc6b.a9c7c1c8c9d.d#dadbdca2cD", +"dd.l.ldedfdgcac#bfbec4dh#xdidjdjdiaUdhbdazbrbPcJcbc9dkdldmdndodp", +"dd.l.ldqdrdsaKdtbv#XaxaTamdudvdwaQdxaTaxdy#OdzbPcJc0dAdBdCdDa2dp", +"dE.l.ldFdGdHdIdJdKdLdMdNdOdPdQdQdP.9dRdSdTdUdVdIdWdXdYdZd0d1a2dp", +"d2.l.ld3d4d5d6d7d8d9e.e#eaebececedeeefe.d9egeheiejekelemeneoepeq", +"er.leseteu#tevewexeyezeAeBeCeDeEeFeGeHeIeJeKeLeMeN#seOeP.zeQa2.#", +"eRaf.l.yeseSeTeUeVaeeWeXeYeZe0e1e2e3e4e5e6cCcCe7e8e9f.f#.zeJfa.i", +"Qtfbfc#sdoa2epfdeQfeff.1#sfgePafdoa2epaFaFfhfifjfkflfjfmfn.jag.i", +"Qt.i.ifofofpfqfqfrfsfeftepfdfeeQfufvfwfxfxfyfzfA.ifBfCfCfC.i.iQt", +"QtQtQtQtQtQtfzfDfEeAfteDeAeCeCeCfFfFeCeAffeDfGfifHfIQtQtQtQtQtQt", +"QtQtQtQtQtfJfg.l.l.l.meJeA.CaHaHfKahfLfM.1aH.l.l.lfNfCQtQtQtQtQt", +"QtQtQtQtQtfO.l.ka5aH#tfPfQeHfReDfSeIaffda2fTbla5.l.lfUQtQtQtQtQt", +"QtQtQtQtQtfHfVfVfLfL.mfW.peEeIeJfXfYeJeHfF.mfLfLfKfPfZQtQtQtQtQt", +"QtQtQtQtQtfBf0eFf1f1fP.p.p.p.p.p.p.peAfPfPfWf1f2f3f4.iQtQtQtQtQt", +"QtQtQtQtQtQt.if5f6fkfYeFeEeEfSfSfSfSeEeEeFf7f8f9g..i.iQtQtQtQtQt", +"QtQtQtQtQtQtQt.i.i.2g#.gga.fgbgcgcgdgegfgggh.A.i.iQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQt.i.i.ifqfofpfpfofq.i.i.i.iQtQtQtQtQtQtQtQtQt" +] + +class Form1(QDialog): + def __init__(self,parent = None,name = None,modal = 0,fl = 0): + QDialog.__init__(self,parent,name,modal,fl) + + self.image0 = QPixmap(image0_data) + + if not name: + self.setName("Form1") + + + + self.groupBox3 = QGroupBox(self,"groupBox3") + self.groupBox3.setGeometry(QRect(10,320,680,133)) + self.groupBox3.setColumnLayout(0,Qt.Vertical) + self.groupBox3.layout().setSpacing(6) + self.groupBox3.layout().setMargin(11) + groupBox3Layout = QHBoxLayout(self.groupBox3.layout()) + groupBox3Layout.setAlignment(Qt.AlignTop) + + layout92 = QGridLayout(None,1,1,0,6,"layout92") + + self.textLabel2_4 = QLabel(self.groupBox3,"textLabel2_4") + + layout92.addWidget(self.textLabel2_4,0,1) + + self.textLabel5 = QLabel(self.groupBox3,"textLabel5") + + layout92.addWidget(self.textLabel5,1,2) + + self.textLabel1_4 = QLabel(self.groupBox3,"textLabel1_4") + self.textLabel1_4.setAlignment(QLabel.WordBreak | QLabel.AlignVCenter) + + layout92.addMultiCellWidget(self.textLabel1_4,2,2,0,3) + + self.comboBox4 = QComboBox(0,self.groupBox3,"comboBox4") + + layout92.addWidget(self.comboBox4,1,3) + + self.comboBox2 = QComboBox(0,self.groupBox3,"comboBox2") + + layout92.addWidget(self.comboBox2,0,3) + + self.textLabel3 = QLabel(self.groupBox3,"textLabel3") + + layout92.addWidget(self.textLabel3,0,0) + + self.kPushButton1 = KPushButton(self.groupBox3,"kPushButton1") + + layout92.addMultiCellWidget(self.kPushButton1,1,1,0,1) + + self.textLabel4 = QLabel(self.groupBox3,"textLabel4") + + layout92.addWidget(self.textLabel4,0,2) + groupBox3Layout.addLayout(layout92) + + self.groupBox1 = QGroupBox(self,"groupBox1") + self.groupBox1.setGeometry(QRect(10,10,320,300)) + self.groupBox1.setSizePolicy(QSizePolicy(7,1,0,0,self.groupBox1.sizePolicy().hasHeightForWidth())) + + self.pixmapLabel1 = QLabel(self.groupBox1,"pixmapLabel1") + self.pixmapLabel1.setGeometry(QRect(11,33,16,17)) + self.pixmapLabel1.setSizePolicy(QSizePolicy(0,0,0,0,self.pixmapLabel1.sizePolicy().hasHeightForWidth())) + self.pixmapLabel1.setScaledContents(1) + + self.textLabel2 = QLabel(self.groupBox1,"textLabel2") + self.textLabel2.setGeometry(QRect(19,45,60,17)) + self.textLabel2.setSizePolicy(QSizePolicy(1,1,0,0,self.textLabel2.sizePolicy().hasHeightForWidth())) + + self.textLabel2_3 = QLabel(self.groupBox1,"textLabel2_3") + self.textLabel2_3.setGeometry(QRect(85,45,213,17)) + self.textLabel2_3.setSizePolicy(QSizePolicy(3,1,0,0,self.textLabel2_3.sizePolicy().hasHeightForWidth())) + + self.textLabel1_3 = QLabel(self.groupBox1,"textLabel1_3") + self.textLabel1_3.setGeometry(QRect(80,20,213,17)) + self.textLabel1_3.setSizePolicy(QSizePolicy(3,1,0,0,self.textLabel1_3.sizePolicy().hasHeightForWidth())) + + self.textLabel1 = QLabel(self.groupBox1,"textLabel1") + self.textLabel1.setGeometry(QRect(19,22,60,17)) + self.textLabel1.setSizePolicy(QSizePolicy(1,1,0,0,self.textLabel1.sizePolicy().hasHeightForWidth())) + + self.pushButton2 = QPushButton(self.groupBox1,"pushButton2") + self.pushButton2.setGeometry(QRect(160,70,144,26)) + self.pushButton2.setSizePolicy(QSizePolicy(5,1,0,0,self.pushButton2.sizePolicy().hasHeightForWidth())) + + self.groupBox2 = QGroupBox(self,"groupBox2") + self.groupBox2.setGeometry(QRect(350,10,348,300)) + self.groupBox2.setSizePolicy(QSizePolicy(5,1,0,0,self.groupBox2.sizePolicy().hasHeightForWidth())) + + LayoutWidget = QWidget(self.groupBox2,"layout11") + LayoutWidget.setGeometry(QRect(12,24,324,44)) + layout11 = QHBoxLayout(LayoutWidget,11,6,"layout11") + + self.pixmapLabel3 = QLabel(LayoutWidget,"pixmapLabel3") + self.pixmapLabel3.setSizePolicy(QSizePolicy(0,0,0,0,self.pixmapLabel3.sizePolicy().hasHeightForWidth())) + self.pixmapLabel3.setPixmap(self.image0) + self.pixmapLabel3.setScaledContents(1) + layout11.addWidget(self.pixmapLabel3) + + layout10 = QGridLayout(None,1,1,0,6,"layout10") + + self.textLabel2_2_2 = QLabel(LayoutWidget,"textLabel2_2_2") + self.textLabel2_2_2.setSizePolicy(QSizePolicy(3,1,0,0,self.textLabel2_2_2.sizePolicy().hasHeightForWidth())) + + layout10.addWidget(self.textLabel2_2_2,1,1) + + self.textLabel1_2_2 = QLabel(LayoutWidget,"textLabel1_2_2") + self.textLabel1_2_2.setSizePolicy(QSizePolicy(4,1,0,0,self.textLabel1_2_2.sizePolicy().hasHeightForWidth())) + + layout10.addWidget(self.textLabel1_2_2,0,1) + + self.textLabel1_2 = QLabel(LayoutWidget,"textLabel1_2") + self.textLabel1_2.setSizePolicy(QSizePolicy(1,1,0,0,self.textLabel1_2.sizePolicy().hasHeightForWidth())) + + layout10.addWidget(self.textLabel1_2,0,0) + + self.textLabel2_2 = QLabel(LayoutWidget,"textLabel2_2") + self.textLabel2_2.setSizePolicy(QSizePolicy(1,1,0,0,self.textLabel2_2.sizePolicy().hasHeightForWidth())) + + layout10.addWidget(self.textLabel2_2,1,0) + layout11.addLayout(layout10) + + self.pushButton2_2 = QPushButton(self.groupBox2,"pushButton2_2") + self.pushButton2_2.setGeometry(QRect(180,70,158,26)) + self.pushButton2_2.setSizePolicy(QSizePolicy(5,1,0,0,self.pushButton2_2.sizePolicy().hasHeightForWidth())) + + self.languageChange() + + self.resize(QSize(702,472).expandedTo(self.minimumSizeHint())) + self.clearWState(Qt.WState_Polished) + + + def languageChange(self): + self.setCaption(self.__tr("Form1")) + self.groupBox3.setTitle(self.__tr("Default Display Settings")) + self.textLabel2_4.setText(self.__tr("1280x1024 @ 60Hz")) + self.textLabel5.setText(self.__tr("DPI:")) + self.textLabel1_4.setText(self.__tr("These settings are defaults. Each user of this computer may specify their own personal settings.")) + self.comboBox4.clear() + self.comboBox4.insertItem(self.__tr("75 DPI (small fonts)")) + self.comboBox4.insertItem(self.__tr("100 DPI (large fonts)")) + self.comboBox4.insertItem(self.__tr("Auto (84 DPI)")) + self.comboBox2.clear() + self.comboBox2.insertItem(self.__tr("Millions (24bit)")) + self.textLabel3.setText(self.__tr("Screen size:")) + self.kPushButton1.setText(self.__tr("Use current settings as system default")) + self.textLabel4.setText(self.__tr("Colors:")) + self.groupBox1.setTitle(self.__tr("Graphics Card")) + self.textLabel2.setText(self.__tr("Memory:")) + self.textLabel2_3.setText(self.__tr("32 Mb")) + self.textLabel1_3.setText(self.__tr("GeForce 2")) + self.textLabel1.setText(self.__tr("Name:")) + self.pushButton2.setText(self.__tr("Configure...")) + self.groupBox2.setTitle(self.__tr("Monitor")) + self.textLabel2_2_2.setText(self.__tr("1600x1200 @ 60Hz")) + self.textLabel1_2_2.setText(self.__tr("Philips 107S")) + self.textLabel1_2.setText(self.__tr("Name:")) + self.textLabel2_2.setText(self.__tr("Max. Resolution:")) + self.pushButton2_2.setText(self.__tr("Configure...")) + + + def __tr(self,s,c = None): + return qApp.translate("Form1",s,c) + +if __name__ == "__main__": + a = QApplication(sys.argv) + QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()")) + w = Form1() + a.setMainWidget(w) + w.show() + a.exec_loop() diff --git a/displayconfig/displayconfigwidgets.py b/displayconfig/displayconfigwidgets.py new file mode 100644 index 0000000..7484ac5 --- /dev/null +++ b/displayconfig/displayconfigwidgets.py @@ -0,0 +1,809 @@ + +from qt import * +from kdecore import * +from kdeui import * +import os +from displayconfigabstraction import * + +# Running as the root user or not? +isroot = os.getuid()==0 + +############################################################################ +class ResizeSlider(QVGroupBox): + """ An abstracted QSlider in a nice box to change the resolution of a screen """ + def __init__(self,parent): + # Screen size group + QVGroupBox.__init__(self,parent) + self.updating_gui = True + self._buildGUI() + self.updating_gui = False + + def _buildGUI(self): + self.setTitle(i18n("Screen Size")) + self.setInsideSpacing(KDialog.spacingHint()) + self.setInsideMargin(KDialog.marginHint()) + + hbox3 = QHBox(self) + hbox3.setSpacing(KDialog.spacingHint()) + label = QLabel(hbox3,"textLabel2_4") + label.setText(i18n("Lower")) + self.screensizeslider = QSlider(hbox3,"slider1") + self.screensizeslider.setMinValue(0) + self.screensizeslider.setMaxValue(4) + self.screensizeslider.setPageStep(1) + self.screensizeslider.setOrientation(QSlider.Horizontal) + self.screensizeslider.setTickmarks(QSlider.Below) + self.connect(self.screensizeslider,SIGNAL("valueChanged(int)"),self.slotResolutionChange) + label = QLabel(hbox3) + label.setText(i18n("Higher")) + + self.resolutionlabel = QLabel(self) + self.resolutionlabel.setText("640x400") + + def setScreen(self, screen): + self.updating_gui = True + self.screen = screen + self.screensizeslider.setMaxValue(len(screen.getAvailableResolutions())-1) + self.screensizeslider.setValue(screen.getResolutionIndex()) + self.updating_gui = False + self.setResolutionIndex(screen.getResolutionIndex()) + + def slotResolutionChange(self,i): + """ Pass signal from slider through to App """ + if self.updating_gui: + return + self.setResolutionIndex(i) + self.emit(PYSIGNAL("resolutionChange(int)"),(i,)) + + def setMaxValue(self,value): + self.updating_gui = True + self.screensizeslider.setMaxValue(value) + self.updating_gui = False + + def setMinValue(self,value): + self.updating_gui = True + self.screensizeslider.setMinValue(value) + self.updating_gui = False + + def setValue(self,value): + self.updating_gui = True + self.screensizeslider.setValue(value) + self.updating_gui = False + + def value(self): + return self.screensizeslider.value() + + def setResolutionLabel(self,text): + self.resolutionlabel.setText(text) + + def setResolutionIndex(self,i): + self.updating_gui = True + width,height = self.screen.getAvailableResolutions()[i] + self.setResolutionLabel(i18n("%1 x %2").arg(width).arg(height)) + self.updating_gui = False + +############################################################################ +class MonitorPreview(QWidget): + """ A ResizableMonitor is an Image in a grid which has resizable edges, + fixed-size corners and is thus expandable. """ + ROTATE_0 = 0 + ROTATE_90 = 1 + ROTATE_180 = 2 + ROTATE_270 = 3 + + def __init__(self, parent=None, imagedir="", name=None): + QWidget.__init__(self,parent) + + self.rotation = MonitorPreview.ROTATE_0 + + self.screen_width = 1280 + self.screen_height = 1024 + + self.reflect_x = False + self.reflect_y = False + + self.setBackgroundMode(Qt.NoBackground) + + self.imagedir = imagedir + "monitor_resizable/" + + self.image_monitor = QPixmap(self.imagedir+"monitor.png") + self.image_monitor_wide = QPixmap(self.imagedir+"monitor_wide.png") + self.image_monitor_r90 = QPixmap(self.imagedir+"monitor_r90.png") + self.image_monitor_wide_r90 = QPixmap(self.imagedir+"monitor_wide_r90.png") + + self.image_background = QPixmap(self.imagedir+"background.png") + self.image_background_wide = QPixmap(self.imagedir+"background_wide.png") + self.image_background_r90 = QPixmap(self.imagedir+"background_r90.png") + self.image_background_wide_r90 = QPixmap(self.imagedir+"background_wide_r90.png") + + self.image_window = QPixmap(self.imagedir+"window_4th.png") + self.image_window_bottom_left = QPixmap(self.imagedir+"window_bottom_left_4th.png") + self.image_window_bottom_right = QPixmap(self.imagedir+"window_bottom_right_4th.png") + + def sizeHint(self): + max_width = max(self.image_monitor.width(), self.image_monitor_wide.width(), + self.image_monitor_r90.width(), self.image_monitor_wide_r90.width()) + max_height = max(self.image_monitor.height(), self.image_monitor_wide.height(), + self.image_monitor_r90.height(), self.image_monitor_wide_r90.height()) + return QSize(max_width, max_height) + + def sizePolicy(self): + return QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + + def paintEvent(self,paint_event): + screen_width = self.screen_width + screen_height = self.screen_height + + # Widescreen format: preview width: 176, height: 99, 16:9 + is_wide = abs(float(screen_width)/float(screen_height)-16.0/9.0) < 0.2 + + if not is_wide: + preview_screen_width = 152 + preview_screen_height = 114 + else: + preview_screen_width = 176 + preview_screen_height = 99 + + if self.rotation==MonitorPreview.ROTATE_0 or self.rotation==MonitorPreview.ROTATE_180: + # Normal, landscape orientation. + if not is_wide: + screen_x_offset = 23 + screen_y_offset = 15 + image_background = self.image_background + else: + screen_x_offset = 23 + screen_y_offset = 29 + image_background = self.image_background_wide + else: + # Portrait orientation. Swap some values around. + t = preview_screen_width + preview_screen_width = preview_screen_height + preview_screen_height = t + + t = screen_width + screen_width = screen_height + screen_height = t + + if not is_wide: + screen_x_offset = 42 + screen_y_offset = 15 + image_background = self.image_background_r90 + else: + screen_x_offset = 50 + screen_y_offset = 15 + image_background = self.image_background_wide_r90 + + # Draw everything off screen in a buffer + preview_buffer = QPixmap(preview_screen_width,preview_screen_height) + painter = QPainter(preview_buffer) + + # Draw the background on the monitor's screen + painter.drawPixmap(0, 0, image_background) + + # Work out the scaling factor for the eye candy in the preview winodw. + scale_factor = 4.0*float(preview_screen_width) / float(screen_width) + transform_matrix = QWMatrix().scale(scale_factor,scale_factor) + + # Draw the little window on the background + scaled_window = self.image_window.xForm(transform_matrix) + + sx = (preview_screen_width-scaled_window.width())/2 + sy = (preview_screen_height-scaled_window.height())/2 + if sx < 0: + sx = 0 + if sy < 0: + sy = 0 + sw = scaled_window.width() + if sw>preview_screen_width: + sw = preview_screen_width + + sh = scaled_window.height() + if sh>preview_screen_height: + sh = preview_screen_height + + painter.drawPixmap(sx, sy, scaled_window, 0, 0, sw, sh) + + # Now draw the clock in the lower right corner + scaled_window = self.image_window_bottom_right.xForm(transform_matrix) + + sx = preview_screen_width - scaled_window.width() + sy = preview_screen_height - scaled_window.height() + sw = scaled_window.width()#preview_screen_width/2 + sh = scaled_window.height() + + sx_offset = 0 + if sx<0: # Some simple clipping for the left edge + sx_offset = -sx + sw = preview_screen_width + sx = 0 + + painter.drawPixmap(sx, sy, scaled_window, sx_offset, 0, sw, sh) + + # Now draw the k menu in the lower left corner + scaled_window = self.image_window_bottom_left.xForm(transform_matrix) + + sx = 0 + sy = preview_screen_height - scaled_window.height() + sw = preview_screen_width/2 # Just draw on the left side of the preview. + sh = scaled_window.height() + painter.drawPixmap(sx, sy, scaled_window, 0, 0, sw, sh) + painter.end() + + # Transform the preview image. Do reflections. + reflect_x = 1 + if self.reflect_x: + reflect_x = -1 + reflect_y = 1 + if self.reflect_y: + reflect_y = -1 + + preview_buffer = preview_buffer.xForm(QWMatrix().scale(reflect_x,reflect_y)) + + # Draw the monitor on another buffer. + off_screen_buffer = QPixmap(self.width(),self.height()) + off_screen_painter = QPainter(off_screen_buffer) + + # Erase the buffer first + off_screen_painter.setBackgroundColor(self.paletteBackgroundColor()) + off_screen_painter.eraseRect(0, 0, off_screen_buffer.width(), off_screen_buffer.height()) + + if self.rotation==MonitorPreview.ROTATE_0 or self.rotation==MonitorPreview.ROTATE_180: + if not is_wide: + image_monitor = self.image_monitor + else: + image_monitor = self.image_monitor_wide + else: + if not is_wide: + image_monitor = self.image_monitor_r90 + else: + image_monitor = self.image_monitor_wide_r90 + + top_edge = self.height()-image_monitor.height() + left_edge = (self.width()-image_monitor.width())/2 + + # Draw the monitor + off_screen_painter.drawPixmap(left_edge, top_edge, image_monitor) + off_screen_painter.end() + + # Copy the preview onto the off screen buffer with the monitor. + bitBlt(off_screen_buffer, left_edge+screen_x_offset, top_edge+screen_y_offset, preview_buffer, + 0, 0, preview_buffer.width(), preview_buffer.height(),Qt.CopyROP, False) + + # Update the widget + bitBlt(self, 0, 0, off_screen_buffer, 0, 0, self.width(), self.height(), Qt.CopyROP, False) + + def setResolution(self,width,height): + self.screen_width = width + self.screen_height = height + self.update() + + def setRotation(self, rotation): + self.rotation = rotation + self.update() + + def setReflectX(self, enable): + self.reflect_x = enable + self.update() + + def setReflectY(self, enable): + self.reflect_y = enable + self.update() + +############################################################################ +class DualMonitorPreview(QWidget): + """ This is the Widget to use elsewhere. It consists of a canvas and an + arbitrary number of gizmos on the canvas. The gizmos can be dragged and + dropped around. Painting is double-buffered so flickering should not occur. + """ + def __init__(self, parent, size, imagedir): + QWidget.__init__(self,parent) + self.setBackgroundMode(Qt.NoBackground) + + self.imagedir = imagedir + "dualhead/" + self.snap_distance = 25 + self.snapping = True + self.size = size + self.position = XSetup.POSITION_LEFTOF + + self.current_screen = 0 + + self.resize(size,size) + self.setMouseTracking(True) + + self.gizmos = [] + self.gizmos.append(MovingGizmo("Monitor 1","monitor_1.png",QPoint(20,50),self.imagedir)) + self.gizmos.append(MovingGizmo("Monitor 2","monitor_2.png",QPoint(180,50),self.imagedir)) + + self.gizmos[0].setWidth(1280) + self.gizmos[0].setHeight(1024) + self.gizmos[0].setHighlightColor(self.colorGroup().highlight()) + self.gizmos[1].setWidth(1280) + self.gizmos[1].setHeight(1024) + self.gizmos[1].setHighlightColor(self.colorGroup().highlight()) + + self.dragging = False + self.dragging_gizmo = 0 + self.drag_handle = None + + self._positionGizmos() + self.setCurrentScreen(0) + + def minimumSizeHint(self): + return QSize(self.size,self.size) + + def setCurrentScreen(self,screen): + self.current_screen = screen + self.gizmos[0].setHighlight(screen==0) + self.gizmos[1].setHighlight(screen==1) + self.update() + + def getCurrentScreen(self): + return self.current_screen + + def setPosition(self,position): + self.position = position + self._positionGizmos() + self.update() + + def getPosition(self): + """Returns one of XSetup.POSITION_LEFTOF, XSetup.POSITION_RIGHTOF, + XSetup.POSITION_ABOVE or XSetup.POSITION_BELOW. + """ + return self.position + + def setScreenResolution(self,screenNumber,width,height): + self.gizmos[screenNumber].setWidth(width) + self.gizmos[screenNumber].setHeight(height) + self.setPosition(self.position) # Reposition and force update. + + def _positionGizmos(self): + g1 = self.gizmos[0] + g2 = self.gizmos[1] + + # Treat POSITION_RIGHTOF and POSITION_BELOW as LEFTOF and ABOVE with the + # gizmos swapped around. + if self.position==XSetup.POSITION_RIGHTOF or self.position==XSetup.POSITION_BELOW: + tmp = g1 + g1 = g2 + g2 = tmp + + if self.position==XSetup.POSITION_LEFTOF or self.position==XSetup.POSITION_RIGHTOF: + x = -g1.getWidth() + y = -max(g1.getHeight(), g2.getHeight())/2 + g1.setPosition(QPoint(x,y)) + + x = 0 + g2.setPosition(QPoint(x,y)) + + else: + x = -max(g1.getWidth(), g2.getWidth())/2 + y = -g1.getHeight() + g1.setPosition(QPoint(x,y)) + + y = 0 + g2.setPosition(QPoint(x,y)) + + def mousePressEvent(self,event): + # Translate the point in the window into our gizmo space. + world_point = self._getGizmoMatrix().invert()[0].map(event.pos()) + + # If the mouse is in the air space of a gizmo, then we change the cursor to + # indicate that the gizmo can be dragged. + for giz in self.gizmos: + if giz.getRect().contains(world_point): + self.setCurrentScreen(self.gizmos.index(giz)) + break + else: + return + + # Pressing down the mouse button on a gizmo also starts a drag operation. + self.dragging = True + self.dragging_gizmo = self.getCurrentScreen() + self.drag_handle = world_point - self.gizmos[self.dragging_gizmo].getPosition() + + # Let other people know that a gizmo has been selected. + self.emit(PYSIGNAL("pressed()"), (self.current_screen,) ) + + def mouseReleaseEvent(self,event): + if not self.dragging: + return + + # Translate the point in the window into our gizmo space. + world_point = self._getGizmoMatrix().invert()[0].map(event.pos()) + + if self._moveGizmo(world_point): + self.setPosition(self.drag_position) + self.emit(PYSIGNAL("positionChanged()"), (self.position,) ) + else: + self.setPosition(self.position) + self.dragging = False + + def mouseMoveEvent(self,event): + # Translate the point in the window into our gizmo space. + world_point = self._getGizmoMatrix().invert()[0].map(event.pos()) + + # If the mouse is in the air space of a gizmo, then we change the cursor to + # indicate that the gizmo can be dragged. + for giz in self.gizmos: + if giz.getRect().contains(world_point): + self.setCursor(QCursor(Qt.SizeAllCursor)) + break + else: + self.unsetCursor() + + if self.dragging: + self._moveGizmo(world_point) + self.update() + + return + + def _moveGizmo(self,worldPoint): + new_drag_position = worldPoint-self.drag_handle + + # Drag gizmo is simply the thing being dragged. + drag_gizmo = self.gizmos[self.dragging_gizmo] + drag_x = new_drag_position.x() + drag_y = new_drag_position.y() + + # Snap gizmo is other (stationary) thing that we "snap" against. + snap_gizmo = self.gizmos[1-self.dragging_gizmo] + snap_x = snap_gizmo.getPosition().x() + snap_y = snap_gizmo.getPosition().y() + + # Calculate the list of "snap points". + snap_points = [ + (snap_x-drag_gizmo.getWidth(), snap_y), # Left of + (snap_x+snap_gizmo.getWidth(), snap_y), # Right of + (snap_x, snap_y-drag_gizmo.getHeight()), # Above + (snap_x, snap_y+snap_gizmo.getHeight())] # Below + + # Find the snap point that the drag gizmo is closest to. + best_index = -1 + best_distance = 0 + i = 0 + for snap_point in snap_points: + dx = snap_point[0] - drag_x + dy = snap_point[1] - drag_y + distance_squared = dx*dx + dy*dy + if best_index==-1 or distance_squared < best_distance: + best_index = i + best_distance = distance_squared + i += 1 + + # Lookup the best dualhead position that this configuration matches. + if self.dragging_gizmo==0: + self.drag_position = [ + XSetup.POSITION_LEFTOF, + XSetup.POSITION_RIGHTOF, + XSetup.POSITION_ABOVE, + XSetup.POSITION_BELOW][best_index] + else: + self.drag_position = [ + XSetup.POSITION_RIGHTOF, + XSetup.POSITION_LEFTOF, + XSetup.POSITION_BELOW, + XSetup.POSITION_ABOVE][best_index] + + # Convert the auto-snap distance in pixels into a distance in the gizmo coordinate system. + world_snap_distance = self.snap_distance / self._getGizmoToPixelsScaleFactor() + + # Should this drag gizmo visually snap? + snapped = False + if best_distance <= (world_snap_distance*world_snap_distance): + new_drag_position = QPoint(snap_points[best_index][0],snap_points[best_index][1]) + snapped = True + + # Move the gizmo + self.gizmos[self.dragging_gizmo].setPosition(new_drag_position) + + return snapped + + def paintEvent(self,event=None): + QWidget.paintEvent(self,event) + + # Paint to an off screen buffer first. Later we copy it to widget => flicker free. + off_screen_buffer = QPixmap(self.width(),self.height()) + off_screen_painter = QPainter(off_screen_buffer) + + # Erase the buffer first + off_screen_painter.setBackgroundColor(self.colorGroup().mid() ) + off_screen_painter.eraseRect(0, 0, off_screen_buffer.width(), off_screen_buffer.height()) + + # + off_screen_painter.setWorldMatrix(self._getGizmoMatrix()) + + # Paint the non-selected gizmo first. + self.gizmos[ 1-self.current_screen ].paint(off_screen_painter) + + # Now paint the selected gizmo + self.gizmos[self.current_screen].paint(off_screen_painter) + + # Turn off the world matrix transform. + off_screen_painter.setWorldXForm(False) + + # Draw the rounded border + off_screen_painter.setPen(QPen(self.colorGroup().dark(),1)) + off_screen_painter.drawRoundRect(0,0,self.width(),self.height(),2,2) + + off_screen_painter.end() + + # Update the widget + bitBlt(self, 0, 0, off_screen_buffer, 0, 0, self.width(), self.height(), Qt.CopyROP, False) + + def _getGizmoMatrix(self): + matrix = QWMatrix() + matrix.translate(self.width()/2,self.height()/2) + + scale_factor = self._getGizmoToPixelsScaleFactor() + matrix.scale(scale_factor,scale_factor) + return matrix + + def _getGizmoToPixelsScaleFactor(self): + g1 = self.gizmos[0] + g2 = self.gizmos[1] + size = min(self.width(),self.height()) + vscale = float(self.height()) / (2.1 * (g1.getHeight()+g2.getHeight())) + hscale = float(self.width()) / (2.1 * (g1.getWidth()+g2.getWidth())) + return min(vscale,hscale) + +############################################################################ +class MovingGizmo(object): + """A gizmo represents a screen/monitor. It also has a width and height that + correspond to the resolution of screen.""" + + def __init__(self,label,filename,initial_pos=QPoint(0,0),imagedir="."): + self.width = 100 + self.height = 100 + self.pixmap = QPixmap(imagedir+filename) + + self.highlight = False + self.highlight_color = QColor(255,0,0) + + self.setPosition(initial_pos) + + # Used for caching the scaled pixmap. + self.scaled_width = -1 + self.scaled_height = -1 + + def setHighlight(self,enable): + self.highlight = enable + + def setHighlightColor(self,color): + self.highlight_color = color + + def setPosition(self,position): + self.position = position + + def getSize(self): + return QSize(self.width,self.height) + + def getPosition(self): + return self.position + + def getRect(self): + return QRect(self.position,self.getSize()) + + def setWidth(self,width): + self.width = width + + def getWidth(self): + return self.width + + def setHeight(self,height): + self.height = height + + def getHeight(self): + return self.height + + def paint(self,painter): + painter.save() + if self.highlight: + pen = QPen(self.highlight_color,6) + painter.setPen(pen) + + painter.drawRect(self.position.x(), self.position.y(), self.width, self.height) + + to_pixels_matrix = painter.worldMatrix() + top_left_pixels = to_pixels_matrix.map(self.position) + bottom_right_pixels = to_pixels_matrix.map( QPoint(self.position.x()+self.width, self.position.y()+self.height) ) + + # Scale the pixmap. + scaled_width = bottom_right_pixels.x() - top_left_pixels.x() + scaled_height = bottom_right_pixels.y() - top_left_pixels.y() + + if (scaled_width,scaled_height) != (self.scaled_width,self.scaled_height): + scale_matrix = QWMatrix() + scale_matrix.scale( + float(scaled_width)/float(self.pixmap.width()), + float(scaled_height)/float(self.pixmap.height()) ) + + self.scaled_pixmap = self.pixmap.xForm(scale_matrix) + (self.scaled_width,self.scaled_height) = (scaled_width,scaled_height) + + # Paste in the scaled pixmap. + bitBlt(painter.device(), top_left_pixels.x(), top_left_pixels.y(), self.scaled_pixmap, 0, 0, + self.scaled_pixmap.width(), self.scaled_pixmap.height(),Qt.CopyROP, False) + + painter.restore() + +############################################################################ +class GfxCardWidget(QVGroupBox): + def __init__(self, parent, xsetup, gfxcard, gfxcarddialog, monitordialog): + global imagedir + QVGroupBox.__init__(self,parent) + + self.xsetup = xsetup + self.gfxcard = gfxcard + self.gfxcarddialog = gfxcarddialog + self.monitordialog = monitordialog + self._buildGUI() + self._syncGUI() + + def _buildGUI(self): + # Create the GUI + + gridwidget = QWidget(self) + grid = QGridLayout(gridwidget,2+3*len(self.gfxcard.getScreens())) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(1,0) + grid.setColStretch(2,0) + grid.setColStretch(3,1) + grid.setColStretch(4,0) + + gfxcardpic = QLabel(gridwidget) + gfxcardpic.setPixmap(UserIcon('hi32-gfxcard')) + grid.addMultiCellWidget(gfxcardpic,0,1,0,0) + + label = QLabel(gridwidget) + label.setText(i18n("Graphics card:")) + grid.addWidget(label,0,1) + + self.gfxcardlabel = QLabel(gridwidget) + grid.addWidget(self.gfxcardlabel,0,2) + + label = QLabel(gridwidget) + label.setText(i18n("Driver:")) + grid.addWidget(label,1,1) + + self.driverlabel = QLabel(gridwidget) + grid.addMultiCellWidget(self.driverlabel,1,1,2,3) + + gfxbutton = QPushButton(gridwidget) + gfxbutton.setText(i18n("Configure...")) + self.connect(gfxbutton,SIGNAL("clicked()"),self.slotGfxCardConfigureClicked) + grid.addWidget(gfxbutton,0,4) + gfxbutton.setEnabled(self.xsetup.mayModifyXorgConfig()) + + # Add all of the screens + row = 2 + count = 1 + self.monitorlabels = [] + self.monitor_buttons = [] + self.monitor_roles = [] + for screen in self.gfxcard.getScreens(): + frame = QFrame(gridwidget) + frame.setFrameShape(QFrame.HLine) + frame.setFrameShadow(QFrame.Sunken) + grid.addMultiCellWidget(frame,row,row,0,4) + row += 1 + + monitorpic = QLabel(gridwidget) + monitorpic.setPixmap(UserIcon('hi32-display')) + grid.addMultiCellWidget(monitorpic,row,row+1,0,0) + + # Monitor label + label = QLabel(gridwidget) + if len(self.gfxcard.getScreens())==1: + label.setText(i18n("Monitor:")) + else: + label.setText(i18n("Monitor #%1:").arg(count)) + grid.addWidget(label,row,1) + + self.monitorlabels.append(QLabel(gridwidget)) + grid.addMultiCellWidget(self.monitorlabels[-1],row,row,2,3) + + # Role pulldown + if len(self.xsetup.getAllScreens())!=1: + label = QLabel(gridwidget) + label.setText(i18n("Role:")) + grid.addWidget(label,row+1,1) + + role_combo = KComboBox(False,gridwidget) + role_combo.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + self.monitor_roles.append(role_combo) + role_combo.insertItem(i18n("Primary (1)")) + role_combo.insertItem(i18n("Secondary (2)")) + if len(self.xsetup.getAllScreens())>=3: + role_combo.insertItem(i18n("Unused")) + self.connect(role_combo,SIGNAL("activated(int)"),self.slotRoleSelected) + grid.addWidget(role_combo,row+1,2) + role_combo.setEnabled(self.xsetup.mayModifyXorgConfig()) + + monitorbutton = QPushButton(gridwidget) + self.monitor_buttons.append(monitorbutton) + monitorbutton.setText(i18n("Configure...")) + self.connect(monitorbutton,SIGNAL("clicked()"),self.slotMonitorConfigureClicked) + grid.addWidget(monitorbutton,row,4) + monitorbutton.setEnabled(self.xsetup.mayModifyXorgConfig()) + row += 2 + count += 1 + + def syncConfig(self): + self._syncGUI() + + def _syncGUI(self): + if self.gfxcard.getGfxCardModel() is not None: + self.setTitle(self.gfxcard.getGfxCardModel().getName()) + self.gfxcardlabel.setText(self.gfxcard.getGfxCardModel().getName()) + + if self.gfxcard.isProprietaryDriver(): + try: + # Displayconfig thinks there is a proprietary driver + self.driverlabel.setText(self.gfxcard.getGfxCardModel().getProprietaryDriver()) + except TypeError, errormsg: + # If there isn't it dies, so try again LP: #198269 + self.driverlabel.setText(self.gfxcard.getGfxCardModel().getDriver()) + else: + self.driverlabel.setText(self.gfxcard.getGfxCardModel().getDriver()) + else: + self.setTitle(i18n("")) + self.gfxcardlabel.setText(i18n("")) + self.driverlabel.setText(i18n("")) + + # Sync the screens and monitors. + for i in range(len(self.gfxcard.getScreens())): + screen = self.gfxcard.getScreens()[i] + + if screen.getMonitorModel() is None: + monitor_name = i18n("") + else: + monitor_name = QString(screen.getMonitorModel().getName()) + if screen.getMonitorAspect()==ModeLine.ASPECT_16_9: + monitor_name.append(i18n(" (widescreen)")) + self.monitorlabels[i].setText(monitor_name) + + if len(self.xsetup.getAllScreens())!=1: + self.monitor_roles[i].setCurrentItem( + {XSetup.ROLE_PRIMARY: 0, + XSetup.ROLE_SECONDARY: 1, + XSetup.ROLE_UNUSED: 2} + [self.xsetup.getScreenRole(screen)]) + + def slotGfxCardConfigureClicked(self): + result = self.gfxcarddialog.do(self.gfxcard.getGfxCardModel(), \ + self.gfxcard.isProprietaryDriver(), self.gfxcard.getDetectedGfxCardModel(), + self.gfxcard.getVideoRam()) + + (new_card_model, new_proprietary_driver, new_video_ram) = result + + if new_card_model is self.gfxcard.getGfxCardModel() and \ + new_proprietary_driver==self.gfxcard.isProprietaryDriver() and \ + new_video_ram==self.gfxcard.getVideoRam(): + return + self.gfxcard.setGfxCardModel(new_card_model) + self.gfxcard.setProprietaryDriver(new_proprietary_driver) + self.gfxcard.setVideoRam(new_video_ram) + self._syncGUI() + self.emit(PYSIGNAL("configChanged"), () ) + + def slotMonitorConfigureClicked(self): + screen_index = self.monitor_buttons.index(self.sender()) + screen_obj = self.gfxcard.getScreens()[screen_index] + + (new_monitor_model,new_aspect) = self.monitordialog.do(screen_obj.getMonitorModel(), + screen_obj.getMonitorAspect(), + self.xsetup.getGfxCards()[0].getScreens()[0] is screen_obj) + + screen_obj.setMonitorModel(new_monitor_model) + screen_obj.setMonitorAspect(new_aspect) + self._syncGUI() + self.emit(PYSIGNAL("configChanged"), () ) + + def slotRoleSelected(self,index): + screen_index = self.monitor_roles.index(self.sender()) + screen_obj = self.gfxcard.getScreens()[screen_index] + self.xsetup.setScreenRole(screen_obj,[XSetup.ROLE_PRIMARY,XSetup.ROLE_SECONDARY,XSetup.ROLE_UNUSED][index]) + + self._syncGUI() + self.emit(PYSIGNAL("configChanged"), () ) diff --git a/displayconfig/driver-options.txt b/displayconfig/driver-options.txt new file mode 100644 index 0000000..100a60e --- /dev/null +++ b/displayconfig/driver-options.txt @@ -0,0 +1,1054 @@ +Driver options: +=================== + +This document contains driver specific options for Xorg and XFree86 and what +effect they are meant to have. These options are only valid in conjuntion with +the specified driver and generally have no meaning / effect when used with +other graphics drivers. + +o fbdev +o fglrx (binary ATi driver) +o i740 +o i810 +o mga +o nv +o nvidia +o radeon +o sis +o vesa + +Driver fbdev: +-------------- +(source: man fbdev) + + Option "fbdev" "string" + The framebuffer device to use. Default: /dev/fb0. + + Option "ShadowFB" "boolean" + Enable or disable use of the shadow framebuffer layer. Default: on. + + Option "Rotate" "string" + Enable rotation of the display. The supported values are "CW" (clock‐ + wise, 90 degrees), "UD" (upside down, 180 degrees) and "CCW" (counter + clockwise, 270 degrees). Implies use of the shadow framebuffer layer. + Default: off. + + +Driver fglrx (ATi binary driver): +---------------------------------- + +Section "Device" + Identifier "Radeon 9600XT - fglrx" + Driver "fglrx" +# ### generic DRI settings ### +# === disable PnP Monitor === + #Option "NoDDC" +# === disable/enable XAA/DRI === + Option "no_accel" "no" + Option "no_dri" "no" +# === misc DRI settings === + Option "mtrr" "off" # disable DRI mtrr mapper, driver has its own code for mtrr +# ### FireGL DDX driver module specific settings ### +# === Screen Management === + Option "DesktopSetup" "0x00000000" + Option "MonitorLayout" "AUTO, AUTO" + Option "IgnoreEDID" "off" + Option "HSync2" "" + Option "VRefresh2" "" + Option "ScreenOverlap" "0" +# === TV-out Management === + Option "NoTV" "yes" + Option "TVStandard" "NTSC-M" + Option "TVHSizeAdj" "0" + Option "TVVSizeAdj" "0" + Option "TVHPosAdj" "0" + Option "TVVPosAdj" "0" + Option "TVHStartAdj" "0" + Option "TVColorAdj" "0" + Option "GammaCorrectionI" "0x00000000" + Option "GammaCorrectionII" "0x00000000" +# === OpenGL specific profiles/settings === + Option "Capabilities" "0x00000000" +# === Video Overlay for the Xv extension === + Option "VideoOverlay" "on" +# === OpenGL Overlay === +# Note: When OpenGL Overlay is enabled, Video Overlay +# will be disabled automatically + Option "OpenGLOverlay" "off" +# === Center Mode (Laptops only) === + Option "CenterMode" "off" +# === Pseudo Color Visuals (8-bit visuals) === + Option "PseudoColorVisuals" "off" +# === QBS Management === + Option "Stereo" "off" + Option "StereoSyncEnable" "1" +# === FSAA Management === + Option "FSAAEnable" "yes" + Option "FSAAScale" "4" + Option "FSAADisableGamma" "no" + Option "FSAACustomizeMSPos" "no" + Option "FSAAMSPosX0" "0.000000" + Option "FSAAMSPosY0" "0.000000" + Option "FSAAMSPosX1" "0.000000" + Option "FSAAMSPosY1" "0.000000" + Option "FSAAMSPosX2" "0.000000" + Option "FSAAMSPosY2" "0.000000" + Option "FSAAMSPosX3" "0.000000" + Option "FSAAMSPosY3" "0.000000" + Option "FSAAMSPosX4" "0.000000" + Option "FSAAMSPosY4" "0.000000" + Option "FSAAMSPosX5" "0.000000" + Option "FSAAMSPosY5" "0.000000" +# === Misc Options === + Option "UseFastTLS" "0" + Option "BlockSignalsOnLock" "on" + Option "UseInternalAGPGART" "yes" + Option "ForceGenericCPU" "no" + BusID "PCI:1:0:0" # vendor=1002, device=4152 + Screen 0 +EndSection + +Driver i740: +------------- +(No info yet) + + +Driver i810: +------------- + + Option "NoAccel" "boolean" + Disable or enable acceleration. Default: acceleration is enabled. + + Option "SWCursor" "boolean" + Disable or enable software cursor. Default: software cursor is dis‐ + able and a hardware cursor is used for configurations where the + hardware cursor is available. + + Option "ColorKey" "integer" + This sets the default pixel value for the YUV video overlay key. + Default: undefined. + + Option "CacheLines" "integer" + This allows the user to change the amount of graphics memory used for + 2D acceleration and video. Decreasing this amount leaves more for 3D + textures. Increasing it can improve 2D performance at the expense of + 3D performance. Default: depends on the resolution, depth, and + available video memory. The driver attempts to allocate at least + enough to hold two DVD-sized YUV buffers by default. The default + used for a specific configuration can be found by examining the Xorg + log file. + + Option "DRI" "boolean" + Disable or enable DRI support. Default: DRI is enabled for configu‐ + rations where it is supported. + + The following driver Options are supported for the i810 and i815 chipsets: + + Option "DDC" "boolean" + Disable or enable DDC support. Default: enabled. + + Option "Dac6Bit" "boolean" + Enable or disable 6-bits per RGB for 8-bit modes. Default: 8-bits + per RGB for 8-bit modes. + + Option "XvMCSurfaces" "integer" + This option enables XvMC. The integer parameter specifies the number + of surfaces to use. Valid values are 6 and 7. Default: XvMC is dis‐ + abled. + + The following driver Options are supported for the 830M and later chipsets: + + Option "VBERestore" "boolean" + Enable or disable the use of VBE save/restore for saving and restor‐ + ing the initial text mode. This is disabled by default because it + causes lockups on some platforms. However, there are some cases + where it must enabled for the correct restoration of the initial + video mode. If you are having a problem with that, try enabling this + option. Default: Disabled. + + Option "VideoKey" "integer" + This is the same as the "ColorKey" option described above. It is + provided for compatibility with most other drivers. + + Option "XVideo" "boolean" + Disable or enable XVideo support. Default: XVideo is enabled for + configurations where it is supported. + + Option "MonitorLayout" "anystr" + Allow different monitor configurations. e.g. "CRT,LFP" will configure + a CRT on Pipe A and an LFP on Pipe B. Regardless of the primary + heads’ pipe it is always configured as ",". Addition‐ + ally you can add different configurations such as "CRT+DFP,LFP" which + would put a digital flat panel and a CRT on pipe A, and a local flat + panel on pipe B. For single pipe configurations you can just specify + the monitors types on Pipe A, such as "CRT+DFP" which will enable the + CRT and DFP on Pipe A. Valid monitors are CRT, LFP, DFP, TV, CRT2, + LFP2, DFP2, TV2 and NONE. NOTE: Some configurations of monitor types + may fail, this depends on the Video BIOS and system configuration. + Default: Not configured, and will use the current head’s pipe and + monitor. + + Option "Clone" "boolean" + Enable Clone mode on pipe B. This will setup the second head as a + complete mirror of the monitor attached to pipe A. NOTE: Video over‐ + lay functions will not work on the second head in this mode. If you + require this, then use the MonitorLayout above and do (as an example) + "CRT+DFP,NONE" to configure both a CRT and DFP on Pipe A to achieve + local mirroring and disable the use of this option. Default: Clone + mode on pipe B is disabled. + + Option "CloneRefresh" "integer" + When the Clone option is specified we can drive the second monitor at + a different refresh rate than the primary. Default: 60Hz. + + Option "CheckLid" "boolean" + On mobile platforms it’s desirable to monitor the lid status and + switch the outputs accordingly when the lid is opened or closed. By + default this option is on, but may incur a very minor performance + penalty as we need to poll a register on the card to check for this + activity. It can be turned off using this option. This only works + with the 830M, 852GM and 855GM systems. Default: enabled. + + Option "FlipPrimary" "boolean" + When using a dual pipe system, it may be preferable to switch the + primary screen to the alternate pipe to display on the other monitor + connection. NOTE: Using this option may cause text mode to be + restored incorrectly, and thus should be used with caution. Default: + disabled. + + Option "DisplayInfo" "boolean" + It has been found that a certain BIOS call can lockup the Xserver + because of a problem in the Video BIOS. The log file will identify if + you are suffering from this problem and tell you to turn this option + off. Default: enabled + + Option "DevicePresence" "boolean" + Tell the driver to perform an active detect of the currently con‐ + nected monitors. This option is useful if the monitor was not con‐ + nected when the machine has booted, but unfortunately it doesn’t + always work and is extremely dependent upon the Video BIOS. Default: + disabled + + +Driver mga: +------------ + + Option "ColorKey" "integer" + Set the colormap index used for the transparency key for the depth 8 + plane when operating in 8+24 overlay mode. The value must be in the + range 2-255. Default: 255. + + Option "HWCursor" "boolean" + Enable or disable the HW cursor. Default: on. + + Option "MGASDRAM" "boolean" + Specify whether G100, G200 or G400 cards have SDRAM. The driver + attempts to auto-detect this based on the card’s PCI subsystem ID. + This option may be used to override that auto-detection. The mga + driver is not able to auto-detect the presence of of SDRAM on sec‐ + ondary heads in multihead configurations so this option will often + need to be specified in multihead configurations. Default: + auto-detected. + + Option "NoAccel" "boolean" + Disable or enable acceleration. Default: acceleration is enabled. + + Option "NoHal" "boolean" + Disable or enable loading the "mga_hal" module. Default: the module + is loaded when available and when using hardware that it supports. + + Option "OverclockMem" + Set clocks to values used by some commercial X Servers (G100, G200 + and G400 only). Default: off. + + Option "Overlay" "value" + Enable 8+24 overlay mode. Only appropriate for depth 24. Recognized + values are: "8,24", "24,8". Default: off. (Note: the G100 is unac‐ + celerated in the 8+24 overlay mode due to a missing hardware fea‐ + ture.) + + Option "PciRetry" "boolean" + Enable or disable PCI retries. Default: off. + + Option "Rotate" "CW" + + Option "Rotate" "CCW" + Rotate the display clockwise or counterclockwise. This mode is unac‐ + celerated. Default: no rotation. + + Option "ShadowFB" "boolean" + Enable or disable use of the shadow framebuffer layer. Default: off. + + Option "SyncOnGreen" "boolean" + Enable or disable combining the sync signals with the green signal. + Default: off. + + Option "UseFBDev" "boolean" + Enable or disable use of on OS-specific fb interface (and is not sup‐ + ported on all OSs). See fbdevhw(4x) for further information. + Default: off. + + Option "VideoKey" "integer" + This sets the default pixel value for the YUV video overlay key. + Default: undefined. + + Option "TexturedVideo" "boolean" + This has XvImage support use the texture engine rather than the video + overlay. This option is only supported by G200 and later chips, and + only at 16 and 32 bits per pixel. Default: off. + + +Driver nv: +----------- +(source: man nv) + Option "HWCursor" "boolean" + Enable or disable the HW cursor. Default: on. + + Option "NoAccel" "boolean" + Disable or enable acceleration. Default: acceleration is enabled. + + Option "UseFBDev" "boolean" + Enable or disable use of on OS-specific fb interface (and is not sup- + ported on all OSs). See fbdevhw(4x) for further information. + Default: off. + + Option "CrtcNumber" "integer" + GeForce2 MX, nForce2, Quadro4, GeForce4, Quadro FX and GeForce FX may + have two video outputs. The driver attempts to autodetect which one + the monitor is connected to. In the case that autodetection picks + the wrong one, this option may be used to force usage of a particular + output. The options are "0" or "1". Default: autodetected. + + Option "FlatPanel" "boolean" + The driver usually can autodetect the presence of a digital flat + panel. In the case that this fails, this option can be used to force + the driver to treat the attached device as a digital flat panel. + With this driver, a digital flat panel will only work if it was + POSTed by the BIOS, that is, the machine must have booted to the + panel. If you have a dual head card you may also need to set the + option CrtcNumber described above. Default: off. + + Option "FPDither" "boolean" + Many digital flat panels (particularly ones on laptops) have only 6 + bits per component color resolution. This option tells the driver to + dither from 8 bits per component to 6 before the flat panel truncates + it. This is only supported in depth 24 on GeForce2 MX, nForce2, + GeForce4, Quadro4, Geforce FX and Quadro FX. Default: off. + + Option "FPScale" "boolean" + Supported only on GeForce4, Quadro4, Geforce FX and Quadro FX. This + option tells to the driver to scale lower resolutions up to the flat + panel's native resolution. Default: on. + + Option "Rotate" "CW" + + Option "Rotate" "CCW" + Rotate the display clockwise or counterclockwise. This mode is unac- + celerated. Default: no rotation. + + Note: The Resize and Rotate extension will be disabled if the Rotate + option is used. + + Option "ShadowFB" "boolean" + Enable or disable use of the shadow framebuffer layer. Default: off. + + + +Driver nvidia.ko (binary driver): +---------------------------------- +(source: ftp://download.nvidia.com/XFree86/Linux-x86/1.0-7174/README.txt) + + Option "NvAGP" "integer" + Configure AGP support. Integer argument can be one of: + 0 : disable agp + 1 : use NVIDIA's internal AGP support, if possible + 2 : use AGPGART, if possible + 3 : use any agp support (try AGPGART, then NVIDIA's AGP) + Please note that NVIDIA's internal AGP support cannot + work if AGPGART is either statically compiled into your + kernel or is built as a module, but loaded into your + kernel (some distributions load AGPGART into the kernel + at boot up). Default: 3 (the default was 1 until after + 1.0-1251). + + Option "NoLogo" "boolean" + Disable drawing of the NVIDIA logo splash screen at + X startup. Default: the logo is drawn. + + Option "RenderAccel" "boolean" + Enable or disable hardware acceleration of the RENDER + extension. THIS OPTION IS EXPERIMENTAL. ENABLE IT AT YOUR + OWN RISK. There is no correctness test suite for the + RENDER extension so NVIDIA can not verify that RENDER + acceleration works correctly. Default: hardware + acceleration of the RENDER extension is disabled. + + Option "NoRenderExtension" "boolean" + Disable the RENDER extension. Other than recompiling + the X-server, XFree86 does not seem to have another way of + disabling this. Fortunatly, we can control this from the + driver so we export this option. This is useful in depth + 8 where RENDER would normally steal most of the default + colormap. Default: RENDER is offered when possible. + + Option "UBB" "boolean" + Enable or disable Unified Back Buffer on any Quadro + based GPUs (Quadro4 NVS excluded); please see + Appendix M for a description of UBB. This option has + no affect on non-Quadro chipsets. Default: UBB is on + for Quadro chipsets. + + Option "NoFlip" "boolean" + Disable OpenGL flipping; please see Appendix M for + a description. Default: OpenGL will swap by flipping + when possible. + + Option "DigitalVibrance" "integer" + Enables Digital Vibrance Control. The range of valid + values are 0 through 255. This feature is not available + on products older than GeForce2. Default: 0. + + Option "Dac8Bit" "boolean" + Most Quadro parts by default use a 10 bit color look + up table (LUT) by default; setting this option to TRUE forces + these graphics chips to use an 8 bit (LUT). Default: + a 10 bit LUT is used, when available. + + Option "Overlay" "boolean" + Enables RGB workstation overlay visuals. This is only + supported on Quadro4 and Quadro FX chips (Quadro4 NVS + excluded) in depth 24. This option causes the server to + advertise the SERVER_OVERLAY_VISUALS root window property + and GLX will report single and double buffered, Z-buffered + 16 bit overlay visuals. The transparency key is pixel + 0x0000 (hex). There is no gamma correction support in + the overlay plane. This feature requires XFree86 version + 4.1.0 or newer (or the Xorg X server). NV17/18 based + Quadros (ie. 500/550 XGL) have additional restrictions, + namely, overlays are not supported in TwinView mode + or with virtual desktops larger than 2046x2047 in any + dimension (eg. it will not work in 2048x1536 modes). + Quadro 7xx/9xx and Quadro FX will offer overlay visuals + in these modes (TwinView, or virtual desktops larger + than 2046x2047), but the overlay will be emulated with + a substantial performance penalty. RGB workstation + overlays are not supported when the Composite extension is + enabled. Default: off. + + Option "CIOverlay" "boolean" + Enables Color Index workstation overlay visuals with + identical restrictions to Option "Overlay" above. + The server will offer visuals both with and without a + transparency key. These are depth 8 PseudoColor visuals. + Enabling Color Index overlays on X servers older than + XFree86 4.3 will force the RENDER extension to be disabled + due to bugs in the RENDER extension in older X servers. + Color Index workstation overlays are not supported when the + Composite extension is enabled. Default: off. + + Option "TransparentIndex" "integer" + When color index overlays are enabled, use this option + to choose which pixel is used for the transparent pixel + in visuals featuring transparent pixels. This value + is clamped between 0 and 255 (Note: some applications + such as Alias's Maya require this to be zero + in order to work correctly). Default: 0. + + Option "OverlayDefaultVisual" "boolean" + When overlays are used, this option sets the default + visual to an overlay visual thereby putting the root + window in the overlay. This option is not recommended + for RGB overlays. Default: off. + + Option "RandRRotation" "boolean" + Enable rotation support for the XRandR extension. This + allows use of the XRandR X server extension for + configuring the screen orientation through rotation. + This feature is supported on GeForce2 or better hardware + using depth 24. This requires an XOrg 6.8.1 or newer + X server. This feature does not work with hardware overlays, + emulated overlays will be used instead at a substantial + performance penalty. See APPENDIX W for details. + Default: off. + + Option "SWCursor" "boolean" + Enable or disable software rendering of the X cursor. + Default: off. + + Option "HWCursor" "boolean" + Enable or disable hardware rendering of the X cursor. + Default: on. + + Option "CursorShadow" "boolean" Enable or disable use of a + shadow with the hardware accelerated cursor; this is a + black translucent replica of your cursor shape at a + given offset from the real cursor. This option is + only available on GeForce2 or better hardware (ie + everything but TNT/TNT2, GeForce 256, GeForce DDR and + Quadro). Default: no cursor shadow. + + Option "CursorShadowAlpha" "integer" + The alpha value to use for the cursor shadow; only + applicable if CursorShadow is enabled. This value must + be in the range [0, 255] -- 0 is completely transparent; + 255 is completely opaque. Default: 64. + + Option "CursorShadowXOffset" "integer" + The offset, in pixels, that the shadow image will be + shifted to the right from the real cursor image; only + applicable if CursorShadow is enabled. This value must + be in the range [0, 32]. Default: 4. + + Option "CursorShadowYOffset" "integer" + The offset, in pixels, that the shadow image will be + shifted down from the real cursor image; only applicable + if CursorShadow is enabled. This value must be in the + range [0, 32]. Default: 2. + + Option "ConnectedMonitor" "string" + Allows you to override what the NVIDIA kernel module + detects is connected to your video card. This may + be useful, for example, if you use a KVM (keyboard, + video, mouse) switch and you are switched away when + X is started. In such a situation, the NVIDIA kernel + module cannot detect what display devices are connected, + and the NVIDIA X driver assumes you have a single CRT. + + Valid values for this option are "CRT" (cathode ray + tube), "DFP" (digital flat panel), or "TV" (television); + if using TwinView, this option may be a comma-separated + list of display devices; e.g.: "CRT, CRT" or "CRT, DFP". + + NOTE: anything attached to a 15 pin VGA connector is + regarded by the driver as a CRT. "DFP" should only be + used to refer to flatpanels connected via a DVI port. + + Default: string is NULL. + + Option "UseEdidFreqs" "boolean" + This option causes the X server to use the HorizSync + and VertRefresh ranges given in a display device's EDID, + if any. EDID provided range information will override + the HorizSync and VertRefresh ranges specified in the + Monitor section. If a display device does not provide an + EDID, or the EDID does not specify an hsync or vrefresh + range, then the X server will default to the HorizSync + and VertRefresh ranges specified in the Monitor section. + + Option "IgnoreEDID" "boolean" + Disable probing of EDID (Extended Display Identification + Data) from your monitor. Requested modes are compared + against values gotten from your monitor EDIDs (if any) + during mode validation. Some monitors are known to lie + about their own capabilities. Ignoring the values that + the monitor gives may help get a certain mode validated. + On the other hand, this may be dangerous if you do not + know what you are doing. Default: Use EDIDs. + + Option "NoDDC" "boolean" + Synonym for "IgnoreEDID" + + Option "FlatPanelProperties" "string" + Requests particular properties of any connected flat + panels as a comma-separated list of property=value pairs. + Currently, the only two available properties are 'Scaling' + and 'Dithering'. The possible values for 'Scaling' are: + 'default' (the driver will use whatever scaling state + is current), 'native' (the driver will use the flat + panel's scaler, if it has one), 'scaled' (the driver + will use the NVIDIA scaler, if possible), 'centered' + (the driver will center the image, if possible), + and 'aspect-scaled' (the driver will scale with the + NVIDIA scaler, but keep the aspect ratio correct). + The possible values for 'Dithering' are: 'default' + (the driver will decide when to dither), 'enabled' (the + driver will always dither when possible), and 'disabled' + (the driver will never dither). If any property is not + specified, it's value shall be 'default'. An example + properties string might look like: + + "Scaling = centered, Dithering = enabled" + + Option "UseInt10Module" "boolean" + Enable use of the X Int10 module to soft-boot all + secondary cards, rather than POSTing the cards through + the NVIDIA kernel module. Default: off (POSTing is done + through the NVIDIA kernel module). + + Option "TwinView" "boolean" + Enable or disable TwinView. Please see APPENDIX I for + details. Default: TwinView is disabled. + + Option "TwinViewOrientation" "string" + Controls the relationship between the two display devices + when using TwinView. Takes one of the following values: + "RightOf" "LeftOf" "Above" "Below" "Clone". Please see + APPENDIX I for details. Default: string is NULL. + + Option "SecondMonitorHorizSync" "range(s)" + This option is like the HorizSync entry in the Monitor + section, but is for the second monitor when using + TwinView. Please see APPENDIX I for details. Default: + none. + + Option "SecondMonitorVertRefresh" "range(s)" + This option is like the VertRefresh entry in the Monitor + section, but is for the second monitor when using + TwinView. Please see APPENDIX I for details. Default: + none. + + Option "MetaModes" "string" + This option describes the combination of modes to use + on each monitor when using TwinView. Please see APPENDIX + I for details. Default: string is NULL. + + Option "NoTwinViewXineramaInfo" "boolean" + When in TwinView, the NVIDIA X driver normally provides + a Xinerama extension that X clients (such as window + managers) can use to to discover the current TwinView + configuration. Some window mangers can get confused by + this information, so this option is provided to disable + this behavior. Default: TwinView Xinerama information + is provided. + + Option "TVStandard" "string" + Please see (app-j) APPENDIX J: CONFIGURING TV-OUT. + + Option "TVOutFormat" "string" + Please see (app-j) APPENDIX J: CONFIGURING TV-OUT. + + Option "TVOverScan" "Decimal value in the range 0.0 to 1.0" + Valid values are in the range 0.0 through 1.0; please see + (app-j) APPENDIX J: CONFIGURING TV-OUT. + + Option "Stereo" "integer" + Enable offering of quad-buffered stereo visuals on Quadro. + Integer indicates the type of stereo glasses being used: + + 1 - DDC glasses. The sync signal is sent to the glasses + via the DDC signal to the monitor. These usually + involve a passthrough cable between the monitor and + video card. + + 2 - "Blueline" glasses. These usually involve + a passthrough cable between the monitor and video + card. The glasses know which eye to display based + on the length of a blue line visible at the bottom + of the screen. When in this mode, the root window + dimensions are one pixel shorter in the Y dimension + than requested. This mode does not work with virtual + root window sizes larger than the visible root window + size (desktop panning). + + 3 - Onboard stereo support. This is usually only found + on professional cards. The glasses connect via a + DIN connector on the back of the video card. + + 4 - TwinView clone mode stereo (aka "passive" stereo). + On video cards that support TwinView, the left eye + is displayed on the first display, and the right + eye is displayed on the second display. This is + normally used in conjuction with special projectors + to produce 2 polarized images which are then viewed + with polarized glasses. To use this stereo mode, + you must also configure TwinView in clone mode with + the same resolution, panning offset, and panning + domains on each display. + + Stereo is only available on Quadro cards. Stereo + options 1, 2, and 3 (aka "active" stereo) may be used + with TwinView if all modes within each metamode have + identical timing values. Please see (app-l) APPENDIX + L: PROGRAMMING MODES for suggestions on making sure the + modes within your metamodes are identical. The identical + modeline requirement is not necessary for Stereo option 4 + ("passive" stereo). Currently, stereo operation may + be "quirky" on the original Quadro (NV10) chip and + left-right flipping may be erratic. We are trying + to resolve this issue for a future release. Default: + Stereo is not enabled. + + Stereo options 1, 2, and 3 (aka "active" stereo) are not + supported on Digital Flatpanels. + + Option "AllowDFPStereo" "boolean" + By default, the NVIDIA X driver performs a check which + disables active stereo (stereo options 1, 2, and 3) + if the X screen is driving a DFP. The "AllowDFPStereo" + option bypasses this check. + + Option "NoBandWidthTest" "boolean" + As part of mode validation, the X driver tests if a + given mode fits within the hardware's memory bandwidth + constraints. This option disables this test. Default: + the memory bandwidth test is performed. + + Option "IgnoreDisplayDevices" "string" + This option tells the NVIDIA kernel module to completely + ignore the indicated classes of display devices when + checking what display devices are connected. You may + specify a comma-separated list containing any of "CRT", + "DFP", and "TV". + + For example: + + Option "IgnoreDisplayDevices" "DFP, TV" + + will cause the NVIDIA driver to not attempt to detect + if any flatpanels or TVs are connected. + + This option is not normally necessary; however, some video + BIOSes contain incorrect information about what display + devices may be connected, or what i2c port should be + used for detection. These errors can cause long delays + in starting X. If you are experiencing such delays, you + may be able to avoid this by telling the NVIDIA driver to + ignore display devices which you know are not connected. + + NOTE: anything attached to a 15 pin VGA connector is + regarded by the driver as a CRT. "DFP" should only be + used to refer to flatpanels connected via a DVI port. + + Option "MultisampleCompatibility" "boolean" + Enable or disable the use of separate front and back + multisample buffers. This will consume more memory + but is necessary for correct output when rendering to + both the front and back buffers of a multisample or + FSAA drawable. This option is necessary for correct + operation of SoftImage XSI. Default: a singlemultisample + buffer is shared between the front and back buffers. + + Option "NoPowerConnectorCheck" "boolean" + The NVIDIA X driver will abort X server initialization + if it detects that a GPU that requires an external power + connector does not have an external power connector + plugged in. This option can be used to bypass this test. + Default: the power connector test is performed. + + Option "XvmcUsesTextures" "boolean" + Forces XvMC to use the 3D engine for XvMCPutSurface + requests rather than the video overlay. Default: video + overlay is used when available. + + Option "AllowGLXWithComposite" "boolean" + Enables GLX even when the Composite X extension is loaded. + ENABLE AT YOUR OWN RISK. OpenGL applications will not + display correctly in many circumstances with this setting + enabled. Default: GLX is disabled when Composite is + loaded. + + Option "ExactModeTimingsDVI" "boolean" + Forces the initialization of the X server with the exact + timings specified in the ModeLine. Default: For DVI + devices, the X server inilializes with the closest mode in + the EDID list. + + +Driver radeon: +--------------- +(source: manpage radeon) + + Option "SWcursor" "boolean" + Selects software cursor. The default is off. + + Option "NoAccel" "boolean" + Enables or disables all hardware acceleration. + The default is to enable hardware acceleration. + + Option "Dac6Bit" "boolean" + Enables or disables the use of 6 bits per color component when in 8 + bpp mode (emulates VGA mode). By default, all 8 bits per color com- + ponent are used. + The default is off. + + Option "VideoKey" "integer" + This overrides the default pixel value for the YUV video overlay key. + The default value is 0x1E. + + Option "UseFBDev" "boolean" + Enable or disable use of an OS-specific framebuffer device interface + (which is not supported on all OSs). MergedFB does not work when + this option is in use. See fbdevhw(4x) for further information. + The default is off. + + Option "AGPMode" "integer" + Set AGP data transfer rate. (used only when DRI is enabled) + 1 -- x1 (default) + 2 -- x2 + 4 -- x4 + others -- invalid + + Option "AGPFastWrite" "boolean" + Enable AGP fast write. + (used only when DRI is enabled) + The default is off. + + Option "BusType" "string" + Used to replace previous ForcePCIMode option. Should only be used + when driver’s bus detection is incorrect or you want to force a AGP + card to PCI mode. Should NEVER force a PCI card to AGP bus. + PCI -- PCI bus + AGP -- AGP bus + PCIE -- PCI Express (falls back to PCI at present) + (used only when DRI is enabled) + The default is auto detect. + + Option "DDCMode" "boolean" + Force to use the modes queried from the connected monitor. + The default is off. + + Option "DisplayPriority" "string" + Used to prevent flickering or tearing problem caused by display + buffer underflow. + AUTO -- Driver calculated (default). + BIOS -- Remain unchanged from BIOS setting. + Use this if the calculation is not correct + for your card. + HIGH -- Force to the highest priority. + Use this if you have problem with above options. + This may affect performence slightly. + The default value is AUTO. + + Option "MonitorLayout" "string" + This option is used to overwrite the detected monitor types. This is + only required when driver makes a false detection. The possible mon- + itor types are: + NONE -- Not connected + CRT -- Analog CRT monitor + TMDS -- Desktop flat panel + LVDS -- Laptop flat panel + This option can be used in following format: + Option "MonitorLayout" "[type on primary], [type on secondary]" + For example, Option "MonitorLayout" "CRT, TMDS" + + Primary/Secondary head for dual-head cards: + (when only one port is used, it will be treated as the primary + regardless) + Primary head: + DVI port on DVI+VGA cards + LCD output on laptops + Internal TMDS port on DVI+DVI cards + Secondary head: + VGA port on DVI+VGA cards + VGA port on laptops + External TMDS port on DVI+DVI cards + + The default value is undefined. + + Option "MergedFB" "boolean" + This enables merged framebuffer mode. In this mode you have a single + shared framebuffer with two viewports looking into it. It is similar + to Xinerama, but has some advantages. It is faster than Xinerama, + the DRI works on both heads, and it supports clone modes. + Merged framebuffer mode provides two linked viewports looking into a + single large shared framebuffer. The size of the framebuffer is + determined by the Virtual keyword defined on the Screen section of + your XF86Config file. It works just like regular virtual desktop + except you have two viewports looking into it instead of one. + For example, if you wanted a desktop composed of two 1024x768 view- + ports looking into a single desktop you would create a virtual desk- + top of 2048x768 (left/right) or 1024x1536 (above/below), e.g., + Virtual 2048 768 or Virtual 1024 1536 + The virtual desktop can be larger than larger than the size of the + viewports looking into it. In this case the linked viewports will + scroll around in the virtual desktop. Viewports with different sizes + are also supported (e.g., one that is 1024x768 and one that is + 640x480). In this case the smaller viewport will scroll relative to + the larger one such that none of the virtual desktop is inaccessable. + If you do not define a virtual desktop the driver will create one + based on the orientation of the heads and size of the largest defined + mode in the display section that is supported on each head. + The relation of the viewports in specified by the CRT2Position + Option. The options are Clone , LeftOf , RightOf , Above , and + Below. MergedFB is enabled by default if a monitor is detected on + each output. If no position is given it defaults to clone mode (the + old clone options are now deprecated, also, the option OverlayOnCRTC2 + has been replaced by the Xv attribute XV_SWITCHCRT; the overlay can + be switched to CRT1 or CRT2 on the fly in clone mode). + The maximum framebuffer size that the 2D acceleration engine can han- + dle is 8192x8192. The maximum framebuffer size that the 3D engine + can handle is 2048x2048. + Note: Page flipping does not work well in certain configurations with + MergedFB. If you see rendering errors or other strange behavior, + disable page flipping. Also MergedFB is not compatible with the UseF- + BDev option. + The default value is undefined. + + Option "CRT2HSync" "string" + Set the horizontal sync range for the secondary monitor. It is not + required if a DDC-capable monitor is connected. + For example, Option "CRT2HSync" "30.0-86.0" + The default value is undefined. + + Option "CRT2VRefresh" "string" + Set the vertical refresh range for the secondary monitor. It is not + required if a DDC-capable monitor is connected. + For example, Option "CRT2VRefresh" "50.0-120.0" + The default value is undefined. + + Option "CRT2Position" "string" + Set the relationship of CRT2 relative to CRT1. Valid options are: + Clone , LeftOf , RightOf , Above , and Below + For example, Option "CRT2Position" "RightOf" + The default value is Clone. + + Option "MetaModes" "string" + MetaModes are mode combinations for CRT1 and CRT2. If you are using + merged frame buffer mode and want to change modes (CTRL-ALT-+/-), + these define which modes will be switched to on CRT1 and CRT2. The + MetaModes are defined as CRT1Mode-CRT2Mode (800x600-1024x768). Modes + listed individually (800x600) define clone modes, that way you can + mix clone modes with non-clone modes. Also some programs require + "standard" modes. + Note: Any mode you use in the MetaModes must be defined in the + Screen section of your XF86Config file. Modes not defined there will + be ignored when the MetaModes are parsed since the driver uses them + to make sure the monitors can handle those modes. If you do not + define a MetaMode the driver will create one based on the orientation + of the heads and size of the largest defined mode in the display sec- + tion that is supported on each head. + Modes 1024x768 800x600 640x480 + For example, Option "MetaModes" "1024x768-1024x768 800x600-1024x768 + 640x480-800x600 800x600" + The default value is undefined. + + Option "OverlayOnCRTC2" "boolean" + Force hardware overlay to clone head. + The default value is off. + + Option "NoMergedXinerama" "boolean" + Since merged framebuffer mode does not use Xinerama, apps are not + able to intelligently place windows. Merged framebuffer mode pro- + vides its own pseudo-Xinerama. This allows Xinerama compliant appli- + cations to place windows appropriately. There are some caveats. + Since merged framebuffer mode is able to change relative screen sizes + and orientations on the fly, as well has having overlapping view- + ports, pseudo-Xinerama, might not always provide the right hints. + Also many Xinerama compliant applications only query Xinerama once at + startup; if the information changes, they may not be aware of the + change. If you are already using Xinerama (e.g., a single head card + and a dualhead card providing three heads), pseudo-Xinerama will be + disabled. + This option allows you turn off the driver provided pseudo-Xinerama + extension. + The default value is FALSE. + + Option "MergedXineramaCRT2IsScreen0" "boolean" + By default the pseudo-Xinerama provided by the driver makes the left- + most or bottom head Xinerama screen 0. Certain Xinerama-aware appli- + cations do special things with screen 0. To change that behavior, + use this option. + The default value is undefined. + + Option "MergedDPI" "string" + The driver will attempt to figure out an appropriate DPI based on the + DDC information and the orientation of the heads when in merged + framebuffer mode. If this value does not suit you, you can manually + set the DPI using this option. + For example, Option "MergedDPI" "100 100" + The default value is undefined. + + Option "IgnoreEDID" "boolean" + Do not use EDID data for mode validation, but DDC is still used for + monitor detection. This is different from NoDDC option. + The default value is off. + + Option "PanelSize" "string" + Should only be used when driver cannot detect the correct panel size. + Apply to both desktop (TMDS) and laptop (LVDS) digital panels. When + a valid panel size is specified, the timings collected from DDC and + BIOS will not be used. If you have a panel with timings different + from that of a standard VESA mode, you have to provide this informa- + tion through the Modeline. + For example, Option "PanelSize" "1400x1050" + The default value is none. + + Option "PanelOff" "boolean" + Disable panel output. + The default value is off. + + Option "EnablePageFlip" "boolean" + Enable page flipping for 3D acceleration. This will increase perfor- + mance but not work correctly in some rare cases, hence the default is + off. + Note: Page flipping does not work well in certain configurations with + MergedFB. If you see rendering errors or other strange behavior, + disable page flipping. + + Option "ForceMinDotClock" "frequency" + Override minimum dot clock. Some Radeon BIOSes report a minimum dot + clock unsuitable (too high) for use with television sets even when + they actually can produce lower dot clocks. If this is the case you + can override the value here. Note that using this option may damage + your hardware. You have been warned. The frequency parameter may be + specified as a float value with standard suffixes like "k", "kHz", + "M", "MHz". + + Option "RenderAccel" "boolean" + Enables or disables hardware Render acceleration. This driver does + not support component alpha (subpixel) rendering. It is only sup- + ported on Radeon series up to and including 9200 (9500/9700 and newer + unsupported). The default is to enable Render acceleration. + + Option "SubPixelOrder" "string" + Force subpixel order to specified order. Subpixel order is used for + subpixel decimation on flat panels. + NONE -- No subpixel (CRT like displays) + RGB -- in horizontal RGB order (most flat panels) + BGR -- in horizontal BGR order (some flat panels) + + This option is intended to be used in following cases: + 1. The default subpixel order is incorrect for your panel. + 2. Enable subpixel decimation on analog panels. + 3. Adjust to one display type in dual-head clone mode setup. + 4. Get better performance with Render acceleration on digital panels + (use NONE setting). + The default is NONE for CRT, RGB for digital panels + + Option "DynamicClocks" "boolean" + Enable dynamic clock scaling. The on-chip clocks will scale dynami- + cally based on usage. This can help reduce heat and increase battery + life by reducing power usage. Some users report reduced 3D prefor- + mance with this enabled. The default is off. + + + +Driver sis: +------------ + + Option "NoAccel" "boolean" + Disable or enable 2D acceleration. Default: acceleration is enabled. + + Option "HWCursor" "boolean" + Enable or disable the HW cursor. Default: HWCursor is on. + + Option "SWCursor" "boolean" + The opposite of HWCursor. Default: SWCursor is off. + + Option "Rotate" "CW" + Rotate the display clockwise. This mode is unaccelerated, and uses + the Shadow Frame Buffer layer. Using this option disables the Resize + and Rotate extension (RandR). Default: no rotation. + + Option "Rotate" "CCW" + Rotate the display counterclockwise. This mode is unaccelerated, and + uses the Shadow Frame Buffer layer. Using this option disables the + Resize and Rotate extension (RandR). Default: no rotation. + + Option "ShadowFB" "boolean" + Enable or disable use of the shadow framebuffer layer. Default: + Shadow framebuffer is off. + + Option "CRT1Gamma" "boolean" + Enable or disable gamma correction. Default: Gamma correction is on. + + + +Driver vesa: +------------- +(source: man vesa) + + Option "ShadowFB" "boolean" + Enable or disable use of the shadow framebuffer layer. Default: on. + This option is recommended for performance reasons. + diff --git a/displayconfig/energy.py b/displayconfig/energy.py new file mode 100644 index 0000000..5c3f681 --- /dev/null +++ b/displayconfig/energy.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'energy.ui' +# +# Created: Fri Jun 24 03:45:58 2005 +# by: The PyQt User Interface Compiler (pyuic) 3.14.1 +# +# WARNING! All changes made in this file will be lost! + + +from qt import * + +class DPMSTab(QDialog): + def __init__(self,parent = None,name = None,modal = 0,fl = 0): + QDialog.__init__(self,parent,name,modal,fl) + + if not name: + self.setName("DPMSTab") + + + DPMSTabLayout = QVBoxLayout(self,11,6,"DPMSTabLayout") + + titlelayout = QHBoxLayout(None,0,6,"titlelayout") + topspacer = QSpacerItem(221,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + titlelayout.addItem(topspacer) + + self.energystarpix = QLabel(self,"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("energystar.png")) + self.energystarpix.setScaledContents(1) + titlelayout.addWidget(self.energystarpix) + DPMSTabLayout.addLayout(titlelayout) + + self.screensavergroup = QGroupBox(self,"screensavergroup") + self.screensavergroup.setCheckable(1) + self.screensavergroup.setColumnLayout(0,Qt.Vertical) + self.screensavergroup.layout().setSpacing(6) + self.screensavergroup.layout().setMargin(11) + screensavergroupLayout = QHBoxLayout(self.screensavergroup.layout()) + screensavergroupLayout.setAlignment(Qt.AlignTop) + spacer4 = QSpacerItem(101,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + screensavergroupLayout.addItem(spacer4) + + self.screensavertext = QLabel(self.screensavergroup,"screensavertext") + screensavergroupLayout.addWidget(self.screensavertext) + + self.screensavercombo = QComboBox(0,self.screensavergroup,"screensavercombo") + screensavergroupLayout.addWidget(self.screensavercombo) + DPMSTabLayout.addWidget(self.screensavergroup) + + self.dpmsgroup = QGroupBox(self,"dpmsgroup") + self.dpmsgroup.setCheckable(1) + self.dpmsgroup.setColumnLayout(0,Qt.Vertical) + self.dpmsgroup.layout().setSpacing(6) + self.dpmsgroup.layout().setMargin(11) + dpmsgroupLayout = QHBoxLayout(self.dpmsgroup.layout()) + dpmsgroupLayout.setAlignment(Qt.AlignTop) + spacer4_2 = QSpacerItem(244,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + dpmsgroupLayout.addItem(spacer4_2) + + self.dpmstext = QLabel(self.dpmsgroup,"dpmstext") + dpmsgroupLayout.addWidget(self.dpmstext) + + self.dpmscombo = QComboBox(0,self.dpmsgroup,"dpmscombo") + dpmsgroupLayout.addWidget(self.dpmscombo) + DPMSTabLayout.addWidget(self.dpmsgroup) + bottomspacer = QSpacerItem(51,160,QSizePolicy.Minimum,QSizePolicy.Expanding) + DPMSTabLayout.addItem(bottomspacer) + + self.languageChange() + + self.resize(QSize(508,372).expandedTo(self.minimumSizeHint())) + self.clearWState(Qt.WState_Polished) + + + def languageChange(self): + self.setCaption(self.__tr("Display power saving")) + self.screensavergroup.setTitle(self.__tr("Enable screensaver")) + self.screensavertext.setText(self.__tr("Start screensaver after")) + self.dpmsgroup.setTitle(self.__tr("Enable display powermanagement")) + self.dpmstext.setText(self.__tr("Switch display off after")) + + + def __tr(self,s,c = None): + return qApp.translate("DPMSTab",s,c) diff --git a/displayconfig/energystar.png b/displayconfig/energystar.png new file mode 100644 index 0000000000000000000000000000000000000000..75611401dd4cef25f1fd9d07def04e8293f162f2 GIT binary patch literal 24160 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 v<|XsAY+TiNZnlGaf%+^J6S>sp{R=Pv +# option parsing and database comparison by Fred New +# ini parsing completely rewritten by Matt Domsch 2006 +# +# Copyright 2002 Red Hat, Inc. +# Copyright 2006 Dell, Inc. +# Copyright 2007 Sebastian Heinlein +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# You should have received a copy of the GNU Library Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" +Provides an importer for Microsoft Windows monitor descriptions + +The code can be used as a python module for or as a script to add new monitor +definitions to a monitor database. + +In code example: Read the list of monitors from an inf file. + +import infimport +monitors = infimport.get_monitors_from_inf(PATH) + +Script example: To check for monitors of an inf file that are not yet in the database. + +./infimport.py MONITORS.inf /usr/share/hwdata/MonitorsDB +""" + +import sys +import string +import re +import ConfigParser +import os + +import logging + +logging.basicConfig() +log = logging.getLogger("infimport") +#log.setLevel(logging.DEBUG) +log.setLevel(logging.INFO) + +# this is a class to deal with various file line endings and leading whitespace +# converts all \r line endings to \n. +# It also strips leading whitespace. +# NOTE: be sure to always return _something_, even if it is just "\n", or we +# break the file API. (nothing == eof) +class myFile(object): + def __init__(self, *args): + self.fd = open(*args) + + def close(self): + return self.fd.close() + + def readline(self, *args): + line = self.fd.readline(*args) + line = line.replace('\r', '\n') + line = line.replace('\n\n', '\n') + line = line.lstrip(" \t") + return line + + +# we will use this to override default option parsing in ConfigParser to handle +# Microsoft-style "INI" files. (Which do not necessarily have " = value " after +# the option name +OPTCRE = re.compile( + r'(?P