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'(?PNy~N`kLfk<5J?Uv!J=iv8UV(Y|!yy_wF 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 QjjnX3OWo?0AOZjYJK{RQ@c;k- literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam14.png b/kde/displayconfig/pics/gammapics/MGam14.png new file mode 100644 index 0000000000000000000000000000000000000000..2cff6881d1fc5235a173f02e72f9be6db50e3cd3 GIT binary patch literal 489 zcmeAS@N?&q;$mQ6;Pv!y31VPiGz79US(t(3f`^=?K*}J%C&U#>DK;uEBolHXA&c$qLX^IO!tM6Bgo{?7Rf931< zr)N!aqb|-ATDqMvZAN07B#gLr_03WDuNTyNcYN>^|B^XdHs|LGyB~L6UYyteYWMl* z%Wr;MbuT@hzVNiSeCS6`lf{x}Gu(`4ZJjkYvhD0U-O$bMr$x@@@oYbww)=eBs+(IM zW_6`KZ`*aVb79uElm!sQ7kkoR`e4j&`LlDSU*+Fk4%D+R@$mlK&-M)vMP|#>lvn7v zU3ki|DzRnR%{>QrjL$sdOsmgVJNr5LhgFGj_rl7*pAx2hSR5!k`(&0&rFX~A2)1K7 zqQA{fr!UVr>8tv^ThU!7=!e1D(Os)z4*}Q$iB}B2U(1 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam16.gif b/kde/displayconfig/pics/gammapics/MGam16.gif new file mode 100644 index 0000000000000000000000000000000000000000..4dd9af3c86109d0e1b89b854582df22cc17fcd7a GIT binary patch literal 1877 zcmV-b2del-Nk%w1VKD(|0J8u900000Yinx&Yinx&Yinx&Yo==erfX}a0H$kerT}ZE zYo-8erfUGEYip*arT_o{EC2ui05Jh+000F3kjP1^y*TU5yZ>M)j$~<`XsWJk>%MR- z&vb3yc&_h!@BhG{a7Zi~kI1BQ$!t2G(5Q4uty-_xtai)odcWYXcuX#v&*-#z&2GEj z@VIMDijE{;H0tAzll$Vy7n46lNoS&K%2Lz*~ zq^G8-sH>{2tgosS00gtOw70goxVyT&yuZ3R0mH<_#>dFX%FD?V3eV8d($mz{*4Nb( z1KZr)-rwNi;^W~I4(I6U>g(+7?(gk64fFK%_V@Vt`uq750{;L53LHqVpuvL$DI8eH zu%W|;5F<*Q7?A>iix@L%+{m$`$B+IUg)tjRvgAl-*ix!onNl0MmoQ_#GzYJy&6_pl z@!QF>=T3hJg9;rQR3XKqNRuA52(qcur%gqgoJy4>N|so&s&px{t5=vbaf%&lQ>V|f zXn6u9%C@agqe|n-MamSa-MdbuR^7{&*j25714HBbwXk7sVaY09jLvM@$B^l*bxXN2 zK)7==Yi3v%ujkJg_4XY-^{?R5SP36q-8C`e*J2$5riLC$03wQPwppTz z60k_)oOH%$qXaqT8Pbj;5$WfjLRxhsHc%F7sFPbwNe-8cHmW69Uxq2^Ib+^)=A~z* zNl8#{cIqaaq#RY}sCC{+i=L>4#wRm?whE}A63BPxtcE5kL4c0t+9;$FOzLZ;_FyXP zhMKPF>9KDXwW6q`{x&P4si{UQ-Z8FLJ0z?G$V%(BP1;HTuDXVsC9eedIw`RA46E*i z#d3=5yCQ-rr?d372yL{ePHT*{{Qiiow%i6B>tJ$M3R=VkBDn8KaojMK??4!y)`(3r${wMC#=+1fA-;V7&qreAG*6_p=i@KVs zBafo;4>4bA)p$PtTlCTsN&Pg|AIf_;+dJC5_pwd5Q}|{woi4Ak87zO?8(g{gCp`c01AxUF-=-u+!1J{(R5U{%jhx58ysT;&5A@Ug zq(Q+#S@0Ve%wXs?$ieS%z$p4?hS8{E1+iDMEGA5YT=bzA{|Ckl z4G|z@G?5t@g2p9w$7*W~&J&^7o<_RIk&T379Qi}Z{#25ZSH$9iHpw7PZgPuxbVt&t zLmlg2=ad7{4tG}h5bq>1Jc}fbERSIvU+2Nz6$gk(J1F zh$~;|k-N>3G_|~?G!>A`U0So3zKq8YgUQW$46~T0RAn+*xlCqq#F@|ROEjlx%W7Iv zm)OkaI)K^D$9xl<*AeG9xl>MaY6P9>+{-%Ic^r4Xb2;&pXFBLvPqf?Eo;u>EJ`JiPJ_L%Qdg7=?O{ynW1dFD+;;BxJsw-{;jH>$Ls#eXaF9N~Uu6p&W zU=6ES$4b_+n)R$`O{-eh%GS2J^{sG?YYj4zgNs<;qH^tnT~Bobyz+IgdEEnG>(ah+ zE^s4=l!adh8(6`@)FSQUt7PrE*UH*LvUtL5VISMrJXjW{bhT_`MLSu)YWA<3?W`RD zOW9huHnOONZEH_^+S#hsv!H#gZADAk*)q1YvAr#BSsR+(z81KLy=!g@5i8v0I`_HI PjjnX3OWhJg0000xTA{!% literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam16.png b/kde/displayconfig/pics/gammapics/MGam16.png new file mode 100644 index 0000000000000000000000000000000000000000..59dc49303013bcf3aa161d488270415b60355b20 GIT binary patch literal 485 zcmeAS@N?&q;$mQ6;Pv!y31VPiGz79US(t(3f`^=?K*}J%C&U#>DK;uEBork=1_|8hcXcxWvb8`H~)8|qu*Ka!c z{`Bl+bGIy-J89`w#rKH!jahfC z-vPO|0?l?8h@8DA16J#1JPW1`!TguIxz_z>?(a*1Ce~$der(VE$9@4~c2$;HXXyqQyOLZsYv5JA6Zk z(&^ybvzxs9$|CGfid`&vx^1;3N95{>H~sbBCYiFYotn0ifAdoBqdrlyZ{7`w_09Zy g<%hMwZOzU3XYb^jJMNtS7Z~6Sp00i_>zopr0O}FbBme*a literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam18.gif b/kde/displayconfig/pics/gammapics/MGam18.gif new file mode 100644 index 0000000000000000000000000000000000000000..a73c8324961e5f0dcadb729dcbf91fca3bf27800 GIT binary patch literal 1877 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam20.gif b/kde/displayconfig/pics/gammapics/MGam20.gif new file mode 100644 index 0000000000000000000000000000000000000000..2e98232a495656f89d78230f7dac8f0c8340d54f GIT binary patch literal 1877 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam24.gif b/kde/displayconfig/pics/gammapics/MGam24.gif new file mode 100644 index 0000000000000000000000000000000000000000..c7bde1117958d962e287381b4f010e81916f5429 GIT binary patch literal 1883 zcmV-h2c-B%Nk%w1VKD(|0J8u900000kB^T4kB^T4kB^T4kH3!qzmJc<0KboqzW|TF zkG}wqzmEXFkB`5M)j$~<`XsWJk>%MR- z&vb3yc&_h!@BhG{a7Zi~kI1BQ$!t2G(5Q4uty-_xtai)odcWYXcuX#v&*-#z&2GEj z@VIMDijE{;H0tAzll$Vy7n46lNoS&K%2Lz*~ zq^G8-sH>{2tgosS00gtOw70goxVyT&yuZ3R0mH<_#>dFX%FD?V3eV8d($mz{*4Nb( z1KZr)-rwNi;^W~I4(I6U>g(+7?(gk64fFK%_V@Vt`uq750{;L53LHqVpuvL$DI8eH zu%W|;5F<*Q7?A>iix@L%+{m$`$B+IUg)tjRvgAl-*ix!onNl0MmoQ_#GzYJy&6_pl z@!QF>=T3hJg9;rQR3XKqNRuA52(qcur%gqgoJy4>N|so&s&px{t5=vbaf%&lQ>V|f zXn6u9%C@agqe|n-MamSa-MdbuR^7{&*j25714HBbwXk7sVaY09jLvM@$B^l*bxXN2 zK)7==Yi3v%ujkJg_4XY-^{?R5SP36q-8C`e*J2$5riLC$03wQPwppTz z60k_)oOH%$qXaqT8Pbj;5$WfjLRxhsHc%F7sFPbwNe-8cHmW69Uxq2^Ib+^)=A~z* zNl8#{cIqaaq#RY}sCC{+i=L>4#wRm?whE}A63BPxtcE5kL4c0t+9;$FOzLZ;_FyXP zhMKPF>9KDXwW6q`{x&P4si{UQ-Z8FLJ0z?G$V%(BP1;HTuDXVsC9eedIw`RA46E*i z#d3=5yCQ-rr?d372yL{ePHT*{{Qiiow%i6B>tJ$M3R=VkBDn8KaojMK??4!y)`(3r${wMC#=+1fA-;V7&qreAG*6_p=i@KVs zBafo;4>4bA)p$PtTlCTsN&Pg|AIf_;+dJC5_pwd5Q}|{woi4Ak87zO?8(g{gCp`c01AxUF-=-u+!1J{(R5U{%jhx58ysT;&5A@Ug zq(Q+#S@0Ve%wXs?$ieS%z$p4?hS8{E1+iDMEGA5YT=bzA{|Ckl z4G|z@G?5t@g2p9w$7*W~&J&^7o<_RIk&T379Qi}Z{#25ZSH$9iHpw7PZgPuxbVt&t zLmlg2=ad7{4tG}h5bq>1Jc}fbERSIvU+2Nz6$gk(J1F zh$~;|k-N>3G_|~?G!>A`U0So3zKq8YgUQW$46~T0RAn+*xlCqq#F@|ROEjlx%W7Iv zm)OkaI)K^D$9xl<*AeG9xl>MaY6P9>+{-%Ic^r4Xb2;&pXFBLvPqf?Eo;u>EJ`JiPJ_L%Qdg7=?O{ynW1dFD+;;BxJsw-{;jH>$Ls#eXaF9N~Uu6p&W zU=6ES$4b_+n)R$`O{-eh%GS2J^{sG?D-AM&gNw=nrgH7WMcQYBz3%m{c+IOG{tDRk zopXU3K_mtUi>FfwmJf_=tYiO5*;>G~vbS)o1tM$N$L^uBmkkYOCmUM6B6hQZ&1_^n zTL;4WwX>srt!ZV;+0-I-wXB^3W@%em(#{sPfn}_4ZJS#<(ABV=)vRol7aQ8)iZ-~! VEv|H@OWo>P_qy24t_LCj06Y28#@PS> literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/gammapics/MGam24.png b/kde/displayconfig/pics/gammapics/MGam24.png new file mode 100644 index 0000000000000000000000000000000000000000..9faa4b52e7ca68760419f4999062752b475ca8d8 GIT binary patch literal 488 zcmeAS@N?&q;$mQ6;Pv!y31VPiGz79US(t(3f`^=?K*}J%C&U#=KcqNrDUiLv%on!B~#s(!ogRYeaL|9ZE1 z&8)fmey(`;^Gr$I)5ThLjcF!}CC_HKZI0Spe0s;t=A{|mQf|6zjy`;M$IXA;3p2`+ zcieN?%0&2at^9nq-SOA{8SO=@k+RLk~z_;o!)_P4!eCYKY> nO;0zD<$im%j{nTIQ*W4ESLIh9UMRT<80HL~u6{1-oD!M<{)5;^ literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/hi32-display.png b/kde/displayconfig/pics/hi32-display.png new file mode 100644 index 0000000000000000000000000000000000000000..2acfd7996a4ce069f72c74fd024b0ffb38b38409 GIT binary patch literal 1926 zcmd^A`9ItD1OA|DRaHIEsc*l!TJ_qh)iPy@(uyPBxYn|*;)tt~cICO;&RwQvx+V1S^OnM^|2n4O8t?cl*se#t`E@_?7gs2$q z@9)>O3D+|7DJv^WeHIfH6?MWA7sm$q`T5Z`lEuZv!QOc)l^PcpM;n)Ay@A))*O!-9 zf-8V8L!@rtO&ON?7XTuWm_~Z~r#*jWv{%dkZnwI{J-{Wjv8Shp)C=k=ifA<2NgD!Z zxW$nuaCI+pAPDB><^ux*KX5t|5)!MSIHzyVr7RH?I$>GS*q*<3FU>7AQDJgNK2}4gc24srH`q;Kl;l-(rr}dId za%@osd31E_E=73t(UwbOZ&p^;zB94pHc3`;V!;T^S_3JaQhfPNO-&7l!zr5rq50dc z3ESD(*)ucT(}tdb*&+&sf=(5lG{-bIH{;7UBYqPCAWdkN{O2nZ<}fUkN-e`S^J(Pk zAsflb$zC*}wi#}I@xz(#C;)(rjEqFK1OT|Is;Y!e&ZUs){V?V7Oh`5lI`qiZuh=`H zHN6WEkR#dFuU$^5b!SD2SumK7dz*j$g zzMBbh?=)vUduBxtSjPcm()MtBJ%>Awazx#xfm$x`NC6LW0N$>iSi6%RTb(%ta3wpD z&-8W?ORCc(hgnT}q?f!EU1j#{IC#3UkvHxa3&O)WHcXvS!VP)ZhpYamq zo-OpsBS22UI=WvU?cd1tEH7*^2~xWlghEkNuLa|rFJbr>UM46dwE2}DroU?!`*&`A z9KUg+#au^0rP$&B_*U^m58`{3=FdkaXGIVBow52~Y{|v(Nrr;wVycd!-cI6U4R!TP z+NQ^%+}z!tr@3a>S@mRumGN#;2<0N&fOPc5{#lIn@6~R=H)Y6L^O9ueHvQbTu_(mS z!Db~RTGyc4m?T-}UeT=K^UoU`@n|i_*z!MQ%ij)u{L7=y+yl-UgNOHD^sEJvyoRa& zmUgHG%ImdLpKZRC4oqKR2N|X5_e!r;xd#97eT;m5hr!`OCc6p0aXjm~sa6Hu5|-II z@vEz2JPD|g4)>hgg}@JESU*N)!`q+j;^!+bto-5|-Bt4b2alE#wxkHlVj9wxpK>y>{_%4v-@#HWC7Mr70M1(|AH{TkQX)mzm`AAu!ZL&=RmukF$iuwZcTf4V2k zSZjZ76VB61=UKR%Pm|~SX6khg9*cLNv+C=fOmE-NSNuyQUNa&#|IYnJcJ0Pux1X10 ziMCL@UccvI*HA;k@x6QZ9#ZS*rz^?T%^E&cw?fj!TY{A2%FR*CbEV~(s`3#sv~Th^v&i$b`N*@~P#k zvCKefEc{XE!E;+O4@E1Y)%IN}NNTJ}X-tYYa#sE}3H5&9v|r%4{d;)Dydy+X|62y3KOl_i?+ EH)A)DKL7v# literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/hi32-gfxcard.png b/kde/displayconfig/pics/hi32-gfxcard.png new file mode 100644 index 0000000000000000000000000000000000000000..0101e1ecf4964cffbaef439873ee0aa188313a87 GIT binary patch literal 1618 zcmV-Y2CeytP)gSBxPduLSV9yxaIF@bVB$!CN5)qL|I4o^g z7$HT11qj3fN5G1eg#-c##e)0=wj~rVKq3Ui5pr;3gdd6Uc*bO6&)D;x?&_|7R9D@5 zKNi(#7z0ww0)*rv)uOJdzH{z5-*>CvAs*s?40ZD4$yY~5M?V2jtJOL${q&`0m%^pD z|62lr0|yT5J#pg1dVn`CzPbMAr%yk@=NV!LKb-y9+CeKJo$l^H3Hk+=j91SpvS=F?fuT|LW;3oAjrUcV5> z@it@3aX~QJ;2aVWvg+TWr6M*|HM?$s0uT@l(|a93ZiHZI6DupLBwMPeatE;r@4&t|T_AWb3?(};?b_@u9HGD4W39!QdeBu&spV-*zZF<(7B!|%Vf$b4>&{l!l)*O?(&>M(ZqFpG1ZvpZg9dEH%n zqKI8gs~Zy~DS2IFYjK>$J8kaVyUoJ%0@rr^nJ>NYWsK@mu600bjn*2-rU0iXw{oPe z%P&@c$n%x2AZS*qRc7C~$hFBUeEb{FGQ4vOY3vbi5DyMd!4%naYctdHGt?Gp%-){m z$iXMsRvzKr%6#uYxz7P(Om=B-4y=O4CPhJ2Fh1X#TSGI6==y*H8ggTLa|lr>#}AIx_+oJ@ zpB^|$*LATL(&32bzjKtw!3q{|MavPl`RnL;PRjZ>gp~C}*eu Qga7~l07*qoM6N<$f*m&ZzW@LL literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/background.png b/kde/displayconfig/pics/monitor_resizable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..1f588bb5b7c1e271d06263cddc7c23dccbf3f00c GIT binary patch literal 3978 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/background_r90.png b/kde/displayconfig/pics/monitor_resizable/background_r90.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb1ec2c309c9f2047ceab839371f919fe9cd77a GIT binary patch literal 2958 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/monitor.png b/kde/displayconfig/pics/monitor_resizable/monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5c6f966a7b17776aefa4c57bd45d51706c4982 GIT binary patch literal 7568 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/monitor_r90.png b/kde/displayconfig/pics/monitor_resizable/monitor_r90.png new file mode 100644 index 0000000000000000000000000000000000000000..4be958769aafe060f65fd1efd716f2dde5fde287 GIT binary patch literal 7727 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/monitor_wide.png b/kde/displayconfig/pics/monitor_resizable/monitor_wide.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e2b0f3fb8dea31cacee4397a176c9724ce90f2 GIT binary patch literal 8017 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 V)BLhG3~W#1XsGBYS1VdY{2wPQ?wbGr literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/window_4th.png b/kde/displayconfig/pics/monitor_resizable/window_4th.png new file mode 100644 index 0000000000000000000000000000000000000000..90a1867a5f8ac5814b61184ea66f409e094a6c59 GIT binary patch literal 14592 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 JT_$N7@_(lYNLK&= literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/window_bottom_left_4th.png b/kde/displayconfig/pics/monitor_resizable/window_bottom_left_4th.png new file mode 100644 index 0000000000000000000000000000000000000000..e86c5007416500f9a338618bb243cfd0b0b77503 GIT binary patch literal 11400 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 literal 0 HcmV?d00001 diff --git a/kde/displayconfig/pics/monitor_resizable/window_bottom_right_4th.png b/kde/displayconfig/pics/monitor_resizable/window_bottom_right_4th.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff6a1e60ab2015fb44c66d3fdbb3f0fcf61521c GIT binary patch literal 1528 zcmV{|qev00o#yL_t(&-tC&tZxiVm$3L%UUjK+4 zlDar{f}0o<5tLQ3)&|g4DHQ?ruz~|wBoy`1Tcv8R{RikF5(jSm4=RC(kcbeKh#C+A zs|{Wx+61T*XO$mIV2u+O9AnRn=SL4whPbS>7CGOn0!^Fe{0FL9ZzP?VQ(V)M-AJa5hU0o#@43bPH-8kE} zSy@>@2*JUF2N6P`X&TGR%M1(*Af*IA*LC)Kfmm2r@G1rmj~2!4_xt~Ev9$*L5QwwU z2($b`u=PWYFQcy*O`8ZIel+go%a>fgew_yo9xydEMLwVB>eZ_>8Vx2VCn*+-NGYk; z>pXq>lp8m0aPs6yE?l_4_$}-aBNJyP9Xeqw0ae(n4km^Lg4rNv2EL3Pxplg9*Ciqpbq@~d5Fb) zn4!eK+27f=BUDv&^8xhs_Hz34X|mZYQc8w~hFDx&B$vw}rR3JFTO<+*&Ye3)KA)#n ztC3EpxqJ66-QC@Ec6Oqws#`Jme7>e{=-n55D3p5%H6h*=3^HF(Q=i7}p{PX_b z2s#Gxt8s>tQKUA9E~Dsr2fAV6H>MH$e~Y3hIF7?!-GyyIRE5_HB=^CYFW`%$0*24M z&opiSuO(U1fF+M)N z(>m-i9j{{W@c0N6MZvahR8?hqtxUb6k8h4==+Nq9mllx+e~nTplN$IH3;8k~9bFjT z)d(e2SIm0_E=+}D9p={IyH`8c+R7G3qJH;_zBebp#>NKI)6@8TKCWE3LMD@8Zf=fJ zsl?3842430o}M0hdV0v^a%h^y=;$azLqh;$vsuQ*#@zL7+vdTt!7tivQt_ZyF?e{i zif!8nArL~4Us+`Tn}0FLOGG-&Z~l6n=bs-YYMPk3&C1F;!^1IbeMi)eOLarKWw2jEwx~e#gef-n(}D_U)!~(=?e~ zl`Iua{`^(ls~9{yTE#F7gb+w6`TfWtzP~aBxh#DBHO4>Yup13*pTulb`Q@?CsMji3 zn&FDvw(Ta7n~qNDHEeD=l((Q(uTx%o%^!!}RSdhcjE^09eu&u_<=33+h3L;Z5#@5( z`&GijqXkUUY!bJjX)OKW8;nQaQa^KsV}H6xqq53&{x4XDg=5;-kpYrl{tnx=-!q27 z^KG<9H+NSgg;&rWwi${3htWQR_HfY6xmz`4vsn%tIDoF}Y;A3E`0!!o=jZ9~?`LUg ziQ(a45{U%ma+&q@bp{6qxqtsYM~)mJnN0HR*)z7cw@IZ^O%+AgdkH;yM zN(do1e*8G0Q0Sd8Xqx63gNH{8Xqtu)fb8x^B?k9(k{2Fbo6B zvIqnMT)upn^XJdAyu3`MQX!Q}@#4h`QmGW>av9sU>FVlY-@bj^ym^!H@o^qKdPF=P zM@q@s+8V)NkW41S!omWF4jm#Ei#6pGo12@SF?e{ifTAcEhVfBVgQ}|Ny54f^r{w%l ev)lU*%>M#ZCv*mP@`k+t0000Hq)(AW1|) zRCwBjlS^zHRTPH5d*_ZHW7qG*kEF37X%uJxK~y#%1wrTrQ6bo}DMeJQ$^sT$A`nPO zupvS$KwTmb8wx^!luC&}K_Cz%&_~)likq~K?ZkF$$C(5Vr}y^+KqfO5D3wNz3W;#FN1`}7m^*&v%a@+)GkqU~03jsR&N5YXk?Gm# zEpbkaUtL_ftpHBE@cbLcpLzCeC0qsua(sJvmtZEwjhaRo0x1QpPK_nwU6z|`)Xl9_ zaVq~Zi@c=(66y4?<2rov^=ByI;~ssHLOhS|cmzxdKK#~wm2&7iDjkomfE{Ta15g0r zkd?}3GfY1I7^V{Z?MLZ)U?7l&1l!?M^fW8-VJ=2y+&{c)Ms_pqEm6KJAeBsXcWv^e zadz4*25gV*Lki1MM8gupl^ApLJU3)9a%rTwP|dC^tZ&aAT%eSh)LPSMHW@7BiQ0Qy zv0L0TltTpsc!EgWqF}91cqEjXYP3?9ueKkWTFA~SKm-H9nC&>^^I1GR>RWZj`opvu zZAv3?3=iU^98{;_jz(5BKd%)pZB{Ec6<}J?NHUX5ph61I_ZYwb2!>%GWsllT8zUH^ zKNEv=07u36YPoOq&AIiHz@Ab{SynV+bsd}GT!KAE<7kbL0^jxMi%4RYMsE$Vvi&+U zH+t3AK7MCX+r2V?GR<%@7z(O8w{9VoBo&Jgi-hmK4}sPmm8~|mJ-|QJW9W3v&~~q} zpG1{N^d*afSs_FgVMy>?T00$t04XI>2n+$?puy;o!*E?k0SnjxP*x<8_`N8ipBjU8e$|cDwDy=)J>K%PWlC_aNE+JXXY_ z)vRME1A##%m!|1=_;GiJiRM$=j$zW@LL07*qoM6N<$f`i$c`v3p{ literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/exec.png b/kde/mountconfig/pics/exec.png new file mode 100644 index 0000000000000000000000000000000000000000..b0608325994dc7a4fddd19e50e064a4f5b4e959b GIT binary patch literal 1021 zcmV*6RSU|)KK0bzDTwDyFU%q5u=H+Ez<>zO(_v8r!CnKZ4Hzp=UR&H)@Az@)> zc}YprB^NKA-23&bGt2;h0AhhL{{Q{U@Eb_~`uy3Lk(Jd}UPZ-KTtdP?US3{QQc#dp zl#!7^@ZCFxi#KoD0u4}OW?;Al(gzSgU<3aC_~8$02af9I($aTMdfnBQ3!oncU$msg;|NnjvA0U93K*XIdUtjV{Ni#44 zHGli|i{Z!DpA5g)SQviF%QO51YWxQbXb}DL>lX&0j~^M-{{PnlYU2Xw1qdLp0f(+$ z+5PF<83tw$=igt3|G$4T{Qn2kq^`~I_xpE-zd+~zdHR&$_ot5x>@so;29nZZK$Vst z5)z%^76)_#AJ1`osVbKwtxaHoty+^5p$rvt}Ez-?_u^>&hjDf3IJG zo&8r%k>S6qJHx-1uYqB~$ncwmnL!BX4K+DM)f-nXiU0%ty5*HWw4~#M<5C$6b@6}6&-^Y$Hd=V01cy{;@!~Lt5 z7;c<7_5a$9Yd^mK`}YzcfEYoUDI6H`pk$)O!EvC_z`*|h&z}r$-oIyf0Su&jz<9qU zBm4h45AV<0H*Y+9_UHGxPYeuufXWYod=C&nU>AG@MIR7f`|)G@!Ta~^p8{<^`}OOu z>%V`$effcP6HkoXSGLjVB;c7Zq}Bbf7-fq@5@yt>{1%>yb} r4-`5N#P47U=07CH7}${v00=Mupzm{-f0Gnw00000NkvXXu0mjffJx)r literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/file.png b/kde/mountconfig/pics/file.png new file mode 100644 index 0000000000000000000000000000000000000000..df9d426e57dec193642628fa5ad50a04003ce22c GIT binary patch literal 1007 zcmV|AL+N`PnmucWc)&{J3?C zfyv#SLDIvWfsK!k;pZoyG5mY5o#ET=-3)&| zbTIs8kO2h<13&;Vft>cYx0gZY$`uA~1qB9XDJcedMmC0@lV>n&>WyXi@ui1>P4OKA z0}oKWAOi#Urz;FhTRIqiy#K(!$jZh55I`({fG%Wx^M-+C=S~I&pf`T7^D-QMbdh0y zo-o5BS1yLzH#iw0AAM%f7X>Q34h(`@3=Dq-{xJOh`x_KR3;+Sd!obSP@Ig`%9EiL} zk1}ksl4W?XG@U`#>?Z^3yNe9>*cBKW?(i|h9(lqL^!f+G&(FZ{5|?IRV&h==|Mw3A zKmf6@05uBq^)WmW5o2JVGL_+i(O-stx-S_7e*s+vjD+7m7#Q~c7h&Lk{ei*b9TUSd zqZ9_-sB#95=T8~_0%HjvfLQ(jql;HikU_q?li`_@JHz|i)eJAMZejStBh7H{9UH^8 zoBtSoet6IDgHwy)U0ewRyOk3ICpRm@--ma>(FhPgOrW#}ipqbVJ~1eGI5F5d6)-To z;AeRC=>@}GV66On^PWNUgCK*mQ$7Qiod*Nsw+{?|zkFc;#vnNQ0Ro5xmV7~p=xEMZs`vg`4!ys3K^Z^7A3o|n_BMUGnxwyE% z0wDbR-!BFsVSWZ%QFDeLe||9h`1O;4lamvy34}px5CCV&89Id|^dcMA&(4zL%X z$r|P=21pjgV1v{GGZVw-&!2w*1P~)PH+Q0xlvElhJ%KVGdR9iy;vg{~W_kVk_1E|B d-%kPvFaS&uw23Kryd3}l002ovPDHLkV1m3N!&U$Q literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/greenled.png b/kde/mountconfig/pics/greenled.png new file mode 100644 index 0000000000000000000000000000000000000000..ac36b32e90319a8c4c73b51102d36a6352ec22d3 GIT binary patch literal 466 zcmV;@0WJQCP)*z&fB*miU`a$l zR2b8p(mzkaKp4mIFYQqzf^_g-jI#w3F8wG7E9&`3#SBzd2V_*foOTw8F`r)X#j+-hy>MjCF4Y{Y%c?UWcCH2dG&6j3g8#o`0!>U}V=2)Pv_2&wiV+s7yq9R8UYE zfjz=A1@&){0~lQ31N$59q-lq^|0WP5=M^07*qo IM6N<$g0s!TiU0rr literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/greyled.png b/kde/mountconfig/pics/greyled.png new file mode 100644 index 0000000000000000000000000000000000000000..b01d502695c6e34d79882c7202baca53b29dc48f GIT binary patch literal 389 zcmeAS@N?&q;$mQ6;Pv!y2?El5Ak4uAB;`GHF9IpHByV>YhW{YAVDIwDKoQOYkH}&M z25un`X1sK_?hjCqy~NYkmHi5*w5SaC66?hl3=E9Io-U3d5|`&rGR$js5OBTkR>mvt z@Pp%|U{GLVjmZ52kKCqNI!OI$k19o$C=A!E}!}HzxH#P_gDBF zG+7;HEn|PcBhy$g&+q{2iS9)g7|vNvDdt-$k~yo6$=-8POQ48hpYpY+TiaZ-eM3Xp ze&}^Q%4giHW6iLC`DM*Lck_4-H*6IXjQnN5GbJ^%Vg2i`tL&c@pYxoQ<8&>G;gJR3 z^Y9N347(Qjp7^f7G2?&yfeZh)uy%6%{wt?{>)h$zJo}oKWk%h6Xg+J*>uCpdJzVzK za#vi8THg>pd)BhVZzYVMSFJkpw=RC}Z=SEU{;}8DPBi{!?09U!({zBlvEzaXZ)vQw hU?ls4wO3f&uu&8h4vqK|P3v&4Ygq8}p(dh{D>=DWghMv>v4Ev@x~I+9ba8B`9zb!u^EeSE3{{}jMz3)K79Zr}OxUMlTnpFH|teZ$G&!Iq3j z^tMQNNTg{xeny-D30{@S|u?=e6CwENqPf$lv6zXWz1*iTzecthPYn$xcswAxX071suDTIifk zb6`!1{zLKp$J$tnJv@68@U(+FdoIK`^sOiT0$z^8Op3nZ9TMYD8L9Q66f%4kxz3jU zTZB5(Xw?AmO)b5tV5Zyty>LU9lC3cL5YkylPk~#5Xq?@vLb$1K_9NN$l_1<&2dk^{VYct^}$E;bsU#Vd+W`VTt`GmYU_SN|}aym}34l!H*2O;~gy1 zKV~%auvssdWtY4KwgbU1R9qNMf>WecbKzA0sVni?=g=8zM-S@wJcE(R2Na7X(eAz| zO?(>Qi&kH45v3~pmxEaoCtKmtSTS{Hg&$WFKb8stQ|1hlW(L!>P?NP$E$1?E<+=Sw zkD+Z|9#A4)w#?OWJG(b&o&Ig2vd>FaZiN!z&&ALeF{@B6J4i)A8FBL7weq=lg9t(Q z(L`WqYom4!_~d`{jOe}5qaA-o&i9CVBs#0C^PL3}^Sy$9zIg-CShu>qui*qby>sn1 X;WIAaXu}x200000NkvXXu0mjfcC~@i literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-burner.png b/kde/mountconfig/pics/hi16-burner.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f0e56c9b6dcefeabf7f2e9ffbeec4fdaf3b6ae GIT binary patch literal 899 zcmV-}1AP36P)5lKWr zRCwBLlTS>WbsUGE-}}D({|{(8u#gEG!o+1FNRefV9!x~y7EN}TqB9cL2+2fdh?OqaOJVqB0+W8z34k_^UxZE+=#l@+?KWp5~L-_p13`}4r!bo$)Bhv$>; z;qwvP!`Y#_rz5=w4u>Cl;!$_#{su|a(i4k`^JBkHeslWgZ)aDEQ~V!CA0aYv{H4=I zYEjYUCQFRltxC%?2?p5Ldr^7 zN?Ddtn)1YluZ{pN02N^P$o{VfpFj94D}REpz~Kd11*-^F7VLm#2jKt&UHhVK>Dh5P zy^uVwJly1p9vawpi1;PQuR`h)CQHGKOlmZxwu(=GSIJ;V{(BoZA16p6R?=ezWz*h^3W9xl% zC{8nSF0tqjY*+iE#TL{}D8-==gTfq$JbNApcWO?%sKF(`W`Y~2v4NvPl>i@oa`J=j z)`nnz$Iw?)Qc#Y=#ziQ`!4t;kZmH4M3TB3I1Gqg94na5w_U)*dGC!X9bl9irHrD4s zNkQQ%Y$ibf!bSRaHmi)yt&9_@hx;0#Ap+hGu=jw|OY=^DW3aVz8=*a5T)QhPU=@SR z#4_{B==k#48wou}^A7Ot2H6Jr>tM#fCcxwY#X?G&%Zl?98cn=3O;PS-sO{^W1E5^v2|G&fUIjXPtjd zqjD7jZQyGISp8MeUtbj- zxtz${nwVRi`sH%|djn?szYAHfzxay$^4Q3K1O9>i&guk^1x$dZH$_d~Q?~Vr6il2d Z@fUt{SdaGlS+W2C002ovPDHLkV1gM&r(FO5 literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-cdrom.png b/kde/mountconfig/pics/hi16-cdrom.png new file mode 100644 index 0000000000000000000000000000000000000000..3f68c16dd416e5b00dc4ff56a3be28c0e0564e77 GIT binary patch literal 833 zcmV-H1HSx;P)-zgT0A1^H}y`Gtwd1c!+YPDLkTrPiiadB}1;IEfVU*`7q_BYqp*M6F& z^io=Dy1cw>uB@zl`9^qWXXivpNkl|SDN;&N9LE%fAtgydT5BRAdU|@Ijg5_i|IzE~ z>tEOFb-KU5r%tCsQ4|f}AP6W7LyF^g(4^65(ER-TrvSih+kSO$aBz~%X6-17Kt!-@ z8$t*G01<&P1|b9(V~FDzBO@a)O>?nSDt#-8#o~vvv$JmnK>*ivp|wUBh9Dv+rSSax z4A1kx7(*C_2*VKVb{lJJYqQhS)33$i;^O;p91k2g=ZK;R?RFc@X7kx`95yvI#T~~% z6h-Ludgyk$AR;U*EUXI8^WNs1!!QhR&LM>8yU^z5<|lJ=b0f#c$A?l%5D|ReM-T+C zZ5!Eac2a1qmFIaNB3PCM&N(>e7#bR4TU%T2O-xK=ecy*^novq12m)xWVcRysFib_g zUjNOqtapVFuq+Eg2uw~+qSb0)cX#(IB0{IrL6Ris^?FFt6oz3yYmK|RyE~&=t(MyD zHeA<*X_|0dw?6?K$HD06DDwF{7-LA1fDJ6X0hm>+~SgkdL z5EvdF1`(lLE(eE)ho2GA?;HT)IR3Lzsoal^jeU^M=dC!7A*DnnlR+kv0p}c+WkD&0 zv$M0#{{H?KNs@dI0K6aPZnyhOsZ{#u_VzZ5P)4+(_B+xD1t6Z6l$SGmx3PEQqn<*Lm^v3 zMM4$_o!nd;okfaM5p?L#;^1I(2nC@h4cI26#grs^Cc)(Nygy0q>tdl;9Xj-xpXc|& z%MbWpp_Gnvb#>(^rJn&r|7lDJdAhW;q;73(v8kyk=Jk4;0AAnJ-Bn( zF{b7I-ZY@V|scTCnqNe1OkvG36A6N!C>%ZE|>epx3{;y9UdNj ztm`^dRYf|TMyXiD-rgR@$Hp)-GXqW2007l$6_O;u@At#ya?w;OW%G?jBYSvw_>2&8 zx7ln$*LCD_IZRAUARdq7^70ZK$DvlMfe-@2Fi@-2u&}W3@$~dG@%x95+S=N__xJa+ zP$%u^wqF$rg)(Cdlu`hI z%jJS;n&{~0K&#b4qtQSjkw|4SnHYfTbq}hRWvQYlj+~vH36xR{3=DwhdDylM+qMyl z#nSux`*Q$(0BBwZFaTfc^?E@R#ZgVu1VIqc*VhNbFc6JK(>psmaR8qH*f*KTuBZp) za``7AWGomAQp>V1J3D)@y}kYBYTUZ6|80rLvaEG?cPjuwx0QYYI3_GkkluLT00000 LNkvXXu0mjfAG%wr literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-hdd.png b/kde/mountconfig/pics/hi16-hdd.png new file mode 100644 index 0000000000000000000000000000000000000000..ec2bb622adca1ae0c6e44692cf6a66bf7991252a GIT binary patch literal 724 zcmV;_0xSKAP) zbNBs^dF|%wj6+B7MzT0Y8ib}d>;#=+s; zM3&_Wr7Fe|0g$FeV~i2A^A&&m`45X_hxdw2@8}1Q0D|`nLJCAA$VW>Al$oXN25gp+ zrg^igQ18989|@Sv7g(*?FEb($K?npqsZ~^^Wz!FAx(;JBd0tdIK-n}eyK$i3ZaG_S zSob4JYHVV$CIMnD+ktU%EG{m9Kwj3(&OuYwH?#`$UC;dAr?!RoSi*GYenA=yz=S+@4R=9=j~H&?!AHcqSyh# zIQ)I@==e_8wj3V5MNyP!t+>3rKq<{M4U|<)QdIoXo$_}2CVm(fyD=3-asTM(qt8Qd zK7aPLFvYN_frI}o=_WeHyVqz)78~NcXxYp zAmF>5PUqe6`2E$z#d&cKkVwq9>gxkzBO^DadV9MYc6axU<>j@))>cYYRmI)YbJ_3n zd9J5Y=^w+xqiah`9~}Ts1)%FjLo^y0kHw4)#}tbH607b(+Ri%6RW~e~-Ka37`&OHQ6`n4b~^D$kmZhV<>PU6+|Q;uh(wq zScLkUn!`c$R4I81d&`DUUkp6i`0N45O5BT&-5PD`$N|TI6NKPUEg*&kg23EN@ZG>>21MJLylH~@kzY*Ihzr=z6 f<%;f-fgOO~-ML%num6+>00000NkvXXu0mjfo{M51 literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-memory.png b/kde/mountconfig/pics/hi16-memory.png new file mode 100644 index 0000000000000000000000000000000000000000..4781b8f317083b2e8fa1aac1b23d4701638bd94b GIT binary patch literal 674 zcmV;T0$u%yP)b0 zgoGeb5Hjkfpdf;7tPENyh!K=tBppn)G!L!i373MlX&yHxZaTO9yGV*e(z_mbZ(jI4 z@V@-ueCd(~{W6~*B8S3p8=6i=rHVn_q zxNF5aEct}+_=LE6{zRR4^^X&r9%0=UhSup?Km-Mn1d=2X3t(rt z@d?J&*=3xnH1e?O98zcun#Ao44vwMI?Ij{2l&mZ*Aqw!XxQLCH_ear#jJ@LkrVH36 zsD@O!uSZgv>ky&V5>|pL3JP;Do2`_W7c)D%06_E8Cl;+XIVny_ky0|y1*wwg9Tcn> znVe=i?Mh{=MzxE^oJ_n0jm*z`X>ac%B_&3jEh>BY?76d|>x0iR;7@n|ZlZWcEOOkG zK5sIkZML=D+mui3@a@2sXpKT0yO^}tC`%~g8%>k07*qo IM6N<$g2op#ZU6uP literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-network.png b/kde/mountconfig/pics/hi16-network.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9c8221133ebbdd7416b272a2059e2f398fbe71 GIT binary patch literal 893 zcmV-@1A_dCP)R#n^9) zA~3TiqVcSVC2}Gf%Zi<}DL(yjGKtpo`W~nJU3BmHOYi^C<8O8bW`e9n(rl*m#I~};o@SW;GeKZ>0~v|c^=&@+>JAbTHk-~) zax=Mauok*IlF1er93E%j&LWD%fvU*Zbd8C?Dt|n9!sp+PvmW1}?U;uX%`Ph0mAe4a zKi6>d_I)^!ENGo;ku7xHj&8GHQ58(nL_#9EnZoUKVA1SsN2X@t^ZmWR_MQQn{YY<% z|DD^lu7)~?O+vFMsES0z6ckHk$`!#*&Lq4v9ZN5La}i2o2mSymMrQTt`t+!)&fDp0 z_Z@C)IfA>!PD8yDS6vPHe2$gi*mPoH;2M-B_PG572c)~|kDopMV%NC~7cRZM@+ce` zpZIBTEqs5nkc|xk*@MICfnGUt<}G(7Q!FPEE8})`TR+|Hxhfl>_*n7rpsv+Sb@)zd zkkJBxUy^|MSAwdl+UNWGzn*JqI(jIR$qJjzDtWyw0OsbFglP)V+}tdWjEpW_zy4k~ zka~87>dwxtcdb^{F3XDQ^L2Dtt*TX3WjP+-OfN4#yr*eaQmNEdFt~67$OHTZ&Es}H TXQ&>500000NkvXXu0mjfD9f!< literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/hi16-password.png b/kde/mountconfig/pics/hi16-password.png new file mode 100644 index 0000000000000000000000000000000000000000..539c617f65cc1888e1e1ed56341beb3095b81b99 GIT binary patch literal 706 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 literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/kde6.png b/kde/mountconfig/pics/kde6.png new file mode 100644 index 0000000000000000000000000000000000000000..4037802cd2b87abb52325563b0e9087301b47c9f GIT binary patch literal 839 zcmV-N1GxN&P)Uy%R+00(qQO+^RL z3F7{q7w=W zItb-0gboE+LWd4sEDJkibdgTwp@UH#>`N%hSuM4sgp8tEX=~cj^j(;9d%EPzOa0+F zJeS||f4<<4Ll1Y*5YtDFc=H~I2;b*@wk!%IGs-6oS*gjJBqVy6lGZW(tnqZwo^nRI&;|` zDss~)EX-qmUTAG?rM|v?`3wM_T)jjz&r1K)1S1db)6=sXn{^XiU4v}gm_v277uT+K zGTPcmOG{f0NckEh(`@42g&cnT$YaHdd{R?~P!eXEo883b$M_t)g4b;&t-7A+XGfL* z7K?@1$qX5#aH7ng=eopnVTA zKK_srcZ`{t$IN_yqB1yC0H$+56nsubS$hR~E>WcmsiyBTH8-lK>d z0Cdd#LyT@%ja~C%HBOQApzAtSRaG22ewu?VlYEPPCuMd4vjQ@`4VWQ3Izj5DY>F$b z6lmWt7z|583EwGf+)+^ z7>~yl09U@!W`yH6Zbx|gN!YXRE!sz&{dKccRBR?=RSpx85H{Oirz|2#kfJCpx!LH! zFU$cklZlHqE99;#rJt%s5;N;@FyWMWtxxG|Ggx^giT5wz7 RZc_jN002ovPDHLkV1nRub{_x$ literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/laserwarn.png b/kde/mountconfig/pics/laserwarn.png new file mode 100644 index 0000000000000000000000000000000000000000..f14986dc4d4fe13093beeefcdb43489971ace999 GIT binary patch literal 476 zcmV<20VDp2P)T>BjIIR0)ZjU-OX| za3!n8v7PADMDRzIQcA7!JnLmyXxp~Ewoj9kQY#U$vB&+fw)VVL)*a%mbC^*6JS)RDmxhDEw!7KU=V@HL@;tz!d%Y&FyGDX_wL=VlOpmaeb3*|_xV20 zqhKskRaMpP?(XgvUaz-fZflzJ*9g%DELb?Ni@Bp`u1+XXcFe7=pzi9|xGsw(60xEvoJ zmoAq}TCLVaV2AMy6x*ey=3@W|AxI{Z2q9QqT_rESklfr{>~{Mu6y?x5viS>+&ZF6N zg>Ear1}ubzrU|0KDO^q`x}G8)jbz02O%3`_;2rL*=zsROuTT%ZW^{B6EvX}fAR#p3 zkr27+2-)@|ry5FVsd2eCN(0=wT>8AF>ud+4$anr>@aape&V3XVxJe`gzgL13WPau1 zxds}WDk(3b?A^rdM0k~u86e%gWX3WKYJzmD%1xh{#ne2ZP!0HOW+wGvdwNYu!a-?=X8Il8$HT*yl$muz++yESphV*+v@Od5j3zF9p Uf1>OcwEzGB07*qoM6N<$f*K(_MF0Q* literal 0 HcmV?d00001 diff --git a/kde/mountconfig/pics/user.png b/kde/mountconfig/pics/user.png new file mode 100644 index 0000000000000000000000000000000000000000..9625ccc97ce0a2cf9f189a1d8d3a15ab7e2e3553 GIT binary patch literal 756 zcmVt(%lG@}*kW2e9NSY_ zbt&0uuU=kWmd8g1JG=USwSSEeX)48AQ&)S%VBSVxv=;!K4%8I|Tc*{qdndR0=V**8P@JVd6OAqr`1lfZAT*7+ zsUVi5bSe)XA~rJ}>G&`f-+{$qafEzBYp_IuW~IhcPqDKQRwMW`t>u6O#I_U&gr&X; zS58BUElYAZ92DebBLQd%7$7Pc^nfm|gS)Tg>ld19>h?99tY>w3-K_vgVrcwHKM-Bd zNSjmD$&1tUNz5 m^7%_&--|bq$dAFlFu)(DjZ6 literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/ac-adapter.png b/kde/powermanager/pics/ac-adapter.png new file mode 100644 index 0000000000000000000000000000000000000000..a2fb49511c61be39f31a59b65c604c85a6b79885 GIT binary patch literal 563 zcmV-30?hr1P)b)v;?6VHn5p@B7|eo80v-O%{hPHs~biR86ggf(iz7 za&YPBA0UW0xj4GGI9Of8P1{8%Eh5-fDg_a=)gW3y6g<$FQVrH5cgf>mg$`m~>)V4L z2QSa_dEepTeU*r?6QBOqhN*qoqt@XM2USrIIajR1?@k00X)c>ug=>e^K=Dv>EN$p@ zt8nFba5K!MZfirWS%W{9)UM)4>zL!hk2+Or@auBlL_VK*jt8}B=c65Xzsm0}HOtxs z$Ai+?#_1X0o@~Erwsx;o$Z|xgPY4}+;qkZIw`!VvKl+rm%J;yRsC%oQ~sHR$*!q%s=oho1%c(xLq8K#!c$BM(xy_ z_s2O_O7#LE&<8}D$pIOl0el6%ZDt*)i%5H`@e`1v_cO5GA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Oxygen team + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kde/powermanager/pics/battery-charging-000.png b/kde/powermanager/pics/battery-charging-000.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2fedbca70b21cd0756943289758b5696d08f88 GIT binary patch literal 829 zcmV-D1H$}?P)MzCV_|S*E^l&Yo9;Xs0008eNklA$P*`2X9T4(`N3Y)e15At;J?X)NCj7Mo9c>RJp~ z<<@K#87~e6_F8M*2*x}qw{9&4Iv>RN47=99pt&i5WrM9iI?%W#MZ>BT*(7AraP4LjPpAA8s*B)YFv#@ubcy5DGEQsc z6X}eN@@KJZupAH;Aiz@aK32_*cP%LOyD?g8Y^4-cRaK0SkC%YIn3bHqvkTj}*e1*2 z?z===;s^;CNTqCg?`=bB-@JY+rRE$*qfuh9SV=1iVSubLghELyIp^BVz?Iu={FHU* zzjm?MioU+S;tQ|Pxio=55(EeVqoZL)CR@?E^o(h(v4@6+1VBeehY5$np5Qw32FG!6 zbMPbK8khl+e~d{Wm48kdW3s;h%JFPjE3rZF00000NkvXX Hu0mjfWQczm literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-010.png b/kde/powermanager/pics/battery-charging-010.png new file mode 100644 index 0000000000000000000000000000000000000000..2a7b0535959ad2e982d96a3da1bc18b3bcb91906 GIT binary patch literal 858 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 M07*qoM6N<$g7eIRT>t<8 literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-050.png b/kde/powermanager/pics/battery-charging-050.png new file mode 100644 index 0000000000000000000000000000000000000000..00f16d324e69a4a3b04508abad68b68bcea2827c GIT binary patch literal 835 zcmV-J1HAl+P)MzCV_|S*E^l&Yo9;Xs0008kNklP}hod({B2J zQra|&LQ5%x1Q#Mmg)&wRqWHi_l7>7cj5LpV-MPOmG8r9aZkUGt;Kw=Vp6~ha<#+D+ zAtG!x#|L*F67cHn88#V@K56~XdoxTRGG1@r+YI#n`PQW0o4djyKSyc)Gx))o_P3kg z$$p{W@4g?G9{w-j$Ac{$O~;nLbFr|el=Vh76t5Q>7zHFMHOS}ZwM_q{W$Fhl2isMo z78tEup4K|q-`^i;;COtV56(VCE66CHT{G{}VO3r2ZF4(W0Xyfg*47nv`pbMYIDx-K z@H9+|<>lgXqzUI7CKijyMqo>ckKcR3_8kR0AGCqaY?jNZr_6_%1q?4R{IO#zIj3HM zopU4-2{M@s&bcb665(|BBM$at@C+CW+J`rq0``>xf-DsC@YQTHzj;#a*f~cm7Gq^) zrN;4oo#t?2hJ4Xrn*%%po)5|c6nGZK(;in>0}AFVl=1n-pR zI1OP57G8Q>nC)awtFCXtIja8Sjlge(e-eg}UDbR$b%LV@;*r`(H~9)|j2Rmm8cIfq zKRfjo#hl{npXLesG;9I*}iS|cV?!(G1u;%Y}bvPX{gaR9a=qHSkN~X ze`&pVWF%c`Ap97LFFD?42Vc>3k#5NU&1M0zP0mobuCoQNVhxx5iRA z1E44(Rj)*e2udjrus{>wfGy>Db9vlao@*cvtOD8B#w?I4uX7?2{0sJTbQ;l+n~wkh N002ovPDHLkV1hUNf2RNd literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-060.png b/kde/powermanager/pics/battery-charging-060.png new file mode 100644 index 0000000000000000000000000000000000000000..3782b8ddb25112cb5fc47b46b3c1829b79987a34 GIT binary patch literal 830 zcmV-E1Ht@>P)MzCV_|S*E^l&Yo9;Xs0008fNkl?1VIE+ zw1^@q$X?QeNSbg=1|6}7dgpywIBGI;W0HRG<(zxZ_k8Z}{_eRyoO8@oy$2R;av;mC zY33LokJcQLs|rz^>n*n~=K_6uxMr`nE_Ip_ZUuS%8T{^G-G=I|{&@l4ns5C2@L#~6 z4utDNJAYlZk;2-%FT4M0o+>tQ4{$+J;LaUZZs4YJ@$1U1uM>e@;GQT#(OSFK*4FY5 zf9%-xVu+4-A44DO))b}~n^O>Q7Vi?U=G*1`KBW1-5*jh_88fA2J6l?7) zsBn(`Yd*52DuvXb4JZ#*hH|XP=g9exO~b`RHFu<&ow2o+NF>7e_;^XiO)AOe=pf?- zsFff!NDqVr1V{tDNy(Xs9GUdb>3t2h0cnh(wzd{yOo6S(20FeJ81pk^#%5%UnasGkxtUljR#J+o_~ALEhGpT&k0(9IXW`^KL7#Rd{^?S5 zb#+bO2#Rn{usG~MfDrJYPjD^e%Kl>3T69lOj{suEh7SV|7E|+x6T28L-y+erIRr-XL>B z`vBP`f=iw2c=FW&gMnbJ_5F$(`s;ml3leu3j9=DgU+=3^mF%gg(T6roY|ad;hadMmtS+6iDvQ>`&yR1U~G z=Q1T=A%p}B5CSYvRh(BB$9cuM0@A<);QulDK&p71a?a&`1F|xIFu2ZVIsgCw07*qo IM6N<$f`w>_TmS$7 literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-070.png b/kde/powermanager/pics/battery-charging-070.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c09cd70460ac05f20f99d22a5dd11bf743b0c1 GIT binary patch literal 854 zcmV-c1F8IpP)MzCV_|S*E^l&Yo9;Xs0008%NklF&K92ZL`F zJnz5u>dF5C{_9k%r~Q*xS6sZhI}G&re~Ooz4crDK$_>b+vqn-s8A<+V_8??sLzO6051r9oTJ9w-ec&^}CNG+!@OC>3+{yo5_&ecz|6tBbk0 zxmDobDe~zIxRk<$!nM7MBfA7;0D;94jQm?=(UuzWeBXalaese5)6>(dS`lgpfr1gV zg)Ol+KTmrQmf`$k#grA^wR$VY#>VOoqAXTXY>EjePzvr&DXtcz=@E0zVJ9Xg6u{8X zko5NUHVC%Iw%6ldD1WH9Ul5VXYcAl1SUn7>UjP6A07*qoM6N<$f~oY2t^fc4 literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-090.png b/kde/powermanager/pics/battery-charging-090.png new file mode 100644 index 0000000000000000000000000000000000000000..843a91d6b3299186416470925ccbb0efadddfb8c GIT binary patch literal 866 zcmV-o1D*VdP)MzCV_|S*E^l&Yo9;Xs0008@NklA#8?z!hpQpbczTAik~irQELHGXuHU@de}8|cEF6kLd__#e1X*NV7tR53 z%6?=1!l9E2w$q$rpY4~I`(LmRB_pWLcbd2Rf&O%{|6F*y@+PZ%mZtg7;2X~mp6Gir zde^}1(M#(;|1aQQFJy*#pIQG%vdN>3$j<(&cqiGwWkAy+0Db3Tpo^adn*Suw#|BNR z7PxGhP@Ho*K0dzvv2S#bfoFfX!&H8Oz(CoAOieyb!<&v~uez(dr$R*V-UCotDzyoZ zE=9a}`wzk%#Rjmbp1Z3XsaX&aTsE84e}dNvYb{YvRe5EpNcNr@HiW>z;V9ybx!<^x z1gzk=iQ~J22dIeI1&atHBO{c{Wkdvk^};#;Hc?(GS9s!J1?#|h2ts(Ux6T8NI`s%@ zRd}!1#}`)H9g7IrY?jT<%{`8vS~Hx^#cU=pV8J@DAs7o7upZ`1mbbR*#MRwh<|K#n z-ZL;T!1D5P3mk960DKQTR{&WwNJF`F!f^T##RP!DMhsKGCv3R5Eyvz_tcWl=I*Rum z5ox6ZpkbjBz@MQ~5;&6C{&_*T?fCT@hJ_AAp_Pi6nVHs$w3ewG?#n0`Fb2L_FnnCm zj#o@XaC38W24G@hLWhTk0XUX9wnsb`SCODd(twq_mRF01Inp0=G$A7G=XeKv=Fpiv z;EnERQ-MM%*g1El ztAc**{NTOCFDc|daBp8bKNxhgrmI5VdvfdXcqRCH_4EGM#%4>6Zq~GlP+Ge_ITVIR z<5RV~|8Z=#RP6?u3O2@gAOzfyn6Ok;1MI|3X>2s34%Ae&?Uoo-#Ta7&5A*^8^fbqP s&Hg}h8~{~d3yA*KM?j_dTv65fU$?D?zu=$6761SM07*qoM6N<$f`s3eX#fBK literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-charging-100.png b/kde/powermanager/pics/battery-charging-100.png new file mode 100644 index 0000000000000000000000000000000000000000..f604a38e40c4d3d39e441d0bd5afc54dcd725b79 GIT binary patch literal 848 zcmV-W1F!svP)MzCV_|S*E^l&Yo9;Xs0008xNklXgJPYHhL9sO_Qz+d`GvKo{Lf!G*ZWpWs6Gia!>vY6~J( zH-aESXz9X@SkOq@P!uI-=_D^he$Y%NX5PG+_l}FqBo3J;WAMP?-uLeJd^qpCa}OfI zZhhwb!$$>lr!&hg?bkQ+ujwb!XAqg`bgp*;y>l^tHaHl)%L-p*Y5p_#?uEg_eJ@l$ zRxtnU)ygmb3;4J5xuM>dDxdm&>C;J7PyMU7RczoIAX#oeKD=mT?n@)Z&y5@!RGC`f znrcF^*2;;-iOd8}{j|traTcTCfeM(MI7Z@BN9|2}bGs|N_f!@t9fhL{RbHR}lb}b? z2Ij=Ey0V^`g!djbFfbsk7yUQ-cZhmw!Uqc_j_r-m0T>JORm<VY!Fphv7X$ ze)9`fUs=U^?-`jHA$~a~48u04PK7rfL_9wnp)FVkCV?9=Cyi1YQln&nbK&Ts^Ef`IMjxAg~_8V~u zjN~?do(>?X!#j5svsQM*ZI$B4r6W}6+z~CyH58BJ1QaL*H)a)|Mbhz#dGE2))6)uI ze0*FEd;TGhN&+RsSdbkut9+xz(R=;=)GnYG)bX^HBF;Y#rg$Db@@ zYVZ5|+hA+$_3)$cY?in%F-QGT#V21JbB12%x_Kh`P`HJ>99 aY5Wao5{cM4SrwrG0000MzCV_|S*E^l&Yo9;Xs0002#NklbmWyG^K1@Pnx;=>j`IjnujIRe0~Usw$Ia`@YAoiHHzFFae7QKE|k?(Zv`{(Fboc$u4~Qn+zx!7Sb}dq zuW6bN_%I>Er+Y5?)%<@&=Uf0@()m8gC9SLRnsfnvRQ3CTp{h9Nc0d4L0RavveoOv6 o#e3ikd;!Nhe*{h`KdI{F7o2QdKW|r!$p8QV07*qoM6N<$g6(*VsQ>@~ literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-010.png b/kde/powermanager/pics/battery-discharging-010.png new file mode 100644 index 0000000000000000000000000000000000000000..8e97a04e2e2147bb1d082317dfcbc97d2cbd94d2 GIT binary patch literal 392 zcmV;30eAk1P)MzCV_|S*E^l&Yo9;Xs0003SNklQ!j1Wi%QE75OoloLRFOx=LDWF~$Ue z%k{cuY3H1@241Z$U~4UL9JAeSvDOBGKNpK8*f~d8mhDyC@1u<|^m;uun@uqIxZ43> z27`0ONs>?$MQ|%B)vd^~tWLBB&huOWq-iQq6a@jzaCiYi4IEu(mgCQa)r8<7@auz!oc;i-oou;-fjqka0000MzCV_|S*E^l&Yo9;Xs0003TNklB@06b#twOdWI+Oz_kxG%E3_+f-hiPJh#^x&rO+0NjsYiXl1^^B z^_NcP-Dn3(caSZg&&lAz*Vp6+YjK0ISfBO*BG zg1{_3YF0$BQ55Ok;LrCqSVR~Ohs@`5L?jUWx*Hrt5k*n#(TZlp*I4Hq{eGW3&%?p% zMa?>Q^Ivfs$INE4@K!WbwPHLTR~rWdXIW+d(lph + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Oxygen team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kde/powermanager/pics/battery-discharging-030.png b/kde/powermanager/pics/battery-discharging-030.png new file mode 100644 index 0000000000000000000000000000000000000000..29d42a7e9fa9b162b5f28ab2e53253c03d3d5fe1 GIT binary patch literal 398 zcmV;90df9`P)MzCV_|S*E^l&Yo9;Xs0003YNklR4+U<6O0KLavJ@_;U1&^LamHu4j zl#`dHX$Cw8k?Zwht{;6NTV)z!9B|{$+dhns;%of&aRk0p^=w*qW1i1D4 sd%r&TeH-`$K7ifHx&!t;->d5J2V!7v0rEu>82|tP07*qoM6N<$g057ie*gdg literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-040.png b/kde/powermanager/pics/battery-discharging-040.png new file mode 100644 index 0000000000000000000000000000000000000000..93d1128276c5b58ab71aa24261a5f406d189e2fe GIT binary patch literal 398 zcmV;90df9`P)MzCV_|S*E^l&Yo9;Xs0003YNklcn%D`p3`7LyTo8Eq zwydm+2sVl${V#YuU)O_0gu!6IYPCW{g20>kCJY=!5k*ln)^S(5Smzv_PKU)}5e(kH z?3_oeI_%G zFK;v&je+MNa=l*6^`kFk|4JHT9B||7ZEwa~`89rfI{;s*diH9Js$z_30S>qW1i1D7 syx10mBaw5C8xG07*qoM6N<$f=o@J>i_@% literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-050.png b/kde/powermanager/pics/battery-discharging-050.png new file mode 100644 index 0000000000000000000000000000000000000000..3d84a36ec0e36ae01b41a5e9c93266ca7608c477 GIT binary patch literal 399 zcmV;A0dW3_P)MzCV_|S*E^l&Yo9;Xs0003ZNklsxJ5e9<+tJMk-sVvycH~#}iQAAM`jdk3WF4j3mr_*7vSk!{| z@B1LIbMCj}IF6Z4r?sO9)o~PAmK_s~f%7~!0BM?Puh*+A=s)(u!KXnTc=$970?%Z| z@#T$1qcQLtR9UYVbN%Q`*}sy;7zf<={kAvbt^69ly&ZrrRXuw>MpZG!v;YU(0Rr55 t|K6_;-fsh+zz48BS+~H>$2(O$`~ZCKZvpjAC_exI002ovPDHLkV1iBLra1rr literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-060.png b/kde/powermanager/pics/battery-discharging-060.png new file mode 100644 index 0000000000000000000000000000000000000000..98f00f8895ac054638adacb00e0faf839194a71f GIT binary patch literal 399 zcmV;A0dW3_P)MzCV_|S*E^l&Yo9;Xs0003ZNklS1VPYlwbn8{G`lG5Qc~8N zKNymk3}3<|8KbIPLamEXui!JOD*gg9KpV2m6tBWstAoLydh^$?mou>wY~hFq&bi9O z)!QlvEF#!Aj`c6_X0ZteiwOOGpY?i;h*TzS7u)}V<2WYI^Ttlxkq*{5M-)XYm&;o4 z;r$Q97L$si@7%XLbfVsjB&t?UvGOe-ip@v?d=GBsp{D;GOCI(rUf|Q4iMnh t&+q;C;OA}N6ZimjC*uy-dw;L0#~%tlZvn)Cd8z;a002ovPDHLkV1lo6uF(Jh literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-070.png b/kde/powermanager/pics/battery-discharging-070.png new file mode 100644 index 0000000000000000000000000000000000000000..5003115dfa319aa505e2b5cbc57a6b6594c54e34 GIT binary patch literal 397 zcmV;80doF{P)MzCV_|S*E^l&Yo9;Xs0003XNkl1~(q7EikMUKYs)$IGuy|c0 zfmIcs=ehj_Ud>kNU{$5l>9AZbQPn76JzM_|oaZ@ZS)OgjO>N>uge=RL&*vxL-Ps4)_Z$5r@KmQ9 ze%@d(7y(a7q*Fb+YoV{?e;ahp32+nU>%lI#Dve*kd*IW|qTl4q4Ch=85a13_;5PK{ rL;Dc=b>IVd2R29T2H1vpYi9dzr`~L{WZ-#d00000NkvXXu0mjfMzCV_|S*E^l&Yo9;Xs0003VNklVPQBB1dY)c!=%XGMMJthJAW|D z%r1Pp%dW*;k~!fX!LW~OWaSH`YFCjR22~cVEMX? z0;?)MNfP@DUN6@1U{z%>7_eHcP*ngni%m1&BuUtAw~gJnr#-xg(CKt2%d!R^-VRY< z5uv5342MHR1XZoe5u>^klgXrBXhN!rpUq|tAj>jK(-eUIV?Q2znpEI%cN_(t>zumt z@;onqrzqmDp51lOSMvWYI_Ctq3H$9}E?6}hzk(fsPcu8~DraUm=URXOcYp%7A-)g( pA;fLq19%5^Cw>R)L%uh&<2R=YY>CV1-vIys002ovPDHLkV1lBHq%i;h literal 0 HcmV?d00001 diff --git a/kde/powermanager/pics/battery-discharging-100.png b/kde/powermanager/pics/battery-discharging-100.png new file mode 100644 index 0000000000000000000000000000000000000000..ea795ea0bdb9d29787fd67ae4a2eb26dedd70c52 GIT binary patch literal 374 zcmV-+0g3*JP)MzCV_|S*E^l&Yo9;Xs0003ANklJvGZ5FVZA!S*5 zYc_F{4{T-(mIIo{h8SaBaQ#-()o!+q`_@NPm3m#%(;m%xlLf4*6lO-fu2I!2P&hh@ z(aVS}d0xaXxT>lEFquqzct6YsJ~q>bX%_IxR)1g-naAh&lmjdx^KcpTZODI%Ap`}k z(tal?B( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_1.svg b/kde/powermanager/pics/battery_charging_1.svg new file mode 100644 index 0000000..213708d --- /dev/null +++ b/kde/powermanager/pics/battery_charging_1.svg @@ -0,0 +1,3551 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_2.svg b/kde/powermanager/pics/battery_charging_2.svg new file mode 100644 index 0000000..e458901 --- /dev/null +++ b/kde/powermanager/pics/battery_charging_2.svg @@ -0,0 +1,2822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_3.svg b/kde/powermanager/pics/battery_charging_3.svg new file mode 100644 index 0000000..d5c431a --- /dev/null +++ b/kde/powermanager/pics/battery_charging_3.svg @@ -0,0 +1,2093 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_4.svg b/kde/powermanager/pics/battery_charging_4.svg new file mode 100644 index 0000000..f6b66df --- /dev/null +++ b/kde/powermanager/pics/battery_charging_4.svg @@ -0,0 +1,1364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_5.svg b/kde/powermanager/pics/battery_charging_5.svg new file mode 100644 index 0000000..77568b7 --- /dev/null +++ b/kde/powermanager/pics/battery_charging_5.svg @@ -0,0 +1,2822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_6.svg b/kde/powermanager/pics/battery_charging_6.svg new file mode 100644 index 0000000..e72630c --- /dev/null +++ b/kde/powermanager/pics/battery_charging_6.svg @@ -0,0 +1,3551 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_7.svg b/kde/powermanager/pics/battery_charging_7.svg new file mode 100644 index 0000000..8a62312 --- /dev/null +++ b/kde/powermanager/pics/battery_charging_7.svg @@ -0,0 +1,1364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_8.svg b/kde/powermanager/pics/battery_charging_8.svg new file mode 100644 index 0000000..c841f00 --- /dev/null +++ b/kde/powermanager/pics/battery_charging_8.svg @@ -0,0 +1,2093 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_charging_9.svg b/kde/powermanager/pics/battery_charging_9.svg new file mode 100644 index 0000000..3a01ca9 --- /dev/null +++ b/kde/powermanager/pics/battery_charging_9.svg @@ -0,0 +1,1358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_0.svg b/kde/powermanager/pics/battery_discharging_0.svg new file mode 100644 index 0000000..8bf6129 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_0.svg @@ -0,0 +1,1941 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_1.svg b/kde/powermanager/pics/battery_discharging_1.svg new file mode 100644 index 0000000..fbbaea8 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_1.svg @@ -0,0 +1,3343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_2.svg b/kde/powermanager/pics/battery_discharging_2.svg new file mode 100644 index 0000000..8297409 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_2.svg @@ -0,0 +1,4713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_3.svg b/kde/powermanager/pics/battery_discharging_3.svg new file mode 100644 index 0000000..abade67 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_3.svg @@ -0,0 +1,4713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_4.svg b/kde/powermanager/pics/battery_discharging_4.svg new file mode 100644 index 0000000..a415ad8 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_4.svg @@ -0,0 +1,1964 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_5.svg b/kde/powermanager/pics/battery_discharging_5.svg new file mode 100644 index 0000000..2a06ab3 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_5.svg @@ -0,0 +1,3334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_6.svg b/kde/powermanager/pics/battery_discharging_6.svg new file mode 100644 index 0000000..149b530 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_6.svg @@ -0,0 +1,4704 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_7.svg b/kde/powermanager/pics/battery_discharging_7.svg new file mode 100644 index 0000000..6b2ed81 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_7.svg @@ -0,0 +1,1964 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_8.svg b/kde/powermanager/pics/battery_discharging_8.svg new file mode 100644 index 0000000..2c8438d --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_8.svg @@ -0,0 +1,3334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +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 + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/battery_discharging_9.svg b/kde/powermanager/pics/battery_discharging_9.svg new file mode 100644 index 0000000..b5eea78 --- /dev/null +++ b/kde/powermanager/pics/battery_discharging_9.svg @@ -0,0 +1,4703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2006-11-06T19:51:02Z + 2006-11-06T19:51:02Z + Illustrator + + + + JPEG + 256 + 140 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA +AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK +DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f +Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAjAEAAwER +AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA +AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB +UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE +1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ +qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy +obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp +0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo ++DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FUh8+/8oN5i/wC2 +Ze/9Q74q/ObJK7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX +Yq7FXYq/TfIq7FUi8+/8oN5i/wC2Ze/9Q74q/OWmSV6Z+XP5SWHmzQH1S41CW1dbh4PTjRWFEVWr +Un/KxdTre0ThnwgXsyn/AKF00j/q8XH/ACKT+uGnE/lqX80O/wChdNI/6vFx/wAik/rjS/y1L+aH +f9C6aR/1eLj/AJFJ/XGl/lqX80O/6F00j/q8XH/IpP640v8ALUv5od/0LppH/V4uP+RSf1xpf5al +/NDv+hdNI/6vFx/yKT+uNL/LUv5od/0LppH/AFeLj/kUn9caX+WpfzQ7/oXTSP8Aq8XH/IpP640v +8tS/mh3/AELppH/V4uP+RSf1xpf5al/NDv8AoXTSP+rxcf8AIpP640v8tS/mh3/Qumkf9Xi4/wCR +Sf1xpf5al/NDv+hdNI/6vFx/yKT+uNL/AC1L+aHf9C6aR/1eLj/kUn9caX+WpfzQ7/oXTSP+rxcf +8ik/rjS/y1L+aHf9C6aR/wBXi4/5FJ/XGl/lqX80O/6F00j/AKvFx/yKT+uNL/LUv5od/wBC6aR/ +1eLj/kUn9caX+WpfzQ7/AKF00j/q8XH/ACKT+uNL/LUv5od/0LppH/V4uP8AkUn9caX+WpfzQ7/o +XTSP+rxcf8ik/rjS/wAtS/mh3/Qumkf9Xi4/5FJ/XGl/lqX80O/6F00j/q8XH/IpP640v8tS/mh3 +/Qumkf8AV4uP+RSf1xpf5al/NDv+hdNI/wCrxcf8ik/rjS/y1L+aHf8AQumkf9Xi4/5FJ/XGl/lq +X80ML/M38sbLyfZWVxb3sl2buR42WRFULxUGoocDnaDXnOSCKp59TF2b9N8irsVSLz5/yg3mL/tm +Xn/UO+Kvzp45JX0P+Qgp5Il/5jZf+IR4Q8x2x/fD+r+t6Ri6p2KuxV2KuxV2KuxV2KuxV2KuxV2K +uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5J/zkQK6PpH/MRJ/wAQGJd32L9Uvc8L44HoX6Z5FXYq +kXnv/lB/MP8A2zLz/qHfFX528ckr6C/IpwnkmQf8vsp/4SPCHl+2P77/ADf1vRfWXF1Vu9ZcVt3r +Litu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3 +rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitu9ZcVt3rLitvKP+cgmD6PpPtcSf8QxLu+xPql7n +h/HA9E9s/wChr/zR/wB9aZ/0jyf9VcFK7/oa/wDNH/fWmf8ASPJ/1VxpVC+/5yc/MjVbK40u6i04 +W1/G9rOUgcN6cylG4kymho22NK8f44Ve6/kvJw8nsP8Al7lP/CphDyvbR/fD+qP0s8+sYXU276xi +tu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK2 +76xitu+sYrbvrGK276xitu+sYrbvrGK276xitu+sYrbvrGK28y/PN+ekaZ7XD/8AEMBd52GfVL3P +G+OB6RV44qi7LR9VvlZrGynulQ0doYnkAJ7HiDTFqyZoQ+qQHvKMt/K3mYXEROkXoAdSSbeXx/1c +aYfnMP8APj8wp/4U8z/9Wi9/6Rpf+acaX85h/nx+YetfljbXun+WjBeW8ttMbiRvTmRo2oQtDRgD +2yQeY7YyxlmuJBHD0+LLfrHvi6q3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9 +Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPf +Fbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3nv5yPz0jT/a4b/iBwF33YJ9cvc8n44HplXjir0/8 +opPT0/UPeZPwU4Q8x2+fXH3Fn/1r3yToOJ31r3xXiUJ7irjftixMlP6x74ot31j3xW3fWPfFbd9Y +98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfF +bd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98Vt31j3xW3fWPfFbd9Y98VthH5qvz0q +z9pz/wAQOAvQ+z59cvcHmfHIvUKvHFXoP5bSelp937zD8FyUXlPaI1OHuLMPrfvkqed43fW/fGl4 +1OS6HLr2w0xM1n1oeONI4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w +760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760P +HGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4w760PHGl4 +w760PHGl4wxT8xJPU0u270n/AONGyMg9J7OSuc/cGAccg9Yq8cVZl5Kcpp89DSsx/wCIrk4PH+03 +1w9xZD67eJyx5ii7128TitFozE98bWi71T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja07 +1T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ +ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tO9U+ONrTvVPjja071T442tMf86Ny0yL2mH/E +Gyub0/sz9c/cGG8cg9gq8cVZT5Xbhp8nvM3/ABFclEvJe0YucPcU49X3yVvOcLvV98bXhbEmNo4X +epja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xep +ja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja +8KT+aTy01PaZf+ItkZPSezY9c/cGK8ci9arcMVT/AEM8NPPvK/8AxFMLy3b4vJH3I/1cbef4Xerj +a8K9JdsbQYt+phtHC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepj +a8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8 +LvUxteF3qY2vCl2vnlpw9pl/4i+AvQezwqcvcx7hgerVeOKplYXUEdp6TvwYSM24JqGCjsD/AC4l +0vaegyZ5AxrYIlbu3ZgqyqSTQCj9T/scFOt/kXN5fNR/ScPg33f240v8i5v6PzRcE/OJXGwapFfY +0/hi67VaWWGXDLmqepjbj8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71Mb +Xhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXh +d6mNrwu9TG14Xepja8KF1U8rD5Sp/wARfCHe9giskvckvHF6hW44q7jiq+3X/SIv9df14qs44qmU +DcbaEf5J/wCJtkS8r2yP33wC71MFup4Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8Lv +UxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUx +teF3qY2vC71MbXhd6mNrwu9TG14Vl2eVi3/GVP8AiL5KLu+xB+8l7ku44XpX3R/g3yh/1Y9P/wCk +WH/mnIq7/BvlD/qx6f8A9IsP/NOKpV5s8p+VYfK2szQ6NYxyx2Ny8ciW0KsrLCxDKQtQQcVfGPHJ +KqI5C0r06ZCTzHaw/ffALvUyNus4Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxt +eF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF3qY2vC71MbXhd6mNrwu9TG14Xepja8LvUxteF +3qY2vC71MbXhd6mNrwu9TG14VOR2Yha7bkjtUf7eTg7fscVkP9X9IW8ck9G++8irsVSjzh/yiWt/ +8wF1/wAmWxV8Q8MkqlJsxGVz5vN9qf3vwW1OQdc6pxV1TirqnFXVOKuqcVdU4q6pxV1TirqnFXVO +KuqcVdU4q6pxV1TirqnFXVOKuqcVdU4q6pxV1TirqnFXVOKuqcVdU4q3GKyfQf1jLIO07J/vD/V/ +SFbhk3oX3rkVdiqUeb/+UT1r/mAuv+TLYq+J+OTVCzikn0ZVPm832p/e/BTyDrnYq7FXYq7FXYq7 +FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVS3FZf9if1jLIO07J/vD/V/SEVxyx6 +F935BXYqlPm7/lFNa/5gLr/ky2KvinJq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY +q7FXYq7FXYq7FXYq7FXYq7FXYq+7MgrsVSrzXw/wtrHOvD6jc8qdaei1aVxV8b/7gv8Al6/5J5NX +f7gv+Xr/AJJ4q7/cF/y9f8k8Vd/uC/5ev+SeKu/3Bf8AL1/yTxV3+4L/AJev+SeKu/3Bf8vX/JPF +Xf7gv+Xr/knirv8AcF/y9f8AJPFXf7gv+Xr/AJJ4q7/cF/y9f8k8Vd/uC/5ev+SeKu/3Bf8AL1/y +TxV3+4L/AJev+SeKu/3Bf8vX/JPFXf7gv+Xr/knirv8AcF/y9f8AJPFXf7gv+Xr/AJJ4q7/cF/y9 +f8k8Vd/uC/5ev+SeKu/3Bf8AL1/yTxV3+4L/AJev+SeKu/3Bf8vX/JPFXf7gv+Xr/knirv8AcF/y +9f8AJPFXf7gv+Xr/AJJ4q7/cF/y9f8k8Vd/uC/5ev+SeKu/3Bf8AL1/yTxV//9k= + + + + + + + image/svg+xml + + +image/svg+xml + + + + \ No newline at end of file diff --git a/kde/powermanager/pics/processor.png b/kde/powermanager/pics/processor.png new file mode 100644 index 0000000000000000000000000000000000000000..1405687d8e608403421fa88e34374801f73dd5ff GIT binary patch literal 908 zcmV;719SX|P)MzCV_|S*E^l&Yo9;Xs0009YNklbL86uoymZ~XkQiWTR>CWb;3g47TUdSMZ=h)Afis=6&ZD)9#jOXx0tB74N9>QAV$ zY$cG2Q5%7blgdQ0l9(vA-^`mgpDe)EbF%KPS2`NaNcY@x@43>T_a0tHkk`WchBzNI zf7#pH`)y}ur|F#Y&N;Bwf-we^QlPaSpI#|-^$k0n&h!2K{rH;VIF5s@t*uX%mX=^~ zaS>-{XR%x^BV!B;g#tR~P$`9+b21nVNWb4FyWGenex5Q&IV2!V}8p?d|RF5z%wbF-=p< zvWzID3dWd%)|wh)sMb0##xQFwGsYCHwV^R4wAO}~mzSzuuRoX~-re0@ou8kNIOkpn z0co0oloGNmo6OP}GX>||R8*(%7WDS@|Mk zbB!^ewJr{aLpw7w^AiAkM&dYT%gf6j3n5&RB)~Zb&N-xMIze9jm2H^nB1w{rF}4=R zF{3LhD?c*Ea5x+iL<9hU{HkfKA|}=6A*zE2X4YiLJGh zWTn)U@~a)>AQ@v1=-k{~V!rj}y(x<=lkG|Y!-KbSNz-U*!-#ESy{FL0g z`!Z;~5Jz`zw{I-Hdj{3YKkC-Xd7qM$-v9c99=3xgj~@TNi{5+q_M4AB9F7Vi3UqiW zLtm}xnO3WO*lZ@ZvXLJG3k5ZzIMO9osi8?9;%17M*jddV(C0F{cazIye} i^Miu}gKxg3ar_JMs)ss$*U>Tn0000 + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kde/serviceconfig/pics/16x16/daemons.png b/kde/serviceconfig/pics/16x16/daemons.png new file mode 100644 index 0000000000000000000000000000000000000000..1705fc8ab5a11f692136c601139ac4e407acacb5 GIT binary patch literal 682 zcmV;b0#*HqP)d-sj~$PD(3{XFJ31!{MCcakj+L($c3|t#%FIuzQ3M zg7MNA&N**wZSCpe;^M>@GDpd#}ioWj`jWOo~Qp&tiim3~eypi)^<9){uGxq#<+ zzA*;p94Z&^s@}l&eWc8jWftcgK@b#%?!farj4?z}jL>Z!+`Emj8E=yLA}+vTHHKz2Iw8-9K@4+MVwG^9uNe?aU~c;C?w3oLP8HTkDi|HzOO8#87m7* zRU^)}yYKn#@1FC!rv<4`Vl*{FPlarH^MNyDt8Br8*=k8rTxp)z^@q~$Tj2D9I zB4lFvC?`*xpjTKw(u z72X(>ymS67D97+<9#lYWd_V8LKFx=J{}=bh#;Ak=MN#0K8wCdX2B_6)BT6YcoeoRO zPl#Q?%!q`~E<;g(_pq@7w?Cz_aD!J5&+wmDPm(t3NGYjStHWCWUmH6DluD%$Yb~u- zi!9HGt3#;uIe2^%f*6V%8mpi+j2$GXO%MeEZmT=uI355heFH>MR5iwsWf@@*AmTE& z%hOB+4M1$Zq!dU&W?`v1g*2K%Ym`z1K@gWprNJ!A`U6l(#ol|YwJ0e$G(N_^n=vB? zUf|S$QKB#a?`d{2W;Y5NlqgyadfzCe!Y~}^8$e1q0N|X%IY)J10HqXHn>q8o!}#zZ z&I_J)B$e^G**H%%kjZgtmrN+qcFjCS36+(2EC_OU(V+;%T9^kVjzJKy1 zjvP6THlDi+UvT^WGK0h8fWx`30D>Th`Ua3v;=S)d++Da&WFGU2_ut~+(KqRK*vTKX z`0TGA@$uz*gz+HG6$l}))?%#{JLT9BpnLWJY1$!88oYC6oP(2tu>8rU5#ga&<@K?D z@?fORg844K$a~tgSfpx|Q-e55wph!2c*6tl( zy9!~TKn+k&8a!Vtf(`)-ssy9QAb`%(X+&;&v%xb{z*X%bYO&_H?uE2N@ z0wg^6vS2YEBWKsmRA!#zM`oPR1iXt7n)Amrrm1x{Gw~e-h16QfOAfFjb*zT z;VrFtgZkPAQVN6u=e{aB@BMawd`}8&?*#81oit};I!vE}f0VjgEVx!3pDjE%+y#%PM7DDpf{ z`UXgnq*W9JVHo1^RLes&{5;p!ZzFAlb~$yuNbVAXxI|puM;HZozo|f$Wo;ns8^BuI z(po#El$27Sh>4b8WMZm`)dKGY2g=_fP!gk|ogbl*Y>?-hcR`-#EnvqQ*zGoKY_(zx zAVW|4PL1-xmwE1$5mcZ6PnH%uy49v#hsC8^y{;*Wq5<@O1g%zUU29!LQ51LEF>R;Z zxl|{47%?_6igTX&YKQu|;r_!fNINM?Dy+3=t=IOXK$@m4=iC!uunS0fR6f$zHFc7NzOB)xf1BuP?V zSXj8Y%lhm%t!za(j^pn+=f>FKCv$r!rKHw+&02efU25Qe==S71IY3_700000NkvXX Hu0mjf0Q3FZ literal 0 HcmV?d00001 diff --git a/kde/serviceconfig/pics/laserwarn.png b/kde/serviceconfig/pics/laserwarn.png new file mode 100644 index 0000000000000000000000000000000000000000..f14986dc4d4fe13093beeefcdb43489971ace999 GIT binary patch literal 476 zcmV<20VDp2P)T>BjIIR0)ZjU-OX| za3!n8v7PADMDRzIQcA7!JnLmyXxp~Ewoj9kQY#U$vB&+fw)VVL)*a%mbC^*6t(%lG@}*kW2e9NSY_ zbt&0uuU=kWmd8g1JG=USwSSEeX)48AQ&)S%VBSVxv=;!K4%8I|Tc*{qdndR0=V**8P@JVd6OAqr`1lfZAT*7+ zsUVi5bSe)XA~rJ}>G&`f-+{$qafEzBYp_IuW~IhcPqDKQRwMW`t>u6O#I_U&gr&X; zS58BUElYAZ92DebBLQd%7$7Pc^nfm|gS)Tg>ld19>h?99tY>w3-K_vgVrcwHKM-Bd zNSjmD$&1tUNz5 m^7%_&--|bq$dAFlFu)(DjZ6 literal 0 HcmV?d00001 diff --git a/kde/userconfig/pics/hi16-encrypted.png b/kde/userconfig/pics/hi16-encrypted.png new file mode 100644 index 0000000000000000000000000000000000000000..b4bf017259947028c7bbf4f5b973c380c87f7ae5 GIT binary patch literal 927 zcmV;Q17Q4#P)k#5U*y}5ANDLPER$JTS;hJI_?VV1UAkq-!i5vAUb(WD0SN%r!r1@;#CZMY zO^^t~pa1`N2=Mc}KY#U#;q<9f|M>+37>!L$89u#z%aEO!S$pR6=@x7T00a=jh0B*2 zE?l`X|Ni60|JQHY^xwQ4&9{jg=bp>e-17bOlLNPHhyfs-M@G5@tZeq7=U5J1oAQi8UP3&mhZoQGBE#V zW%&A&iQ(Tr#@|4(w?USi43h#_7#7|zV~|kPWcb7UkKsQf<1diq=mr1;5X;YfvfPXs z>B@iJXfT*6e^fFOV-B5|BJlppqu)#kVwb*MKfhMs@n=p3<=;QlpD{4}MK=H-fEaJD zR00a;dBP+8RCqFP8gn1eMvv2?dhK+%XlaYai=?lY`pT8JBvHfRw$H~C(fsc_v zL4d^`s7D^z1pooW^8YU*!~cIT8UFux2gIKk7`}dF`2F@F!|PY?7@ofS&j5DJHwFf7 zHlX|e0RxMHfdkn9fB<6o_m3GU^M!%oA0q?fua6A>fSNzOc*F4U@qdOpPk@>~GcbJo z!@$7C0*-?J=m7~3Kr9S@fii!-F#P@Wp5f>9=L{cieqy+LkAdOZBL;@2AAwE>#>+RL z0l$7@ivtD*fB<4*WMpRe2MoC1PoFS+di;su`3nYy$8Q)IUIHEU4yf@HFxtQKf2!xNwZ@B{@AU;vbGVl8TxL002ovPDHLkV1f)V Bs}KMH literal 0 HcmV?d00001 diff --git a/kde/userconfig/pics/hi32-group.png b/kde/userconfig/pics/hi32-group.png new file mode 100644 index 0000000000000000000000000000000000000000..23d6f66d2862577d15bd1ab74ce177d570cc7a62 GIT binary patch literal 2303 zcmVa?bkgb=F#YANari#}c1(bH=pq<_Z&L-#B4nF%!M2M3D# zHkHcTh5+-a1I(NMV{g&Y%Wj)DZ^F7OmQ1;{xhacd3yju8Va%!CA$GlSuygO;!;h6a zH#~9YT)$yTYwK!RTh}H`G(J*@f!G=t94s9=+R?S`gW>4t#;!BLe*`dl<{jB7b64DP z^UX`{Z=G6SYgrPl6-p_LQs^`Vqe$a~jw2_0_q7MNUbnVwa{cVKg>HS5iCI758t_R)xw8n&u%FgBnW}g3YDg)G(pEP zDoK!jA9Du(!itsi@S3M$(gbA;iH1}|s39?eSlASceQbIDxh)6Z?_U3EcX2Cy3B^wRK3>AsXx_ zRNyNZPDUILB~VdNN(FHkQ3-+@S_|1thX=}g0dp3BN#k(Y{D^g5u~|Alk1-O<5`1-~ z!Z8(Ic`K&x_y^>JlN5^q%D{rPU!ZmIGm7FA2v6-BuZe}Hbz?5w&crKXIZ^maq#UTul_C}6du#A zTFvZb%W!Oo5Q5f~tLS{clVa~_O0lM*U@(SK0z20Is<*zrQ9U zim5V}NXJ478)G>bX(6SgAtPC|bP>g#&(pT7jfqnxgOq4tVM)o z5eSBhy{NE^?Rwa*hh^DVwv7-HDFp^Wstwv`1Rw?2LZG$cy{_IJ*ROl%p5v#A`v7I1 z3vgn>av4PE|!~MhZ+wjI>Akg0Qg*jY!8sYmF8HEi8<5 zFqR9p2eyZ?Mhx2nYs5ljRi4_ka~Dt^^Jkq4uh~}2II?8X{PuB$nnuUt4 zE$Ij$Y_t(NPEGYpsoG5%x{jCegzX_L7cA#P6P7(185^V|O0@st*4@uM^w_f-fnFfJ zAi${fi(B64QAYl~y{@x%d%k^cm(}O((;aLcd~@jat@~em>gjEdO&{NMdVEv;XN2uY zX?qCEMvOTX7Dh-A5`!?WzPbOU`yP4Xh2p@_yFm4#5g1d;qnZ4^Ii0ulwyL{x4U=-z zG%(}{I;>)LM{e)51GMjY=JB0RtZ2RTNUkQ^U^`yDv>X@P5m*v!gb7m>ob2mA{HK?< zJ^k$;-1lU0VCWz)z{eJ(i|z*+e^ax5`jmz1ubqBvA>(8)LW2NdB!23%rTecZkG*l| zC-Ea}xa*rYOZ%8B4y4Mqu$n zjY6%_S6|XJ0&@Tre2hl(7YVE=8@!;W)e~PmrFzXlN z#t&QO(QI9f8@5CQlLOLbg>V>Y#R3q9RKo0^b#v*73Do9uWP*Tv5RmhIYJz~ws5wD^ z9RxT^QxJNh7#JGdmV|qNt7N6h$b8kSK&`|YC#eX0IQ;yv^BtM@Q|ev z8KBk}@_;AAhoz-})EFYbbzN_v=iML!@mp<7$mq80EP$#>b1Jo7D_1KXRq+0q!IK^m zfk7h?C@q-&T>qqoeruAI^Qj4a8i8ghaU2I62jpmn*BX?V?3msR8@E zlme|0N|A|TE|JJpS}lJTc3co3c}Z7*cSrLG1ti30z3=30q{Hz zoz0Fcea4K;96mZ%3gQ?kCCgR$@>VzB)Du_EoCh$*e1U5pDlk!T5c8{^Fq~Tp&JiKV zrlXZrt%7C2=u(U^BhHfq;uxZk9+eb*VT~1y`NUtdi%0+tkmcX}+Fe8jh=B0Fc=prR ZzW|^METrrd_HO_H002ovPDHLkV1feiM;-tG literal 0 HcmV?d00001 diff --git a/kde/userconfig/pics/hi32-identity.png b/kde/userconfig/pics/hi32-identity.png new file mode 100644 index 0000000000000000000000000000000000000000..afc3ab57fe7b9734922ca74458d8a64777026eb9 GIT binary patch literal 3148 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 literal 0 HcmV?d00001 diff --git a/kde/userconfig/pics/hi32-user.png b/kde/userconfig/pics/hi32-user.png new file mode 100644 index 0000000000000000000000000000000000000000..8f73aec8ca9b52da6f121c0e0ee71c4aa132e5cd GIT binary patch literal 1806 zcmV+p2l4ocP)t?X3=2b~UfDKbtoiylnyuh9s9IUnJl zKx8qV(n1TL>gH&3HP5f_Tfb&m?=GqumZ2yrxon#F%qZ6`yvw-{hll?@!cT{jytWvK zvauc1V;ea6^3zMi<+o0VcyL4*$vI&pLqbo?2_qF2dLk&|GoOe{Z~j=k{A8`z*~F`W zV^=M$?PULfXK>ZFk_u0fO-IRPV)rPUj*?ABP;4&pMvy9fY+!VX zYq2~-<$9>);C2LfzOADJ+3qDgc^SmLMUn)PqGII@!Z$wV`o)h(MdHNLaV{F>Klj&gmuJpfs zm3JNrz+tD+<8~DnTUr7EqBI#rma$BW;A9L{Tf)(&zeL;WcE}nKxe3`gE{~t~8np-V zQn|ZOW(~;reSVVJ7+GCMmK0P)!Ln2`83}}!!OgoF80bQ?sW5k~)J8xq35rUM&%PA$ zP9U>rfN0v9%nKWSZ!_6k4$~AES@5|1tXkeocUJ&!l^aYG%v|vz3BUro9ppS#!IG)4 z(;f(LHOfDJ@W=5JKRUGJ8yy3?++L5H+FCz;p9j@tgM1ze0&MqlRk6a8OhmFlKz}Fz zAT$yY-;L|ZK>xPxLnt+nGm5KU2+%0%?ht^}#f_nlIDg$b59G@M$mNoNc@zaju&eQ?csPY#U@bhh~0J&$yOrx_g8 z;HUye4LEDT<;RRpaQaWLTgOhk^Xh1v<3OsCdAT((KjuF6eBU@dUd0Ce4H{Og$L?=J zb+|xSnCTd?n^(9x7U#V5O)23HeM02DxrG=n2w-sVTR(g1$kETO>-8cVpXKJ%S#lF& zOpZ@tni35yOQ~D2jkbNg)EJ6nSdJgQ_`;XZCX+YLEO3Gkjz=EZzW-OR42zKoA!b5C zOoxP+jtVgq5#rY`ofE(K7xnlKu{=vWeExeLBY)8usbkIhp!}o zB14TI8tQ9Z;Aw&ArMr9G_b^H(Q)XT5>Vum%uW(d(K~;-pYH3Hbw)$vn@^ksp6b`4x z*Sadk8nwufQ%I@Euy|U3O7cLAU4V)=A^96q$ wQ#@FjicpFlWY_@~07*qoM6N<$f{-X@kpKVy literal 0 HcmV?d00001 diff --git a/kde/wineconfig/pics/16x16/wineconfig.png b/kde/wineconfig/pics/16x16/wineconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6b40e1a472d9f7272615b744fb60e3ce72de81 GIT binary patch literal 441 zcmV;q0Y?6bP)kxx#-Kop06(>J9vZ7ogX#@KoQNL&y%J%K0aCEOA3QP(D(pxwLi0I+eP8~f>g2#r^DC0u93w2mxBP21aR3L!>HsqT{FWrAL>7oSZJar9=V z3=9k!mv4_K0wY@IN(IO7c6g$F$S-7u1)Mp%D2igR&KYF_GAI^n{s)Y4i{Y;yC>0#c z_-e=iT;q8#(~Ep-!I;fg^$mN$olxnYo0bYpgzkb)@7WMJJ>}z%={c%oKL`>4eE{($ zuJj4gap1ac3ZS!-U^1D&w(Y#q+EEmxM3hJ=6W{mysG3@>778IUBGOVyO+;<%l2)q~ jT9(!3oM%B0WSiJeRLfWO6&7Fg00000NkvXXu0mjfOk}p4 literal 0 HcmV?d00001 diff --git a/kde/wineconfig/pics/16x16/wineconfig.svg b/kde/wineconfig/pics/16x16/wineconfig.svg new file mode 100644 index 0000000..b79faf0 --- /dev/null +++ b/kde/wineconfig/pics/16x16/wineconfig.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + diff --git a/kde/wineconfig/pics/32-wine.png b/kde/wineconfig/pics/32-wine.png new file mode 100644 index 0000000000000000000000000000000000000000..e417ba188e1e99b02a7a2ccef0273a66535c94b2 GIT binary patch literal 904 zcmV;319$w1P)=SF6u;73IsdGob5gOpWi)ScLu)D9LGtt)@>m5eZOOj zNwn4pi!#@B+m7QT0Gpeeu`wnIf}rjDei{Tpe6PWyD2jpfQ)jEIt8uMXORLptTWcNN z3xMzYX&8p-Z9esS{Xi)-|B`HMY$Vlc)qc)zd3o911|VkzI6gkUlV7jbyY+l#fUm!5 zq_VJVZ*Sk&XlrY0X&8pxt*x!@O+I3l;H_3`|DyBy;89=ls#0{{E3&dI8TW@M3wFuS zzZ~wluA2ry!1?+4-c4G17J#FpqxkWa$4~Yfj`MfX8%BwPB1GdLIk_D2o0r=^xkPLI z^KDvs#^uA~Q9y{{JC*>81?0zsRwyswFM6C_o<5y>4?G!E`JPoA1OfpFB!co|O$DdC z#}RVxb6Qga7-Qm&co(l=5Qxdm|1$+x;DRe&kY_zvWOE0ocz#q@Ze&o{$&EiO0}GT` zUef247|G~vX>;*Z`MGmlc zF8~UGb8Uf$Yh{qYGyb73#!v66H}x1^VHr*CE`Vavd}B(U@rK{!MR#15hj-PRIs>^b zITK^bYXTB@E^>YqCsdc~8IavQqtljP$qk3a)eqm=pjJgQq~H(nmS5!GycVOZKN$RY z?_WWI>!sU-c27q32@U&5SFFFZ)OKArVi8vH{q)-8r9jLgZGC+`1*{O_;7he;dJB|N zIF8e;)oO$>Sen{Q`lXWw<*RBPQ)N?E0p)mpbb&x;lcva+%g`@Ww5Nfbr4 e(P*@1)%q8GWcc#rf!M(S0000Zj4HrnL5*LVG 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 literal 0 HcmV?d00001 diff --git a/modules/ixf86misc.c b/modules/ixf86misc.c new file mode 100644 index 0000000..90e330e --- /dev/null +++ b/modules/ixf86misc.c @@ -0,0 +1,516 @@ +/*######################################################################### +# ixf86misc.c - # +# ------------------------------ # +# copyright : (C) 2004-2007 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. # +# # +########################################################################### +# A small binding for playing with the gamma and RandR settings under # +# XFree86. # +# # +# Simon Edwards # +#########################################################################*/ + +#include "Python.h" +#include +#include +#include +#include + +/*************************************************************************** + XOpenDisplay(displayname) + + Args: + displayname - String + + Returns: + opaque display reference. +*/ +static void ixf86misc_destroydisplay(void *ptr) { + if(ptr!=NULL) { + XCloseDisplay((Display *)ptr); + } +} + +static PyObject *ixf86misc_xopendisplay(PyObject *self, PyObject *args) { + Display *dpy; + char *displayname = NULL; + + if(!PyArg_ParseTuple(args, "z", &displayname)) { + return NULL; + } + + dpy = XOpenDisplay(displayname); + if(dpy==NULL) { + return Py_BuildValue(""); + } else { + return PyCObject_FromVoidPtr((void *)dpy,ixf86misc_destroydisplay); + } +} + +/*************************************************************************** + DefaultScreen(display) + + Args: + display - display object. + Returns: + + screen number - integer +*/ +static PyObject *ixf86misc_defaultscreen(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + int screen; + + if(!PyArg_ParseTuple(args, "O", &pydisplay)) { + return NULL; + } + screen = DefaultScreen((Display *)PyCObject_AsVoidPtr(pydisplay)); + return Py_BuildValue("i", screen); +} + +/*************************************************************************** + ScreenCount(display) + + Args: + display - display object. + Returns: + + number of screens - integer +*/ +static PyObject *ixf86misc_screencount(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + int count; + + if(!PyArg_ParseTuple(args, "O", &pydisplay)) { + return NULL; + } + count = ScreenCount((Display *)PyCObject_AsVoidPtr(pydisplay)); + return Py_BuildValue("i", count); +} + +/*************************************************************************** + RootWindow(display,screennumber) + +*/ +static PyObject *ixf86misc_rootwindow(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + int screen = 0; + Drawable pydrawable; + + if(!PyArg_ParseTuple(args, "Oi", &pydisplay, &screen)) { + return NULL; + } + + pydrawable = RootWindow((Display *)PyCObject_AsVoidPtr(pydisplay),screen); + return Py_BuildValue("l",pydrawable); +} + +/*************************************************************************** + +*/ +static PyObject *ixf86misc_getgamma(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + int screen = 0; + XF86VidModeGamma gamma; + + if(!PyArg_ParseTuple(args, "Oi", &pydisplay, &screen)) { + return NULL; + } + + if(!XF86VidModeGetGamma((Display *)PyCObject_AsVoidPtr(pydisplay), screen, &gamma)) { + /* FIXME set an exception? */ + return NULL; + } + + return Py_BuildValue("(fff)", gamma.red, gamma.green, gamma.blue); +} + +/*************************************************************************** + +*/ +static PyObject *ixf86misc_setgamma(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display; + int screen = 0; + XF86VidModeGamma gamma; + float red,green,blue; + + if(!PyArg_ParseTuple(args, "Oifff", &pydisplay, &screen, &red, &green, &blue)) { + return NULL; + } + + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + if(!XF86VidModeGetGamma(display, screen, &gamma)) { + return NULL; + } + + gamma.red = red; + gamma.green = green; + gamma.blue = blue; + if(!XF86VidModeSetGamma(display, screen, &gamma)) { + /* FIXME set an exception? */ + return NULL; + } + XFlush(display); + return Py_BuildValue(""); +} + +/*************************************************************************** + +XRRQueryExtension (Display *dpy, + int *event_basep, int *error_basep); +*/ +static PyObject *ixf86misc_xrrqueryextension(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display; + int event_basep, error_basep; + + if(!PyArg_ParseTuple(args, "O", &pydisplay)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + Bool rc = XRRQueryExtension(display, &event_basep, &error_basep); + return Py_BuildValue("(iii)",(int)rc, event_basep, error_basep); +} + +/*************************************************************************** + XRRScreenConfiguration *XRRGetScreenInfo(Display *dpy,Drawable d) +*/ +static void ixf86misc_destroyxrrscreenconfig(void *ptr) { + if(ptr!=NULL) { + XRRFreeScreenConfigInfo((XRRScreenConfiguration *)ptr); + } +} +static PyObject *ixf86misc_xrrgetscreeninfo(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Drawable pydrawable; + XRRScreenConfiguration *xrrconfig; + + if(!PyArg_ParseTuple(args, "Ol", &pydisplay, &pydrawable)) { + return NULL; + } + + xrrconfig = XRRGetScreenInfo((Display *)PyCObject_AsVoidPtr(pydisplay), pydrawable); + + if(xrrconfig==NULL) { + return Py_BuildValue(""); + } else { + return PyCObject_FromVoidPtr((void *)xrrconfig,ixf86misc_destroyxrrscreenconfig); + } +} + +/*************************************************************************** + SizeID XRRConfigCurrentConfiguration(XRRScreenConfiguration *config) +*/ +static PyObject *ixf86misc_xrrconfigcurrentconfiguration(PyObject *self, PyObject *args) { + PyObject *pyconfig = NULL; + Rotation currentrotation; + SizeID currentsize; + + if(!PyArg_ParseTuple(args, "O", &pyconfig)) { + return NULL; + } + currentsize = XRRConfigCurrentConfiguration((XRRScreenConfiguration *)PyCObject_AsVoidPtr(pyconfig), ¤trotation); + return Py_BuildValue("(ll)", (long)currentsize, (long)currentrotation); +} + +/*************************************************************************** + XRRRotations(display,screen) +*/ +static PyObject *ixf86misc_xrrrotations(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display; + int screen = 0; + Rotation currentrotation,availablerotations; + + if(!PyArg_ParseTuple(args, "Oi", &pydisplay, &screen)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + availablerotations = XRRRotations(display, screen, ¤trotation); + return Py_BuildValue("l", (long)availablerotations); +} + +/*************************************************************************** + XRRSizes(display,screen) +*/ +static PyObject *ixf86misc_xrrsizes(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + PyObject *sizelist,*item; + Display *display; + XRRScreenSize *sizes; + int screen = 0; + int numSizes; + int i; + + if(!PyArg_ParseTuple(args, "Oi", &pydisplay, &screen)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + sizelist = PyList_New(0); + sizes = XRRSizes(display, screen, &numSizes); + for(i = 0; i < numSizes; i++) { + item = Py_BuildValue("(iiii)",sizes[i].width, sizes[i].height,sizes[i].mwidth, sizes[i].mheight); + PyList_Append(sizelist, item); + } + + return sizelist; +} + +/*************************************************************************** + short XRRConfigCurrentRate(config) +*/ +static PyObject *ixf86misc_xrrconfigcurrentrate(PyObject *self, PyObject *args) { + PyObject *pyconfig = NULL; + int rate; + + if(!PyArg_ParseTuple(args, "O", &pyconfig)) { + return NULL; + } + rate = XRRConfigCurrentRate((XRRScreenConfiguration *)PyCObject_AsVoidPtr(pyconfig)); + return Py_BuildValue("i", (int)rate); +} + +/*************************************************************************** + +*/ +static PyObject *ixf86misc_xrrrates(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + PyObject *ratelist,*item; + Display *display; + int numrates; + int size; + int screen = 0; + int i; + short *rates; + + if(!PyArg_ParseTuple(args, "Oii", &pydisplay, &screen,&size)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + rates = XRRRates(display, screen, (SizeID)size, &numrates); + + ratelist = PyList_New(0); + for(i = 0; i < numrates; i++) { + item = Py_BuildValue("i",(int)rates[i]); + PyList_Append(ratelist, item); + } + return ratelist; +} + +/*************************************************************************** +Time XRRConfigTimes( XRRScreenConfiguration *config, Time *config_timestamp ) +*/ + +static PyObject *ixf86misc_xrrconfigtimes(PyObject *self, PyObject *args) { + PyObject *pyconfig = NULL; + int rate; + Time ts,ts2; + + if(!PyArg_ParseTuple(args, "O", &pyconfig)) { + return NULL; + } + ts2 = XRRConfigTimes((XRRScreenConfiguration *)PyCObject_AsVoidPtr(pyconfig),&ts); + return Py_BuildValue("l", (long)ts); +} + +/*************************************************************************** +status = XRRSetScreenConfigAndRate(display, config, window, newsize, newrotation, newrefresh, currenttime) +*/ +static PyObject *ixf86misc_xrrsetscreenconfigandrate(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display = NULL; + PyObject *pyconfig = NULL; + Drawable pydrawable; + Rotation newrotation; + long newrefresh; +// Time currenttime; + Status status; + long newsize; + + if(!PyArg_ParseTuple(args, "OOllll", &pydisplay, &pyconfig, &pydrawable, &newsize, &newrotation, &newrefresh /*, ¤ttime*/ )) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + status = XRRSetScreenConfigAndRate(display, (XRRScreenConfiguration *)PyCObject_AsVoidPtr(pyconfig), pydrawable, + (SizeID)newsize, newrotation, newrefresh, CurrentTime); + + return Py_BuildValue("i", (int)status); +} + +/*************************************************************************** + (dotclock,hdisplay,hsyncstart,hsyncend,htotal,vdisplay,vsyncstart,vsyncend,vtotal,flags) = \ + ixf86misc_vidmodegetmodeline(display,screen) + +*/ + +static PyObject *ixf86misc_vidmodegetmodeline(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display = NULL; + long screen; + int dotclock_return; + XF86VidModeModeLine modeline; + PyObject *returnvalue; + + if(!PyArg_ParseTuple(args, "Ol", &pydisplay, &screen)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + if(XF86VidModeGetModeLine(display,screen,&dotclock_return,&modeline)) { + returnvalue = Py_BuildValue("(iiiiiiiiii)", + dotclock_return, + modeline.hdisplay, /* Number of display pixels horizontally */ + modeline.hsyncstart, /* Horizontal sync start */ + modeline.hsyncend, /* Horizontal sync end */ + modeline.htotal, /* Total horizontal pixels */ + modeline.vdisplay, /* Number of display pixels vertically */ + modeline.vsyncstart, /* Vertical sync start */ + modeline.vsyncend, /* Vertical sync start */ + modeline.vtotal, /* Total vertical pixels */ + modeline.flags /* Mode flags */); + if(modeline.private!=NULL) { + XFree(modeline.private); + } + return returnvalue; + } else { + return Py_BuildValue(""); + } + +} + +/*************************************************************************** + + DisplaySize(display,screen_num) + + Args: + display - display object. + screen_num - screen number + Returns: + + dimensions - a tuple consisting of 4 integers (width_pixels, height_pixels, + width_mm, height_mm) +*/ +static PyObject *ixf86misc_displaysize(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display = NULL; + int screennum = 0; + + if(!PyArg_ParseTuple(args, "Oi", &pydisplay,&screennum)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + + return Py_BuildValue("(iiii)", + DisplayWidth(display,screennum), + DisplayHeight(display,screennum), + DisplayWidthMM(display,screennum), + DisplayHeightMM(display,screennum)); +} + +/*************************************************************************** + +*/ +static PyObject *ixf86misc_xscreensaverqueryextension(PyObject *self, PyObject *args) { + PyObject *pydisplay = NULL; + Display *display; + + if(!PyArg_ParseTuple(args, "O", &pydisplay)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + int event_base, error_base; + + int rc = XScreenSaverQueryExtension(display, &event_base, &error_base); + return Py_BuildValue("i", rc); +} + +/*************************************************************************** + +*/ +static void ixf86misc_destroyscreensaver(void *ptr) { + if(ptr!=NULL) { + XFree(ptr); + } +} + +static PyObject *ixf86misc_xscreensaverallocinfo(PyObject *self, PyObject *args) { + XScreenSaverInfo *ss_info; + ss_info = XScreenSaverAllocInfo(); + return PyCObject_FromVoidPtr((void *)ss_info,ixf86misc_destroyscreensaver); +} + +/*************************************************************************** + +*/ + +static PyObject *ixf86misc_xscreensaverqueryinfo(PyObject *self, PyObject *args) { + PyObject *pydisplay; + Display *display; + Drawable window; + PyObject *pyscreensaverinfo; + XScreenSaverInfo *screensaverinfo; + +if(!PyArg_ParseTuple(args, "OlO", &pydisplay, &window, &pyscreensaverinfo)) { + return NULL; + } + display = (Display *)PyCObject_AsVoidPtr(pydisplay); + screensaverinfo = (XScreenSaverInfo *)PyCObject_AsVoidPtr(pyscreensaverinfo); + + int state = 0; + int kind = 0; + unsigned long idle = 0; + unsigned long til_or_since = 0; + if(XScreenSaverQueryInfo(display, window, screensaverinfo)) { + state = screensaverinfo->state; + kind = screensaverinfo->kind; + til_or_since = screensaverinfo->til_or_since; + idle = screensaverinfo->idle; + } + + return Py_BuildValue("(iikk)", state, kind ,til_or_since, idle); +} + +/*************************************************************************** + +*/ + +static struct PyMethodDef ixf86misc_methods[] = { + { "XOpenDisplay", ixf86misc_xopendisplay, METH_VARARGS }, + { "DefaultScreen", ixf86misc_defaultscreen, METH_VARARGS }, + { "ScreenCount", ixf86misc_screencount, METH_VARARGS }, + { "GetGamma", ixf86misc_getgamma, METH_VARARGS }, + { "SetGamma", ixf86misc_setgamma, METH_VARARGS }, + { "RootWindow", ixf86misc_rootwindow, METH_VARARGS }, + { "XRRGetScreenInfo", ixf86misc_xrrgetscreeninfo, METH_VARARGS }, + { "XRRConfigCurrentConfiguration", ixf86misc_xrrconfigcurrentconfiguration, METH_VARARGS }, + { "XRRRotations", ixf86misc_xrrrotations, METH_VARARGS }, + { "XRRSizes", ixf86misc_xrrsizes, METH_VARARGS }, + { "XRRConfigCurrentRate", ixf86misc_xrrconfigcurrentrate, METH_VARARGS }, + { "XRRRates", ixf86misc_xrrrates, METH_VARARGS }, + { "XRRConfigTimes", ixf86misc_xrrconfigtimes, METH_VARARGS }, + { "XRRSetScreenConfigAndRate", ixf86misc_xrrsetscreenconfigandrate, METH_VARARGS }, + { "XF86VidModeGetModeLine", ixf86misc_vidmodegetmodeline, METH_VARARGS }, + { "DisplaySize", ixf86misc_displaysize, METH_VARARGS }, + { "XScreenSaverQueryExtension", ixf86misc_xscreensaverqueryextension, METH_VARARGS }, + { "XScreenSaverAllocInfo", ixf86misc_xscreensaverallocinfo, METH_VARARGS }, + { "XScreenSaverQueryInfo", ixf86misc_xscreensaverqueryinfo, METH_VARARGS }, + { "XRRQueryExtension", ixf86misc_xrrqueryextension, METH_VARARGS }, + { NULL, NULL } +}; + +void initixf86misc(void) { + PyObject *ixf86misc = Py_InitModule3("ixf86misc",ixf86misc_methods,"Bindings for some XFree86 config functions."); + +} diff --git a/modules/xf86misc.py b/modules/xf86misc.py new file mode 100755 index 0000000..8b952d8 --- /dev/null +++ b/modules/xf86misc.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +########################################################################### +# xf86misc.py - # +# ------------------------------ # +# copyright : (C) 2004 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. # +# # +########################################################################### +"""A simple interface for changing the current gamma setting under XFree86. +""" +import ixf86misc +import os, time + +class XF86Screen(object): + + RR_Rotate_0 = 1 + RR_Rotate_90 = 2 + RR_Rotate_180 = 4 + RR_Rotate_270 = 8 + RR_Reflect_X = 16 + RR_Reflect_Y = 32 + + def __init__(self,display,screenid): + self.screenid = screenid + self.display = display + self.ssinfo = None + self.starttime = time.time() + self.resettime = 0 + self.lastidle = 0 + + self.screenconfig = None + self._load() + + def _load(self): + # Check for the presence of the xrandr extension. + try: + (rc,x,y) = ixf86misc.XRRQueryExtension(self.display) + if rc==0: + return + except AttributeError, errmsg: + print "Trapped AttributeError:", errmsg, " - attempting to continue." + return + + self.screenconfig = ixf86misc.XRRGetScreenInfo(self.display, ixf86misc.RootWindow(self.display, self.screenid)) + if self.screenconfig is not None: + (self.currentsizeid,self.currentrotation) = ixf86misc.XRRConfigCurrentConfiguration(self.screenconfig) + self.availablerotations = ixf86misc.XRRRotations(self.display, self.screenid) + self.sizes = ixf86misc.XRRSizes(self.display, self.screenid) + self.currentrefreshrate = ixf86misc.XRRConfigCurrentRate(self.screenconfig) + + def resolutionSupportAvailable(self): + return self.screenconfig is not None + + def getScreenId(self): + return self.screenid + + def getGamma(self): + return ixf86misc.GetGamma(self.display,self.screenid) + + def setGamma(self,gammatuple): + ixf86misc.SetGamma(self.display,self.screenid,gammatuple[0],gammatuple[1],gammatuple[2]) + + def getRotation(self): + return self.currentrotation + + def getAvailableRotations(self): + return self.availablerotations + + def getSize(self): + return self.sizes[self.currentsizeid] + + def getSizeID(self): + return self.currentsizeid + + def getAvailableSizes(self): + return self.sizes[:] + + def getRefreshRate(self): + return self.currentrefreshrate + + def getAvailableRefreshRates(self,sizeid): + return ixf86misc.XRRRates(self.display,self.screenid,sizeid) + + def setScreenConfigAndRate(self, sizeid, rotation, refresh): + rc = ixf86misc.XRRSetScreenConfigAndRate(self.display, self.screenconfig, \ + ixf86misc.RootWindow(self.display, self.screenid), sizeid, rotation, refresh) + #ixf86misc.XRRConfigTimes(self.screenconfig) \ + + self._load() + return rc # FIXME handle failures due to the timestamp. + + def getDimensions(self): + return ixf86misc.DisplaySize(self.display,self.screenid) + + def getIdleSeconds(self): + data = self.__getScreenSaverInfo() + if data is None: + return 0 + + (state, kind, til_or_since, idle) = data + idletime = idle/1000.0 + + if (self.lastidle > idletime) or (self.resettime > idletime): # Something has moved in the meantime + self.starttime = 0 + self.resettime = 0 + else: + idletime = idletime - self.resettime + self.lastidle = idletime + return idletime + + def resetIdleSeconds(self): + self.resettime = time.time() - self.starttime + + # See man XScreenSaver(3) + def __getScreenSaverInfo(self): + if self.ssinfo is None: + if ixf86misc.XScreenSaverQueryExtension(self.display): + self.ssinfo = ixf86misc.XScreenSaverAllocInfo() + else: + return 0 # Error actually. + + return ixf86misc.XScreenSaverQueryInfo(self.display, + ixf86misc.RootWindow(self.display, self.screenid), self.ssinfo) + + +class XF86Server(object): + def __init__(self,displayname=None): + if displayname==None: + if 'DISPLAY' in os.environ: + displayname = os.environ['DISPLAY'] + else: + displayname = ":0.0" + self.displayname = displayname + self.display = ixf86misc.XOpenDisplay(displayname) + if self.display is None: + raise XF86Error, "Couldn't connect to X server." + + self._defaultscreen = ixf86misc.DefaultScreen(self.display) + + self.screens = [] + for i in range(ixf86misc.ScreenCount(self.display)): + self.screens.append(XF86Screen(self.display,i)) + + def getDefaultScreen(self): + return self.screens[self._defaultscreen] + + def getDisplay(self): + return self.display + + def getDisplayName(self): + return self.displayname + + def getScreens(self): + return self.screens[:] + + def resolutionSupportAvailable(self): + return self.screens[0].resolutionSupportAvailable() + +class XF86Error(Exception): + """Just an exception when some goes wrong with X.""" + +if __name__=='__main__': + xg = XF86Server() + xs = xg.getDefaultScreen() + print "Number of screens:",str(len(xg.screens)) + print "Idle seconds:",xs.getIdleSeconds() + print + print "Gamma:"+str(xs.getGamma()) + print + if xg.resolutionSupportAvailable(): + print "SizeID:"+str(xs.getSizeID()) + print "Size:"+str(xs.getSize()) + sizes = xs.getAvailableSizes() + print "Available Sizes:" + str(sizes) + print + print "Rotation:" + str(xs.getRotation()) + print "Available Rotations:" + str(xs.getAvailableRotations()) + print + print "Refresh rate:" + str(xs.getRefreshRate()) + print "Refresh rates for the current screen:"+str(xs.getAvailableRefreshRates(xs.getSizeID())) + + for i in range(len(sizes)): + print "All Refresh Rates:"+str(xs.getAvailableRefreshRates(i)) + xs.setScreenConfigAndRate(0,1,75) + print "SizeID:"+str(xs.getSizeID()) + print "Size:"+str(xs.getSize()) + sizes = xs.getAvailableSizes() + print "Available Sizes:" + str(sizes) + else: + print "(no resolution / randr support available)" diff --git a/mountconfig/MicroHAL.py b/mountconfig/MicroHAL.py new file mode 100755 index 0000000..9ce8b83 --- /dev/null +++ b/mountconfig/MicroHAL.py @@ -0,0 +1,884 @@ +#!/usr/bin/python +########################################################################### +# MicroHAL.py - # +# ------------------------------ # +# begin : Tue Oct 30 2004 # +# copyright : (C) 2004 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 +from SimpleCommandRunner import * + +############################################################################ +class MicroHAL__(object): + + # Major device numbers for Linux block devices that support partitions. + partitionblockdevs = [ + 3, # IDE harddisks + 8, # SCSI disks + 13, # 8-bit MFM/RLL/IDE controller + 14, # BIOS harddrive callback support {2.6} + 21, # Acorn MFM hard drive interface + 22, # Second IDE hard disk/CD-ROM interface + 28, # ACSI disk (68k/Atari) + 33, # Third IDE hard disk/CD-ROM interface + 34, # Fourth IDE hard disk/CD-ROM interface + 36, # MCA ESDI hard disk + 44, # Flash Translation Layer (FTL) filesystems + 45, # Parallel port IDE disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 49, # Mylex DAC960 PCI RAID controller; second controller + 50, # Mylex DAC960 PCI RAID controller; third controller + 51, # Mylex DAC960 PCI RAID controller; fourth controller + 52, # Mylex DAC960 PCI RAID controller; fifth controller + 53, # Mylex DAC960 PCI RAID controller; sixth controller + 54, # Mylex DAC960 PCI RAID controller; seventh controller + 55, # Mylex DAC960 PCI RAID controller; eigth controller + 56, # Fifth IDE hard disk/CD-ROM interface + 57, # Sixth IDE hard disk/CD-ROM interface + 65, # SCSI disk devices (16-31) + 66, # SCSI disk devices (32-47) + 67, # SCSI disk devices (48-63) + 68, # SCSI disk devices (64-79) + 69, # SCSI disk devices (80-95) + 70, # SCSI disk devices (96-111) + 71, # SCSI disk devices (112-127) + 72, # Compaq Intelligent Drive Array, first controller + 73, # Compaq Intelligent Drive Array, second controller + 74, # Compaq Intelligent Drive Array, third controller + 75, # Compaq Intelligent Drive Array, fourth controller + 76, # Compaq Intelligent Drive Array, fifth controller + 77, # Compaq Intelligent Drive Array, sixth controller + 78, # Compaq Intelligent Drive Array, seventh controller + 79, # Compaq Intelligent Drive Array, eigth controller + 80, # I2O hard disk + 81, # I2O hard disk + 82, # I2O hard disk + 83, # I2O hard disk + 84, # I2O hard disk + 85, # I2O hard disk + 86, # I2O hard disk + 87, # I2O hard disk + 88, # Seventh IDE hard disk/CD-ROM interface + 89, # Eighth IDE hard disk/CD-ROM interface + 90, # Ninth IDE hard disk/CD-ROM interface + 91, # Tenth IDE hard disk/CD-ROM interface + 92, # PPDD encrypted disk driver + 95, # IBM S/390 DASD block storage + 101, # AMI HyperDisk RAID controller + 102, # Compressed block device + 104, # Compaq Next Generation Drive Array, first controller + 105, # Compaq Next Generation Drive Array, second controller + 106, # Compaq Next Generation Drive Array, third controller + 107, # Compaq Next Generation Drive Array, fourth controller + 108, # Compaq Next Generation Drive Array, fifth controller + 109, # Compaq Next Generation Drive Array, sixth controller + 110, # Compaq Next Generation Drive Array, seventh controller + 111, # Compaq Next Generation Drive Array, eigth controller + 112, # IBM iSeries virtual disk + 114, # IDE BIOS powered software RAID interfaces such as the Promise Fastrak + 128, # SCSI disk devices (128-143) + 129, # SCSI disk devices (144-159) + 130, # SCSI disk devices (160-175) + 131, # SCSI disk devices (176-191) + 132, # SCSI disk devices (192-207) + 133, # SCSI disk devices (208-223) + 134, # SCSI disk devices (224-239) + 135, # SCSI disk devices (240-255) + 136, # Mylex DAC960 PCI RAID controller; ninth controller + 137, # Mylex DAC960 PCI RAID controller; tenth controller + 138, # Mylex DAC960 PCI RAID controller; eleventh controller + 139, # Mylex DAC960 PCI RAID controller; twelfth controller + 140, # Mylex DAC960 PCI RAID controller; thirteenth controller + 141, # Mylex DAC960 PCI RAID controller; fourteenth controller + 142, # Mylex DAC960 PCI RAID controller; fifteenth controller + 143, # Mylex DAC960 PCI RAID controller; sixteenth controller + 160, # Promise SX8 8-port SATA Disks on First Controller + 161 # Promise SX8 8-port SATA Disks on Second Controller + ] + + floppydevs = [ + 2, # Floppy disks + 40 # Syquest EZ135 parallel port removable drive + ] + + cdromsdevs = [ + 11, # SCSI CD-ROM devices + 12, # MSCDEX CD-ROM callback support {2.6} + 15, # Sony CDU-31A/CDU-33A CD-ROM + 16, # GoldStar CD-ROM + 17, # Optics Storage CD-ROM + 18, # Sanyo CD-ROM + 20, # Hitachi CD-ROM (under development) + 23, # Mitsumi proprietary CD-ROM + 24, # Sony CDU-535 CD-ROM + 25, # First Matsushita (Panasonic/SoundBlaster) CD-ROM + 26, # Second Matsushita (Panasonic/SoundBlaster) CD-ROM + 27, # Third Matsushita (Panasonic/SoundBlaster) CD-ROM + 28, # Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM + 29, # Aztech/Orchid/Okano/Wearnes CD-ROM + 30, # Philips LMS CM-205 CD-ROM + 32, # Philips LMS CM-206 CD-ROM + 41, # MicroSolutions BackPack parallel port CD-ROM + 46, # Parallel port ATAPI CD-ROM devices + 47, # Parallel port ATAPI disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 113 # IBM iSeries virtual CD-ROM + ] + + burnerpacketdevs = [ + 97 # Packet writing for CD/DVD devices + ] + + # We provide a mapping between filesystems and kernelmodules, so filesystems + # that are built as modules can be loaded on demand. (In fact, mountconfig will + # load all filesystem modules needed to be able to mount all fstab entries.) + FilesystemProcDriver = [ + # fstab name, /proc name, kernel module name + ('auto','autofs','autofs4'), + ('iso9660','iso9660','isofs'), + ('nfs','nfsd','nfs') + ] + + ############################################################################ + def __init__(self): + self.devices = None + self.supportedfs = None + self.partitionsizelines = None + + ############################################################################ + def getDevices(self): + if self.devices is None: + self.devices = [] + # Scan through /sys/block for devices. Find out which disks are + # installed and should be shown in the listview. For real + # disks we put a 'group' in the listview and under the group + # we list the partitions, whether they are mounted or not. + # FIXME: Check if sysfs is mounted. + blockdevices = os.listdir("/sys/block") + blockdevices.sort() + + for blockdevice in blockdevices: + # We are looking for block devices that represent hard disks or + # things that have partitions. + # Grab the major device number + fhandle = open(os.path.join("/sys/block",blockdevice,"dev")) + devnumbers = fhandle.read() + fhandle.close() + devnum = int(devnumbers.split(":")[0]) + # Is it on our list of partition devices? + if devnum in MicroHAL.partitionblockdevs: + fulldevice = os.path.join("/dev",blockdevice) + + # Check for removable devices. + fhandle = open(os.path.join("/sys/block",blockdevice,"removable")) + removable = fhandle.read().strip()=="1" + fhandle.close() + + if not removable: + newdisk = Disk() + else: + if os.readlink(os.path.join("/sys/block",blockdevice,"device")).split(os.path.sep)[5].startswith("usb"): + newdisk = USBDisk() + else: + newdisk = RemovableDisk() + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.removable = removable + newdisk.modelname = self.getModelName(fulldevice) + + if not removable or isinstance(newdisk, USBDisk): + # We have a not removable block device or a USB Disk here. + partitions = os.listdir(os.path.join("/sys/block",blockdevice)) + partitions.sort() + i = 1 + for partition in partitions: + # Look for a partitions device names and not the other + # stuff that lives in the directory. + if partition.startswith(blockdevice): + fullpartition = os.path.join("/dev",partition) + newpartition = Partition() + newpartition.dev = fullpartition + newpartition.size = self.getPartitionSize(fullpartition) + newpartition.num = i + newdisk.partitions.append(newpartition) + i += 1 + self.devices.append(newdisk) + + elif devnum in MicroHAL.cdromsdevs: + fulldevice = os.path.join("/dev",blockdevice) + newdisk = RemovableDisk() + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.modelname = self.getModelName(fulldevice) + self.devices.append(newdisk) + + elif devnum in MicroHAL.burnerpacketdevs: + fulldevice = os.path.join("/dev",blockdevice) + newdisk = BurnerDisk(self) + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.modelname = self.getModelName(fulldevice) + + self.devices.append(newdisk) + + return self.devices[:] + + ############################################################################ + def getPartitionSize(self,devicename): + partitionname = os.path.basename(devicename) + + if self.partitionsizelines is None: + fhandle = open('/proc/partitions') + self.partitionsizelines = fhandle.readlines() + fhandle.close() + + i = 0 + for line in self.partitionsizelines: + if i>=2: + (major, minor, blocks, name) = line.split() + if name==partitionname: + blocks = int(blocks) # 1K blocks now. + if blocks<1024: + return str(blocks)+" Kb" + if blocks<1024*1024: + return str(round(float(blocks)/1024.0,1))+" Mb" + blocks /= 1024 + if blocks<1024*1024: + return str(round(float(blocks)/1024.0,1))+" Gb" + blocks /= 1024 + return str(round(float(blocks)/1024.0,1))+" Tb" + i += 1 + return None + + ############################################################################ + def getIDEModel(self,devname): + try: + fhandle = open(os.path.join("/proc/ide",os.path.basename(devname),"model")) + model = fhandle.read() + fhandle.close() + return model.strip() + except (OSError, IOError): + return None + + ############################################################################ + def getSCSIModel(self,devname): + try: + fhandle_model = open(os.path.join("/sys/block",os.path.basename(devname),"device/model")) + fhandle_vendor = open(os.path.join("/sys/block",os.path.basename(devname),"device/vendor")) + model = fhandle_model.read()[:-1] + vendor = fhandle_vendor.read()[:-1] + fhandle_model.close() + fhandle_vendor.close() + except (OSError, IOError): + pass + if len(model) + len(vendor) == 0: + return None + return vendor + " " + model + + ############################################################################ + def getModelName(self,devname): + modelname = self.getIDEModel(devname) + if modelname is None: + modelname = self.getSCSIModel(devname) + if modelname is None: + modelname = devname + return " '"+modelname+"'" + + ############################################################################ + def getSupportedFileSystems(self): + if self.supportedfs is None: + if os.path.isfile("/proc/filesystems"): + fhandle = open("/proc/filesystems") + self.supportedfs = [] + for fs in fhandle.readlines(): + try: + self.supportedfs.append(fs.strip().split()[1]) + except IndexError: + self.supportedfs.append(fs.strip().split()[0]) + # The following filesystems aren't found there, but usually they are + # supported. + self.supportedfs.extend(('swap','shm')) + return self.supportedfs[:] + + ############################################################################ + def isSupportedFileSystem(self,fs): + # Look up the /proc and kernel driver name for the given filesystem type. + module = fs + proc = fs + for entry in self.FilesystemProcDriver: + if entry[0]==fs: + proc = entry[1] + module = entry[2] + + if proc not in self.getSupportedFileSystems(): + # The filesystem is not supported by the running kernel, + # but it might be built as module, so we try to load that. + retval, msg = SimpleCommandRunner().run(["/sbin/modprobe",module]) + if retval > 0: + print msg + print "Couldn't load driver " + module + " for filesystem " + fs + # Force refresh of list of supported filesystems + self.supportedfs = None + return proc in self.getSupportedFileSystems() + +############################################################################ +class Device(object): + def __init__(self): + self.dev = None + self.major = None + self.removable = None + self.uuid = None + self.label = None + + def getDev(self): + return self.dev + + def getMajor(self): + return self.major + + def getName(self): + return self.dev + + def getUUID(self): + if not self.uuid: + return "" + return self.uuid + + def getLabel(self): + if not self.label: + return "" + return self.label + + def isRemovable(self): + return self.removable + + def __str__(self): + return "Name: %s, Device: %s, Major: %i, " % (self.getName(), + self.getDev(), + self.getMajor()) + +############################################################################ +class Disk(Device): + def __init__(self): + super(Disk,self).__init__() + self.removable = False + self.partitions = [] + self.modelname = None + self.iconname = "hi16-hdd" + + def getModelName(self): + return self.modelname + + def getName(self): + if self.getModelName(): + return i18n("Disk ")+self.getModelName() + else: + return i18n("Unknown Disk") + + def getPartitions(self): + return self.partitions[:] + + def appendPartition(self,partition): + self.partitions.append(partition) + def cmpNum(a,b): return cmp(a.num,b.num) + self.partitions.sort(cmpNum) + + def __str__(self): + x = Device.__str__(self) + "Partitions: [" + for part in self.partitions: + x += "[" + x += str(part) + x += "], " + x += "]," + return x + +############################################################################ +class RemovableDisk(Disk): + def __init__(self): + super(RemovableDisk,self).__init__() + self.iconname = "hi16-cdrom" + self.removable = True + + def getName(self): + return "Optical Disk "+self.getModelName() + +############################################################################ +class USBDisk(Disk): + def __init__(self): + super(USBDisk,self).__init__() + self.iconname = "hi16-usbpen" + self.removable = True + + def getName(self): + return "Removable USB Disk "+self.getModelName() + +############################################################################ +class BurnerDisk(RemovableDisk): + def __init__(self): + super(BurnerDisk,self).__init__() + self.iconname = "hi16-burner" + + def getName(self): + return "Burner "+self.modelname + +############################################################################ +class Floppy(Device): + def isRemovable(self): + return True + + def getName(self): + return "Floppy" + +############################################################################ +class Partition(Device): + def __init__(self): + super(Partition,self).__init__() + self.num = None + self.size = None + self.iconname = "hi16-hdd" + + def getName(self): + return str(self.num)+" Partition "+ self.getSize() + # A group item for all of the other kernel/system mount entries. + + def getSize(self): + return self.size + + def __str__(self): + return "Device: %s, Num: %i, Size: %s, Label: %s, UUID: %s" % (self.dev, self.num, self.getSize(), + self.getLabel(), self.getUUID()) + +############################################################################ +class FakeSystemDevice(object): + def getName(self): return "System" + def getIconName(self): return "hi16-blockdevice" + +############################################################################ +class MicroHAL(object): + + # Major device numbers for Linux block devices that support partitions. + partitionblockdevs = [ + 3, # IDE harddisks + 8, # SCSI disks + 13, # 8-bit MFM/RLL/IDE controller + 14, # BIOS harddrive callback support {2.6} + 21, # Acorn MFM hard drive interface + 22, # Second IDE hard disk/CD-ROM interface + 28, # ACSI disk (68k/Atari) + 33, # Third IDE hard disk/CD-ROM interface + 34, # Fourth IDE hard disk/CD-ROM interface + 36, # MCA ESDI hard disk + 44, # Flash Translation Layer (FTL) filesystems + 45, # Parallel port IDE disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 49, # Mylex DAC960 PCI RAID controller; second controller + 50, # Mylex DAC960 PCI RAID controller; third controller + 51, # Mylex DAC960 PCI RAID controller; fourth controller + 52, # Mylex DAC960 PCI RAID controller; fifth controller + 53, # Mylex DAC960 PCI RAID controller; sixth controller + 54, # Mylex DAC960 PCI RAID controller; seventh controller + 55, # Mylex DAC960 PCI RAID controller; eigth controller + 56, # Fifth IDE hard disk/CD-ROM interface + 57, # Sixth IDE hard disk/CD-ROM interface + 65, # SCSI disk devices (16-31) + 66, # SCSI disk devices (32-47) + 67, # SCSI disk devices (48-63) + 68, # SCSI disk devices (64-79) + 69, # SCSI disk devices (80-95) + 70, # SCSI disk devices (96-111) + 71, # SCSI disk devices (112-127) + 72, # Compaq Intelligent Drive Array, first controller + 73, # Compaq Intelligent Drive Array, second controller + 74, # Compaq Intelligent Drive Array, third controller + 75, # Compaq Intelligent Drive Array, fourth controller + 76, # Compaq Intelligent Drive Array, fifth controller + 77, # Compaq Intelligent Drive Array, sixth controller + 78, # Compaq Intelligent Drive Array, seventh controller + 79, # Compaq Intelligent Drive Array, eigth controller + 80, # I2O hard disk + 81, # I2O hard disk + 82, # I2O hard disk + 83, # I2O hard disk + 84, # I2O hard disk + 85, # I2O hard disk + 86, # I2O hard disk + 87, # I2O hard disk + 88, # Seventh IDE hard disk/CD-ROM interface + 89, # Eighth IDE hard disk/CD-ROM interface + 90, # Ninth IDE hard disk/CD-ROM interface + 91, # Tenth IDE hard disk/CD-ROM interface + 92, # PPDD encrypted disk driver + 95, # IBM S/390 DASD block storage + 101, # AMI HyperDisk RAID controller + 102, # Compressed block device + 104, # Compaq Next Generation Drive Array, first controller + 105, # Compaq Next Generation Drive Array, second controller + 106, # Compaq Next Generation Drive Array, third controller + 107, # Compaq Next Generation Drive Array, fourth controller + 108, # Compaq Next Generation Drive Array, fifth controller + 109, # Compaq Next Generation Drive Array, sixth controller + 110, # Compaq Next Generation Drive Array, seventh controller + 111, # Compaq Next Generation Drive Array, eigth controller + 112, # IBM iSeries virtual disk + 114, # IDE BIOS powered software RAID interfaces such as the Promise Fastrak + 128, # SCSI disk devices (128-143) + 129, # SCSI disk devices (144-159) + 130, # SCSI disk devices (160-175) + 131, # SCSI disk devices (176-191) + 132, # SCSI disk devices (192-207) + 133, # SCSI disk devices (208-223) + 134, # SCSI disk devices (224-239) + 135, # SCSI disk devices (240-255) + 136, # Mylex DAC960 PCI RAID controller; ninth controller + 137, # Mylex DAC960 PCI RAID controller; tenth controller + 138, # Mylex DAC960 PCI RAID controller; eleventh controller + 139, # Mylex DAC960 PCI RAID controller; twelfth controller + 140, # Mylex DAC960 PCI RAID controller; thirteenth controller + 141, # Mylex DAC960 PCI RAID controller; fourteenth controller + 142, # Mylex DAC960 PCI RAID controller; fifteenth controller + 143, # Mylex DAC960 PCI RAID controller; sixteenth controller + 160, # Promise SX8 8-port SATA Disks on First Controller + 161 # Promise SX8 8-port SATA Disks on Second Controller + ] + + floppydevs = [ + 2, # Floppy disks + 40 # Syquest EZ135 parallel port removable drive + ] + + cdromsdevs = [ + 11, # SCSI CD-ROM devices + 12, # MSCDEX CD-ROM callback support {2.6} + 15, # Sony CDU-31A/CDU-33A CD-ROM + 16, # GoldStar CD-ROM + 17, # Optics Storage CD-ROM + 18, # Sanyo CD-ROM + 20, # Hitachi CD-ROM (under development) + 23, # Mitsumi proprietary CD-ROM + 24, # Sony CDU-535 CD-ROM + 25, # First Matsushita (Panasonic/SoundBlaster) CD-ROM + 26, # Second Matsushita (Panasonic/SoundBlaster) CD-ROM + 27, # Third Matsushita (Panasonic/SoundBlaster) CD-ROM + 28, # Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM + 29, # Aztech/Orchid/Okano/Wearnes CD-ROM + 30, # Philips LMS CM-205 CD-ROM + 32, # Philips LMS CM-206 CD-ROM + 41, # MicroSolutions BackPack parallel port CD-ROM + 46, # Parallel port ATAPI CD-ROM devices + 47, # Parallel port ATAPI disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 113 # IBM iSeries virtual CD-ROM + ] + + burnerpacketdevs = [ + 97 # Packet writing for CD/DVD devices + ] + + # We provide a mapping between filesystems and kernelmodules, so filesystems + # that are built as modules can be loaded on demand. (In fact, mountconfig will + # load all filesystem modules needed to be able to mount all fstab entries.) + FilesystemProcDriver = [ + # fstab name, /proc name, kernel module name + ('auto','autofs','autofs4'), + ('iso9660','iso9660','isofs'), + ('nfs','nfsd','nfs') + ] + + ############################################################################ + def __init__(self): + self.devices = None + self.supportedfs = None + + ############################################################################ + def getDevices(self): + if self.devices is None: + self.devices = [] + + retval, msg = SimpleCommandRunner().run(["/usr/bin/lshal"]) + if retval > 0: + return [] + + partition_to_uid = {} + uid_to_disk = {} + + READING_TOP = 0 + READING_DEVICE = 1 + state = READING_TOP + + parsed_hash = None + current_uid = None + + for line in msg.split('\n'): + + if state==READING_TOP: + if line.startswith("udi ="): + parsed_hash = {} + current_uid = self._parseString(line[6:]) + state = READING_DEVICE + + elif state==READING_DEVICE: + if line=="" or not line.startswith(" "): + # Detect the end of this block of device data. + state = READING_TOP + + if u"info.category" in parsed_hash: + + new_device = None + + capabilities_string = u" ".join(parsed_hash[u"info.capabilities"]) + capabilities = self._parseStringList(capabilities_string) + + category = self._parseString(' '.join(parsed_hash[u"info.category"])) + if category==u"volume": + # Is it a volume? + + is_disc = parsed_hash.get(u"volume.is_disc") + if is_disc is not None and is_disc[0]=='true': + continue + + is_partition = parsed_hash.get(u"volume.is_partition") + if is_partition is not None: + is_partition = is_partition[0] + + if is_partition=='true': + new_device = Partition() + new_device.num = int(parsed_hash[u"volume.partition.number"][0]) + partition_to_uid[new_device] = current_uid + + if u"info.parent" in parsed_hash: + parent_uid = self._parseString(' '.join(parsed_hash[u"info.parent"])) + partition_to_uid[new_device] = parent_uid + + else: + new_device = Disk() + uid_to_disk[current_uid] = new_device + + if u"volume.uuid" in parsed_hash: + new_device.uuid = self._parseString(' '.join(parsed_hash[u"volume.uuid"])) + + if u"volume.label" in parsed_hash: + new_device.label = self._parseString(parsed_hash[u"volume.label"][0]) + + if u"volume.size" in parsed_hash: + size = parsed_hash[u"volume.size"][0] + new_device.size = self.formatSizeBytes(int(size)) + else: + new_device.size = "?" + + + # is it a storage device? + elif category==u"storage": + storage_model = self._parseString(' '.join(parsed_hash[u"storage.model"])) + storage_removable = parsed_hash[u"storage.removable"][0]==u"true" + + if u"storage.cdrom" in capabilities: + + if u"storage.cdrom.cdrw" in parsed_hash \ + and parsed_hash[u"storage.cdrom.cdrw"][0]==u"true": + new_device = BurnerDisk() + else: + new_device= RemovableDisk() + + elif u"storage.floppy" in capabilities: + new_device = FloppyDevice() + else: + if u"storage.bus" in parsed_hash \ + and self._parseString(' '.join(parsed_hash[u"storage.bus"]))==u"usb": + + new_device = USBDisk() + else: + new_device = Disk() + + new_device.modelname = storage_model + uid_to_disk[current_uid] = new_device + else: + # Not interesting, skip it. + continue + + # Handle the generic properties. + new_device.dev = self._parseString(' '.join(parsed_hash[u"block.device"])) + new_device.major = int(parsed_hash[u"block.major"][0]) + + self.devices.append(new_device) + + else: + # Keep on accumulating info about this device. + parts = line.split() + parsed_hash[ parts[0] ] = parts[2:] + + # Attach the partitions to thier devices. + for partition in partition_to_uid.keys(): + parent = partition_to_uid[partition] + if parent in uid_to_disk.keys(): + parent_device = uid_to_disk[parent] + parent_device.appendPartition(partition) + self.devices.remove(partition) + + return self.devices[:] + + ############################################################################ + def _parseStringList(self,source_string): + STATE_TOP = 0 + STATE_STRING = 1 + + state = STATE_TOP + current_string = "" + string_list = [] + for c in source_string: + if state==STATE_TOP: + if c=='}': + break + if c=="'": + state = STATE_STRING + else: + if c=="'": + state = STATE_TOP + string_list.append(current_string) + current_string = "" + else: + current_string += c + + return string_list + + ############################################################################ + def _parseString(self,source_string): + STATE_TOP = 0 + STATE_STRING = 1 + + state = STATE_TOP + current_string = "" + for c in source_string: + if state==STATE_TOP: + if c=="'": + state = STATE_STRING + else: + if c=="'": + break + else: + current_string += c + return current_string + + ############################################################################ + def formatSizeBytes(self,size): + if size<1024: + return str(size+" B") + if size<1024*1042: + return str(round(float(size)/1024.0,1))+" Kb" + size /= 1024 + if size<1024*1024: + return str(round(float(size)/1024.0,1))+" Mb" + size /= 1024 + if size<1024*1024: + return str(round(float(size)/1024.0,1))+" Gb" + size /= 1024 + return str(round(float(size)/1024.0,1))+" Tb" + + ############################################################################ + def getSupportedFileSystems(self): + if self.supportedfs is None: + if os.path.isfile("/proc/filesystems"): + fhandle = open("/proc/filesystems") + self.supportedfs = [] + for fs in fhandle.readlines(): + try: + self.supportedfs.append(fs.strip().split()[1]) + except IndexError: + self.supportedfs.append(fs.strip().split()[0]) + # The following filesystems aren't found there, but usually they are + # supported. + self.supportedfs.extend(('swap','shm')) + return self.supportedfs[:] + + ############################################################################ + def isSupportedFileSystem(self,fs): + # Look up the /proc and kernel driver name for the given filesystem type. + module = fs + proc = fs + for entry in self.FilesystemProcDriver: + if entry[0]==fs: + proc = entry[1] + module = entry[2] + + if proc not in self.getSupportedFileSystems(): + # The filesystem is not supported by the running kernel, + # but it might be built as module, so we try to load that. + retval, msg = SimpleCommandRunner().run(["/sbin/modprobe",module]) + if retval > 0: + print msg + print "Couldn't load driver " + module + " for filesystem " + fs + # Force refresh of list of supported filesystems + self.supportedfs = None + return proc in self.getSupportedFileSystems() + + ############################################################################ + def getDeviceByLabel(self, label): + for device in self.getDevices(): + if device.getLabel()==label: + return device + + if isinstance(device,Disk): + for partition in device.getPartitions(): + if partition.getLabel()==label: + return partition + return None + + def getLabelByDevice(self, device): + for item in self.getDevices(): + for partition in item.partitions: + if partition.dev==device: + return partition.label + print "No Label found for ",device + return "" + + def getUUIDByDevice(self, device): + for item in self.getDevices(): + for partition in item.partitions: + #print partition, partition.getUUID() + if partition.dev==device: + return partition.uuid + print "No UUID found for ",device + return "" + + def getDeviceByUUID(self, uuid): + for device in self.getDevices(): + if device.getUUID()==uuid: + return device + + if isinstance(device,Disk): + for partition in device.getPartitions(): + if partition.getUUID()==uuid: + return partition + + return None + +############################################################################ +if __name__=='__main__': + hal = MicroHAL() + for item in hal.getDevices(): + print(str(item)) + + print + + #""" + for item in hal.getDevices(): + for partition in item.partitions: + print partition, partition.getLabel() + #""" + #realhal = RealHAL() + #for item in realhal.getDevices(): + # print(str(item)) + + print + + diff --git a/mountconfig/SMBShareSelectDialog.py b/mountconfig/SMBShareSelectDialog.py new file mode 100644 index 0000000..171cf5d --- /dev/null +++ b/mountconfig/SMBShareSelectDialog.py @@ -0,0 +1,573 @@ +########################################################################### +# SMBShareSelectDialog.py - Dialog for selecting an SMB share on a network# +# ------------------------------ # +# begin : Tue Oct 30 2004 # +# copyright : (C) 2004 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### + +from qt import * +from kdeui import * +from kdecore import * +from kio import * + +############################################################################ +class SMBShareSelectDialog(KDialogBase): + + STATUS_IDLE = 0 + STATUS_SEARCH_TOP_LEVEL = 1 + STATUS_SEARCH = 2 + STATUS_RESOLVE = 3 + + ######################################################################## + def __init__(self,parent,name=None): + super(SMBShareSelectDialog,self).__init__(parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + self.updatinggui = False + + self.resize(600,400) + + vbox = self.makeVBoxMainWidget() + + hbox = QHBox(vbox) + hbox.setSpacing(self.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi32-samba")) + + hbox.setStretchFactor(tmplabel,0) + + self.headinglabel = QLabel(hbox) + self.headinglabel.setText(i18n("Select a network share")) + hbox.setStretchFactor(self.headinglabel,1) + + hbox2 = QHBox(vbox) + + # The main treeview where the action happens. + self.treeview = KListView(hbox2) + self.treeview.addColumn("(hidden)") + self.treeview.header().hide() + self.treeview.setRootIsDecorated(True) + + self.connect(self.treeview,SIGNAL("expanded(QListViewItem *)"),self.slotNodeExpanded) + self.connect(self.treeview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotNodeSelected) + self.connect(self.treeview,SIGNAL("clicked(QListViewItem *)"),self.slotClicked) + self.dirlister = KDirLister() + self.dirlister.setDirOnlyMode(True) + self.dirlister.setAutoUpdate(False) + self.dirlister.setAutoErrorHandlingEnabled(True,self) + self.connect(self.dirlister,SIGNAL("newItems(const KFileItemList &)"),self.slotNewItems) + self.connect(self.dirlister,SIGNAL("completed()"),self.slotDirListCompleted) + self.connect(self.dirlister,SIGNAL("canceled()"),self.slotDirListCanceled) + self.connect(self.dirlister,SIGNAL("redirection(const KURL &,const KURL &)"),self.slotDirListRedirection) + self.enableButtonOK(False) + + # The "Connect as" part + widget = QWidget(hbox2) + grid = QGridLayout(widget,6,4,KDialog.spacingHint()) + grid.setRowStretch(5,1) + + tmplabel = QLabel(widget) + tmplabel.setPixmap(UserIcon("hi16-password")) + grid.addWidget(tmplabel,0,0) + + self.connectaslabel = QLabel(widget) + self.connectaslabel.setText("Connect to 'XXX' as:") + grid.addMultiCellWidget(self.connectaslabel,0,0,1,3) + + self.guestradio = QRadioButton(widget) + self.guestradio.setChecked(True) + grid.addWidget(self.guestradio,1,1) + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Guest")) + grid.addWidget(tmplabel,1,2) + self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked) + + self.userradio = QRadioButton(widget) + grid.addWidget(self.userradio,2,1) + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Username:")) + grid.addWidget(tmplabel,2,2) + self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked) + + self.usernameedit = KLineEdit(widget) + grid.addWidget(self.usernameedit,2,3) + self.connect(self.usernameedit,SIGNAL("textChanged(const QString &)"),self.slotUsernameChanged) + + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Password:")) + grid.addWidget(tmplabel,3,2) + + self.passwordedit = KLineEdit(widget) + grid.addWidget(self.passwordedit,3,3) + + self.reconnectbutton = KPushButton(i18n("Reconnect now"),widget) + grid.addMultiCellWidget(self.reconnectbutton,4,4,1,3) + self.connect(self.reconnectbutton,SIGNAL("clicked()"),self.slotReconnectClicked) + + self.dirlistertimer = None + + ######################################################################## + def choose(self,currenturl): + self.lookupqueue = [] + self.selecteditem = None + + self.treeview.clear() + self.url_to_list_item_map = {} + + # Fill the first level + root_url = KURL("smb:/") + self.rootitem = SMBShareListViewItem(self.treeview, i18n("Network Neighbourhood"), root_url, self) + + self.searchurl = currenturl + self._updateConnectGUI() + self.enableButtonOK(False) + self._openDefaultURL() + + self.spintimerid = self.startTimer(250) + self.exec_loop() + self.stopResolve() + + self.killTimer(self.spintimerid) + + if self.result()==self.Accepted: + currenturl = self.selecteditem.getURL() + + self.url_to_list_item_map = None + + return currenturl + + ######################################################################## + def _openDefaultURL(self): + if self.searchurl is not None: + rc = self.rootitem.selectURL(self.searchurl) + if rc==self.rootitem.OPEN_SUCCESS: + self.currenturl = self.searchurl + self.searchurl = None + self.enableButtonOK(True) + elif rc==self.rootitem.OPEN_FAIL or rc==self.rootitem.OPEN_SUCCESS_INVALID: + self.searchurl = None + + ######################################################################## + def stopResolve(self): + if self.dirlistertimer is not None: + self.killTimer(self.dirlistertimer) + self.dirlister.stop() + for item in self.lookupqueue: + item.cancelResolve() + self.lookupqueue = [] + + self.searchurl = None # Stop trying to open this URL too. + + ######################################################################## + def setOpen(self,item,open): + if item.isResolved(): + KListView.setOpen(self.treeview,item,open) + else: + item.startResolve(True) + + ######################################################################## + def appendToResolveQueue(self,item): + if item not in self.lookupqueue: + self.lookupqueue.append(item) + self._startDirLister() + return True + else: + return False + + ######################################################################## + def slotNodeExpanded(self,item): + self.setOpen(item,True) + + ######################################################################## + def slotClicked(self): + if self.treeview.selectedItem() is None: + self.selecteditem = None + self._updateConnectGUI() + self.enableButtonOK(False) + + ######################################################################## + def slotNodeSelected(self,item): + self.selecteditem = item + self._updateConnectGUI() + self.enableButtonOK(item.getLevel()==item.LEVEL_DIR) + + if not self.selecteditem.isResolved(): + self.selecteditem.startResolve(False) + + ######################################################################## + def slotNewItems(self,items): + for entry in items: + newitem = SMBShareListViewItem(self.lookupqueue[0], unicode(entry.name()), KURL(entry.url()), self) + self.url_to_list_item_map[unicode(entry.url().prettyURL())] = newitem + # Notice how I copied the KURL object and QString (to a python string) + + ######################################################################## + def slotDirListCompleted(self): + item = self.lookupqueue[0] + item.setBusyIcon(False) + del self.lookupqueue[0] + + item.resolveComplete() + self._startDirLister() + + self._openDefaultURL() + + ######################################################################## + def slotDirListCanceled(self): + self.stopResolve() + + ######################################################################## + def slotDirListRedirection(self,oldUrl,newUrl): + list_item = self.url_to_list_item_map[unicode(oldUrl.prettyURL())] + list_item.setURL(KURL(newUrl)) # The copy is important. + + # Reselect the selected node. (This will force a refresh). + if self.selecteditem is not None: + self.updatinggui = True + self.slotNodeSelected(self.selecteditem) + self.updatinggui = False + + ######################################################################## + def slotUsernameChanged(self,newtext): + self.reconnectbutton.setEnabled(self.usernameedit.text()!="") + + ######################################################################## + def slotReconnectClicked(self): + if self.updatinggui: + return + self.updatinggui = True + + if self.selecteditem is None: # Sanity check. + return + + # The user wants to change how we connect to this remote machine. + + machineitem = self.selecteditem.getMachineItem() + if machineitem is None: + return # Shouldn't happen. + + self.stopResolve() + + # Grab the URL object before we delete the listviewitem that holds it. + selectedurl = self.selecteditem.getURL() + + # Close up the machine item and remove the items under the machine item. + machineitem.unresolve() + + # Set the username/password for the machine item. + if self.guestradio.isChecked(): + machineitem.getURL().setUser(QString.null) + machineitem.getURL().setPass(QString.null) + selectedurl.setUser(QString.null) + selectedurl.setPass(QString.null) + else: + machineitem.getURL().setUser(self.usernameedit.text()) + machineitem.getURL().setPass(self.passwordedit.text()) + selectedurl.setUser(self.usernameedit.text()) + selectedurl.setPass(self.passwordedit.text()) + self.selecteditem = None + self._updateConnectGUI() + + self.searchurl = selectedurl + self._openDefaultURL() + self.updatinggui = False + + ######################################################################## + def _startDirLister(self): + if self.dirlistertimer is None: + # Check the URL lister queue the next the event loop runs. + # Don't get all "recursed up"! + self.dirlistertimer = self.startTimer(0) + + ######################################################################## + def timerEvent(self,event): + KDialogBase.timerEvent(self,event) + if self.spintimerid==event.timerId(): + # Spin the current folder icon + if len(self.lookupqueue)!=0: + self.lookupqueue[0].setBusyIcon(True) + elif event.timerId()==self.dirlistertimer: + self.killTimer(self.dirlistertimer) + self.dirlistertimer = None + if self.dirlister.isFinished(): + if len(self.lookupqueue)!=0: + self.dirlister.openURL(self.lookupqueue[0].getURL()) + + ######################################################################## + def slotGuestRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + + if self.selecteditem is None: + return + + if state==QButton.Off: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + + selectedurl = self.selecteditem.getURL() + self.reconnectbutton.setEnabled(unicode(selectedurl.user())!="") + + self.updatinggui = False + + ######################################################################## + def slotUserRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + if state==QButton.Off: + self.userradio.setChecked(True) + self.guestradio.setChecked(False) + + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + + username = unicode(self.usernameedit.text()) + password = unicode(self.passwordedit.text()) + selectedurl = self.selecteditem.getURL() + if username!="" and password!="" and \ + ((unicode(selectedurl.user())!=username) or (unicode(selectedurl.pass_())!=password)): + self.reconnectbutton.setEnabled(True) + else: + self.reconnectbutton.setEnabled(False) + + self.updatinggui = False + + ######################################################################## + def _updateConnectGUI(self): + if self.selecteditem is not None: + selectedurl = self.selecteditem.getURL() + self.guestradio.setEnabled(True) + self.userradio.setEnabled(True) + self.usernameedit.setEnabled(selectedurl.hasUser()) + self.passwordedit.setEnabled(selectedurl.hasUser()) + self.connectaslabel.setText(i18n("Connect to '%1' as:").arg(selectedurl.host())) + if selectedurl.hasUser(): + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.usernameedit.setText(selectedurl.user()) + self.passwordedit.setText(selectedurl.pass_()) + else: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setText("") + self.usernameedit.setText("") + self.reconnectbutton.setEnabled(False) + else: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.guestradio.setEnabled(False) + self.userradio.setEnabled(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.connectaslabel.setText(i18n("Connect to 'machine' as:")) + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setText("") + self.usernameedit.setText("") + self.reconnectbutton.setEnabled(False) + +############################################################################ +class SMBShareListViewItem(KListViewItem): + # Return codes for selectURL() + OPEN_SUCCESS = 1 + OPEN_SUCCESS_INVALID = 2 + OPEN_FAIL = 0 + OPEN_BUSY = 3 + + # Node types. + LEVEL_ROOT = 0 + LEVEL_WORKGROUP = 1 + LEVEL_MACHINE = 2 + LEVEL_DIR = 3 # and deeper. + + ######################################################################## + def __init__(self,parentitem,name,url,smbdialog): + KListViewItem.__init__(self,parentitem,name) + if not isinstance(parentitem,SMBShareListViewItem): + self._setIcon(0) + self.setSelectable(False) + else: + self._setIcon(parentitem.depth()+1) + self.setSelectable(parentitem.getLevel()>=self.LEVEL_WORKGROUP) + self.setExpandable(True) + + if url.hasPath() and url.path(-1)!="/": + parts = [x for x in unicode(url.path(-1)).split("/") if x!=""] + self.component = parts[-1].lower() + elif url.hasHost(): + self.component = unicode(url.host()).lower() + else: + self.component = None + + self.smbdialog = smbdialog + self.resolved = False + self.url = url + self.autoopen = False + self.animationcounter = 0 + + ######################################################################## + def getURL(self): + return self.url + + ######################################################################## + def setURL(self,url): + self.url = url + + ######################################################################## + def getComponent(self): + return self.component + + ######################################################################## + def isResolved(self): + return self.resolved + + ######################################################################## + def startResolve(self,autoopen): + if self.smbdialog.appendToResolveQueue(self): + self.setBusyIcon(True) + self.autoopen = self.autoopen or autoopen + + ######################################################################## + def cancelResolve(self): + self.setBusyIcon(False) + self.autoopen = False + self.resolved = False + while self.childCount()!=0: + self.takeItem(self.firstChild()) + self.setOpen(False) + + ######################################################################## + def unresolve(self): + self.cancelResolve() + + ######################################################################## + def getMachineItem(self): + if self.getLevel()<=self.LEVEL_WORKGROUP: + return None + elif self.getLevel()==self.LEVEL_DIR: + return self.parent().getMachineItem() + else: + return self + + ######################################################################## + def _setIcon(self,depth): + if depth==self.LEVEL_ROOT or depth==self.LEVEL_WORKGROUP: + self.setPixmap(0,SmallIcon("network")) + elif depth==self.LEVEL_MACHINE: + self.setPixmap(0,SmallIcon("network_local")) + else: + self.setPixmap(0,SmallIcon("folder")) + + ######################################################################## + def setBusyIcon(self,on): + if on: + self.setPixmap(0,UserIcon("kde1")) + self.setPixmap(0,UserIcon("kde"+str(self.animationcounter+1))) + self.animationcounter += 1 + self.animationcounter %= 6 + else: + self._setIcon(self.depth()) + + ######################################################################## + def resolveComplete(self): + self.resolved = True + if self.childCount()==0: + self.setExpandable(False) + else: + if self.autoopen: + self.setOpen(True) + ######################################################################## + def getLevel(self): + if self.depth()>self.LEVEL_DIR: + return self.LEVEL_DIR + else: + return self.depth() + + ######################################################################## + # This is one of the more nasty pieces of code. It tries to select a given + # URL in the treeview. Opening and resolving the contents of URLs as neccessary + # while at the same time trying not have list everything on the network. + # Another wrinkle is that the treeview contains a level of workgroups while + # a given URL omits the workgroup a jumps directly to the machine name. + def selectURL(self,targeturl): + path = unicode(targeturl.path(-1)) + parts = [x for x in path.split("/") if x!=""] + if targeturl.hasHost(): + tmp = [targeturl.host()] + tmp.extend(parts) + parts = tmp + + if self.getLevel()==self.LEVEL_ROOT: + # Root item. + # We should first resolve our contents. the Workgroups. + if not self.resolved: + self.startResolve(True) + return self.OPEN_BUSY + else: + if len(parts)==0: + # The URL is really short, and is not selectable. + # So we just say that we couldn't resolve/select it. + return self.OPEN_SUCCESS_INVALID + else: + # OK, the url has some more components. Ask each of the Workgroup items + # to help resolve it. + kid = self.firstChild() + while kid is not None: + rc = kid.selectURL(targeturl) + if rc==self.OPEN_SUCCESS or rc==self.OPEN_SUCCESS_INVALID: + kid.setOpen(True) + return rc + elif rc==self.OPEN_BUSY: + return rc + kid = kid.nextSibling() + return self.OPEN_FAIL + elif self.getLevel()==self.LEVEL_WORKGROUP: + # Workgroup level + if not self.resolved: + self.startResolve(False) + return self.OPEN_BUSY + else: + # Find a child named after the next part of the URL path. + kid = self.firstChild() + partname = parts[0].lower() + while kid is not None: + if kid.getComponent()==partname: + self.setOpen(True) + return kid.selectURL(targeturl) + kid = kid.nextSibling() + return self.OPEN_FAIL + elif self.getLevel()==self.LEVEL_MACHINE: + # Machine level + if len(parts)==1: + # The URL is successfully resolved but is not selectable! + return self.OPEN_SUCCESS_INVALID + else: + # Share level + if len(parts)==self.depth()-1: + self.smbdialog.treeview.setSelected(self,True) + return self.OPEN_SUCCESS + + if not self.resolved: + self.startResolve(True) + return self.OPEN_BUSY + else: + # Find a child item that matches the next part of the URL path. + kid = self.firstChild() + partname = parts[self.depth()-1].lower() + while kid is not None: + if kid.getComponent()==partname: + return kid.selectURL(targeturl) + kid = kid.nextSibling() + return self.OPEN_FAIL diff --git a/mountconfig/SimpleCommandRunner.py b/mountconfig/SimpleCommandRunner.py new file mode 100644 index 0000000..d533563 --- /dev/null +++ b/mountconfig/SimpleCommandRunner.py @@ -0,0 +1,69 @@ +########################################################################### +# SimpleCommandRunner.py - description # +# ------------------------------ # +# begin : Tue May 17 2005 # +# 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. # +# # +########################################################################### +from qt import * +from kdecore import * +import locale + +debug = False +#debug = True + + +class SimpleCommandRunner(QObject): + ######################################################################## + def __init__(self): + QObject.__init__(self) + + ######################################################################## + def run(self,cmdlist,STDOUT_only=False): + """Run the given command and return the result. + + Keyword arguments: + cmdlist - Command and arguments. Given as a list of strings. The first item is + the executable. + STDOUT_only - Do not return STDERR in the output stream. + + Returns a tuple (rc,output). rc is the numeric return code from + the command, or None if the command couldn't be started. output + is the output from stdout and stderr. + """ + global debug + if debug: print cmdlist + self.STDOUT_only = STDOUT_only + self.output = u"" + proc = KProcess() + proc.setEnvironment("LANG","US") + proc.setEnvironment("LC_ALL","US") + self.connect(proc,SIGNAL("receivedStdout(KProcess *,char *,int)"),self.slotStdout) + self.connect(proc,SIGNAL("receivedStderr(KProcess *,char *,int)"),self.slotStderr) + proc.setArguments(cmdlist) + rc = None + if proc.start(proc.Block,proc.AllOutput)==True: + if proc.normalExit(): + rc = proc.exitStatus() + return (rc,self.output) + + ######################################################################## + def slotStdout(self,proc,buffer,buflen): + global debug + if debug: print "slotStdout() |"+buffer+"|" + self.output += buffer.decode(locale.getpreferredencoding()) + + ######################################################################## + def slotStderr(self,proc,buffer,buflen): + global debug + if debug: print "slotStderr() |"+buffer+"|" + if not self.STDOUT_only: + self.output += buffer.decode(locale.getpreferredencoding()) diff --git a/mountconfig/fuser.py b/mountconfig/fuser.py new file mode 100644 index 0000000..d898b37 --- /dev/null +++ b/mountconfig/fuser.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +########################################################################### +# fuser.py - description # +# ------------------------------ # +# begin : Wed Jun 15 2005 # +# copyright : (C) 2005-2006 by Sebastian Kuegler # +# email : sebas@vizZzion.org # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### +""" +TODO: +- Fix running standalone: + * KCmdLineArgs stuff. +""" + +import sys +import os +from qt import * +from kdeui import * +#import kdedesigner +from fuser_ui import * +from SimpleCommandRunner import * + +standalone = __name__ == "__main__" + +class FileProcess(QListViewItem): + """ A FileProcess is simply one line from lsof, one filedescriptor that's in use + by a process represented as a listviewitem in the lsof processtable. """ + + # Available signals. + signals = { + "TERM":15, + "KILL":9 } + + # Column names mapping. + cols = { + "pname":0, + "pid":1, + "powner":2, + "pfile":3 } + + def __init__(self,parent,pid,isparent=False): + QListViewItem.__init__(self,parent) + self.setPid(pid) + self.isparent = isparent + self.pfile = "" + self.pix = None + + def setPid(self,pid): + self.pid = pid + + def setName(self,pname): + self.pname = pname + + def setOwner(self,powner): + self.powner = powner + + def setFile(self,pfile): + self.pfile = pfile + + def setPixmaps(self,pix): + """ Eats a dict with pixmaps. """ + self.pix = pix + + def sendSignal(self,signal): + """ Parses a signal string representation or a signal number and sends it to + the process.""" + if not self.isparent: + print "Item is not a process, only a filedescriptor." + return + try: + signal_int = int(signal) + except ValueError: + try: + signal_int = self.signals[signal] + except IndexError: + print "No known signal received ", signal + return False + try: + rc = os.kill(int(self.pid),signal_int) # TODO: Catch OSError + except OSError, message: + print "OSError: Couldn't %s %s %s" % (signal,self.pname,self.pid) + print message + if not rc: + print "Successfully sent signal ", signal_int, " to process ", self.pid + return True + print "Signal %i didn't succeed" % signal_int + return False + + def fillColumns(self): + """ Writes strings into columns once an entry is completed. """ + if self.isparent: + self.setText(self.cols["pid"],self.pid) + self.setText(self.cols["pname"],self.pname) + self.setText(self.cols["powner"],self.powner) + self.setPixmap(0,self.pix["exec"]) + self.setPixmap(1,self.pix["pid"]) + self.setPixmap(2,self.pix["owner"]) + else: + self.setText(self.cols["pfile"],self.pfile) + self.setPixmap(3,self.pix["file"]) + +######################################################################################################## +class FUser(FUserUI): + """ done() / result() return 0 on successful umount and 1 if cancelled. """ + + def __init__(self,device,parentdialog=None,lsof_bin='/usr/sbin/lsof',kapp=None): + FUserUI.__init__(self,parentdialog,name = None,modal = 0,fl = 0) + self.device = device + self.fileprocesses = [] + self.lsof_bin = '/usr/sbin/lsof' + self.setLsof(lsof_bin) + self.setApp(kapp) + + self.processlist.clear() + self.processhidden = False + # We're having processes blocking umounting, show that. + self.umountbutton.setEnabled(False) + + self.explanationlabel.setText( + unicode(i18n("""The volume %s is in use and can not be disabled.
+
+ The processes that are blocking %s are listed below. These processes must be closed + before %s can be disabled. + Killing a process may cause data loss! Make sure all work is saved before killing an + application. + """)) % (self.device,self.device,self.device)) + + self.connect(self.cancelbutton,SIGNAL("clicked()"),self.slotCancelButtonClicked) + self.connect(self.killbutton,SIGNAL("clicked()"),self.slotKillButtonClicked) + self.connect(self.killallbutton,SIGNAL("clicked()"),self.slotKillallButtonClicked) + self.connect(self.refreshbutton,SIGNAL("clicked()"),self.refreshProcesslist) + self.connect(self.processlist,SIGNAL("selectionChanged()"),self.slotSelectionChanged) + self.connect(self.umountbutton,SIGNAL("clicked()"),self.slotUmountButtonClicked) + + # TODO: Make optionsbutton resize dialog if processframe is hidden, hide Optionsbutton until then. + self.optionsbutton.hide() + self.readPixmaps() + self.warningimage.setPixmap(MainBarIcon("messagebox_warning")) + + # Delayed initialisation. + QTimer.singleShot(0,self.isMounted) + QTimer.singleShot(0,self.refreshProcesslist) + + def setApp(self,app): + """ We need a reference to the (K|Q)Application for certain things, e.g. setting + the MouseCursor. """ + self.app = app + + def setLsof(self,path): + """ Where's the lsof binary? """ + if os.path.isfile(path): + self.lsof_bin = path + else: + print path, " is not a valid binary, keeping %s", self.lsof_bin + + def readPixmaps(self): + self.pix = { + "exec": UserIcon("exec"), + "owner": UserIcon("user"), + "pid": UserIcon("tux"), + "file": UserIcon("file")} + + def refreshProcesslist(self): + """ Read lsof output and add the processdescriptors to the listview. """ + kapp = self.app + + kapp.setOverrideCursor(QCursor(Qt.BusyCursor)) + + self.processlist.clear() + rc, output = SimpleCommandRunner().run([self.lsof_bin,'-FpcLn',self.device],True) + procs = output.split() + + self.processes = [] + self.realprocesses = [] + for line in procs: + line = str(line) + type = line[0] + info = line[1:] + + if type is "p": + pid = info + parentproc = FileProcess(self.processlist,pid,True) + self.processes.append(parentproc) + self.realprocesses.append(parentproc) + parentproc.setPixmaps(self.pix) + files = 0 + + if type == "c": + pname = info + parentproc.setName(pname) + + if type == "L": + powner = info + parentproc.setOwner(powner) + + if type == "n": + pfile = info + childproc = FileProcess(parentproc,pid) + self.processes.append(childproc) + childproc.setPixmaps(self.pix) + childproc.setFile(pfile) + childproc.setOwner(powner) + childproc.setName(pname) + if files == 0: + parentproc.fillColumns() + files += 1 + childproc.fillColumns() + + kapp.restoreOverrideCursor() + + # Enable / disable buttons which are (in)appropriate. + self.killallbutton.setEnabled(len(self.realprocesses)!=0) + self.killbutton.setEnabled(len(self.realprocesses)!=0) + self.umountbutton.setEnabled(len(self.realprocesses)==0) + if self.processlist.selectedItem() == None: + self.killbutton.setEnabled(False) + + def isMounted(self): + rc,output = SimpleCommandRunner().run(["/bin/mount"],False) + mounts = [] + for line in output.split('\n'): + try: + mounts.append(line.split()[0]) + except IndexError: + pass + ismounted = self.device in mounts + self.umountbutton.setEnabled(ismounted) + return ismounted + + def slotCancelButtonClicked(self): + self.done(1) + + def slotKillButtonClicked(self): + try: + self.processlist.selectedItem().sendSignal("KILL") + self.refreshProcesslist() + except AttributeError: + print "No killable item selected." + + def slotKillallButtonClicked(self): + for process in self.realprocesses: + process.sendSignal("KILL") + self.refreshProcesslist() + + def slotOptionsButtonCLicked(self): + self.processhidden = not self.processhidden + self.processframe.setHidden(self.processhidden) + + def slotSelectionChanged(self): + """ Check if item is a process or a file, disable killbutton for children. """ + selected = self.processlist.selectedItem() + if not selected.isparent: + self.killbutton.setEnabled(False) + else: + self.killbutton.setEnabled(True) + + def slotUmountButtonClicked(self): + SimpleCommandRunner + rc, output = SimpleCommandRunner().run(['/bin/umount',self.device]) + if rc == 0: + print "%s successfully unmounted." % self.device + # Close dialog and return 0 - sucessfully umounted. + self.done(0) + else: + print "Unmounting %s failed: %s" % (self.device,output[:-1]) + self.isMounted() + +################################################################################################ +if standalone: + device = "/dev/hda1" + print 'Device is ', device + + cmd_args = KCmdLineArgs.init(sys.argv, "FUser", + "A graphical frontend to fuser, but without using it :-)", "0.2") + + # ---------------------------------------------------------------------------- + # FIXME: All the arg-parsing stuff does not work yet since I don't understand KCmdLineArgs. + options = [("device ", "Device to umount")] + KCmdLineArgs.addCmdLineOptions(options) + args = KCmdLineArgs.parsedArgs() + # print args.count() + # ---------------------------------------------------------------------------- + + kapp = KApplication() + KGlobal.iconLoader().addAppDir("guidance") + fuserapp = FUser(device) + + fuserapp.setApp(kapp) + kapp.setMainWidget(fuserapp) + fuserapp.show() + kapp.exec_loop() diff --git a/mountconfig/fuser_ui.ui b/mountconfig/fuser_ui.ui new file mode 100644 index 0000000..4499f93 --- /dev/null +++ b/mountconfig/fuser_ui.ui @@ -0,0 +1,352 @@ + +FUserUI +Python:from kdeui import * +Python:from kdecore import * +Sebastian Kuegler + + + Process + + + + 0 + 0 + 546 + 536 + + + + + 5 + 5 + 0 + 0 + + + + + 400 + 250 + + + + + 400 + 250 + + + + Error: Volume in use + + + + unnamed + + + + layout10 + + + + unnamed + + + + warningimage + + + + 5 + 5 + 0 + 0 + + + + + + + false + + + + + explanationlabel + + + + 5 + 5 + 1 + 0 + + + + + 0 + 0 + + + + The volume $VOLUME is in use and can not be disabled. + +The processes that are blocking $VOLUME are listed below. These processes must be closed before $VOLUME can be disabled. +Killing a process may cause data loss. Make sure all work is saved before killing an application. + + + WordBreak|AlignVCenter + + + + + + + processframe + + + + 5 + 5 + 1 + 1 + + + + StyledPanel + + + Raised + + + + unnamed + + + 5 + + + 4 + + + + layout7 + + + + unnamed + + + + + Process + + + true + + + true + + + + + Process ID + + + true + + + true + + + + + Owner + + + true + + + true + + + + + File + + + true + + + true + + + + processlist + + + + 7 + 7 + 1 + 1 + + + + Manual + + + true + + + true + + + true + + + LastColumn + + + + + layout11 + + + + unnamed + + + + killallbutton + + + Kill all listed processes + + + + + listbuttonspacer + + + Horizontal + + + Expanding + + + + 184 + 16 + + + + + + killbutton + + + Kill process + + + + + refreshbutton + + + Refresh list + + + + + + + + + + + layout12 + + + + unnamed + + + + optionsbutton + + + + 1 + 0 + 0 + 0 + + + + >>> Options + + + + + bottombuttonspacer + + + Horizontal + + + Expanding + + + + 280 + 16 + + + + + + umountbutton + + + + 1 + 0 + 0 + 0 + + + + Disable volume + + + + + cancelbutton + + + + 1 + 0 + 0 + 0 + + + + Cancel + + + + + + +QPixmap + + + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + + diff --git a/mountconfig/mountconfig.desktop b/mountconfig/mountconfig.desktop new file mode 100644 index 0000000..e0fa311 --- /dev/null +++ b/mountconfig/mountconfig.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=Disk & Filesystems +Name[da]=Disk & Filesystemer +Name[el]=Δίσκοι & συστήματα αρχείων +Name[es]=Disco y sistemas de archivos +Name[et]=Ketas ja failisüsteemid +Name[it]=Dischi e filesystem +Name[ja]=ディスクとファイルシステム +Name[nl]=Schijven en bestandssystemen +Name[pt]=Disco & Sistemas de Ficheiros +Name[pt_BR]=Disco & Sistemas de Arquivos +Name[sr]=Диск и фајл-системи +Name[sr@Latn]=Disk i fajl-sistemi +Name[sv]=Disk- och filsystem +Name[xx]=xxDisk & Filesystemsxx +name[en_GB]=Disk & Filesystems +Comment=Disk & Filesystem Configuration +Comment[el]=Ρυθμίσεις δίσκων & συστημάτων αρχείων +Comment[es]=Configuración del disco y sistema de archivos +Comment[et]=Ketta ja failisüsteemi seadistamine +Comment[it]=Configurazione di dischi e filesystem +Comment[ja]=ディスクとファイルシステムの設定 +Comment[nl]=Schijven en bestandssystemen instellen +Comment[pt]=Configuração do Disco & Sistema de Ficheiros +Comment[pt_BR]=Configuração de Disco e Sistemas de Arquivos +Comment[sr]=Подешавање диска и фајл-система +Comment[sr@Latn]=Podešavanje diska i fajl-sistema +Comment[sv]=Disk och filsysteminitierníng +Comment[xx]=xxDisk & Filesystem Configurationxx +Icon=disksfilesystems.png +Encoding=UTF-8 +X-KDE-ModuleType=Library +X-KDE-Library=mountconfig +X-KDE-FactoryName=mountconfig +X-KDE-RootOnly=true +Type=Application +Exec=kcmshell System/mountconfig +Categories=Qt;KDE;X-KDE-settings-system; +GenericName=Mount Point Editor +GenericName[el]=Επεξεργαστής σημείων προσάρτησης +GenericName[es]=Editor del punto de montaje +GenericName[et]=Ühenduspunktide redaktor +GenericName[it]=Editor dei punti di montaggio +GenericName[ja]=マウントポイントエディタ +GenericName[nl]=Aankoppelpunten bewerken +GenericName[pt]=Editor de Pontos de Montagem +GenericName[pt_BR]=Editor de Pontos de Montagem +GenericName[sr]=Уређивач тачки монтирања +GenericName[sr@Latn]=Uređivač tački montiranja +GenericName[sv]=Editor för monteringspunkter +GenericName[xx]=xxMount Point Editorxx diff --git a/mountconfig/mountconfig.py b/mountconfig/mountconfig.py new file mode 100755 index 0000000..1f31f13 --- /dev/null +++ b/mountconfig/mountconfig.py @@ -0,0 +1,3303 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +########################################################################### +# mountconfig.py - description # +# ------------------------------ # +# begin : Fri Nov 30 2003 # +# copyright : (C) 2003 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### + +from qt import * +from kdeui import * +from kdecore import * +from kfile import * +from kio import * +import sys +import os +import os.path +from types import StringType,UnicodeType +import pwd +import grp +import math +import locale +import codecs +import subprocess +import MicroHAL +from SMBShareSelectDialog import * +from SimpleCommandRunner import * +from fuser import * +import sizeview + +programname = "Disk & Filesystem Configuration" +version = "0.8.0" + +# Are we running as a separate standalone application or in KControl? +standalone = __name__=='__main__' + +# Running as the root user or not? +isroot = os.getuid()==0 +allowuuid = True +allowlabel = True + + +""" +Universal Options +----------------- + +async/sync +atime/noatime +auto/noauto +dev/nodev +exec/noexec +ro/rw +suid/nosuid +dirsync +nouser/user/users + +defaults =>rw, suid, dev, exec, auto, nouser, and async. + +Automatically set +================= +_netdev +The filesystem resides on a device that requires network access (used to +prevent the system from attempting to mount these filesystems until the +network has been enabled on the system). + +remount +Attempt to remount an already-mounted file system. This is commonly used +to change the mount flags for a file system, especially to make a readonly +file system writeable. It does not change device or mount point. + +Supported filesystems +--------------------- +nfs +ext2 +ext3 +reiserfs +vfat +ntfs +udf +iso9660 +supermount +reiser4 +xfs +jfs +hfs +hfsplus + +cifs (replacement for smbfs) +auto + +swap + +proc +sysfs +usbdevfs +procbususb + +TODO +---- +* SMB finished the connection username nad password fields. +* SMB entry: finished writing the config. +* SMBSelector: setting the username and password. + +""" + +############################################################################ +class UserComboBox(KComboBox): + def __init__(self,parent,name=None): + KComboBox.__init__(self,parent,name) + tmplist = [] + users = pwd.getpwall() + for user in users: + uid = int(user[2]) + username = user[4] + tmplist.append( (int(uid),"%s (%s)" % (username,uid)) ) + tmplist.sort(lambda a,b: cmp(a[1],b[1])) + self.userlist = [] + for user in tmplist: + self.insertItem(user[1]) + self.userlist.append(user[0]) + + ######################################################################## + def setUID(self,uid): + if uid in self.userlist: + self.setCurrentItem(self.userlist.index(int(uid))) + return True + else: + return False + + ######################################################################## + def UID(self): + return self.userlist[self.currentItem()] + +############################################################################ +class GroupComboBox(KComboBox): + def __init__(self,parent,name=None): + KComboBox.__init__(self,parent,name) + self.grouplist = [] + groups = grp.getgrall() + tmplist = [] + for group in groups: + gid = group[2] + groupname = group[0] + tmplist.append( (int(gid),"%s (%s)" % (groupname,gid)) ) + tmplist.sort(lambda a,b: cmp(a[1],b[1])) + self.grouplist = [] + for group in tmplist: + self.insertItem(group[1]) + self.grouplist.append(group[0]) + + ######################################################################## + def setGID(self,gid): + if gid in self.grouplist: + self.setCurrentItem(self.grouplist.index(int(gid))) + return True + else: + return False + + ######################################################################## + def GID(self): + return self.grouplist[self.currentItem()] + + +############################################################################ +class MountEntryExt(object): + use_as_device = "devicenode" # Can be one of "devicenode", "uuid" or "label" + showdevice = True + showlabel = False + showuuid = False + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + if base==None: + self.device = unicode(i18n("")) + self.mountpoint = unicode(i18n("")) + self.mounttype = 'ext2' + self.uuid = "" + self.label = "" + self.extraoptions = "noauto" + self.fs_freq = 0 + self.fs_passno = 0 + self.enabled = False + self.managed = False + self.device_string = "" + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + parts = base.split() + + device_ref = MountEntry.decodeMountEntryString(parts[0]) + self.uuid = "" + self.label = "" + if device_ref.startswith("UUID="): + self.uuid = device_ref[5:] + self.setUseAsDevice("uuid") + mapped_device = microhal.getDeviceByUUID(self.uuid) + if mapped_device is not None: + self.device = mapped_device.getDev() + else: + self.device = "" + try: + self.label = mapped_device.getLabel() + except AttributeError: + pass + elif device_ref.startswith("LABEL="): + self.label = device_ref[6:] + self.setUseAsDevice("label") + mapped_device = microhal.getDeviceByLabel(self.label) + if mapped_device is not None: + self.device = mapped_device.getDev() + else: + self.device = "" + else: + self.device = device_ref + + self.mountpoint = MountEntry.decodeMountEntryString(parts[1]) + self.mounttype = MountEntry.decodeMountEntryString(parts[2]) + self.extraoptions = MountEntry.decodeMountEntryString(parts[3]) + self.fs_freq = int(parts[4]) + self.fs_passno = int(parts[5]) + self.enabled = False + + options = self.extraoptions.split(",") + self.managed = "managed" in options + try: + options.remove("managed") + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + # This is a new entry, but it's based on another one. + self.device = base.device + self.mountpoint = base.mountpoint + self.mounttype = base.mounttype + self.extraoptions = base.extraoptions + self.fs_freq = base.fs_freq + self.fs_passno = base.fs_passno + self.uuid = base.uuid + self.enabled = base.enabled + self.managed = False + self.iconname = self.getIconName() + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExt() + # FIXME: use "newobject = self.__class__()" and get rid of the newobject parameter. + newobject.device = self.device + newobject.mountpoint = self.mountpoint + newobject.mounttype = self.mounttype + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.extraoptions = self.extraoptions + newobject.fs_freq = self.fs_freq + newobject.fs_passno = self.fs_passno + newobject.enabled = self.enabled + newobject.uuid = self.uuid + newobject.label = self.label + return newobject + + ######################################################################## + def cleanup(self): + # This method is called after the entry has been removed from the + # mounttable. + pass + + ######################################################################## + def setMountType(self,mounttypename): self.mounttype = mounttypename + + ######################################################################## + def isFileSystemAvailable(self): + return microhal.isSupportedFileSystem(self.mounttype) + + def getDevice(self): return self.device + def setDevice(self,device): self.device = device + def setUseAsDevice(self,use_as): self.use_as_device = use_as + def getUseAsDevice(self): return self.use_as_device + def setUUID(self,uuid): self.uuid = uuid + def setLabel(self,label): self.label = label + def getMountPoint(self): return self.mountpoint + def setMountPoint(self,mountpoint): self.mountpoint = mountpoint + def getExtraOptions(self): return self.extraoptions + def setExtraOptions(self,extraoptions): self.extraoptions = extraoptions + def getFSFreq(self): return self.fs_freq + def setFSFreq(self,fs_freq): self.fs_freq = fs_freq + def getFSPassno(self): return self.fs_passno + def setFSPassno(self,fs_passno): self.fs_passno = fs_passno + def isManaged(self): return self.managed + + def getUUID(self): + if not self.uuid: + return "" + return self.uuid + + def getLabel(self): + try: + if not self.label: + return "" + return self.label + except AttributeError: + return "" + + def getDeviceString(self): + if self.getUseAsDevice() == "label": + if self.label != "": + return MountEntry.encodeMountEntryString("LABEL="+self.label) + else: + print "No Label set, preventing you from shooting yourself in the foot" + elif self.getUseAsDevice() == "uuid": + if self.uuid != "": + return "UUID="+self.uuid + return MountEntry.encodeMountEntryString("UUID="+self.uuid) + else: + print "No UUID set, preventing you from shooting yourself in the foot" + return MountEntry.encodeMountEntryString(self.device) + + ######################################################################## + def getName(self): + if os.path.basename(self.device).startswith("fd"): + return "Floppy" + else: + return self.mountpoint + + ######################################################################## + def getIconName(self): + if self.device is not None and os.path.basename(self.device).startswith("fd"): + return "hi16-floppy" + else: + return "hi16-blockdevice" + + ######################################################################## + def updateStatus(self,mtablist): + self.enabled = self.mountpoint in mtablist + + ######################################################################## + def getFstabOptions(self): + if self.extraoptions!="": + return self.extraoptions.split(",") + else: + return [] + + ######################################################################## + def getFstabLine(self): + # Construct the options field. + _options = self.getFstabOptions() + options = [] + # Remove whitespace and dupes + for o in _options: + if o.strip() not in options: + options.append(o.strip()) + + return self.getDeviceString() + \ + u" " + MountEntry.encodeMountEntryString(self.mountpoint.replace("%20","\040")) + \ + u" " + MountEntry.encodeMountEntryString(self.mounttype) + \ + u" " + MountEntry.encodeMountEntryString(u",".join(options)) + \ + u" " + unicode(self.fs_freq) + u" " + unicode(self.fs_passno) + + ######################################################################## + def getCategory(self): + return self.device + + ######################################################################## + def isEnabled(self): return self.enabled + + ######################################################################## + def enable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(["/bin/mount",self.mountpoint]) + finally: + self._setBusy(parentdialog,False) + if rc!=0: + self.handleMountFailure(parentdialog,rc,output,True) + + ######################################################################## + def disable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(["/bin/umount",self.mountpoint]) + finally: + self._setBusy(parentdialog,False) + if rc!=0: + self.handleMountFailure(parentdialog,rc,output,False) + + ######################################################################## + def handleMountFailure(self,parentdialog,rc,output,mount_action=True): + """ + Keyword arguments: + mount_action - True=enable, False=disable + """ + global kapp + if mount_action: + msg = i18n("An error occurred while enabling %1.\n\nThe system reported: %2").arg( \ + self.mountpoint).arg(output) + captionmsg = i18n("Unable to enable %1").arg(self.mountpoint) + else: + msg = i18n("An error occurred while disabling %1.\n\nThe system reported: %2").arg( + self.mountpoint).arg(output) + captionmsg = i18n("Unable to disable %1").arg(self.mountpoint) + + extramsg = unicode(i18n("Return code from mount was %1.\n").arg(rc)) + + if (rc & 1)!=0: + extramsg += unicode(i18n("\"incorrect invocation or permissions\"\n")) + if (rc & 2)!=0: + extramsg += unicode(i18n("\"system error (out of memory, cannot fork, no more loop devices)\"\n")) + if (rc & 4)!=0: + extramsg += unicode(i18n("\"internal mount bug or missing nfs support in mount\"\n")) + if (rc & 8)!=0: + extramsg += unicode(i18n("\"user interrupt\"\n")) + if (rc & 16)!=0: + extramsg += unicode(i18n("\"problems writing or locking /etc/mtab\"\n")) + if (rc & 32)!=0: + extramsg += unicode(i18n("\"mount failure\"\n")) + if (rc & 64)!=0: + extramsg += unicode(i18n("\"some mount succeeded\"\n")) + + in_use = False + if not mount_action: + # Use lsof to find out what is blocking the device. + lsof_bin = '/usr/bin/lsof' + rc, output = SimpleCommandRunner().run([lsof_bin,'-FncL',str(self.mountpoint)]) + if rc==0: + # Check if there is one or more processes using the device. + in_use = len(output.split())>3 + if in_use: + # Start fuser.py which lists open filedescriptors on device and offers to get + # rid of them. + fuser = FUser(str(self.mountpoint),None,lsof_bin,kapp) + fuser.exec_loop() + in_use_message = "" + if fuser.result() != 0: + in_use_message = unicode(i18n("Unmounting %1 failed or was cancelled.").arg(self.device)) + extramsg += in_use_message + else: + extramsg += unicode(i18n("(none)")) + + if not in_use: + KMessageBox.detailedSorry(parentdialog, msg, extramsg, captionmsg) + + ######################################################################## + def _setBusy(self,parentdialog,flag): + global kapp + if flag: + kapp.setOverrideCursor( QCursor(Qt.WaitCursor) ) + parentdialog.setEnabled(False) + + # It is necessary to process some of the events in the event queue. + # Otherwise the user won't see that the window is disabled. + # ( setEnabled() here above doesn't redraw the window immediately. + # Redrawing is done via the event queue.) + kapp.processEvents() + else: + parentdialog.setEnabled(True) + kapp.restoreOverrideCursor() + +############################################################################ +class MountEntryExtCommonUnix(MountEntryExt): + + USERMOUNT_NO = 0 + USERMOUNT_ONE = 1 + USERMOUNT_ANY = 2 + USERMOUNT_OWNER = 3 + + ######################################################################## + # Base can be either a fstab format line of text, or another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtCommonUnix,self).__init__(base) + + if isinstance(base,MountEntryExtCommonUnix): + # Being initalised from an existing object. + # Only mess with objects + self.atime = base.atime + self.auto = base.auto + self.writeable = base.writeable + self.usedevpoints = base.usedevpoints + self.showlabel = True + self.showuuid = True + self.allowexecutables = base.allowexecutables + self.allowsuid = base.allowsuid + self.allowusermount = base.allowusermount + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + + self.atime = True + if "noatime" in options: + self.atime = False + self.auto = True + if "noauto" in options: + self.auto = False + self.writeable = True + if "ro" in options: + self.writeable = False + self.usedevpoints = True + if "nodev" in options: + self.usedevpoints = False + self.allowexecutables = True + if "noexec" in options: + self.allowexecutables = False + self.allowsuid = True + if "nosuid" in options: + self.allowsuid = False + self.allowusermount = self.USERMOUNT_NO + if "user" in options: + self.allowusermount = self.USERMOUNT_ONE + if "users" in options: + self.allowusermount = self.USERMOUNT_ANY + if "owner" in options: + self.allowusermount = self.USERMOUNT_OWNER + + self.showlabel = True + self.showuuid = True + + for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \ + "owner","users","user","suid","nosuid","exec","noexec","rw","ro"]: + try: + options.remove(x) + except ValueError: + pass + + self.extraoptions = ",".join(options) + + else: + # Set some sane defaults. + self.showlabel = True + self.showuuid = True + self.atime = True + self.auto = False + self.writeable = True + self.usedevpoints = False + self.allowexecutables = False + self.allowsuid = False + self.allowusermount = self.USERMOUNT_NO + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtCommonUnix() + super(MountEntryExtCommonUnix,self).copy(newobject) + newobject.atime = self.atime + newobject.auto = self.auto + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.uuid = self.uuid + newobject.label = self.label + newobject.writeable = self.writeable + newobject.usedevpoints = self.usedevpoints + newobject.allowexecutables = self.allowexecutables + newobject.allowsuid = self.allowsuid + newobject.allowusermount = self.allowusermount + return newobject + + ######################################################################## + def getFstabOptions(self): + options = [] + + # These options must appear before the others. 'user', according to the + # mount man page implies 'noexec' too, BUT the noexec can be overridden + # by specifying 'exec' after the 'user' keyword. Therefore 'exec' etc + # must come after 'user', 'users' and friends. + options.append(['nouser','user','users','owner'][self.allowusermount]) + + super_options = super(MountEntryExtCommonUnix,self).getFstabOptions() + options.extend(super_options) + + options.append(['noatime','atime'][self.atime]) + options.append(['noauto','auto'][self.auto]) + options.append(['ro','rw'][self.writeable]) + options.append(['nodev','dev'][self.usedevpoints]) + options.append(['noexec','exec'][self.allowexecutables]) + options.append(['nosuid','suid'][self.allowsuid]) + return options + + ######################################################################## + # atime/noatime + def getAtime(self): return self.atime + def setAtime(self,val): self.atime = val + # auto/noauto + def getMountAtBoot(self): return self.auto + def setMountAtBoot(self,val): self.auto = val + # ro/rw + def getWritable(self): return self.writeable + def setWritable(self,val): self.writeable = val + # dev, nodev + def getUseDevPoints(self): return self.usedevpoints + def setUseDevPoints(self,val): self.usedevpoints = val + # exec/noexec + def getAllowExecutables(self): return self.allowexecutables + def setAllowExecutable(self,val): self.allowexecutables = val + # suid/nosuid + def getSUID(self): return self.allowsuid + def setSUID(self,val): self.allowsuid = val + # nouser/user/users/owner + def setAllowUserMount(self,val): self.allowusermount = val + def getAllowUserMount(self): return self.allowusermount + + +############################################################################ +# Common unix filesystems, but for local hard disks. i.e. partitions. +class MountEntryExtCommonUnixLocal(MountEntryExtCommonUnix): + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtCommonUnixLocal,self).__init__(base) + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtCommonUnixLocal() + super(MountEntryExtCommonUnixLocal,self).copy(newobject) + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + +############################################################################ +class MountEntryExtAlien(MountEntryExt): + + USERMOUNT_NO = 0 + USERMOUNT_ONE = 1 + USERMOUNT_ANY = 2 + USERMOUNT_OWNER = 3 + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtAlien,self).__init__(base) + + if isinstance(base,MountEntryExtAlien): + self.uid = base.uid + self.gid = base.gid + self.label = base.label + self.writeable = base.writeable + self.auto = base.auto + self.allowusermount = base.allowusermount + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + self.uid = 0 + self.gid = 0 + options = self.extraoptions.split(",") + newoptions = [] + for line in options: + if line.startswith("uid="): + try: + self.uid = int(line[4:]) + except ValueError: + self.uid = 0 + elif line.startswith("gid="): + try: + self.gid = int(line[4:]) + except ValueError: + self.gid = 0 + else: + # We hang on to unknown options for later. + newoptions.append(line) + options = newoptions + + self.writeable = True + if "ro" in options: + self.writeable = False + self.auto = True + if "noauto" in options: + self.auto = False + self.allowusermount = self.USERMOUNT_NO + if "user" in options: + self.allowusermount = self.USERMOUNT_ONE + if "users" in options: + self.allowusermount = self.USERMOUNT_ANY + if "owner" in options: + self.allowusermount = self.USERMOUNT_OWNER + + for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \ + "owner","users","user","suid","nosuid","exec","noexec","rw", \ + "ro"]: + try: + options.remove(x) + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + self.uid = 0 + self.gid = 0 + self.writeable = False + self.auto = False + self.allowusermount = self.USERMOUNT_NO + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtAlien() + super(MountEntryExtAlien,self).copy(newobject) + newobject.uid = self.uid + newobject.gid = self.gid + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.writeable = self.writeable + newobject.auto = self.auto + newobject.allowusermount = self.allowusermount + return newobject + + ######################################################################## + def getFstabOptions(self): + # Construct the options field. + options = super(MountEntryExtAlien,self).getFstabOptions() + options.append('uid='+unicode(self.uid)) + options.append('gid='+unicode(self.gid)) + options.append(['noauto','auto'][self.auto]) + options.append(['ro','rw'][self.writeable]) + options.append(['nouser','user','users','owner'][self.allowusermount]) + return options + + ######################################################################## + def getUID(self): return self.uid + def setUID(self,val): self.uid = val + def getGID(self): return self.gid + def setGID(self,val): self.gid = val + + # ro/rw + def getWritable(self): return self.writeable + def setWritable(self,val): self.writeable = val + # auto/noauto + def getMountAtBoot(self): return self.auto + def setMountAtBoot(self,val): self.auto = val + # nouser/user/users/owner + def setAllowUserMount(self,val): self.allowusermount = val + def getAllowUserMount(self): return self.allowusermount + +############################################################################ +class MountEntryExtVFAT(MountEntryExtAlien): + def __init__(self,base=None): + super(MountEntryExtVFAT,self).__init__(base) + + if isinstance(base,MountEntryExtVFAT): + self.suppresspermissionerrors = base.suppresspermissionerrors + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + self.suppresspermissionerrors = "quiet" in options + try: + options.remove("quiet") + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + self.suppresspermissionerrors = False + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtVFAT() + super(MountEntryExtVFAT,self).copy(newobject) + newobject.suppresspermissionerrors = self.suppresspermissionerrors + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtVFAT,self).getFstabOptions() + if self.suppresspermissionerrors: + options.append('quiet') + return options + + def getSuppressPermissionErrors(self): return self.suppresspermissionerrors + def setSuppressPermissionErrors(self,val): self.suppresspermissionerrors = val + +############################################################################ +class MountEntryExtSMB(MountEntryExtAlien): + CREDENTIALSBASENAME = "/etc/fstab_smb_credentials_" + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtSMB,self).__init__(base) + + if isinstance(base,MountEntryExtSMB): + self.username = base.username + self.password = base.password + self.credentialsfile = base.credentialsfile + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + self.username = None + self.password = "" + self.credentialsfile = None + + newoptions = [] + options = self.extraoptions.split(",") + for line in options: + if line.startswith("username="): + self.username = line[9:] + elif line.startswith("password="): + self.password = line[9:] + elif line.startswith("credentials="): + self.credentialsfile = line[12:] + try: + fhandle = codecs.open(self.credentialsfile,'r',locale.getpreferredencoding()) + for line in fhandle.readlines(): + if line.startswith("username"): + self.username = line[8:].strip()[1:].strip() + elif line.startswith("password"): + self.password = line[8:].strip()[1:].strip() + fhandle.close() + + if not self.credentialsfile.startswith(self.CREDENTIALSBASENAME): + self.credentialsfile = None + + except IOError: + self.credentialsfile = None + + elif line=="guest": + pass + else: + # We hang on to unknown options for later. + newoptions.append(line) + options = newoptions + + if self.username == "": + self.username = None + + self.extraoptions = ",".join(options) + + else: + self.username = None + self.password = "" + self.credentialsfile = None + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSMB() + super(MountEntryExtSMB,self).copy(newobject) + newobject.username = self.username + newobject.password = self.password + newobject.credentialsfile = self.credentialsfile + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + ######################################################################## + def cleanup(self): + if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile): + os.remove(self.credentialsfile) + + ######################################################################## + def getIconName(self): + return "hi16-network" + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtSMB,self).getFstabOptions() + if self.username is None: + if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile): + os.remove(self.credentialsfile) + options.append("guest") # This option should stop mount(8) from asking for a password. + else: + # Write out the credentials file + if self.credentialsfile is None: + i = 1 + while os.path.exists(self.CREDENTIALSBASENAME+unicode(i)): + i += 1 + self.credentialsfile = self.CREDENTIALSBASENAME+unicode(i) + fd = os.open(self.credentialsfile,os.O_WRONLY|os.O_CREAT,0600) + fhandle = os.fdopen(fd,'w') + fhandle.write((u"username = %s\npassword = %s\n" % (self.username,self.password)) + .encode(locale.getpreferredencoding(),'replace') ) + fhandle.close() + options.append(u"credentials="+self.credentialsfile) + return options + + ######################################################################## + def getUsername(self): return self.username + def setUsername(self,username): self.username = username + def getPassword(self): return self.password + def setPassword(self,password): self.password = password + +############################################################################ +class MountEntryExtSystem(MountEntryExt): + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtSystem,self).__init__(base) + self.use_as_device = "devicenode" + self.label = "" + self.showuuid = False + self.showlabel = False + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSystem() + super(MountEntryExtSystem,self).copy(newobject) + return newobject + + ######################################################################## + def getCategory(self): + return "system" + + def disable(self,parentdialog): + """ This shouldn't happen since system entries have the disable button disabled """ + msg = i18n("Disabling %1 is not supported.").arg(self.mountpoint) + extramsg = i18n("""Some system devices cannot be disabled because they are needed for \ + basic functionality of the operating system.""") + KMessageBox.detailedSorry(parentdialog,msg,extramsg,\ + i18n("Error occurred while disabling %1").arg(self.mountpoint)) + +############################################################################ +class MountEntryExtSwap(MountEntryExt): + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtSwap,self).__init__(base) + + if isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + try: + options.remove('defaults') + except ValueError: + pass + self.extraoptions = u",".join(options) + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSwap() + super(MountEntryExtSwap,self).copy(newobject) + return newobject + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtSwap,self).getFstabOptions() + if len(options)==0: + # Make sure there is at least one option in the list. + options.append('defaults') + return options + + ######################################################################## + def updateStatus(self,mtablist): + + this_device = self.device + if this_device is None: + # Find the device name by its UUID. + if self.uuid: + hal_device = microhal.getDeviceByUUID(self.uuid) + if self.label: + hal_device = microhal.getDeviceByLabel(self.label) + if hal_device is None: + self.enabled = False + return + this_device = hal_device.getDev() + + # If the device is a symlink, then grab the complete target. + if os.path.islink(this_device): + this_device = os.path.join(os.path.dirname(this_device),os.readlink(this_device)) + + fhandle = open("/proc/swaps") + lines = fhandle.readlines() + fhandle.close() + + try: del lines[0] + except IndexError: pass + + self.enabled = False + for line in lines: + parts = line.split() + if parts[0]==this_device: + self.enabled = True + return + + ######################################################################## + # Returns a list of command+arguments + def enable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(['/sbin/swapon',self.device]) + if rc!=0: + msg = i18n("An error occurred while enabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output) + KMessageBox.sorry(parentdialog,msg,\ + i18n("Error occurred while enabling swap partition %1").arg(self.device)) + finally: + self._setBusy(parentdialog,False) + + ######################################################################## + # Returns a list of command+arguments or None. + def disable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(['/sbin/swapoff',self.device]) + if rc!=0: + msg = i18n("An error occurred while disabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output) + KMessageBox.sorry(parentdialog,msg,\ + i18n("Error occurred while disabling swap partition %1").arg(self.device)) + finally: + self._setBusy(parentdialog,False) + +############################################################################ +# This represents a mount entry. +# +# It also does a little trick with the MountEntryExt classes. MountEntry +# objects kind of 'change' class under your nose when they are set to +# different mount types. The handling of the different kinds of mount types +# is handled by MountEntryExt objects and subclasses. + +class MountEntry(object): + + MountTypes = { + 'proc' : (MountEntryExtSystem,i18n("proc")), + 'sysfs' : (MountEntryExtSystem,i18n("sysfs")), + 'rootfs' : (MountEntryExtSystem,i18n("rootfs")), + 'bdev' : (MountEntryExtSystem,i18n("bdev")), + 'sockfs' : (MountEntryExtSystem,i18n("sockfs")), + 'tmpfs' : (MountEntryExtSystem,i18n("tmpfs")), + 'shm' : (MountEntryExtSystem,i18n("shm")), + 'pipefs' : (MountEntryExtSystem,i18n("pipefs")), + 'devfs' : (MountEntryExtSystem,i18n("devfs - Device File System")), + 'devpts' : (MountEntryExtSystem,i18n("devpts")), + 'ramfs' : (MountEntryExtSystem,i18n("ramfs")), + 'auto' : (MountEntryExtCommonUnix,i18n("Automatic")), + 'usbdevfs' : (MountEntryExtSystem,i18n("usbdevfs")), + 'procbususb' : (MountEntryExtSystem,i18n("procbususb")), + 'usbfs' : (MountEntryExtSystem,i18n("usbfs")), + 'supermount' : (MountEntryExt,i18n("supermount")), + 'swap' : (MountEntryExtSwap,i18n("Swap - Linux Swap Space")), + + 'nfs' : (MountEntryExtCommonUnix,i18n("NFS - Network File System")), + 'cifs' : (MountEntryExtSMB,i18n("Windows File Sharing")), + + 'ext2' : (MountEntryExtCommonUnixLocal,i18n("Ext2 - Second Extended FS")), + 'ext3' : (MountEntryExtCommonUnixLocal,i18n("Ext3 - Third Extended FS")), + 'reiserfs' : (MountEntryExtCommonUnixLocal,i18n("ReiserFS")), + 'reiser4' : (MountEntryExtCommonUnixLocal,i18n("Reiser4")), + 'xfs' : (MountEntryExtCommonUnixLocal,i18n("XFS - SGI's journaling filesystem")), + 'hfs' : (MountEntryExtCommonUnixLocal,i18n("HFS - Apple's Hierarchical File System")), + 'hfsplus' : (MountEntryExtVFAT,i18n("HFS+ - Apple's modernized Hierarchical File System")), + 'jfs' : (MountEntryExtCommonUnixLocal,i18n("JFS - IBM's Journaled File System")), + 'vfat' : (MountEntryExtVFAT,i18n("VFAT - Microsoft FAT File Systems")), + 'ntfs' : (MountEntryExtVFAT,i18n("NTFS - NT File System")), + 'udf' : (MountEntryExtSystem,i18n("udf")), + 'iso9660' : (MountEntryExt,i18n("iso9660 - CD-ROM")), + } + + notInFstab = False + maydisable = True # Some entries, such as /proc can't be disabled. + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + try: + self.extensionObjects = {} + if base==None: + self.mounttype = 'auto' + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + parts = base.split() + self.mounttype = parts[2] + # 'udf,iso9660' seems default for some devices in fstab, + # check if all listed filesystems are available, if yes set to 'auto'. + if len(self.mounttype.split(',')) > 1: + """ + # We could check here, but then we'd need a reference to MicroHAL. + #for m in self.mounttype.split(','): + #if m not in supported_fs: + # print "Filesystem ", m, "not supported by the kernel" + # break + """ + self.mounttype = "auto" + else: + # This is a new entry, but it's based on another one. + self.mounttype = base.mounttype + self.extension = self.MountTypes[self.mounttype][0](base) + self.extensionObjects[self.mounttype] = self.extension + except (KeyError,IndexError): + raise InvalidMountEntryError, u"Unable to parse mount entry:"+unicode(base) + + ######################################################################## + def getMountType(self): + return self.mounttype + + ######################################################################## + def setMountType(self,newtypename): + if newtypename not in self.extensionObjects: + try: + self.extensionObjects[newtypename] = self.MountTypes[newtypename][0](self.extension) + except KeyError: + raise NotImplementedError, "Unknown mounttype:"+newtypename + self.mounttype = newtypename + self.extension = self.extensionObjects[newtypename] + self.extension.setMountType(newtypename) + + ######################################################################## + def copy(self): + newentry = MountEntry() + newentry.mounttype = self.mounttype + newext = self.extension.copy() + newentry.use_as_device = self.use_as_device + newentry.showlabel = self.showlabel + newentry.showdevice = self.showdevice + newentry.showuuid = self.showuuid + newentry.extensionObjects[self.mounttype] = newext + newentry.extension = newext + return newentry + + ######################################################################## + def inPlaceCopyFrom(self,sourceentry): + self.extension.cleanup() + + tmpcopy = sourceentry.copy() + self.extensionObjects = tmpcopy.extensionObjects + self.mounttype = tmpcopy.mounttype + self.extension = tmpcopy.extension + + # Override the attribute lookup, set/get, to use the extension object + ######################################################################## + def __getattr__(self,name): + try: + return getattr(self.extension,name) + except AttributeError, a: + print a + + ######################################################################## +# FIXME +## def __setattr__(self,name,value): +## if 'extension' in self.__dict__: +## if name in self.extension.__dict__: +## setattr(self.extension,name,value) +## return +## self.__dict__[name] = value + + ######################################################################## + def getMountTypes(): + return MountEntry.MountTypes.keys() + getMountTypes = staticmethod(getMountTypes) + + ######################################################################## + def getMountTypeLongName(typename): + return MountEntry.MountTypes[typename][1] + getMountTypeLongName = staticmethod(getMountTypeLongName) + + ######################################################################## + def encodeMountEntryString(string): + newstring = u"" + for c in string: + if c==' ': + newstring += "\\040" + elif c=="\t": + newstring += "\\012" + elif c=='\\': + newstring += "\\134" + else: + newstring += c + return newstring + encodeMountEntryString = staticmethod(encodeMountEntryString) + + ######################################################################## + def decodeMountEntryString(string): + newstring = "" + while string!="": + if len(string)>=4 and string[0]=='\\' and isoct(string[1]) \ + and isoct(string[2]) and isoct(string[3]): + newstring += chr(64*(ord(string[1])-ord('0')) + \ + 8*(ord(string[2])-ord('0')) + (ord(string[3])-ord('0'))) + string = string[4:] + else: + newstring += string[0] + string = string[1:] + return newstring + decodeMountEntryString = staticmethod(decodeMountEntryString) + +############################################################################ +class MountEntryComment(MountEntry): + """ This represents a comment mount entry or generally something that we don't + understand (might be comment, might be fstab syntax we don't know, might be + a faulty line in there). We don't want to wipe that stuff out, but we can't + deal with it in a sensible way, so we keep it in a MountEntryComment, + exclude it from most operations, but will write it back to fstab afterwards + + As a result of that we only define the stuff that's necessary, namely saving + the fstab line and returning it when writing.""" + + ######################################################################## + def __init__(self,base=None): + self.row = base + + ######################################################################## + def getFstabLine(self): return self.row + +############################################################################ +def isoct(c): return c in '01234567' + +############################################################################ +class InvalidMountEntryError(Exception): + + ######################################################################## + def __init__(self,arg=None): + self.arg = arg + + ######################################################################## + def __str__(self): + return str(self.arg) + +############################################################################ +class MountTable(object): + + ######################################################################## + def __init__(self,fstab_filename,mtab_filename): + self.fstab_filename = fstab_filename + self.mtab_filename = mtab_filename + + self.entries = [] + self.allentries = [] + + # sysfs does not need an entry in fstab, so we add it even if it's not + # in there, it's mounted automatically anyway and shows up in mtab + sysfs_in_fstab = False + usbdevfs_in_fstab = False + + fhandle = codecs.open(self.fstab_filename,'r',locale.getpreferredencoding()) + for row in fhandle.readlines(): + row = row.strip('\n') # Carefully remove any trailing newline. + if row.strip().startswith("#") or row.strip()=="": + entry = MountEntryComment(row) + else: + try: + entry = MountEntry(row) + self.append(entry) + + if entry.getMountType() == "sysfs": + sysfs_in_fstab = True + if entry.getMountType() == "usbdevfs" or "procbususb": + usbdevfs_in_fstab = True + if entry.getMountType() == "proc": + entry.maydisable = False + except InvalidMountEntryError: + entry = MountEntryComment(row) + # We keep a list with references to _all_ entries, also the comments, + # this is the one we'll use to write out our new fstab, only 'real' + # entries (real == entries we understand) are added to self to let them + # be handled by iterator. + # allentries includes comments and invalid lines, self doesn't. + self.allentries.append(entry) + fhandle.close() + + if not sysfs_in_fstab: + sysfsentry = MountEntry(u"sysfs /sys sysfs defaults 0 0") + sysfsentry.notInFstab = True + sysfsentry.maydisable = False + #self.append(sysfsentry) + + if not usbdevfs_in_fstab: + usbdevfsentry = MountEntry(u"procbususb /proc/bus/usb usbdevfs defaults 0 0") + usbdevfsentry.notInFstab = True + usbdevfsentry.maydisable = False + self.append(usbdevfsentry) + + self.updateStatus() + + ######################################################################## + def append(self,entry): + self.entries.append(entry) + + ######################################################################## + def remove(self,entry): + self.allentries.remove(entry) + entry.cleanup() + + ######################################################################## + def updateStatus(self,entry=None): + mtablist = self.getMtabList() + if entry==None: + for entry in self.entries: + entry.updateStatus(mtablist) + else: + entry.updateStatus(mtablist) + + ######################################################################## + def getMtabList(self): + fhandle = open(self.mtab_filename) + mtablist = [] + for row in fhandle.readlines(): + if row.strip()[0]!='#': + parts = row.split() + mtablist.append(parts[1]) + fhandle.close() + return mtablist + + ######################################################################## + def updateFstabOnDisk(self): + fhandle = None + try: + try: + fhandle = codecs.open(self.fstab_filename+"~","w",locale.getpreferredencoding(),'replace') + for entry in self.allentries: + if not entry.notInFstab: + line = entry.getFstabLine() + fhandle.write(line+u"\n") + print line + fhandle.close() + fhandle = None + + # Move it over the original + os.rename(self.fstab_filename+"~",self.fstab_filename) + return True + finally: + if fhandle: + fhandle.close() + except IOError: + return False + + ######################################################################## + # We make this class look like a container, and just forward everything + # on to the entries attribute. + def __contains__(self,item): + return self.entries.__contains(item) + ######################################################################## + def __delitem__(self,key): + raise NotImplementedError, "No __delitem__ on MountTable." + + ######################################################################## + def __getitem__(self,key): + return self.entries.__getitem__(key) + ######################################################################## + + def __iter__(self): + return self.entries.__iter__() + ######################################################################## + def __len__(self): + return self.entries.__len__() + ######################################################################## + def __setitem__(self,key,value): + raise NotImplementedError, "No __setitem__ on MountTable." + +############################################################################ +class MountEntryDialogOptions(QWidget): + + deviceexample = i18n("(for example /dev/hdb3)") + + ######################################################################## + def __init__(self,parent,showmountpoint=True,showdevice=True, + showfs_freq=True,showfs_passno=True,showuuid=True,showlabel=True): + QWidget.__init__(self,parent) + self.showmountpoint = showmountpoint + self.showdevice = showdevice + self.showfs_freq = showfs_freq + self.showfs_passno = showfs_passno + self.showuuid = showuuid + self.showlabel = showlabel + self._fillPage() + # TODO: KDirSelectDialog needs "Create new Folder" + self.mountpointdialog = KDirSelectDialog("/",True,self,"Select Mount Point",True) + + ######################################################################## + def _fillPage(self): + row = 0 + grid = QGridLayout(self,1,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + + if self.showmountpoint: + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if self.showdevice: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addMultiCellWidget(example,row,row,1,3) + row += 1 + + if self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + else: + if self.showdevice: + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addMultiCellWidget(self.devicelineedit,row,row,1,3) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + + if self.showuuid: + label = QLabel(i18n("Device UUID:"),self) + grid.addWidget(label,row,0) + self.uuidlineedit = KLineEdit(self) + grid.addMultiCellWidget(self.uuidlineedit,row,row,1,3) + row += 1 + + if self.showlabel: + label = QLabel(i18n("Device Label:"),self) + grid.addWidget(label,row,0) + self.labellineedit = KLineEdit(self) + grid.addMultiCellWidget(self.labellineedit,row,row,1,3) + row += 1 + + label = QLabel(i18n("Options:"),self) + grid.addWidget(label,row,0) + self.optionslineedit = KLineEdit(self) + grid.addMultiCellWidget(self.optionslineedit,row,row,1,3) + row += 1 + + if self.showfs_freq: + label = QLabel(i18n("fs_freq:"),self) + grid.addWidget(label,row,0) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,self) + grid.addWidget(self.fsfreqspinbox,row,1) + + if self.showfs_passno: + label = QLabel(i18n("fs_passno:"),self) + grid.addWidget(label,row,2) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,self) + grid.addWidget(self.fspassnospinbox,row,3) + row += 1 + + grid.addWidget(QWidget(self),row,0) + + for x in range(grid.numRows()): + grid.setRowStretch(x,0) + grid.setRowStretch(grid.numRows()-1,1) + + ######################################################################## + def displayMountEntry(self,entry): + global allowuuid, allowlabel + if self.showmountpoint: + self.mountpointlineedit.setText(entry.getMountPoint()) + + uuid = entry.getUUID() + if entry.getDevice() == "" and uuid != "": + device = microhal.getDeviceByUUID(uuid) + self.devicelineedit.setText(uuid) + else: + if self.showdevice: + self.devicelineedit.setText(entry.getDevice()) + + if self.showuuid: + if entry.getUUID()!="": + self.uuidlineedit.setText(entry.getUUID()) + + if self.showlabel: + if entry.getLabel()!="": + self.labellineedit.setText(entry.getLabel()) + + if entry.getUseAsDevice() == "devicenode": + if self.showdevice: + self.devicelineedit.setEnabled(True) + self.devicecheckbox.setChecked(True) + if self.showuuid: + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + if self.showlabel: + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + elif entry.getUseAsDevice() == "label": + if self.showlabel: + self.labellineedit.setEnabled(True) + self.labelcheckbox.setChecked(True) + if self.showdevice: + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + if self.showuuid: + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + elif entry.getUseAsDevice() == "uuid": + if self.showdevice: + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + if self.showlabel: + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + if self.showuuid: + self.uuidlineedit.setEnabled(True) + self.uuidcheckbox.setChecked(True) + +## if allowlabel: +## self.labellineedit.setText(entry.getLabel()) +## if allowuuid: +## self.uuidlineedit.setText(entry.getUUID()) +## self.devicelineedit.setText(entry.getDevice()) + + self.optionslineedit.setText(entry.getExtraOptions()) + if self.showfs_freq: + self.fsfreqspinbox.setValue(entry.getFSFreq()) + if self.showfs_passno: + self.fspassnospinbox.setValue(entry.getFSPassno()) + + ######################################################################## + def undisplayMountEntry(self,entry): + if self.showmountpoint: + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + if self.showdevice: + entry.setDevice( unicode(self.devicelineedit.text()) ) + + if self.showuuid and self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setUUID(None) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + if self.showlabel and self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + if allowuuid and self.showuuid: + if self.uuidcheckbox.isChecked(): + entry.setUseAsDevice("uuid") + if allowlabel and self.showlabel: + if self.labelcheckbox.isChecked(): + entry.setUseAsDevice("label") + if self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setUseAsDevice("devicenode") + + entry.setExtraOptions( unicode(self.optionslineedit.text()) ) + if self.showfs_freq: + entry.setFSFreq(self.fsfreqspinbox.value()) + if self.showfs_passno: + entry.setFSPassno(self.fspassnospinbox.value()) + + ######################################################################## + def slotBrowseMountPointClicked(self): + fileurl = KURL() + fileurl.setPath(self.mountpointlineedit.text()) + self.mountpointdialog.setCurrentURL(fileurl) + if self.mountpointdialog.exec_loop()==QDialog.Accepted: + self.mountpointlineedit.setText(self.mountpointdialog.url().path()) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.labelcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.labellineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + + def slotLabelCheckboxClicked(self): + if self.labellineedit.text() == "": + label = microhal.getLabelByDevice(unicode(self.devicelineedit.text())) + self.labellineedit.setText(label) + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(False) + self.labelcheckbox.setChecked(True) + self.labellineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsCommonUnix(MountEntryDialogOptions): + + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedCommonUnixDialog(None) + + ######################################################################## + def _fillPage(self): + + row = 0 + grid = QGridLayout(self,1,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setRowStretch(6,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"), \ + self.slotBrowseMountPointClicked) + grid.addWidget(hbox,row,1) + row += 1 + + if self.showuuid or self.showlabel: + + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + if self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + if self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + else: + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addWidget(self.devicelineedit,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addWidget(self.autocheckbox,row,1) + row += 1 + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addWidget(self.writeablecheckbox,row,1) + row += 1 + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,row,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addWidget(self.usermountcombobox,row,1) + row += 1 + + #grid.addWidget(,9,0) + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addWidget(button,row,1,Qt.AlignRight) + row += 1 + + grid.addWidget(QWidget(self),row,0) + + for x in range(grid.numRows()): + grid.setRowStretch(x,0) + grid.setRowStretch(grid.numRows()-1,1) + + ######################################################################## + def displayMountEntry(self,entry): + self.devicelineedit.setText(entry.getDevice()) + + if self.showuuid: + if entry.getUUID()!="": + self.uuidlineedit.setText(entry.getUUID()) + + if self.showlabel: + if entry.getLabel()!="": + self.labellineedit.setText(entry.getLabel()) + + if entry.getUseAsDevice() == "devicenode": + self.devicelineedit.setEnabled(True) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + elif entry.getUseAsDevice() == "label": + self.labellineedit.setEnabled(True) + self.labelcheckbox.setChecked(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + elif entry.getUseAsDevice() == "uuid": + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + self.uuidcheckbox.setChecked(True) + + self.devicelineedit.setText(entry.getDevice()) + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.writeablecheckbox.setChecked(entry.getWritable()) + self.accesstime = entry.getAtime() + self.allowexecutable = entry.getAllowExecutables() + self.allowsuid = entry.getSUID() + self.usedevpoints = entry.getUseDevPoints() + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + + ######################################################################## + def undisplayMountEntry(self,entry): + + entry.setDevice( unicode(self.devicelineedit.text()) ) + if self.showuuid: + if self.devicecheckbox.isChecked() or self.labelcheckbox.isChecked(): + entry.setUUID(None) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + + if self.showlabel: + if self.devicecheckbox.isChecked() or self.uuidcheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + if not self.showlabel and not self.showuuid: + if self.uuidcheckbox.isChecked() or self.labelcheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.devicelineedit.text()) ) + + if allowuuid: + if self.uuidcheckbox.isChecked(): + entry.setUseAsDevice("uuid") + if allowlabel: + if self.labelcheckbox.isChecked(): + entry.setUseAsDevice("label") + if self.devicecheckbox.isChecked(): + entry.setUseAsDevice("devicenode") + + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setAtime(self.accesstime) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setUseDevPoints(self.usedevpoints) + entry.setAllowExecutable(self.allowexecutable) + entry.setSUID(self.allowsuid) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + + ######################################################################## + def slotAdvancedClicked(self): + (self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, self.options, self.fsfreq, + self.fspassno)\ + = self.advanceddialog.do(self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, + self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + + def slotLabelCheckboxClicked(self): + if self.labellineedit.text() == "": + label = microhal.getLabelByDevice(unicode(self.devicelineedit.text())) + self.labellineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.labellineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsSys(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent,True,False,False,False,False,False) + +############################################################################ +class MountEntryDialogOptionsSwap(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent,False,True,False,False) + +class MountEntryDialogOptionsNfs(MountEntryDialogOptionsCommonUnix): + + deviceexample = i18n("(for example 192.168.1.66:/export)") + +############################################################################ +class MountEntryDialogOptionsVFAT(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedPlainDialog(None) + self.updatinggui= False + + ######################################################################## + def _fillPage(self): + global allowuuid, allowlabel + + row = 0 + grid = QGridLayout(self,11,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + grid.setRowStretch(10,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if allowuuid or allowlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + if allowuuid and self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + if allowlabel and self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + else: + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addMultiCellWidget(self.devicelineedit,row,row,1,3) + row += 1 + + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addMultiCellWidget(self.autocheckbox,row,row,1,3) + row += 1 + + # Security & Safety line. + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi16-lock")) + hbox.setStretchFactor(tmplabel,0) + tmplabel = QLabel(hbox) + tmplabel.setText(i18n("Security & Safety")) + hbox.setStretchFactor(tmplabel,0) + sep = KSeparator(KSeparator.Horizontal,hbox) + hbox.setStretchFactor(sep,1) + grid.addMultiCellWidget(hbox,row,row,0,3) + row += 1 + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addMultiCellWidget(self.writeablecheckbox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Files belong to user:"),self) + grid.addWidget(label,row,0) + self.uidcombobox = UserComboBox(self) + grid.addMultiCellWidget(self.uidcombobox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Files belong to group:"),self) + grid.addWidget(label,row,0) + self.gidcombobox = GroupComboBox(self) + grid.addMultiCellWidget(self.gidcombobox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,row,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addMultiCellWidget(self.usermountcombobox,row,row,1,3) + row += 1 + + self.suppresspermissionerrorcheckbox = QCheckBox(i18n("Suppress permission errors"),self) + grid.addMultiCellWidget(self.suppresspermissionerrorcheckbox,row,row,1,3) + row += 1 + + row += 1 + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addMultiCellWidget(button,row,row,1,3,Qt.AlignRight) + + ######################################################################## + def displayMountEntry(self,entry): + global allowuuid, allowlabel + + uuid = entry.getUUID() + if entry.getDevice() == "" and uuid != "": + device = microhal.getDeviceByUUID(uuid) + self.devicelineedit.setText(uuid) + else: + self.devicelineedit.setText(entry.getDevice()) + + if allowuuid: + self.uuidlineedit.setText(uuid) + if entry.getUUID()!="": + self.uuidlineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidcheckbox.setChecked(True) + else: + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + + if allowlabel: # If the filesystem has a label override the UUID settings + self.labellineedit.setText(entry.getLabel()) + if entry.getLabel()!="": + self.labellineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.labelcheckbox.setChecked(True) + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + else: + if entry.getUUID()!="": + self.uuidlineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidcheckbox.setChecked(True) + else: + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + + if allowlabel: + self.labellineedit.setText(entry.getLabel()) + if allowuuid: + self.uuidlineedit.setText(entry.getUUID()) + self.devicelineedit.setText(entry.getDevice()) + + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.writeablecheckbox.setChecked(entry.getWritable()) + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + self.uidcombobox.setUID(entry.getUID()) + self.gidcombobox.setGID(entry.getGID()) + self.suppresspermissionerrorcheckbox.setChecked(entry.getSuppressPermissionErrors()) + + ######################################################################## + def undisplayMountEntry(self,entry): + global allowuuid, allowlabel + if allowuuid: + if self.devicecheckbox.isChecked(): + entry.setUUID(None) + else: + if allowlabel: + if self.labelcheckbox.isChecked(): + entry.setLabel( unicode(self.labellineedit.text()) ) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + + if allowlabel: + if self.devicecheckbox.isChecked(): + entry.setLabel(None) + else: + if allowuuid: + if self.uuidcheckbox.isChecked(): + entry.setUUID( unicode(self.uuidlineedit.text()) ) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + entry.setDevice( unicode(self.devicelineedit.text()) ) + + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + entry.setUID(self.uidcombobox.UID()) + entry.setGID(self.gidcombobox.GID()) + entry.setSuppressPermissionErrors(self.suppresspermissionerrorcheckbox.isChecked()) + + ######################################################################## + def slotAdvancedClicked(self): + (self.options, self.fsfreq, self.fspassno)\ + = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsSMB(MountEntryDialogOptions): + + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedPlainDialog(None) + self.updatinggui= False + + ######################################################################## + def _fillPage(self): + grid = QGridLayout(self,14,4) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + grid.setRowStretch(12,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,0,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,0,0,1,3) + + label = QLabel(i18n("Network Share:"),self) + grid.addWidget(label,1,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.devicelineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.devicelineedit,1) + self.devicelinebutton = KPushButton(i18n("Scan..."),hbox) + hbox.setStretchFactor(self.devicelinebutton,0) + self.connect(self.devicelinebutton,SIGNAL("clicked()"),self.slotBrowseDeviceLineClicked) + grid.addMultiCellWidget(hbox,1,1,1,3) + + # Connect as: + connectaslabel = QLabel(self) + connectaslabel.setText(i18n("Connect as:")) + grid.addWidget(connectaslabel,2,0) + + self.guestradio = QRadioButton(self) + self.guestradio.setChecked(True) + grid.addWidget(self.guestradio,2,1) + tmplabel = QLabel(self) + tmplabel.setText(i18n("Guest")) + grid.addMultiCellWidget(tmplabel,2,2,2,3) + self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked) + + self.userradio = QRadioButton(self) + grid.addWidget(self.userradio,3,1) + tmplabel = QLabel(self) + tmplabel.setText(i18n("Username:")) + grid.addWidget(tmplabel,3,2) + self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked) + + self.usernameedit = KLineEdit(self) + grid.addWidget(self.usernameedit,3,3) + + tmplabel = QLabel(self) + tmplabel.setText(i18n("Password:")) + grid.addWidget(tmplabel,4,2) + + self.passwordedit = KLineEdit(self) + grid.addWidget(self.passwordedit,4,3) + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addMultiCellWidget(self.autocheckbox,5,5,1,3) + + # Security & Safety line. + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi16-lock")) + hbox.setStretchFactor(tmplabel,0) + tmplabel = QLabel(hbox) + tmplabel.setText(i18n("Security & Safety")) + hbox.setStretchFactor(tmplabel,0) + sep = KSeparator(KSeparator.Horizontal,hbox) + hbox.setStretchFactor(sep,1) + grid.addMultiCellWidget(hbox,6,6,0,3) + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addMultiCellWidget(self.writeablecheckbox,7,7,1,3) + + label = QLabel(i18n("Files belong to user:"),self) + grid.addWidget(label,8,0) + self.uidcombobox = UserComboBox(self) + grid.addMultiCellWidget(self.uidcombobox,8,8,1,3) + + label = QLabel(i18n("Files belong to group:"),self) + grid.addWidget(label,9,0) + self.gidcombobox = GroupComboBox(self) + grid.addMultiCellWidget(self.gidcombobox,9,9,1,3) + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,10,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addMultiCellWidget(self.usermountcombobox,10,10,1,3) + + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addMultiCellWidget(button,13,13,1,3,Qt.AlignRight) + + self.selectsmbdialog = None + + ######################################################################## + def slotBrowseDeviceLineClicked(self): + if self.updatinggui: + return + self.updatinggui = True + + if self.selectsmbdialog is None: + self.selectsmbdialog = SMBShareSelectDialog(None) + + # This just converts a \\zootv\data\ share name to smb:/zootv/data + parts = [x.replace("/",'\\') for x in unicode(self.devicelineedit.text()).split('\\') if x!=""] + oldurl = u"smb:/"+("/".join(parts) ) + + urlobj = KURL(oldurl) + if self.userradio.isChecked(): + urlobj.setUser(self.usernameedit.text()) + urlobj.setPass(self.passwordedit.text()) + + newurlobj = self.selectsmbdialog.choose(urlobj) + # This just converts smb:/zootv/data to \\zootv\data. + plainurl = KURL(newurlobj) + plainurl.setUser(QString.null) + plainurl.setPass(QString.null) + parts = [x.replace('\\',"/") for x in unicode(plainurl.url())[4:].split("/") if x !=""] + #convert the first part to an IP address + nmboutput = subprocess.Popen(["nmblookup",parts[0]], stdout=subprocess.PIPE).stdout + nmboutput.readline() + ipaddress = nmboutput.readline().split(" ")[0] + parts[0] = ipaddress + self.devicelineedit.setText(r'\\'+('\\'.join(parts))) + + if not newurlobj.hasUser(): + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.usernameedit.setText("") + self.passwordedit.setText("") + else: + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + self.usernameedit.setText(newurlobj.user()) + self.passwordedit.setText(newurlobj.pass_()) + + self.updatinggui = False + + ######################################################################## + def displayMountEntry(self,entry): + self.devicelineedit.setText(entry.getDevice()) + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.writeablecheckbox.setChecked(entry.getWritable()) + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + self.uidcombobox.setUID(entry.getUID()) + self.gidcombobox.setGID(entry.getGID()) + + if entry.getUsername() is None: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.usernameedit.setText("") + self.passwordedit.setText("") + else: + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + self.usernameedit.setText(entry.getUsername()) + self.passwordedit.setText(entry.getPassword()) + + ######################################################################## + def undisplayMountEntry(self,entry): + entry.setDevice( unicode(self.devicelineedit.text()) ) + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + entry.setUID(self.uidcombobox.UID()) + entry.setGID(self.gidcombobox.GID()) + + if self.guestradio.isChecked(): + entry.setUsername(None) + entry.setPassword(None) + else: + entry.setUsername( unicode(self.usernameedit.text()) ) + entry.setPassword( unicode(self.passwordedit.text()) ) + + ######################################################################## + def slotAdvancedClicked(self): + (self.options, self.fsfreq, self.fspassno)\ + = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotGuestRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + + if state==QButton.Off: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + + self.updatinggui = False + + ######################################################################## + def slotUserRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + if state==QButton.Off: + self.userradio.setChecked(True) + self.guestradio.setChecked(False) + + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + + self.updatinggui = False + +############################################################################ +class ROListBoxItem(QListBoxPixmap): + """A read-only ListBox item that also uses the 'alternate' background colour + as background. + """ + def __init__(self,listbox,pix,text): + QListBoxPixmap.__init__(self,listbox,pix,text) + self.setSelectable(False) + def paint(self,p): + boldfont = QFont(p.font()) + boldfont.setWeight(QFont.Bold) + p.setFont(boldfont) + p.setBackgroundColor(KGlobalSettings.alternateBackgroundColor()) + p.eraseRect(0,0,self.listBox().width(),self.height(self.listBox())) + QListBoxPixmap.paint(self,p) + +############################################################################ +class MountEntryDialog(KDialogBase): + + MountTypeEditorsDisk = { + 'ext2' : MountEntryDialogOptionsCommonUnix, + 'ext3' : MountEntryDialogOptionsCommonUnix, + 'reiserfs' : MountEntryDialogOptionsCommonUnix, + 'reiser4' : MountEntryDialogOptionsCommonUnix, + 'xfs' : MountEntryDialogOptionsCommonUnix, + 'jfs' : MountEntryDialogOptionsCommonUnix, + 'vfat' : MountEntryDialogOptionsVFAT, + 'ntfs' : MountEntryDialogOptionsVFAT, + 'hfsplus' : MountEntryDialogOptionsVFAT, + 'udf' : MountEntryDialogOptions, + 'iso9660' : MountEntryDialogOptions, + } + MountTypeEditorsNetwork = { + 'nfs' : MountEntryDialogOptionsNfs, + 'cifs' : MountEntryDialogOptionsSMB, + } + MountTypeEditorsSystem = { + 'proc' : MountEntryDialogOptionsSys, + 'sysfs' : MountEntryDialogOptionsSys, + 'rootfs' : MountEntryDialogOptions, + 'bdev' : MountEntryDialogOptions, + 'sockfs' : MountEntryDialogOptions, + 'tmpfs' : MountEntryDialogOptions, + 'shm' : MountEntryDialogOptions, + 'pipefs' : MountEntryDialogOptions, + 'ramfs' : MountEntryDialogOptionsSys, + 'devfs' : MountEntryDialogOptions, + 'devpts' : MountEntryDialogOptionsSys, + 'auto' : MountEntryDialogOptionsCommonUnix, + 'usbdevfs' : MountEntryDialogOptions, + 'procbususb' : MountEntryDialogOptions, + 'usbfs' : MountEntryDialogOptions, + 'supermount' : MountEntryDialogOptions, + 'swap' : MountEntryDialogOptionsSwap + } + + ######################################################################## + def __init__(self,parent): + KDialogBase.__init__(self,parent,None,True,"Configuration",KDialogBase.Ok|KDialogBase.Cancel, + KDialogBase.Cancel) + + self.updatingGUI = True + + # Maps MountEntry classes to MountEntryDialogOptions objects + self.mountTypeToOptionWidget = {} + + # Maps indexes in the combobox to mounttypes + self.comboIndexToMountType = [] + self.currentOptionWidget = None + + self.topvbox = QVBox(self) + self.setMainWidget(self.topvbox) + self.topvbox.setSpacing(self.spacingHint()) + + hb = QHBox(self.topvbox) + hb.setSpacing(self.spacingHint()) + self.topvbox.setStretchFactor(hb,0) + + label = QLabel(i18n("Type:"),hb) + hb.setStretchFactor(label,0) + self.mounttypecombo = KComboBox(hb) + + # Disk types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-hdd"),i18n("Disk Filesystems")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsDisk.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + # Network types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-network"),i18n("Network Filesystems")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsNetwork.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + # System types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-blockdevice"),i18n("Operating System")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsSystem.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + self.MountTypeEditors = self.MountTypeEditorsDisk.copy() + self.MountTypeEditors.update(self.MountTypeEditorsNetwork) + self.MountTypeEditors.update(self.MountTypeEditorsSystem) + + #hb.setStretchFactor(self.runlevelcombo,0) + self.connect(self.mounttypecombo, SIGNAL("activated(int)"), self.slotMountTypeChanged) + + widget = QWidget(hb) + hb.setStretchFactor(widget,1) + + # Create the stack of option edit widgets. + gb = QVGroupBox(self.topvbox) + self.topvbox.setStretchFactor(gb,1) + self.optionsstack = QWidgetStack(gb) + + for mounttype in self.MountTypeEditors: + editpage = self.MountTypeEditors[mounttype](self.optionsstack) + self.mountTypeToOptionWidget[mounttype] = editpage + self.optionsstack.addWidget(editpage) + + self.fsunavailablelabel = QHBox(gb) + self.fsunavailablelabel.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(self.fsunavailablelabel) + self.fsunavailablelabel.setStretchFactor(tmplabel,0) + tmplabel.setPixmap(SmallIcon('info')) + label = QLabel(i18n("This filesystem type is currently unavailable on the running kernel."), + self.fsunavailablelabel) + self.fsunavailablelabel.setStretchFactor(label,1) + self.fsunavailablelabel.hide() + + self.updatingGUI = False + + ####################################################################### + def doEditMount(self,mounttable,mountentry): + self.newEntry = False + self.mounttable = mounttable + self.originalMountEntry = mountentry + self.currentMountEntry = mountentry.copy() + + self.updatingGUI = True + self.selectEntry(self.currentMountEntry.getMountType()) + self.updatingGUI = False + if self.exec_loop()==QDialog.Accepted: + # All of the update stuff is in slotOk() + return True + return False + + ####################################################################### + def doNewMount(self,mounttable,defaultdevice): + self.newEntry = True + self.mounttable = mounttable + self.currentMountEntry = MountEntry() + if defaultdevice is not None: + self.currentMountEntry.setDevice(defaultdevice) + self.updatingGUI = True + self.currentOptionWidget = None + self.selectEntry(self.currentMountEntry.mounttype) + self.updatingGUI = False + if self.exec_loop()==QDialog.Accepted: + self.mounttable.allentries.append(self.currentMountEntry) + self.mounttable.updateFstabOnDisk() + return self.currentMountEntry + return None + + ####################################################################### + def selectEntry(self,mounttype): + #if self.currentOptionWidget!=None: + # # Update the mount entry from the + # self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry) + self.currentMountEntry.setMountType(mounttype) + # Update GUI + self.mounttypecombo.setCurrentItem(self.comboIndexToMountType.index(mounttype)) + self.currentOptionWidget = self.mountTypeToOptionWidget[mounttype] + self.currentOptionWidget.displayMountEntry(self.currentMountEntry) + self.optionsstack.raiseWidget(self.currentOptionWidget) + if microhal.isSupportedFileSystem(mounttype): + self.fsunavailablelabel.hide() + else: + self.fsunavailablelabel.show() + + ####################################################################### + def slotMountTypeChanged(self,index): + if self.updatingGUI==False: + self.updatingGUI = True + self.selectEntry(self.comboIndexToMountType[index]) + self.updatingGUI = False + + ####################################################################### + def slotOk(self): + global allowlabel, allowuuid + self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry) + if allowuuid: + if self.currentOptionWidget.uuidcheckbox.isChecked(): + self.currentMountEntry.setUseAsDevice("uuid") + if allowlabel: + if self.currentOptionWidget.labelcheckbox.isChecked(): + self.currentMountEntry.setUseAsDevice("label") + conflictentry = None + if self.newEntry: + for entry in self.mounttable: + if entry.getMountPoint()==self.currentMountEntry.getMountPoint(): + # Conflict found. + conflictentry = entry + else: + # Check if the mountpoint is already in use elsewhere in the mounttable. + if self.originalMountEntry.getMountPoint()!=self.currentMountEntry.getMountPoint(): + for entry in self.mounttable: + if (entry.getMountPoint()==self.currentMountEntry.getMountPoint() + and entry is not self.originalMountEntry): + # Conflict found. + conflictentry = entry + if conflictentry is not None: + if KMessageBox.warningContinueCancel(self, \ + i18n("The mountpoint '%1' is already in use by another entry?\nContinue?").arg( + self.currentMountEntry.getMountPoint()), \ + i18n("Mountpoint already in use"))!=KMessageBox.Continue: + return + + if self.currentMountEntry.getMountType() in MountEntryDialog.MountTypeEditorsDisk.keys(): + # If device is not in /dev and it's no bind mount, ask if that's meant this way ... + options = self.currentMountEntry.getFstabOptions() + if (not self.currentMountEntry.getDevice().startswith("/dev/") + and not ("loop" in options or "bind" in options)): + ask = KMessageBox.warningYesNoCancel(self, + i18n("'%1' does not seem to be a device and the option 'bind' has not been specified in the \ + \"Advanced\" page?\n Should I add the 'loop' option?").arg(self.currentMountEntry.device), + i18n("Options may be missing")) + if ask==KMessageBox.Cancel: + return + elif ask==KMessageBox.Yes: + # Add loop option + extraoptions = self.currentMountEntry.getExtraOptions().split(',') + extraoptions.append('loop') + self.currentMountEntry.setExtraOptions(','.join(extraoptions)) + + if (not os.path.isdir(self.currentMountEntry.getMountPoint()) + and not os.path.isfile(self.currentMountEntry.getMountPoint()) + and not self.currentMountEntry.mounttype == 'swap'): + ask = KMessageBox.warningYesNoCancel(self, + i18n("""The mountpoint '%1' does not exist. You will not be able to enable it until it is created.\ + \nShould I create the mountpoint?""").arg(self.currentMountEntry.getMountPoint()), + i18n("Mountpoint does not exist")) + if ask==KMessageBox.Cancel: + return + elif ask==KMessageBox.Yes: + os.mkdir(self.currentMountEntry.getMountPoint()) + elif os.path.isfile(self.currentMountEntry.getMountPoint()): + if KMessageBox.warningContinueCancel(self, + i18n("""The mountpoint '%1' is a file, but it has to be a directory. You will probably not \ + be able to enable it.\nContinue?""").arg(self.currentMountEntry.getMountPoint()), + i18n("Invalid mountpoint"))!=KMessageBox.Continue: + return + + if self.newEntry==False: + # How to Change a Mount Entry. + # 1. Disable the exisiting entry (if needed) + # 2. Update existing entry from the mount table. + # 3. Enable the updated entry (if needed) + # 4. Write new fstab file. + # 5. Enable the new entry (if needed) + + # 1. Disable the exisiting entry (if needed) + enabled = self.originalMountEntry.isEnabled() + if enabled: + self.disablingold = True + self.originalMountEntry.disable(self) + self.originalMountEntry.inPlaceCopyFrom(self.currentMountEntry) + self.mounttable.updateFstabOnDisk() + if enabled and self.originalMountEntry.isFileSystemAvailable(): + self.originalMountEntry.enable(self) + self.accept() + +############################################################################ +class MountEntryAdvancedCommonUnixDialog(KDialogBase): + ######################################################################## + def __init__(self,parent,name=None): + KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + + grid = self.makeGridMainWidget(2,Qt.Horizontal) + grid.setSpacing(self.spacingHint()) + + QWidget(grid) + self.accesstimecheckbox = QCheckBox(i18n("Update file access timestamps"),grid) + + QWidget(grid) + self.allowexecutablecheckbox = QCheckBox(i18n("Allow Executables"),grid) + + QWidget(grid) + self.allowsuidcheckbox = QCheckBox(i18n("Allow the SUID and SGID attributes"),grid) + + QWidget(grid) + self.usedevpointscheckbox = QCheckBox(i18n("Allow device points"),grid) + + label = QLabel(i18n("Options:"),grid) + self.optionslineedit = KLineEdit(grid) + + label = QLabel(i18n("fs_freq:"),grid) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid) + + label = QLabel(i18n("fs_passno:"),grid) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid) + + ######################################################################## + def do(self,atime,allowexecutable,allowsuid,usedevpoints,options,fsfreq,fspassno): + + self.accesstimecheckbox.setChecked(atime) + self.allowexecutablecheckbox.setChecked(allowexecutable) + self.allowsuidcheckbox.setChecked(allowsuid) + self.usedevpointscheckbox.setChecked(usedevpoints) + self.optionslineedit.setText(options) + self.fsfreqspinbox.setValue(fsfreq) + self.fspassnospinbox.setValue(fspassno) + self.exec_loop() + return ( self.accesstimecheckbox.isChecked(), + self.allowexecutablecheckbox.isChecked(), + self.allowsuidcheckbox.isChecked(), + self.usedevpointscheckbox.isChecked(), + unicode(self.optionslineedit.text()), + self.fsfreqspinbox.value(), + self.fspassnospinbox.value()) + +############################################################################ +class MountEntryAdvancedPlainDialog(KDialogBase): + ######################################################################## + def __init__(self,parent,name=None): + KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + + grid = self.makeGridMainWidget(2,Qt.Horizontal) + grid.setSpacing(self.spacingHint()) + + label = QLabel(i18n("Options:"),grid) + self.optionslineedit = KLineEdit(grid) + + label = QLabel(i18n("fs_freq:"),grid) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid) + + label = QLabel(i18n("fs_passno:"),grid) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid) + + ######################################################################## + def do(self,options,fsfreq,fspassno): + self.optionslineedit.setText(options) + self.fsfreqspinbox.setValue(fsfreq) + self.fspassnospinbox.setValue(fspassno) + self.exec_loop() + return (unicode(self.optionslineedit.text()), self.fsfreqspinbox.value(), self.fspassnospinbox.value()) + +############################################################################ +class MountListViewItem(KListViewItem): + ######################################################################## + def __init__(self,parentitem,mountentry,haldevice=None): + self.haldevice = haldevice + self.mountentry = mountentry + if self.mountentry is None: + # There is no mount entry right now. This acts as a place holder + # for now. + KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","",self.haldevice.getDev(),"") + else: + if mountentry.isEnabled(): + enabled = i18n("Enabled") + else: + enabled = i18n("Disabled") + if self.haldevice is not None: + name = self.haldevice.getName() + else: + name = self.mountentry.getName() + KListViewItem.__init__(self, parentitem, \ + name, + self.mountentry.getMountPoint(), \ + self.mountentry.mounttype, \ + self.mountentry.getDevice(), \ + enabled) + + if self.mountentry.isEnabled(): + self.setPixmap(4,UserIcon("greenled")) + else: + self.setPixmap(4,UserIcon("greyled")) + self.__updateIcon() + + ######################################################################## + def hasHAL(self): + return self.haldevice is not None + + ######################################################################## + def getHAL(self): return self.haldevice + + ######################################################################## + def updateDisplay(self): + if self.mountentry is not None: + if self.mountentry.isEnabled(): + enabled = i18n("Enabled") + self.setPixmap(4,UserIcon("greenled")) + else: + enabled = i18n("Disabled") + self.setPixmap(4,UserIcon("greyled")) + + if self.haldevice is not None: + self.setText(0,self.haldevice.getName()) + else: + self.setText(0,self.mountentry.getMountPoint()) + + self.setText(1,self.mountentry.getMountPoint()) + self.setText(2,self.mountentry.mounttype) + + if self.mountentry.getDevice() is not None: + self.setText(3,self.mountentry.getDevice()) + else: + uuid_device = microhal.getDeviceByUUID(self.mountentry.getUUID()) + label_device = microhal.getDeviceByUUID(self.mountentry.getUUID()) + if label_device is not None: + self.setText(3,label_device.getDev()+" (Label)") + elif real_device is not None: + self.setText(3,real_device.getDev()+" (UUID)") + else: + self.setText(3,"UUID="+self.mountentry.getUUID()) + + self.setText(4,enabled) + else: + self.setText(0,self.haldevice.getName()) + self.setText(1,"") + self.setText(2,"") + self.setText(3,self.haldevice.getDev()) + self.setText(4,"") + self.setPixmap(4,QPixmap()) + self.__updateIcon() + + ######################################################################## + def setMountEntry(self,entry): + self.mountentry = entry + self.updateDisplay() + + ######################################################################## + def getMountEntry(self): + return self.mountentry + + ######################################################################## + def getDevice(self): return self.haldevice.getDev() + + ######################################################################## + + def __updateIcon(self): + if self.haldevice is not None: + self.setPixmap(0,UserIcon(self.haldevice.iconname)) + else: + self.setPixmap(0,UserIcon(self.mountentry.getIconName())) + +############################################################################ +class MountGroupListViewItem(KListViewItem): + ######################################################################## + def __init__(self,parentitem,haldevice): + self.haldevice = haldevice + KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","","","") + + if self.haldevice is not None: + iconname = self.haldevice.iconname + else: + iconname = self.mountentry.getIconName() + self.setPixmap(0,UserIcon(iconname)) + + ######################################################################## + def getMountEntry(self): + return None + + ######################################################################## + def updateDisplay(self): + pass + + def hasHAL(self): + return False + +############################################################################ +# Try translating this code to C++. I dare ya! +if standalone: + programbase = KDialogBase +else: + programbase = KCModule + +class MountConfigApp(programbase): + ######################################################################## + def __init__(self,parent=None,name=None): + global standalone,isroot + KGlobal.locale().insertCatalogue("guidance") + + if standalone: + KDialogBase.__init__(self,KJanusWidget.Plain,i18n("Disk & Filesystems"), + KDialogBase.User1|KDialogBase.Close, KDialogBase.Close) + self.setButtonText(KDialogBase.User1,i18n("About")) + topwidget = self.plainPage() + else: + KCModule.__init__(self,parent,name) + self.setButtons(0) + self.aboutdata = MakeAboutData() + topwidget = self + + # Create a configuration object. + self.config = KConfig("mountconfigrc") + + KGlobal.iconLoader().addAppDir("guidance") + self.updatingGUI = False + self.mounttable = MountTable('/etc/fstab','/etc/mtab') + self.selectedentry = None + self.aboutus = KAboutApplication(self) + self.sizeviewdialogs = {} + toplayout = QVBoxLayout(topwidget, 0, KDialog.spacingHint()) + #topwidget.setEnabled(isroot) + + hb = QHBox(topwidget) + hb.setSpacing(KDialog.spacingHint()) + #if standalone: + # hb.setMargin(KDialog.marginHint()) + + toplayout.addWidget(hb) + + label = QLabel(hb) + label.setPixmap(UserIcon("kcmpartitions")) + hb.setStretchFactor(label,0) + + label = QLabel(i18n("Available Disks and Filesystems:"),hb) + hb.setStretchFactor(label,1) + + self.mountlist = KListView(topwidget,"Mount list") + toplayout.addWidget(self.mountlist) + self.mountlist.addColumn(i18n("Name")) + self.mountlist.addColumn(i18n("Mount Point")) + self.mountlist.addColumn(i18n("Type")) + self.mountlist.addColumn(i18n("Device")) + self.mountlist.addColumn(i18n("Enabled")) + self.mountlist.setAllColumnsShowFocus(True) + self.mountlist.setSelectionMode(QListView.Single) + self.mountlist.setRootIsDecorated(True) + self.mountlist.setSorting(-1) + self.connect(self.mountlist, SIGNAL("selectionChanged(QListViewItem *)"), self.slotListClicked) + # Doubleclick in item opens modify dialogue. + self.connect(self.mountlist, SIGNAL("doubleClicked(QListViewItem *)"), self.slotModifyClicked) + # Rightclick: Open ContextMenu + self.connect(self.mountlist, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"), + self.slotContextMenu) + + hbox = QHBox(topwidget) + toplayout.addWidget(hbox) + hbox.setSpacing(KDialog.spacingHint()) + + toplayout.setStretchFactor(hbox,0) + self.newbutton = KPushButton(i18n("New..."),hbox) + hbox.setStretchFactor(self.newbutton,1) + self.connect(self.newbutton,SIGNAL("clicked()"),self.slotNewClicked) + self.newbutton.setEnabled(isroot) + + self.modifybutton = KPushButton(i18n("Modify..."),hbox) + hbox.setStretchFactor(self.modifybutton,1) + self.connect(self.modifybutton,SIGNAL("clicked()"),self.slotModifyClicked) + + self.deletebutton = KPushButton(i18n("Delete..."),hbox) + hbox.setStretchFactor(self.deletebutton,1) + self.connect(self.deletebutton,SIGNAL("clicked()"),self.slotDeleteClicked) + + self.enablebutton = KPushButton(i18n("Enable"),hbox) + hbox.setStretchFactor(self.enablebutton,1) + self.connect(self.enablebutton,SIGNAL("clicked()"),self.slotEnableClicked) + + self.disablebutton = KPushButton(i18n("Disable"),hbox) + hbox.setStretchFactor(self.disablebutton,1) + self.connect(self.disablebutton,SIGNAL("clicked()"),self.slotDisableClicked) + + self.detailsbutton = KPushButton(i18n("Details..."),hbox) + hbox.setStretchFactor(self.detailsbutton,1) + self.connect(self.detailsbutton,SIGNAL("clicked()"),self.slotDetailsClicked) + + self.devstolistitems = None + self.uuidstolistitems = None + self.mountentriestolistitems = None + self.__updateMountList() + self.__selectEntry(self.mounttable[0]) + + self.configuredialog = MountEntryDialog(None) + + ######################################################################## + def exec_loop(self): + global programbase + self.__loadOptions() + programbase.exec_loop(self) + self.__saveOptions() + + ######################################################################## + def slotContextMenu(self,lv,lv_item,p): + + hal_device = lv_item.haldevice + if hal_device is not None and not isinstance(hal_device,MicroHAL.FakeSystemDevice): + + self.cmenu = KPopupMenu(self,"MyActions") + if isinstance(hal_device,MicroHAL.RemovableDisk) or isinstance(lv_item,MountListViewItem): + self.cmenu.insertItem(i18n("Modify..."), self.slotModifyClicked, 0, 0) + self.cmenu.insertItem(i18n("Delete..."), self.slotDeleteClicked, 0, 1) + if not isroot: + self.cmenu.setItemEnabled(0,False) + self.cmenu.setItemEnabled(1,False) + elif isinstance(hal_device,MicroHAL.Disk) or isinstance(hal_device,MicroHAL.USBDisk): + self.cmenu.insertItem(i18n("Show details..."), self.slotDetailsClicked, 0, 0) + self.cmenu.insertItem(i18n("New..."), self.slotNewClicked, 0, 1) + if not isroot: + self.cmenu.setItemEnabled(1,False) + + self.cmenu.exec_loop(p) + + ######################################################################## + def slotUser1(self): + self.aboutus.show() + + ######################################################################## + def slotEnableClicked(self): + if self.selectedentry!=None: + self.selectedentry.enable(self) + self.mounttable.updateStatus(self.selectedentry) + self.__updateEntry(self.selectedentry) + self.enablebutton.setEnabled(not self.selectedentry.isEnabled()) + self.disablebutton.setEnabled(self.selectedentry.isEnabled()) + + ######################################################################## + def slotDisableClicked(self): + if self.selectedentry!=None: + self.__disableEntry() + + ######################################################################## + def slotModifyClicked(self): + global isroot + if not isroot: + return + + if self.selectedentry!=None: + self.configuredialog.doEditMount(self.mounttable,self.selectedentry) + + lvi = self.mountentriestolistitems[self.selectedentry] + if lvi.hasHAL(): + if lvi.getHAL().getDev()!=self.selectedentry.getDevice(): + # The (device-)item in the listview no longer matches this mount entry. + del self.mountentriestolistitems[self.selectedentry] + lvi.setMountEntry(None) + lvi.updateDisplay() + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + elif self.selectedentry.getDevice() is not None \ + and self.selectedentry.getDevice() in self.devstolistitems: + # The mount entry can now merged with any existing (HAL-)item. + # Remove the existing lose item. + self.mountlist.takeItem(lvi) + del self.mountentriestolistitems[self.selectedentry] + del self.devstolistitems[self.selectedentry.getDevice()] + del lvi + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + elif self.selectedentry.getUUID() is not None \ + and self.selectedentry.getUUID() in self.uuidstolistitems: + # The mount entry can now merged with any existing (HAL-)item. + # Remove the existing lose item. + self.mountlist.takeItem(lvi) + del self.mountentriestolistitems[self.selectedentry] + del self.uuidstolistitems[self.selectedentry.getUUID()] + del lvi + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + self.__updateEntry(self.selectedentry) + self.__selectEntry(self.selectedentry) + else: + self.slotNewClicked() + + ######################################################################## + def slotNewClicked(self): + defaultdevice = None + if self.selectedentry is None: + lvi = self.mountlist.selectedItem() + if lvi is not None and lvi.hasHAL() and (lvi.getMountEntry() is None): + defaultdevice = lvi.getDevice() + newentry = self.configuredialog.doNewMount(self.mounttable,defaultdevice) + if newentry!=None: + self.updatingGUI = True + self.__insertMountEntryIntoListView(newentry) + self.__selectEntry(newentry) + self.updatingGUI = False + + ######################################################################## + def slotDeleteClicked(self): + if self.selectedentry!=None: + if self.selectedentry.isEnabled(): + if not self.__disableEntry(): + return # If we couldn't disable it, then we can't continue. + message = i18n("Are you sure you want to delete mount '%1' of type %2 at '%3'?\n " + + "(This will only remove the mount, no data will be deleted.)") \ + .arg(self.selectedentry.getMountPoint()).arg(self.selectedentry.mounttype).arg( + self.selectedentry.getDevice()) + if KMessageBox.warningYesNo(self,message,i18n("Delete Mount?"))==KMessageBox.Yes: + lvi = self.mountentriestolistitems[self.selectedentry] + if not lvi.hasHAL(): + self.mountlist.takeItem(lvi) + del lvi + del self.mountentriestolistitems[self.selectedentry] + else: + lvi.setMountEntry(None) + self.mounttable.remove(self.selectedentry) + self.mounttable.updateFstabOnDisk() + self.__selectEntry(None) + + ######################################################################## + def slotDetailsClicked(self): + # Popup a dialog showing disklayout and a graphical represenation of 'df' + hal_device = self.mountlist.selectedItem().haldevice + if isinstance(hal_device,MicroHAL.Disk): + blk = hal_device.getDev() + devicepath, devicename = ('/'.join(blk.split('/')[0:-1])+'/', blk.split('/')[-1]) + # We keep a dict with those widgets, that saves us some time reading out all the values. + if devicename not in self.sizeviewdialogs.keys(): + self.sizeviewdialogs[devicename] = sizeview.SizeView(self,devicename,devicepath) + self.sizeviewdialogs[devicename].exec_loop() + else: + self.sizeviewdialogs[devicename].exec_loop() + else: + print "Sizeview doesn't support",blk.__class__," yet." + + ######################################################################## + def slotListClicked(self,item): + if self.updatingGUI==False: + self.__selectEntry(item.getMountEntry()) + + ######################################################################## + def __disableEntry(self): + self.selectedentry.disable(self) + self.mounttable.updateStatus(self.selectedentry) + self.__updateEntry(self.selectedentry) + self.enablebutton.setEnabled(not self.selectedentry.isEnabled() and self.selectedentry.isFileSystemAvailable()) + self.disablebutton.setEnabled(self.selectedentry.isEnabled()) + return not self.selectedentry.isEnabled() + + ######################################################################## + def __updateEntry(self,selectedentry): + # Update the display. + lvi = self.mountentriestolistitems[selectedentry] + lvi.updateDisplay() + + ######################################################################## + def __loadOptions(self): + self.config.setGroup("General") + size = self.config.readSizeEntry("Geometry") + if size.isEmpty()==False: + self.resize(size) + + ####################################################################### + def __saveOptions(self): + global isroot + if isroot: + return + self.config.setGroup("General") + self.config.writeEntry("Geometry", self.size()) + self.config.sync() + + ######################################################################## + def __updateMountList(self): + self.mountentriestolistitems = {} + + self.mountlist.clear() + + self.listgroups = {} + self.devstolistitems = {} + self.uuidstolistitems = {} + + lasttopitem = None + + # Find out which disks are installed and should be shown in the + # listview. For real disks we put a 'group' in the listview and + # under the group we list the partitions, whether they are + # mounted or not. + for blockdevice in microhal.getDevices(): + # We are looking for block devices that represent hard disks or + # things that have partitions and are not removable + if (blockdevice.major in microhal.partitionblockdevs and not blockdevice.removable) \ + or isinstance(blockdevice,MicroHAL.USBDisk): + + # We have a not removable block device. + # We want to create a listitem for the device and subitems + # for each partition. + groupitem = MountGroupListViewItem(self.mountlist,blockdevice) + groupitem.setOpen(True) + lasttopitem = groupitem + lvi = None + for partition in blockdevice.getPartitions(): + # Try to find a matching mount entry for this partition. + lastlvi = lvi + lvi = MountListViewItem(groupitem,None,partition) + + if partition.getUUID() is not None: + self.uuidstolistitems[partition.getUUID()] = lvi + if partition.getDev() is not None: + self.devstolistitems[partition.getDev()] = lvi + + if lastlvi is not None: + lvi.moveItem(lastlvi) + elif blockdevice.getMajor() in microhal.cdromsdevs or blockdevice.isRemovable(): + # Removable block device, assume CDROM (even if it's a partitionblockdevice) + lvi = MountListViewItem(self.mountlist,None,blockdevice) + if blockdevice.getUUID() is not None: + self.uuidstolistitems[blockdevice.getUUID()] = lvi + if blockdevice.getDev() is not None: + self.devstolistitems[blockdevice.getDev()] = lvi + lasttopitem = lvi + + + systemdevice = MicroHAL.FakeSystemDevice() + systemdevice.iconname = systemdevice.getIconName() + groupitem = MountGroupListViewItem(self.mountlist,systemdevice) + + if lasttopitem is not None: + groupitem.moveItem(lasttopitem) + lasttopitem = groupitem + + self.listgroups["system"] = groupitem + + self.mountentriestolistitems = {} + for entry in self.mounttable: + self.__insertMountEntryIntoListView(entry) + + ######################################################################## + def __insertMountEntryIntoListView(self,mountentry): + if mountentry.getDevice() in self.devstolistitems: + lvi = self.devstolistitems[mountentry.getDevice()] + lvi.setMountEntry(mountentry) + elif mountentry.getUUID() in self.uuidstolistitems: + lvi = self.uuidstolistitems[mountentry.getUUID()] + lvi.setMountEntry(mountentry) + else: + cat = mountentry.getCategory() # Place it under a special node? + if cat not in self.listgroups: + lvi = MountListViewItem(self.mountlist,mountentry) + item = self.mountlist.firstChild() + else: + lvi = MountListViewItem(self.listgroups[cat],mountentry) + item = self.listgroups[cat].firstChild() + + # Move the item to the end of this (sub-list). + while item.nextSibling() is not None: + item = item.nextSibling() + lvi.moveItem(item) + + self.mountentriestolistitems[mountentry] = lvi + + ######################################################################## + def __selectEntry(self,mountentry): + if mountentry is not None and isroot: + lvi = self.mountentriestolistitems[mountentry] + self.mountlist.setSelected(lvi,True) + self.enablebutton.setEnabled(not mountentry.isEnabled() and mountentry.isFileSystemAvailable()) + self.selectedentry = mountentry + # disable unsupported stuff, such as SystemEntries that canot be disabled and modified + if not mountentry.maydisable: + disable = False + else: + disable = mountentry.isEnabled() + if mountentry.notInFstab: + delete = False + modify = False + else: + delete = True + modify = True + + self.disablebutton.setEnabled(disable) + self.deletebutton.setEnabled(delete) + self.modifybutton.setEnabled(modify) + + else: + self.enablebutton.setEnabled(False) + self.disablebutton.setEnabled(False) + self.deletebutton.setEnabled(False) + self.modifybutton.setEnabled(False) + self.detailsbutton.setEnabled(False) + self.selectedentry = None + selected_item = self.mountlist.selectedItem() + if selected_item is not None: + self.detailsbutton.setEnabled(isinstance(selected_item.haldevice,MicroHAL.Disk) \ + and not isinstance(selected_item.haldevice,MicroHAL.RemovableDisk)) + else: + self.detailsbutton.setEnabled(False) + + ####################################################################### + # KControl virtual void methods + def load(self): + pass + def save(self): + pass + def defaults(self): + pass + def sysdefaults(self): + pass + + def aboutData(self): + # Return the KAboutData object which we created during initialisation. + return self.aboutdata + def buttons(self): + # Only supply a Help button. Other choices are Default and Apply. + return KCModule.Help + +############################################################################ +# Factory function for KControl +def create_mountconfig(parent,name): + global kapp, microhal + microhal = MicroHAL.MicroHAL() + kapp = KApplication.kApplication() + return MountConfigApp(parent, name) + +############################################################################ +def MakeAboutData(): + aboutdata = KAboutData("mountconfig",programname,version,"Disk & Filesystem Configuration Tool", + KAboutData.License_GPL, "Copyright (C) 2003-2007 Simon Edwards") + aboutdata.addAuthor("Simon Edwards","Developer","simon@simonzone.com", + "http://www.simonzone.com/software/guidance") + aboutdata.addAuthor("Sebastian Kügler","Developer","sebas@kde.org","http://vizZzion.org"); + return aboutdata + +if standalone: + aboutdata = MakeAboutData() + KCmdLineArgs.init(sys.argv,aboutdata) + + microhal = MicroHAL.MicroHAL() + kapp = KApplication() + sysvapp = MountConfigApp() + sysvapp.exec_loop() diff --git a/mountconfig/sizeview.py b/mountconfig/sizeview.py new file mode 100644 index 0000000..bbb1f5b --- /dev/null +++ b/mountconfig/sizeview.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python + +from qt import * +from kdecore import * +import sys, os + +def getLabel(blocks): + """ Translates blocksize into human readable labels, such as 17.3 Gb, 2.1 Mb. """ + + try: + blocks = int(blocks) # 1K blocks now. + except ValueError: + return i18n("n/a") + if blocks<1024: + return i18n("%1 Kb").arg(blocks) + if blocks<1024*1024: + return i18n("%1 Mb").arg(round(float(blocks)/1024.0,1)) + blocks /= 1024 + if blocks<1024*1024: + return i18n("%1 Gb").arg(round(float(blocks)/1024.0,1)) + blocks /= 1024 + return i18n("%1 Tb").arg(round(float(blocks)/1024.0,1)) + + +class SizeViewApplication(QApplication): + """ Boilerplate """ + def __init__(self,devicename,devicepath,args=[]): + QApplication.__init__(self,args) + + self.maindialog = SizeView(None,devicename,devicepath) + self.setMainWidget(self.maindialog) + self.maindialog.show() + self.exec_loop() + +class SizeView(QDialog): + """ A SizeView represents a horizontal list of PartitionGroupWidgets. + It supplies the code to read the sizes and the values that have + to be filled in, using the /proc filesystem and the program "df". + """ + + dev_path = "/dev/" # Where to look for the partitions + devicename = "" # Such as hda1 + partitions = {} # List of partitions + sizes = {} # Maps devicenames to blocksizes + mountpoints = {} # Maps devicenames to mountpoints + used = {} # Blocks used on a partition + available = {} # Blocks available + part_types = {} # Maps devicenames to partitiontypes + partitionwidgets = [] # Holds a list of the PartitionGroup widgets + + def __init__(self,parent,devicename,devicepath=None): + self.partitionwidgets = [] + QDialog.__init__(self,None,None,0,0) + self.dialogtitle = i18n("Diskspace & Partitions") + self.setCaption(self.dialogtitle) + self.devicename = devicename + if devicepath: + self.dev_path = devicepath + + # Retrieve all information from the system. + self.readMounts() + self.readSize() + self.readSwaps() + + partitions = self.partitions.keys() + partitions.sort() + + number=1 + for part in partitions: + try: + fill = self.sizes[part] + mountpoint = self.mountpoints[part] + used = self.used[part] + available = self.available[part] + except KeyError: + # Handles empty or not-mounted partitions + fill = None + mountpoint = i18n("n/a") + used = str(i18n("n/a")) + available = str(i18n("n/a")) + + pwidg = PartitionGroup(part,self,fill,number,self.part_types,self.dev_path) + pwidg.setSize(self.partitions[part]) + pwidg.setMountPoint(mountpoint) + pwidg.setUsed(used) + pwidg.setAvailable(available) + pwidg.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.MinimumExpanding,0,0, + pwidg.sizePolicy().hasHeightForWidth())) + self.partitionwidgets.append(pwidg) + number += 1 + + n = len(partitions) + r = 2 + c = 0 + cols = 1 + + # Compute number of rows needed for partitions. + if n%cols > 0: + rows = int(n/cols)+1 + else: + rows = int(n/cols) + if n is 1: rows = 2 + + # Build main Gridlayout. + total_rows = rows+2 + self.grid = QGridLayout(self,total_rows,2,5) + #self.setSizeGripEnabled(1) + + self.buttonCancel = QPushButton(i18n("Close"),self,"buttonCancel") + self.buttonCancel.setAutoDefault(1) + self.buttonCancel.setFixedWidth(80) + self.grid.addWidget(self.buttonCancel,total_rows-1,1,Qt.AlignRight) + + self.grid.setRowStretch(0,0) + self.grid.setRowStretch(total_rows-1,0) + + # Stretch all but first and last rows. + for row in range(1,total_rows-1): + self.grid.setRowStretch(row,5) + + self.clearWState(Qt.WState_Polished) + self.connect(self.buttonCancel,SIGNAL("clicked()"),self.hide) + + #self.mainlabel = QLabel(""+self.dialogtitle+"",self) + #self.grid.addWidget(self.mainlabel,0,0) + + self.diskgroup = DiskGroup(self,self.devicename,self.dev_path,self.partitions,self.totalsize,self.mountpoints) + self.grid.addMultiCellWidget(self.diskgroup,1,1,0,1) + + for pw in self.partitionwidgets: + self.grid.addWidget(pw,r,c) + if c is cols: + r += 1 + c = 0 + else: + c += 1 + + def readSize(self): + fhandle = open("/proc/partitions","r") + self.partitions = {} + self.totalsize = 0 + for line in fhandle.readlines(): + try: + major,minor,blocks,name = line.split() + if name == self.devicename: + self.totalsize = blocks + if name[:len(self.devicename)] == self.devicename and len(name) > len(self.devicename): + self.partitions[name] = blocks + except ValueError: + pass + fhandle.close() + + def readMounts(self): + fhandle = os.popen("/bin/df") + for l in fhandle.readlines(): + v = l.split() + try: + p,s = v[0].split("/")[2],v[4][:-1] + self.sizes[p] = s + self.mountpoints[p] = v[5] + self.used[p] = v[2] + self.available[p] = v[3] + self.part_types[p] = "filesystem" + except IndexError: + pass + fhandle.close() + + def readSwaps(self): + fhandle = open("/proc/swaps") + for line in fhandle.readlines(): + try: + device,type,size,used,priority = line.split() + device = device[len(self.dev_path):] + self.used[device] = used + self.sizes[device] = round(float(used)/float(size)*100 ,1) + self.available[device] = str(int(size)-int(used)) + self.mountpoints[device] = "swap" + self.part_types[device] = "swap" + except: + pass + fhandle.close() + + """ + def __show__(self): + print self.partitions + print "Device", self.devicename, self.totalsize + for p in self.partitions: + print p, self.partitions[p], self.partitions[p] + """ +class DiskGroup(QGroupBox): + """ Shows an overview of the physical layout of the disks, with the different partitions on it. """ + + def __init__(self,parent,device,dev_path,partitions,totalsize,mountpoints): + + QGroupBox.__init__(self,parent,"DiskViewGroup") + self.setTitle(i18n("Disk %1%2").arg(dev_path).arg(device)) + self.mountpoints = mountpoints + self.partitions = partitions + self.totalsize = totalsize + + self.setColumnLayout(0,Qt.Vertical) + self.layout().setSpacing(6) + self.layout().setMargin(11) + DiskViewGroupLayout = QVBoxLayout(self.layout()) + DiskViewGroupLayout.setAlignment(Qt.AlignTop) + colors = ["dark orange","dodger blue","gold","green","firebrick","navy","darkorange","darkblue"] + + self.diskview = DiskView(self,self.percentages(),colors) + self.diskview.setScaledContents(1) + DiskViewGroupLayout.addWidget(self.diskview) + + parts = self.partitions.keys() + parts.sort() + self.percentages() + + cols = 3 # Number of columns to use for colorlabels. + rows = len(parts)/cols + mod = len(parts)%cols + if mod > 0: + rows += cols-mod + + # We multiply the number of cols by 3, first for the colorlabel, second for the name, third for spacing. + cols = cols*3 + DiskViewPartitionListLayout = QGridLayout(DiskViewGroupLayout,rows,cols) + + i = cl = r = c = 0 + ps = ls = {} + for dev in parts: + ps[i] = LegendLabel(self,colors[cl]) + DiskViewPartitionListLayout.addWidget(ps[i],r,c) + try: + lbl = self.mountpoints[dev] + except KeyError: + lbl = "not mounted" + ls[i] = QLabel(self,lbl+'
('+dev_path+dev+')',self) + DiskViewPartitionListLayout.addWidget(ls[i],r,c+1) + cl += 1 + if cl == len(colors): + cl = 0 + i += 1 + if c is cols: + c = 0 + r += 1 + else: + c += 3 + + def percentages(self): + + p_t = 0 + for p in self.partitions.values(): + p_t += int(p) + + self.perc = {} + for p in self.partitions.keys(): + self.perc[p] = float(float(self.partitions[p])/float(p_t)) + return self.perc + + +class PartitionGroup(QGroupBox): + """ Represents a groupbox with the filled bar and a couple of labels with + information about the partition in it.""" + + blocksize = 0 + title = str(i18n("Partition")) + + def __init__(self,device,parent,fill_percent,number,part_types,dev_path): + QGroupBox.__init__(self,parent) + self.part_types = part_types + self.dev_path = dev_path + self.setGeometry(QRect(110,100,370,203)) + self.setColumnLayout(0,Qt.Vertical) + self.layout().setSpacing(3) + self.layout().setMargin(5) + self.setMinimumSize(280,120) + + partitiongroup_layout = QGridLayout(self.layout()) + partitiongroup_layout.setAlignment(Qt.AlignTop) + self.available = QLabel(i18n("available"),self) + partitiongroup_layout.addWidget(self.available,3,4) + + self.device = QLabel(i18n("device"),self) + partitiongroup_layout.addMultiCellWidget(self.device,1,1,3,4) + + self.partpixmap = PartitionView(self,fill_percent,self.part_types,device) + self.partpixmap.setScaledContents(1) + + partitiongroup_layout.addMultiCellWidget(self.partpixmap,0,0,0,4) + self.textLabel1_3 = QLabel("textLabel1_3",self) + partitiongroup_layout.addWidget(self.textLabel1_3,3,0) + self.totalsize = QLabel("totalsize",self) + partitiongroup_layout.addWidget(self.totalsize,2,1) + self.textLabel1_2 = QLabel(self,"textLabel1_2") + partitiongroup_layout.addWidget(self.textLabel1_2,2,0) + self.textLabel1 = QLabel(self,"textLabel1") + partitiongroup_layout.addWidget(self.textLabel1,1,0) + self.textLabel3_2 = QLabel(self,"textLabel3_2") + partitiongroup_layout.addMultiCellWidget(self.textLabel3_2,2,2,2,3) + self.percentfilled = QLabel(self,"percentfree") + partitiongroup_layout.addWidget(self.percentfilled,2,4) + self.textLabel3_3 = QLabel(self,"textLabel3_3") + partitiongroup_layout.addWidget(self.textLabel3_3,3,2) + self.textLabel3 = QLabel(self,"textLabel3") + partitiongroup_layout.addWidget(self.textLabel3,1,2) + self.used = QLabel(self,"used") + partitiongroup_layout.addWidget(self.used,3,1) + self.mountpoint = QLabel(self,"mountpoint") + self.mountpoint.setMinimumSize(QSize(60,0)) + partitiongroup_layout.addWidget(self.mountpoint,1,1) + self.clearWState(Qt.WState_Polished) + + self.setTitle(i18n("%1. Partition").arg(number)) + self.textLabel1_3.setText(i18n("Used:")) + self.textLabel1_2.setText(i18n("Total Size:")) + self.textLabel1.setText(i18n("Mountpoint:")) + self.textLabel3_2.setText(i18n("% Used:")) + self.textLabel3_3.setText(i18n("Available:")) + self.textLabel3.setText(i18n("Device:")) + + self.setDevice(self.dev_path+device) + self.setFillPercentage(fill_percent) + + def setSize(self,label): + self.totalsize.setText(getLabel(label)) + + def setDevice(self,device): + self.device.setText(device) + + def setMountPoint(self,mountpoint): + self.mountpoint.setText(mountpoint) + self.setTitle(i18n("Partition %1").arg(mountpoint)) + + def setTotalSize(self,totalsize): + self.totalsize.setText(getLabel(totalsize)) + + def setFillPercentage(self,fill_percent): + self.fill_percent = self.partpixmap.fill_percent = fill_percent + if fill_percent is not None: + self.percentfilled.setText("%s%%" % fill_percent) + else: + self.percentfilled.setText(i18n("Unknown")) + + def setUsed(self,used): + self.used.setText(getLabel(used)) + + def setAvailable(self,available): + self.available.setText(getLabel(available)) + +class LegendLabel(QLabel): + """ Show some color in the DiskView legend """ + + def __init__(self,parent,color="green",style=QBrush.SolidPattern): + QLabel.__init__(self,parent,"bla") + self.w = 40 + self.h = 20 + self.pmsize = QSize(self.w,self.h) + self.pm = QPixmap(self.pmsize) + self.linewidth = 2 + self.color = QColor(color) + self.style = style + self.framecolor = QColor("black") + self.paintMe() + self.setPixmap(self.pm) + self.setScaledContents(1) + self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0, + self.sizePolicy().hasHeightForWidth())) + + def paintMe(self): + p = QPainter(self.pm) + p.fillRect(0,0,self.w,self.h,QBrush(self.color,self.style)) + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.w-self.linewidth/2,self.h-self.linewidth/2) + p.end() + +class PartitionView(QLabel): + """ PartitionView is a label carryig a pixmap. This class's main purpose is handlig layout + of the underlying pixmap.""" + w = 250 + h = 35 + def __init__(self,parent,fill_percent,part_types,device): + self.part_types = part_types + self.fill_percent = fill_percent + QLabel.__init__(self,parent,"pview") + self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0, + self.sizePolicy().hasHeightForWidth())) + self.setMinimumSize(QSize(self.w,self.h)) + self.setPixmap(PartitionPixmap(QSize(self.w,self.h),self.fill_percent,self.part_types,device)) + self.setScaledContents(1) + self.setAlignment(QLabel.AlignCenter) + +class DiskView(PartitionView): + """ PartitionView is a label carryig a pixmap. This class's main purpose is handlig layout + of the underlying pixmap.""" + + w = 540 + h = 50 + linewidth = 2 + + def __init__(self,parent,percents,colors): + QLabel.__init__(self,parent) + + self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0, + self.sizePolicy().hasHeightForWidth())) + self.setPixmap(DiskPixmap(percents,colors,(self.w,self.h))) + self.setScaledContents(1) + self.setAlignment(QLabel.AlignCenter) + +class DiskPixmap(QPixmap): + + linewidth = 2 # Width of surrounding frame + + def __init__(self,percents,colors,(w,h)): + self.percents = percents + self.w,self.h = w,h + self.colors = colors + QPixmap.__init__(self,w,h) + self.paintMe() + + def paintMe(self): + p = QPainter(self) + w,h = self.w,self.h + i = 0 + x0 = 0 + y = 0 + + # Paint background, this is interesting for empty partitions. + p.fillRect(0,0,w,h,QBrush(QColor("white"))) + + parts = self.percents.keys() + parts.sort() + xa = wa = 0 + for part in parts: + W = (w * self.percents[part]) + # We need to adjust a little to avoid to get wholes. + if x0>0: xa = 2 + if W < self.w: wa = 2 + p.fillRect(x0-xa,0,W+wa,h,QBrush(QColor(self.colors[i]))) + i += 1 + x0 += W + + # Paint Frame around it. + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.width()-self.linewidth/2,self.height()-self.linewidth/2) + p.end() + + +class PartitionPixmap(QPixmap): + """ A PartitionPixmap is a two colored bar with a black frame. The first color represents the + percentage that's used, the second one the free percentage.""" + linewidth = 2 # Width of surrounding frame + + def __init__(self,pmsize,fill_percent,part_types,device): + QPixmap.__init__(self,pmsize) + + self.pmsize = pmsize # Size of the pixmap + self.part_types = part_types # Array to look up the type of the partition + self.fill_percent = fill_percent + self.device = device # Device name of the partition + + self.w = self.pmsize.width() + self.h = self.pmsize.height() + self.paintMe() + + def paintMe(self): + p = QPainter(self) + try: + fill_percent = int(self.fill_percent) + if self.part_types[self.device] == "swap": + # Swap partitions get blueish colors. + color_used = QColor("blue") + color_free = QColor("lightblue") + else: + # Regular partitions get a red / green color combo. + color_used = QColor("red") + color_free = QColor("forest green") + except (KeyError,TypeError): + # Partition has no fillsize, might be empty or not mounted partition + p.fillRect(0,0,self.w,self.h,QBrush(QColor("darkgrey"))) + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.w-self.linewidth/2,self.h-self.linewidth/2) + p.end() + return + # Total width of the pixmap + W,H = float(self.w),float(self.h) + + # Paint filled == red part of the bar. + x = y = 0 + w = W - (W*(1-(fill_percent/100.00))) + h = H + p.fillRect(x,y,w,h,QBrush(color_used)) + + # Paint green part == space left + x = w + w = W - w + p.fillRect(x,y,w,h,QBrush(color_free)) + + # Paint Frame around it. + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,W-self.linewidth/2,H-self.linewidth/2) + + p.end() + +if __name__ == "__main__": + device = "sdc" + app = SizeViewApplication(device,None,sys.argv) diff --git a/package/mandrake/guidance-kcmdisplayconfig b/package/mandrake/guidance-kcmdisplayconfig new file mode 100644 index 0000000..ea49619 --- /dev/null +++ b/package/mandrake/guidance-kcmdisplayconfig @@ -0,0 +1 @@ +?package(guidance): needs="kde" kde_filename="displayconfig" section="System/Configuration/KDE/Peripherals" title="Display & Monitor" icon="displayconfig.png" command="/usr/bin/displayconfig" kde_command="kcmshell displayconfig" longtitle="Display and monitor settings" kde_opt="\\nEncoding=UTF-8\\nX-KDE-ModuleType=Library\\nX-KDE-Library=displayconfig\\nX-KDE-FactoryName=displayconfig\\nX-KDE-ParentApp=kcontrol\\nX-KDE-RootOnly=true\\nX-KDE-SubstituteUID=true\\nKeywords=resolution;display;monitor;video;X11;xorg;gfx\\n" diff --git a/package/mandrake/guidance-kcmmountconfig b/package/mandrake/guidance-kcmmountconfig new file mode 100644 index 0000000..1473fee --- /dev/null +++ b/package/mandrake/guidance-kcmmountconfig @@ -0,0 +1 @@ +?package(guidance): needs="kde" kde_filename="mountconfig" section="System/Configuration/KDE/System" title="Disk & Filesystems" icon="disksfilesystems.png" command="/usr/bin/mountconfig" kde_command="kcmshell mountconfig" longtitle="Manage disks and filesystems" kde_opt="\\nEncoding=UTF-8\\nX-KDE-ModuleType=Library\\nX-KDE-Library=mountconfig\\nX-KDE-FactoryName=mountconfig\\nX-KDE-ParentApp=kcontrol\\nX-KDE-RootOnly=true\\nX-KDE-SubstituteUID=true\\nKeywords=disk;filesystem;mount\\n" diff --git a/package/mandrake/guidance-kcmserviceconfig b/package/mandrake/guidance-kcmserviceconfig new file mode 100644 index 0000000..5374240 --- /dev/null +++ b/package/mandrake/guidance-kcmserviceconfig @@ -0,0 +1 @@ +?package(guidance): needs="kde" kde_filename="serviceconfig" section="System/Configuration/KDE/System" title="Services" icon="daemons.png" command="/usr/bin/serviceconfig" kde_command="kcmshell serviceconfig" longtitle="Manage running services" kde_opt="\\nEncoding=UTF-8\\nX-KDE-ModuleType=Library\\nX-KDE-Library=serviceconfig\\nX-KDE-FactoryName=serviceconfig\\nX-KDE-ParentApp=kcontrol\\nX-KDE-RootOnly=true\\nX-KDE-SubstituteUID=true\\nKeywords=init.d;service;boot;daemon;demon\\n" diff --git a/package/mandrake/guidance-kcmuserconfig b/package/mandrake/guidance-kcmuserconfig new file mode 100644 index 0000000..ee045e9 --- /dev/null +++ b/package/mandrake/guidance-kcmuserconfig @@ -0,0 +1 @@ +?package(guidance): needs="kde" kde_filename="userconfig" section="System/Configuration/KDE/System" title="Users and Groups" icon="userconfig.png" command="/usr/bin/userconfig" kde_command="kcmshell userconfig" longtitle="Manage user accounts and groups" kde_opt="\\nEncoding=UTF-8\\nX-KDE-ModuleType=Library\\nX-KDE-Library=userconfig\\nX-KDE-FactoryName=userconfig\\nX-KDE-ParentApp=kcontrol\\nX-KDE-RootOnly=true\\nX-KDE-SubstituteUID=true\\nKeywords=user;account;group;password\\n" diff --git a/package/mandrake/guidance.spec b/package/mandrake/guidance.spec new file mode 100644 index 0000000..96ca9c7 --- /dev/null +++ b/package/mandrake/guidance.spec @@ -0,0 +1,79 @@ +%define name guidance +%define version 0.3.0 +%define release 1mdk + +Summary: Configuration Utilities +Name: %{name} +Version: %{version} +Release: %{release} +Packager: Simon Edwards +Source: %{name}-%{version}.tar.bz2 +License: GPL +Group: System/Configuration +URL: http://www.simonzone.com/software/guidance/ +Requires: python +Requires: libpythonize0 +BuildRequires: python-devel +BuildRequires: libpythonize0-devel +Requires: PyKDE +Buildroot: %_tmppath/%name-buildroot + +%description +Guidance. + +Development version. + +%prep +rm -rf $RPM_BUILD_ROOT + +%setup -q + +%build +%install +./setup.py install --root=$RPM_BUILD_ROOT build_kcm +install -D --mode=a=r,u+w package/mandrake/guidance-kcmuserconfig $RPM_BUILD_ROOT/%{_menudir}/guidance-kcmuserconfig +install -D --mode=a=r,u+w package/mandrake/guidance-kcmmountconfig $RPM_BUILD_ROOT/%{_menudir}/guidance-kcmmountconfig +install -D --mode=a=r,u+w package/mandrake/guidance-kcmserviceconfig $RPM_BUILD_ROOT/%{_menudir}/guidance-kcmserviceconfig +install -D --mode=a=r,u+w package/mandrake/guidance-kcmdisplayconfig $RPM_BUILD_ROOT/%{_menudir}/guidance-kcmdisplayconfig + +install -D kde/serviceconfig/pics/16x16/daemons.png $RPM_BUILD_ROOT/%{_iconsdir}/daemons.png +install -D kde/mountconfig/pics/16x16/disksfilesystems.png $RPM_BUILD_ROOT/%{_iconsdir}/disksfilesystems.png +install -D kde/userconfig/pics/16x16/userconfig.png $RPM_BUILD_ROOT/%{_iconsdir}/userconfig.png +install -D kde/displayconfig/pics/16x16/displayconfig.png $RPM_BUILD_ROOT/%{_iconsdir}/displayconfig.png + +rm $RPM_BUILD_ROOT/usr/share/applnk/Settings/Peripherals/displayconfig.desktop +rm $RPM_BUILD_ROOT/usr/share/applnk/Settings/System/mountconfig.desktop +rm $RPM_BUILD_ROOT/usr/share/applnk/Settings/System/serviceconfig.desktop +rm $RPM_BUILD_ROOT/usr/share/applnk/Settings/System/userconfig.desktop +rm $RPM_BUILD_ROOT/usr/share/icons/crystalsvg/16x16/apps/daemons.png +rm $RPM_BUILD_ROOT/usr/share/icons/crystalsvg/16x16/apps/disksfilesystems.png +rm $RPM_BUILD_ROOT/usr/share/icons/crystalsvg/16x16/apps/displayconfig.png +rm $RPM_BUILD_ROOT/usr/share/icons/crystalsvg/16x16/apps/userconfig.png + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%doc README TODO COPYING ChangeLog +%_bindir/* +%_datadir/apps/guidance/* +%_libdir/kde3/* +%_libdir/python2.3/* +%{_menudir}/* +%{_iconsdir}/daemons.png +%{_iconsdir}/disksfilesystems.png +%{_iconsdir}/userconfig.png +%{_iconsdir}/displayconfig.png + +%post +%{update_menus} +%postun +%{clean_menus} + +%changelog +* Sun Mar 20 2005 Simon Edwards 0.3.0-1mdk +- 0.3.0. + +* Thu Dec 9 2004 Simon Edwards 0.2.0-1mdk +- Initial rpm. diff --git a/powermanager/TODO b/powermanager/TODO new file mode 100644 index 0000000..bf6d671 --- /dev/null +++ b/powermanager/TODO @@ -0,0 +1,49 @@ +guidance-power-manager TODO: +------------------------------ +- [brightness preview], see below +- make power-manager non-blocking +- Wait with hibernate / suspending if the plug has just been pulled + Some batteries only report remaining_time correctly after 30 or so + seconds + + +DUNNO's: +--------- +- How to detect how long the machine is idle? +- How to perform actions that need root privileges (and are not available via HAL)? +- Should we notify when battery is fully charged? + +FIXED: +------- +- What is "blank"? "Screen Saver", "Blank Screen" or "Monitor off"? +- Handle CPU hotplugging gracefully (try switching off one CPU when g-p-m is running...) +- Make use of kstandardirs +- Add icons to contextmenu +- Remove brightness controls when not hasBrighness +- Remove battery-specifif items from tooltip when there's no battery +- Make settings dialogue not apply instantly (Save config only on OK and Apply) +- Add cpu frequency to tooltip +- Add icons to tooltip +- Make tooltip size properly +- battery hotplugging +- Implement switchToBattery(), collecting all stuff for onBattery + - notify + - blankscreensaveronly + - lowerbrightness + +- Implement switchToAC(), collecting stuff to run when plugged in: + - notify + - undo blankscreensaveronly + - up brightness +- Clicking onto the slider should move the handle to the closest tick, but how? +- support for second battery + +[brightness preview] +Currently there is only a brightness preview for the currently active scheme (_either_ mains or battery powered). +That means if you want to set the battery brightness when you are currently mains powered, you'll probably +move the battery slider, realise that there is no preview, go to the mains slider, move it till you found a nice +brightness level, then go back to the battery slider and set it there. Would be nicer to have a preview for both sliders: +Whenever you move to a new location, the brightness is adjusted. Has to be tested if it should be set back to +previous value after 5 seconds, or if it should remain till you either move the other slider or click save (assuming +explicit apply). + diff --git a/powermanager/g-p-m-restart b/powermanager/g-p-m-restart new file mode 100755 index 0000000..d957de1 --- /dev/null +++ b/powermanager/g-p-m-restart @@ -0,0 +1,6 @@ +#!/bin/bash +echo "stopping guidance-power-manager ..." +dcop `dcopfind -a "power-manager*"` MainApplication-Interface quit + +echo "starting guidance-power-manager ..." +guidance-power-manager.py & diff --git a/powermanager/gpmhelper.py b/powermanager/gpmhelper.py new file mode 100644 index 0000000..65094f7 --- /dev/null +++ b/powermanager/gpmhelper.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +""" +Copyright 2008 Sebastian Kügler, Canonical Ltd, Luka Renko + +Authors: + Andreas Wenning + +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. + +""" + +""" +A frontend to HAL's power features for KDE - Helper application +This application listens for HAL signals and issues dcop-calls to the +kde-power-manager that originally started it. To avoid the need for +kde-power-manager to shut it's helper down, this application +will automatically shut down if it's kde-power-manager isn't running. +""" + +import dbus, sys, time +from dbus.mainloop.glib import DBusGMainLoop +import gobject +from dcopext import DCOPClient, DCOPObj, DCOPApp + +class GPMHelper(): + def mother_alive(self): + """Check that our mother is still alive""" + found = False + for name in self.dcop.registeredApplications(): + name = str(name) + if name == self.motherName: + found = True + if not found: + """No mother; commit suicide""" + print "guidance-power-manager not alive; exiting" + loop.quit() + + """Called when signal is received""" + def signal_recv(self, *args): + if args[0] == "ButtonPressed": + if args[1] == "brightness-up": + if time.time()-0.02 <= self.last_brightness_up <= time.time(): + """Most likely an extra brightness-up call, discarding""" + print "Extra brightness-up call discarded" + return + try: + ok, foo = self.mother.brightnessUp() + if not ok: + print "brightnessUp-call failed" + return self.mother_alive() + self.last_brightness_up = time.time() + except: + print "brightnessUp-call failed" + return self.mother_alive() + elif args[1] == "brightness-down": + if time.time()-0.02 <= self.last_brightness_down <= time.time(): + """Most likely an extra brightness-down call, discarding""" + print "Extra brightness-down call discarded" + return + try: + ok, foo = self.mother.brightnessDown() + if not ok: + print "brightnessDown-call failed" + return self.mother_alive() + self.last_brightness_down = time.time() + except: + print "brightnessDown-call failed" + return self.mother_alive() + elif args[1] == "sleep": + if time.time()-1 <= self.last_sleep <= time.time(): + """Most likely an extra sleep-call, discarding""" + print "Extra sleep-call discarded" + return + try: + ok, foo = self.mother.suspend() + if not ok: + print "suspend-call failed" + return self.mother_alive() + self.last_sleep = time.time() + except: + print "suspend-call failed" + return self.mother_alive() + elif args[1] == "hibernate": + if time.time()-1 <= self.last_hibernate <= time.time(): + """Most likely an extra hibernate-call, discarding""" + print "Extra hibernate-call discarded" + return + try: + ok, foo = self.mother.hibernate() + if not ok: + print "hibernate-call failed" + return self.mother_alive() + self.last_hibernate = time.time() + except: + print "hibernate failed" + return self.mother_alive() + + def __init__(self): + """Connect to HAL""" + self.dbus_loop = DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus(mainloop=self.dbus_loop) + hal_manager_obj = self.bus.get_object("org.freedesktop.Hal",u'/org/freedesktop/Hal/Manager') + self.hal_manager = dbus.Interface(hal_manager_obj, "org.freedesktop.Hal.Manager") + + """Find button-devices and to connect to""" + button_devices = self.hal_manager.FindDeviceByCapability("button") + for device in button_devices: + self.bus.add_signal_receiver(self.signal_recv, + "Condition", + "org.freedesktop.Hal.Device", + "org.freedesktop.Hal", + device) + """Let's find our mother""" + self.dcop = DCOPClient() + self.dcop.attach() + found = False + for name in self.dcop.registeredApplications(): + name = str(name) + if name.startswith('guidance-'): + self.motherName = name + try: + self.mother = DCOPObj(name, self.dcop, 'power-manager') + found = True + except: + """Do nothing, catched by found=False""" + break + if not found: + """No mother; commit suicide""" + print "No guidance-power-manager is running" + sys.exit() + + """Some laptops issue double sleep/hibernate-calls, we need to discard one in that case""" + self.last_sleep = 0 + self.last_hibernate = 0 + + """And multiple brightness calls is also possible""" + self.last_brightness_up = 0 + self.last_brightness_down = 0 + +if __name__ == "__main__": + gpmh = GPMHelper() + loop = gobject.MainLoop() + loop.run() diff --git a/powermanager/guidance-power-manager.desktop b/powermanager/guidance-power-manager.desktop new file mode 100644 index 0000000..2fdaad0 --- /dev/null +++ b/powermanager/guidance-power-manager.desktop @@ -0,0 +1,35 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Power Manager +Name[el]=Διαχειριστής ενέργειας +Name[es]=Administrador de energía +Name[et]=Toitehaldur +Name[it]=Gestore dell'energia +Name[ja]=電源管理 +Name[nl]=Energiebeheer +Name[pt]=Gestor de Energia +Name[pt_BR]=Gerenciador de Energia +Name[sr]=Управљач енергијом +Name[sr@Latn]=Upravljač energijom +Name[sv]=Energisparfunktion +Name[xx]=xxPower Managerxx +Comment=Power management applet +Comment[el]=Μικροεφαρμογή διαχείρισης ενέργειας +Comment[es]=Applet de administrador de potencia +Comment[et]=Toitehalduse aplett +Comment[it]=Applicazione per la gestione dell'energia +Comment[ja]=電源管理アプレット +Comment[nl]=Energiebeheer-applet +Comment[pt]='Applet' de gestão de energia +Comment[pt_BR]=Mini-aplicativo de gerenciamento de energia +Comment[sr]=Аплет за управљање енергијом +Comment[sr@Latn]=Aplet za upravljanje energijom +Comment[sv]=Miniprogram för energisparfunktion +Comment[xx]=xxPower management appletxx +Icon=guidance-power-manager +Exec=guidance-power-manager +Terminal=false +Type=Application +Categories=Qt;KDE; +OnlyShowIn=KDE; +X-DCOP-ServiceType=Unique diff --git a/powermanager/guidance-power-manager.py b/powermanager/guidance-power-manager.py new file mode 100755 index 0000000..41df5e8 --- /dev/null +++ b/powermanager/guidance-power-manager.py @@ -0,0 +1,1134 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +""" +Copyright 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko + +Authors: + Sebastian Kügler + Jonathan Riddell + Luka Renko + +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. + +""" + +""" +A frontend to HAL's power features for KDE. +Supports screen brightness, battery level, plugged/unplugged notices, laptop lid closing actions +Specification at https://wiki.kubuntu.org/KubuntuPowerManagement + +Issues: + - We have to keep polling HAL rather than listening for signals because the Python DBUS bindings + don't have Qt mainloop integration + - Written in Python so will be slow to load up, will probably port to C++ Qt 4.2 in future + - Should also handle UPS and bluetooth batteries + - systray applet should be hidden if no battery, but then how do you suspend if no battery? + (ksmserver integration please) + - Needs lots more testing + - Use KUniqueApplication again as soon as dcop problem is sorted outc + - dcop calls need patch to dcopexport.py, already submitted upstream +""" + +import os +import sys +import subprocess +import dbus + +from qt import * +from kdecore import * +from kdeui import * + +from dcopext import DCOPClient, DCOPApp # used to lock the screen +from dcopexport import DCOPExObj + +from guidance_power_manager_ui import PowerManagerUI +from notify import NotifyWidget +from tooltip import ToolTip + +from powermanage import * + +POLL_INTERVAL = 5000 # in milliseconds + +class Notify(NotifyWidget): + """ Pop up a passive notication windows. """ + + def __init__(self,parent,msg,icon,caption=None): + NotifyWidget.__init__(self,parent,"notify") + self.setIcon(icon) + self.setText(msg) + if caption: + self.Caption(caption) + + def setIcon(self,pixmap): + """ Set an icon to be displayed in the notification. """ + if pixmap: + self.Icon.setPixmap(pixmap) + + def setCaption(self,caption): + """ Text to show in bold letters. """ + self.Caption.setText(QString("")+caption+QString("")) + + def setText(self,msg): + """" Set actual notification message. """ + self.Text.setText(msg) + + +class PowerManager(PowerManagerUI): + """ Our configuration dialog. """ + + def __init__ (self, parent, name): + PowerManagerUI.__init__(self, parent, name) + KGlobal.iconLoader().addAppDir("guidance") + + # The systray icon should show and hide the KDialogBase, not only this widget, + # therefore, it gets our parent as parent. + self.systray = KSystemTray(parent) + self.icon = "battery-charging-100" + self.systray.setPixmap(QPixmap(UserIcon(self.icon))) + self.connect(self.systray, SIGNAL("quitSelected()"), self.quit) + + # Configuration filename + self.config = KConfig("power-managerrc") + + self.powermanager = PowerManage() + + def prepare(self): + """ Prepare UI. """ + self._initBrightness() + self._initLid() + self._initBattery() + self.lastidlesec = 0 + + self._initConfigKeywords() + + self._initUI(self.parent()) + + self.configToUi() + + # Polling: evil. can't receive signals in python-dbus unless we have a glib mainloop, + # so we need to poll + self.pollTimer = QTimer(self) + self.connect(self.pollTimer, SIGNAL("timeout()"), self.poll) + self.pollTimer.start(POLL_INTERVAL) # 5 second poll, maybe make this configurable + self.poll(False) + + # check CPU freq policy and notify if it was changed + msg = self.checkCpuFreq() + if msg != "": + self.notify(msg) + + self.systray.show() + + def _initBrightness(self): + """ Check for brightness support and disable widgets if it's not there. """ + if not self.powermanager.hasBrightness: + self.PoweredBrightnessLabel.hide() + self.PoweredBrightnessSlider.hide() + self.BatteryBrightnessLabel.hide() + self.BatteryBrightnessSlider.hide() + + def _initLid(self): + """ Check for lid support and disable widgets if it's not there. """ + if not self.powermanager.hasLid: + self.LaptopLidRadios.setEnabled(False) + + def _initCB(self, combo, options, values): + """ Initialize QComboBox with proper values from provided options. """ + combo.clear() + for option in options: + combo.insertItem(values[option]) + + def _getCB(self, combo, options): + """ Get current item from QComboBox from config file (string) value. """ + try: + return options[combo.currentItem()] + except IndexError: + return "" + + def _setCB(self, combo, options, default, value): + """ Set current item in QComboBox from string value. """ + try: + num = options.index(value) + except ValueError: + num = default + pass + combo.setCurrentItem(num) + + def _getRB(self, radios, options): + """ Get current item from QRadioButton from config file (string) value. """ + try: + return options[radios.selectedId()] + except IndexError: + return "" + + def _setRB(self, radios, options, default, value): + """ Set current item in QRadioButton from string value. """ + try: + num = options.index(value) + except ValueError: + num = default + pass + radios.setButton(num) + + def _checkOldConfig(self, value, blank): + """ Convert old numerical values to keywords. """ + try: + num_val = int(value) + except ValueError: + return value + if blank: + if num_val == 0: return 'nothing' + if num_val == 1: return 'blank' + if num_val == 2: return 'suspend' + if num_val == 3: return 'hibernate' + if num_val == 4: return 'shutdown' + else: + if num_val == 0: return 'nothing' + if num_val == 1: return 'suspend' + if num_val == 2: return 'hibernate' + if num_val == 3: return 'shutdown' + return value + + + def _initConfigKeywords(self): + """ Define helper maps used with config file keywords. """ + # map action keyword to displayed name (l10n) + self.act_name = {} + self.act_name['nothing'] = i18n("Do nothing") + self.act_name['blank'] = i18n("Blank screen") + self.act_name['suspend'] = i18n("Suspend") + self.act_name['hibernate'] = i18n("Hibernate") + self.act_name['shutdown'] = i18n("Shutdown") + + # map action keyword to action methods + self.act_call = {} + self.act_call['nothing'] = None + self.act_call['blank'] = self.blankScreen + self.act_call['suspend'] = self.suspend + self.act_call['hibernate'] = self.hibernate + self.act_call['shutdown'] = self.shutdown + + # map action keyword to notification description (l10n) + self.act_notify = {} + self.act_notify['nothing'] = i18n("doing nothing") + self.act_notify['blank'] = i18n("blanking screen") + self.act_notify['suspend'] = i18n("suspending") + self.act_notify['hibernate'] = i18n("hibernating") + self.act_notify['shutdown'] = i18n("shutting down") + + # map action keyword to action icon used in notification window + self.act_icon = {} + self.act_icon['nothing'] = None + self.act_icon['blank'] = None + self.act_icon['suspend'] = SmallIcon("suspend") + self.act_icon['hibernate'] = SmallIcon("hibernate") + self.act_icon['shutdown'] = SmallIcon("exit") + + # map policy keyword to displayed name (l10n) + self.freq_name = {} + self.freq_name['dynamic'] = i18n("Dynamic") + self.freq_name['powersave'] = i18n("Powersave") + self.freq_name['performance'] = i18n("Performance") + + # map policy keyword to policy change methods + self.freq_call = {} + self.freq_call['dynamic'] = self.setCpuPolicyDynamic + self.freq_call['powersave'] = self.setCpuPolicyPowersave + self.freq_call['performance'] = self.setCpuPolicyPerformance + + + def _initUI(self, parent): + """ Build dynamic parts of the UI: context menu and tooltip. """ + self.canSuspend = self.powermanager.canSuspend and not self.config.readBoolEntry("disableSuspend", False) + self.canHibernate = self.powermanager.canHibernate and not self.config.readBoolEntry("disableHibernate", False) + + # Connect some signals. Updates in the dialogue apply instantly + self.connect(self.PoweredBrightnessSlider, SIGNAL("valueChanged(int)"), self.changePoweredBrightness) + self.connect(self.BatteryBrightnessSlider, SIGNAL("valueChanged(int)"), self.changeBatteryBrightness) + + #Add a blank tooltip, the tooltipgroup signals are then used for our KPassivePopup + toolTipGroup = QToolTipGroup(self.systray) + QToolTip.add(self.systray, "", toolTipGroup, "blah") + self.connect(toolTipGroup, SIGNAL("showTip(const QString&)"), self.showTip) + self.connect(toolTipGroup, SIGNAL("removeTip()"), self.hideTip) + + # Popup tooltip showing battery level + self.popup = KPassivePopup(self.systray) + + self.tooltip = ToolTip(self.popup) + + self._addBatteryWidgets() + + self._addCpuWidgets() + self.popup.setView(self.tooltip) + + # fill actions for LID + self.lid_act = ['nothing', 'blank', 'suspend', 'hibernate', 'shutdown'] + self.lid_act_def = 0 + # hide LID close actions that are not supported + if not self.canSuspend: + self.laptopClosedSuspend.hide() + if not self.canHibernate: + self.laptopClosedHibernate.hide() + + # fill in only CPU policies that are supported by HW + self.cb_freq = [] # list of supported cpu freq policies + self.cb_freq_def = 0 # always use first policy as default + if self.powermanager.hasCpuFreqGovernors: + self.cb_freq = self.powermanager.getSupportedCpuPolicies() + if len(self.cb_freq) > 0: + self._initCB(self.PoweredFreqCombo, self.cb_freq, self.freq_name) + self._initCB(self.BatteryFreqCombo, self.cb_freq, self.freq_name) + else: + self.PoweredFreqLabel.hide() + self.PoweredFreqCombo.hide() + self.BatteryFreqLabel.hide() + self.BatteryFreqCombo.hide() + + # fill actions in Idle/Critical battery combo boxes + self.cb_act = ['nothing'] # list of supported actions (keywords) + self.cb_act_def_critical = 0 # default action when critical battery + if self.canSuspend: + self.cb_act.append('suspend') + if self.canHibernate: + self.cb_act.append('hibernate') + self.cb_act_def_critical = len(self.cb_act) - 1 # hibernate + self.cb_act.append('shutdown') + if self.cb_act_def_critical == 0: + self.cb_act_def_critical = len(self.cb_act) - 1 # shutdown + self._initCB(self.PoweredIdleCombo, self.cb_act, self.act_name) + self._initCB(self.BatteryIdleCombo, self.cb_act, self.act_name) + self._initCB(self.BatteryCriticalCombo, self.cb_act, self.act_name) + + self.connect(self.PoweredIdleCombo,SIGNAL("activated(int)"),self.slotPoweredIdleActivated) + self.connect(self.BatteryIdleCombo,SIGNAL("activated(int)"),self.slotBatteryIdleActivated) + self.connect(self.BatteryCriticalCombo,SIGNAL("activated(int)"),self.slotBatteryCriticalActivated) + + # add suspend/hibernate to tray's context menu + menu = self.systray.contextMenu() + if self.canSuspend: + action = KAction( i18n("Suspend"), KShortcut(), self.suspend, + self.systray.actionCollection(), "suspend") + action.setIcon("suspend") + action.plug(menu) + if self.canHibernate: + action = KAction( i18n("Hibernate"), KShortcut(), self.hibernate, + self.systray.actionCollection(), "hibernate") + action.setIcon("hibernate") + action.plug(menu) + + # add list of governators + if self.powermanager.hasCpuFreqGovernors and len(self.cb_freq) > 0: + submenu = KPopupMenu(menu) + for policy in self.cb_freq: + action = KRadioAction(self.freq_name[policy], KShortcut(), + self.freq_call[policy], + self.systray.actionCollection(), policy) + action.setExclusiveGroup("freqs") + action.plug(submenu) + + policy = self.powermanager.getCpuPolicy() + if policy in self.cb_freq: + self.systray.actionCollection().action(policy).setChecked(True); + menu.insertItem(i18n("CPU policy"), submenu) + + + # KGlobalAccel crashes the application in pykde + # see http://mats.gmd.de/pipermail/pykde/2006-May/013224.html + #self.globalActions = KGlobalAccel(self) + #self.suspendShortcut = KShortcut("XF86Sleep") + #self.hibernateShortcut = KShortcut("XF86Standby") + #self.hshutdownShortcut = KShortcut("XF86PowerOff") + #self.globalActions.insert("suspend", i18n("Suspend"), i18n("what's this?"), self.suspendShortcut, #self.suspendShortcut, self.suspend) + #self.globalActions.updateConnections() + + def _initBattery(self): + """ Remove non-battery-related widgets if there's no battery bay. """ + if not self.powermanager.hasBattery: + # Disable the Batterybox in the config dialogue, + self.BatteryBox.setEnabled(False) + # And change the icon in the systray, remove the restore option + # This way, we're basically becoming a systray applet, you can + # hibernate and suspend from + self.systray.setPixmap(QPixmap(UserIcon(self.icon))) + if self.powermanager.hasAC: + self.wasOnBattery = self.powermanager.onBattery() + + def configToUi(self): + """ Setup the the values from the config file in the UI.""" + # brightness. + if self.powermanager.hasBrightness: + brightness_high = self.powermanager.brightness_levels + self.BatteryBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1) + self.PoweredBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1) + self.BatteryBrightnessSlider.setValue(self.config.readNumEntry("batteryBrightness", int(brightness_high/2))) #default middle + self.PoweredBrightnessSlider.setValue(self.config.readNumEntry("poweredBrightness", brightness_high)) #default highest + + tt_text = "Every step increases or decreases the brightness by %i%%" % int(100/brightness_high) + QToolTip.add(self.BatteryBrightnessSlider, tt_text) + QToolTip.add(self.PoweredBrightnessSlider, tt_text) + + self.lockScreenOnResume.setChecked(self.config.readBoolEntry("lockOnResume", True)) + + # Idletime-related configuration + self._setCB(self.PoweredIdleCombo, self.cb_act, 0, str(self.config.readEntry("poweredIdleAction"))) + self.PoweredIdleTime.setValue(self.config.readNumEntry("poweredIdleTime", 60)) + self._setCB(self.BatteryIdleCombo, self.cb_act, 0, str(self.config.readEntry("batteryIdleAction"))) + self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) + + self._setCB(self.PoweredFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("poweredFreqPolicy"))) + self._setCB(self.BatteryFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("batteryFreqPolicy"))) + + self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) # default Do nothing + # battery critical and lid actions. + self._setCB(self.BatteryCriticalCombo, self.cb_act, self.cb_act_def_critical, self._checkOldConfig(self.config.readEntry("batteryCriticalAction", ""), False)) + self._setRB(self.LaptopLidRadios, self.lid_act, self.lid_act_def, self._checkOldConfig(self.config.readEntry("laptopLidAction", ""), True)) + self.CriticalRemainTime.setValue(self.config.readNumEntry("criticalRemainTime", BATTERY_CRITICAL_MINUTES)) + self.criticalLevel = self.CriticalRemainTime.value() + + # Call some slots to disable various spinboxes if necessary + self.slotBatteryCriticalActivated() + self.slotPoweredIdleActivated() + self.slotBatteryIdleActivated() + + + def uiToConfig(self): + """ Read all values from the UI and write them to the config file. """ + self.config.writeEntry("poweredBrightness", self.PoweredBrightnessSlider.value()) + self.config.writeEntry("batteryBrightness", self.BatteryBrightnessSlider.value()) + + self.config.writeEntry("poweredIdleTime", self.PoweredIdleTime.value()) + self.config.writeEntry("poweredIdleAction", self._getCB(self.PoweredIdleCombo, self.cb_act)) + self.config.writeEntry("batteryIdleTime", self.BatteryIdleTime.value()) + self.config.writeEntry("batteryIdleAction", self._getCB(self.BatteryIdleCombo, self.cb_act)) + self.config.writeEntry("poweredFreqPolicy", self._getCB(self.PoweredFreqCombo, self.cb_freq)) + self.config.writeEntry("batteryFreqPolicy", self._getCB(self.BatteryFreqCombo, self.cb_freq)) + + self.config.writeEntry("batteryCriticalAction", self._getCB(self.BatteryCriticalCombo, self.cb_act)) + self.config.writeEntry("criticalRemainTime", self.CriticalRemainTime.value()) + + self.config.writeEntry("laptopLidAction", self._getRB(self.LaptopLidRadios, self.lid_act)) + self.config.writeEntry("lockOnResume", self.lockScreenOnResume.isChecked()) + + self.criticalLevel = self.CriticalRemainTime.value() + + self.config.sync() + + def quit(self): + """ Quit application. """ + kapp.quit() + + def showTip(self, text=""): + """ Pop up the tooltip showing battery data and CPU frequencies. """ + self.popup.show() + + + def showBrightnessPopup(self): + if self.powermanager.onBattery(): + value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue() + else: + value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue() + self.brightnessPopup = KPassivePopup.message('Brightness: '+str(value)+'%', self.systray) + """pop.setTimeout(3000)""" + self.brightnessPopup.show() + + def setBrightnessUp(self): + """Increments slider value by 10%""" + if self.powermanager.onBattery(): + self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())+max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1)) + else: + self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())+max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1)) + self.showBrightnessPopup() + + def setBrightnessDown(self): + """Decrements slider value by 10%""" + if self.powermanager.onBattery(): + self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())-max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1)) + else: + self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())-max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1)) + self.showBrightnessPopup() + + def getBrightness(self): + """Work with percentages - it's a bit nicer""" + if self.powermanager.onBattery(): + value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue() + else: + value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue() + return QString(str(value)) + + def hideTip(self): + """ Hide the tooltip.""" + self.popup.hide() + + def lockScreen(self): + """ locks the screen using kdesktop """ + # create a new DCOP-Client: + client = DCOPClient() + # connect the client to the local DCOP-server: + client.attach() + # create a DCOP-Application-Object to talk to kdesktop: + kdesktop = DCOPApp('kdesktop', client) + # call a DCOP-function: + try: + ok, foo = kdesktop.KScreensaverIface.lock() + except: + print "Unable to lock the screen. The KDE Screensaver does not seem to be running." + def suspend(self): + """ Lock the screen and initiate a suspend to RAM (S3). """ + if self.config.readBoolEntry("lockOnResume", True): + self.lockScreen() + try: + self.warningPopup.hide() + except AttributeError: + pass # No warningpopup, that's OK. + self.powermanager.suspend() + self.powermanager.resetIdleSeconds() + + def hibernate(self): + """ Lock the screen and initiate a suspend to disk (S4). """ + if self.config.readBoolEntry("lockOnResume", True): + self.lockScreen() + try: + self.warningPopup.hide() + except AttributeError: + pass # No warningpopup, that's OK. + self.powermanager.hibernate() + self.powermanager.resetIdleSeconds() + + def shutdown(self): + """ Perform system shutdown. """ + self.powermanager.shutdown() + + def setCpuPolicyDynamic(self): + """Change frequ for all cpu""" + self.powermanager.setCpuPolicy('dynamic') + self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['dynamic'])) + + def setCpuPolicyPerformance(self): + """Change frequ for all cpu""" + self.powermanager.setCpuPolicy('performance') + self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['performance'])) + + def setCpuPolicyPowersave(self): + """Change frequ for all cpu""" + self.powermanager.setCpuPolicy('powersave') + self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['powersave'])) + + def trySuspend(self): + """ If supported, lock the screen and initiate a suspend to RAM (S3). """ + if self.canSuspend: + self.suspend() + else: + print "Warning: DCOP suspend() called, but not supported." + + def tryHibernate(self): + """ If supported, lock the screen and initiate a suspend to disk (S4). """ + if self.canHibernate: + self.hibernate() + else: + print "Warning: DCOP hibernate() called, but not supported." + + def blankScreen(self): + """ Lock and blank screen. """ + if self.config.readBoolEntry("lockOnResume", True): + self.lockScreen() + self.powermanager.blankScreen() + + def _getIcon(self): + """ Set systray icon depending on battery status/level. """ + if self.powermanager.hasBattery: + if self.batt_state == "not present": + self.icon = "ac-adapter" + if self.batt_state == "charged": + self.icon = "battery-charging-100" + elif self.batt_state == "discharging": + if self.batt_level >= 95: + self.icon = "battery-discharging-100" + elif self.batt_level < 95 and self.batt_level >= 85: + self.icon = "battery-discharging-090" + elif self.batt_level < 85 and self.batt_level >= 75: + self.icon = "battery-discharging-070" + elif self.batt_level < 75 and self.batt_level >= 60: + self.icon = "battery-discharging-060" + elif self.batt_level < 65 and self.batt_level >= 45: + self.icon = "battery-discharging-050" + elif self.batt_level < 45 and self.batt_level >= 30: + self.icon = "battery-discharging-040" + elif self.batt_level < 30 and self.batt_level >= 20: + self.icon = "battery-discharging-030" + elif self.batt_level < 20 and self.batt_level >= 10: + self.icon = "battery-discharging-020" + elif self.batt_level < 10 and self.batt_level >= 5: + self.icon = "battery-discharging-010" + else: + self.icon = "battery-discharging-000" + elif self.batt_state == "charging": + if self.batt_level >= 95: + self.icon = "battery-charging-100" + elif self.batt_level < 95 and self.batt_level >= 85: + self.icon = "battery-charging-090" + elif self.batt_level < 85 and self.batt_level >= 75: + self.icon = "battery-charging-070" + elif self.batt_level < 75 and self.batt_level >= 60: + self.icon = "battery-charging-060" + elif self.batt_level < 65 and self.batt_level >= 45: + self.icon = "battery-charging-050" + elif self.batt_level < 45 and self.batt_level >= 30: + self.icon = "battery-charging-040" + elif self.batt_level < 30 and self.batt_level >= 20: + self.icon = "battery-charging-030" + elif self.batt_level < 20 and self.batt_level >= 10: + self.icon = "battery-charging-020" + elif self.batt_level < 10 and self.batt_level >= 5: + self.icon = "battery-charging-010" + else: + self.icon = "battery-charging-000" + else: + self.icon = "ac-adapter" + return self.icon + + def getIcon(self): + """ Return current icon.""" + return UserIcon(self.icon) + + def setIcon(self): + """ Change the systray/tooltip icon.""" + oldIcon = self.icon + self.icon = self._getIcon() + if self.icon != oldIcon: + self.systray.setPixmap(QPixmap(UserIcon(self.icon))) + self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon))) + + def notify(self, msg, icon=None): + """ Send a notification popup. """ + if icon: + icon = QPixmap(icon) + else: + icon = QPixmap(SmallIcon("messagebox_info")) + try: + del self.warningPopup + except: + pass + self.warningPopup = KPassivePopup(self.systray) + label = Notify(self.warningPopup, msg, icon) + self.warningPopup.setView(label) + position = QPoint(5,5) + self.warningPopup.show(position) + + def poll(self,notify=True): + """ Check for changes in plugged in status, battery status and laptop lid closed status. """ + debug( "------------ POLL ---------------") + + self.powermanager.checkHAL() + # Battery stuff: + # check for last state, and run plugged / unplugged message if the state changed. + if self.powermanager.hasBattery: + plugged_num = 0 + self.batt_state = "not present" # unknown yet + self.batt_level = self.batt_remain = 0 + self.batt_rate = self.batt_charge = self.batt_full = 0 + for batt in self.powermanager.batteries: + state, level, remain, rate, current, full = self.powermanager.getBatteryState(batt) + self._updateBatteryWidget(batt, state, level, remain, rate) + + ## notify plugged/unplugged batteries + if state == "not present": + if self.powermanager.batteryIsPresent[batt]: + self.notify(i18n("The battery has been removed.")) + self.powermanager.batteryIsPresent[batt] = False + else: # battery present + if not self.powermanager.batteryIsPresent[batt]: + self.notify(i18n("The battery has been inserted.")) + self.powermanager.batteryIsPresent[batt] = True + + ## get cumulative charge levels/rate + self.batt_rate += rate + self.batt_charge += current + self.batt_full += full + + ## calculate overall level (average of present batteries) + self.batt_remain += remain + self.batt_level += level + plugged_num += 1 + + ## calculate overall state (charging/discharging/charged) + if state in ("charging","discharging"): + self.batt_state = state + elif not self.batt_state in ("charging, discharging"): + self.batt_state = state + + # if we know charge and full -> recalculate overall level + if self.batt_full > 0 and self.batt_charge > 0: + self.batt_level = 100 * self.batt_charge / self.batt_full + else: + # if more than one battery present, we need to calculate average level + if plugged_num > 1: + self.batt_level /= plugged_num + + # if rate is reported, calculate remaining time on our own + if self.batt_rate > 0: + if self.batt_state == "charging": + self.batt_remain = 3600 * (float(self.batt_full - self.batt_charge) / self.batt_rate) + if self.batt_state == "discharging": + self.batt_remain = 3600 * (float(self.batt_charge) / self.batt_rate) + + remain_h = self.batt_remain/3600 + remain_m = (self.batt_remain/60)%60 + + blabel = i18n("Battery:") + if self.batt_state == "charged": + blabel += i18n(" fully charged") + elif self.batt_state == "charging": + blabel += i18n(" %i:%02ih to charge" % (remain_h,remain_m)) + elif self.batt_state == "discharging": + blabel += i18n(" %i:%02ih remaining" % (remain_h,remain_m)) + self.BattMainLabel.setText(blabel) + + # update tray icon if needed + self.setIcon() + + # check battery state + self.checkBatteryCritical() + + # check Idletime + self.checkIdletime() + + # CPU stuff + self._updateCpuWidgets() + + if self.powermanager.hasBattery: + on_battery = self.powermanager.onBattery() + if self.powermanager.wasOnBattery != on_battery: + self.powermanager.wasOnBattery = on_battery + debug("poll: states differ") + if not on_battery: + debug("poll: Now on AC") + if notify: + self.powerHasBeenPlugged() + else: + debug("poll: Now on battery") + if notify: + self.powerHasBeenUnplugged() + else: + debug("poll: state is the same") + + # Lid stuff + if self.powermanager.hasLid: + if self.powermanager.getLidClosedState(): + if not self.powermanager.lidClosedState: + self.powermanager.lidClosedState = True + + action = self._getRB(self.LaptopLidRadios, self.lid_act) + if not self.act_name.has_key(action): + action = self.act_name[self.lid_act_def] + + if self.act_call[action] != None: + note = i18n("Laptop lid is closed, %1 now.").arg(self.act_notify[action]) + self.notify(note, self.act_icon[action]) + QTimer.singleShot(2000, self.act_call[action]) + else: + self.powermanager.lidClosedState = False + + def _addBatteryWidgets(self): + """ Adds progressbars to show battery status to the tooltip.""" + BattLayout = QHBoxLayout(None,0,6,"BattLayout") + + self.BattPixmap = QLabel(self.tooltip,"BattLabLayout") + self.BattPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.BattPixmap.sizePolicy().hasHeightForWidth())) + self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon))) + self.BattPixmap.setScaledContents(1) + BattLayout.addWidget(self.BattPixmap) + self.BattMainLabel = QLabel(self.tooltip,"BattMainLabel") + self.BattMainLabel.setText(i18n("Battery:")) + BattLayout.addWidget(self.BattMainLabel) + + # Add to tooltip + self.tooltip.layout().addLayout(BattLayout) + + # Create a progressbar and a label for every battery found, and add it to tooltip + self.BattLabel = {} + self.BattLayout = {} + self.BattProgress = {} + i = 1 + for batt in self.powermanager.batteries: + self.BattLayout[batt] = QHBoxLayout(None,0,6,"BattBarLayout") + self.BattLabel[batt] = QLabel(self.tooltip,"BattLabel") + if len(self.powermanager.batteries) > 1: + self.BattLabel[batt].setText(i18n("Battery %i" % i)) + self.BattLayout[batt].addWidget(self.BattLabel[batt]) + self.BattProgress[batt] = KProgress(self.tooltip,"BattProgress") + self.BattProgress[batt].setMinimumSize(QSize(200,0)) + self.BattLayout[batt].addWidget(self.BattProgress[batt]) + self.tooltip.layout().addLayout(self.BattLayout[batt]) + i += 1 + + + def _updateBatteryWidget(self, batt, state, level, remain, rate): + """ Retrieve battery information and update the related widgets accordingly. """ + self.BattProgress[batt].setEnabled(True) + self.BattProgress[batt].setTotalSteps(100) + self.BattProgress[batt].setProgress(level) + if state == "not present": + self.BattProgress[batt].setFormat(i18n("not present")) + elif state == "charging": + self.BattProgress[batt].setFormat(i18n("Charging (%p%)")) + elif state == "discharging": + if rate > 0: + showrate = rate/1000 + self.BattProgress[batt].setFormat(i18n("Discharging (%p%)") + " - %.d W" % showrate) + else: + self.BattProgress[batt].setFormat(i18n("Discharging (%p%)")) + else: + self.BattProgress[batt].setFormat("%p%") + + def _addCpuWidgets(self): + """ Adds progressbars to show CPU frequencies to the tooltip.""" + if not SHOW_CPUFREQ: + return + if len(self.powermanager.cpus) == 0: + return + + LabelLayout = QHBoxLayout(None,0,6,"layout5") + + self.CpuPixmap = QLabel(self.tooltip,"CpuPixmap") + self.CpuPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.CpuPixmap.sizePolicy().hasHeightForWidth())) + self.CpuPixmap.setPixmap(QPixmap(UserIcon("processor"))) + self.CpuPixmap.setScaledContents(1) + LabelLayout.addWidget(self.CpuPixmap) + self.CpuMainLabel = QLabel(self.tooltip,"CpuMainLabel") + self.CpuMainLabel.setText(i18n("CPU Frequency:")) + LabelLayout.addWidget(self.CpuMainLabel) + + # Add to tooltip + self.tooltip.layout().addLayout(LabelLayout) + + # Create a progressbar and a label for every CPU found, and add it to tooltip + self.CpuLabel = {} + self.CpuLayout = {} + self.CpuProgress = {} + i = 1 + for cpu in self.powermanager.cpus: + self.CpuLayout[cpu] = QHBoxLayout(None,0,6,"layout2") + self.CpuLabel[cpu] = QLabel(self.tooltip,"CpuLabel") + if len(self.powermanager.cpus) > 1: + self.CpuLabel[cpu].setText(i18n("Processor %i" % i)) + self.CpuLayout[cpu].addWidget(self.CpuLabel[cpu]) + self.CpuProgress[cpu] = KProgress(self.tooltip,"CpuProgress") + self.CpuProgress[cpu].setFormat("%v MHz") + self.CpuLayout[cpu].addWidget(self.CpuProgress[cpu]) + self.tooltip.layout().addLayout(self.CpuLayout[cpu]) + i += 1 + + def slotPoweredIdleActivated(self, index=False): + """ Signal slot for activated powered idle action. """ + if not index: + index = self.PoweredIdleCombo.currentItem() + self.PoweredIdleTime.setEnabled(index != 0) + + def slotBatteryIdleActivated(self, index=False): + """ Signal slot for activated battery idle action. """ + if not index: + index = self.BatteryIdleCombo.currentItem() + self.BatteryIdleTime.setEnabled(index != 0) + + def slotBatteryCriticalActivated(self, index=False): + """ Signal slot for activated battery critical action. """ + if not index: + index = self.BatteryCriticalCombo.currentItem() + self.CriticalRemainTime.setEnabled(index != 0) + + def _updateCpuWidgets(self): + """ Retrieve CPU freq information and update the related widgets accordingly. """ + if not SHOW_CPUFREQ: + return + if len(self.powermanager.cpus) == 0: + return + + clabel = i18n("CPU Frequency:") + " " + policy = self.powermanager.getCpuPolicy() + if self.freq_name.has_key(policy): + clabel += self.freq_name[policy] # get l10n name + else: + clabel += policy + self.CpuMainLabel.setText(clabel) + + for cpu in self.powermanager.cpus: + cpustate = self.powermanager.getCpuState(cpu) + if not cpustate['online']: + self.CpuProgress[cpu].setEnabled(False) + else: + self.CpuProgress[cpu].setEnabled(True) + self.CpuProgress[cpu].setTotalSteps(cpustate['max']) + self.CpuProgress[cpu].setProgress(cpustate['cur']) + if policy != "": + self.systray.actionCollection().action(policy).setChecked(True) + if policy in self.cb_freq: + self.systray.actionCollection().action(policy).setChecked(True) + + def changePoweredBrightness(self, level=None): + """ Mains-powered brigthness slider has been moved. """ + # Check if the state applies and adjust brightness immediately. + if not self.powermanager.onBattery() and self.powermanager.hasBrightness: + if not level: + level = self.PoweredBrightnessSlider.value() + self.powermanager.adjustBrightness(level) + + def changeBatteryBrightness(self, level=None): + """ Battery-powered brigthness slider has been moved. """ + # Check if the state applies and adjust brightness immediately. + if self.powermanager.onBattery() and self.powermanager.hasBrightness: + if not level: + level = self.BatteryBrightnessSlider.value() + self.powermanager.adjustBrightness(level) + + + + + def checkCpuFreq(self): + """ Adjust CPU frequency policy according to current state """ + if not self.powermanager.hasCpuFreqGovernors: + return "" + + if self.powermanager.onBattery(): + policy = str(self.config.readEntry("batteryFreqPolicy")) + else: + policy = str(self.config.readEntry("poweredFreqPolicy")) + if policy == "": + policy = 'dynamic' + + # check if specified policy is supported by HW + if not policy in self.cb_freq: + print "Warning: policy from config file not supported: ", policy + return "" + + current_policy = self.powermanager.getCpuPolicy() + if current_policy != policy: + debug("Switching CPU policy from %s to %s." % (current_policy, policy)) + self.powermanager.setCpuPolicy(policy) + return i18n("CPU frequency policy changed to %1.").arg(self.freq_name[policy]) + elif current_policy == 'dynamic': + debug("Dynamic policy -> update policy (conservative/ondemand)") + self.powermanager.setCpuPolicy(policy) + + debug("CPU policy will stay %s" % current_policy) + return "" + + def powerHasBeenUnplugged(self): + """ Actions to perform when the plug has been pulled.""" + if self.powermanager.hasBrightness: + self.powermanager.adjustBrightness(self.BatteryBrightnessSlider.value()) + self.powermanager.setPowerSave(True) + self.checkBatteryCritical() + self.changeBatteryBrightness() + self.powermanager.setScreensaverBlankOnly(True) + self.powermanager.resetIdleSeconds() + msg = self.checkCpuFreq() + if self.powermanager.hasAC: + self.notify(i18n("The AC adapter has been unplugged, switching to battery mode.")+"\n"+msg, self.getIcon()) + + def powerHasBeenPlugged(self): + """ Actions to perform when AC adapter has been plugged in. """ + if self.powermanager.hasBrightness: + self.powermanager.adjustBrightness(self.PoweredBrightnessSlider.value()) + self.powermanager.setPowerSave(False) + self.changePoweredBrightness() + self.powermanager.setScreensaverBlankOnly(False) + msg = self.checkCpuFreq() + self.powermanager.resetIdleSeconds() + self.notify(i18n("The AC adapter has been plugged in, switching to AC mode.")+"\n"+msg, self.getIcon()) + + def checkBatteryCritical(self): + """ Check for warning and critical battery label and notify-warn or + initiate the configured action. """ + + if not self.powermanager.hasBattery: + return + + if self.batt_state == "discharging": + currentLevel = int(self.batt_remain/60) + + warningLevel = self.criticalLevel + 5 # warn five minutes before critical + criticalLevel = self.criticalLevel + + debug("CurrentBat: %i, WarningBat: %i, CriticalBat: %i" % (currentLevel, warningLevel, criticalLevel)) + # We only want to suspend if the chargelevel is above a certain threshold, + # it sometimes takes some time for HAL to report remaining time correctly + if currentLevel <= criticalLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD: + if not self.powermanager.criticalBatteryState and self.powermanager.onBattery(): + self.powermanager.criticalBatteryState = True + + action = str(self.config.readEntry("batteryCriticalAction")) + if not self.act_name.has_key(action): + action = self.act_name[self.cb_act_def_critical] + + note = i18n("You are about to run out of battery power, %1 now.").arg(self.act_notify[action]) + self.notify(note, self.act_icon[action]) + if self.act_call[action] != None: + QTimer.singleShot(2000, self.act_call[action]) + else: + self.powermanager.criticalBatteryState = False + if currentLevel <= warningLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD: + if not self.powermanager.warningBatteryState: + self.powermanager.warningBatteryState = True + self.notify(i18n("You are low on battery power."), self.getIcon()) + else: + self.powermanager.warningBatteryState = False + + def checkIdletime(self): + """ Reads the idle time and does some action. """ + idlesec = round(self.powermanager.getIdleSeconds()/60, 2) + if self.powermanager.onBattery(): + idleTime = self.config.readNumEntry("batteryIdleTime", 10) + action = str(self.config.readEntry("batteryIdleAction")) + else: + idleTime = self.config.readNumEntry("poweredIdleTime", 60) + action = str(self.config.readEntry("poweredIdleAction")) + if not self.act_name.has_key(action): + action = 'nothing' + + if idlesec - self.lastidlesec > 100: + debug("last: %u" % (idlesec - self.lastidlesec)) + return # probably bogus idleseconds right after suspend + self.lastidlesec = idlesec + if self.act_call[action] == None: + return # doing nothing anyway + if idlesec > idleTime: + note = i18n("System idle for at least %1 minutes, %2 now.").arg(idleTime).arg(self.act_notify[action]) + self.notify(note, self.act_icon[action]) + QTimer.singleShot(2000, self.act_call[action]) + + + +def doDcop(kapp): + """ Register kvandale in dcop, so it can be controlled from outside. """ + my_dcop = kapp.dcopClient() + #my_dcop.attach() + #my_dcop.registerAs("power-manager") + + +class DcopIface (DCOPExObj): + """ Add some interface so we can use powermanager from the outside. """ + def __init__ (self, app, id='power-manager'): + DCOPExObj.__init__ (self, id) + # addMethod (, ) + #self.addMethod ('QString getQuery()', gvd.getZoekbegrip) + + # PM related. + self.addMethod ('void suspend ()', app.trySuspend) + self.addMethod ('void hibernate ()', app.tryHibernate) + self.addMethod ('void shutdown ()', app.shutdown) + + # UI related. + self.addMethod ('void showTip ()', app.showTip) + #self.addMethod ('void show ()', app.parent().show) + #self.addMethod ('void hide ()', app.parent().hide) + + #self.addMethod ('void plugged ()', app.powerHasBeenPlugged) + #self.addMethod ('void unplugged ()', app.powerHasBeenUnplugged) + self.addMethod ('bool onBattery ()', app.powermanager.onBattery) + + self.addMethod('void brightnessUp ()', app.setBrightnessUp) + self.addMethod('void brightnessDown ()', app.setBrightnessDown) + self.addMethod('QString getBrightness ()', app.getBrightness) + + #self.addMethod ('QString getCurrentResult()', gvd.getRawResult) + + +class PowermanagerApp(KDialogBase): + """ The KDialog providing the OK, Apply and Cancel buttons.""" + + def __init__(self,parent=None,name=None): + """ Initialise dialog and set mainwidget. """ + KGlobal.locale().insertCatalogue("guidance") + KGlobal.iconLoader().addAppDir("guidance") + + # We would like to use a KUniqueApplication, but that breaks dcop due to some + # strange bug. The following line is the revenge code for this bug, it is + # intentionally ugly. + if len(os.popen("dcop |grep guidance-").readlines()) > 1: + print "There is already an instance of power manager running. Exiting." + sys.exit(0) + + # Which buttons do we want? + KDialogBase.__init__(self,KJanusWidget.Swallow,i18n("Power Manager"), + KDialogBase.Ok|KDialogBase.Apply|KDialogBase.Cancel|KDialogBase.User1, KDialogBase.Close) + self.pmwidget = PowerManager(self,name) + self.setButtonText(KDialogBase.User1, i18n("About")) + + if not self.pmwidget.powermanager.isLaptop(): + print "This is not a laptop, quitting ... " + sys.exit(1) + + self.pmwidget.prepare() + + self.setMainWidget(self.pmwidget) + self.aboutus = KAboutApplication(self) + + def slotOk(self): + """ The OK button has been pressed, save configuration and pass on do whatever + needs to be done by KDialog. """ + self.pmwidget.uiToConfig() + self.pmwidget.checkCpuFreq() + KDialogBase.slotOk(self) + + def slotApply(self): + """ The Apply button has been pressed, save configuration and pass on do whatever + needs to be done by KDialog. """ + self.pmwidget.uiToConfig() + self.pmwidget.checkCpuFreq() + KDialogBase.slotApply(self) + + def slotCancel(self): + """ The Cancel button has been pressed, reset some values and hide dialogue. """ + # In case brightness has changed, we reset it to the configured value. + if self.pmwidget.powermanager.hasBrightness: + brightness_high = self.pmwidget.powermanager.brightness_levels + if not self.pmwidget.powermanager.onBattery(): + level = self.pmwidget.config.readNumEntry("poweredBrightness", brightness_high) + else: + level = self.pmwidget.config.readNumEntry("batteryBrightness", int(brightness_high/2)) + self.pmwidget.powermanager.adjustBrightness(level) + self.pmwidget.configToUi() + KDialogBase.slotCancel(self) + + def slotUser1(self): + self.aboutus.show() + +# There's a bug in KUniqueApplication that shows the pid in the dcop name, +# this fugly hack works around it. +class PMApp(KApplication): + + def name(self): + return "power-manager" + + +if __name__ == "__main__": + aboutdata = KAboutData("guidance", "Power Manager", "0.8.0", + "Handles battery, display and suspend modes for your computer.", KAboutData.License_GPL, + "(C) 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko", + None, None, "jriddell@ubuntu.com") + aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org","http://vizZzion.org") + aboutdata.addAuthor("Jonathan Riddell", "Developer", "jriddell@ubuntu.com") + aboutdata.addAuthor("Luka Renko", "Developer", "lure@kubuntu.org") + aboutdata.setProgramLogo(QImage("power-manager.png")) + KCmdLineArgs.init(sys.argv, aboutdata) + #kapp = KUniqueApplication(True, True, False) + #kapp = KApplication() + kapp = PMApp(True, True) + mainWindow = PowermanagerApp(None, "main window") + doDcop(kapp) + dcop_iface = DcopIface(mainWindow.pmwidget) + """Start helper module / button listener""" + try: + helperPid = os.spawnl(os.P_NOWAIT, os.path.dirname(__file__)+'/gpmhelper.py', 'gpmhelper.py') + except: + """Non-fatal if this fails""" + print "Unable to start button-listener" + + kapp.exec_loop() + + """Kill helper module / button listener""" + os.system('kill '+str(helperPid)) diff --git a/powermanager/guidance_power_manager_ui.py b/powermanager/guidance_power_manager_ui.py new file mode 100644 index 0000000..13cbc7c --- /dev/null +++ b/powermanager/guidance_power_manager_ui.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'guidance_power_manager_ui.ui' +# +# Created: Thu Apr 10 00:50:39 2008 +# by: The PyQt User Interface Compiler (pyuic) 3.17.4 +# +# WARNING! All changes made in this file will be lost! + + +import sys +from qt import * +from kdecore import KCmdLineArgs, KApplication +from kdecore import i18n +from kdeui import * + + + +class PowerManagerUI(QWidget): + def __init__(self,parent = None,name = None,fl = 0): + QWidget.__init__(self,parent,name,fl) + + if not name: + self.setName("PowerManagerUI") + + self.setMouseTracking(1) + + PowerManagerUILayout = QVBoxLayout(self,11,6,"PowerManagerUILayout") + + self.GeneralSettingsBox = QGroupBox(self,"GeneralSettingsBox") + self.GeneralSettingsBox.setColumnLayout(0,Qt.Vertical) + self.GeneralSettingsBox.layout().setSpacing(6) + self.GeneralSettingsBox.layout().setMargin(11) + GeneralSettingsBoxLayout = QVBoxLayout(self.GeneralSettingsBox.layout()) + GeneralSettingsBoxLayout.setAlignment(Qt.AlignTop) + + self.lockScreenOnResume = QCheckBox(self.GeneralSettingsBox,"lockScreenOnResume") + GeneralSettingsBoxLayout.addWidget(self.lockScreenOnResume) + PowerManagerUILayout.addWidget(self.GeneralSettingsBox) + + self.MainsPoweredBox = QGroupBox(self,"MainsPoweredBox") + self.MainsPoweredBox.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Fixed,0,0,self.MainsPoweredBox.sizePolicy().hasHeightForWidth())) + self.MainsPoweredBox.setColumnLayout(0,Qt.Vertical) + self.MainsPoweredBox.layout().setSpacing(6) + self.MainsPoweredBox.layout().setMargin(11) + MainsPoweredBoxLayout = QVBoxLayout(self.MainsPoweredBox.layout()) + MainsPoweredBoxLayout.setAlignment(Qt.AlignTop) + + layout17 = QHBoxLayout(None,0,6,"layout17") + + self.PoweredBrightnessLabel = QLabel(self.MainsPoweredBox,"PoweredBrightnessLabel") + layout17.addWidget(self.PoweredBrightnessLabel) + + self.PoweredBrightnessSlider = QSlider(self.MainsPoweredBox,"PoweredBrightnessSlider") + self.PoweredBrightnessSlider.setMouseTracking(1) + self.PoweredBrightnessSlider.setAcceptDrops(1) + self.PoweredBrightnessSlider.setMaxValue(7) + self.PoweredBrightnessSlider.setLineStep(1) + self.PoweredBrightnessSlider.setPageStep(1) + self.PoweredBrightnessSlider.setOrientation(QSlider.Horizontal) + self.PoweredBrightnessSlider.setTickmarks(QSlider.Both) + self.PoweredBrightnessSlider.setTickInterval(0) + layout17.addWidget(self.PoweredBrightnessSlider) + MainsPoweredBoxLayout.addLayout(layout17) + + layout13 = QHBoxLayout(None,0,6,"layout13") + spacer12_3_2_2 = QSpacerItem(200,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + layout13.addItem(spacer12_3_2_2) + + self.PoweredIdleLabel = QLabel(self.MainsPoweredBox,"PoweredIdleLabel") + layout13.addWidget(self.PoweredIdleLabel) + + self.PoweredIdleTime = QSpinBox(self.MainsPoweredBox,"PoweredIdleTime") + layout13.addWidget(self.PoweredIdleTime) + + self.PoweredIdleCombo = QComboBox(0,self.MainsPoweredBox,"PoweredIdleCombo") + layout13.addWidget(self.PoweredIdleCombo) + MainsPoweredBoxLayout.addLayout(layout13) + + layout13_2_2 = QHBoxLayout(None,0,6,"layout13_2_2") + spacer12_3_2_2_3_2 = QSpacerItem(200,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + layout13_2_2.addItem(spacer12_3_2_2_3_2) + + self.PoweredFreqLabel = QLabel(self.MainsPoweredBox,"PoweredFreqLabel") + layout13_2_2.addWidget(self.PoweredFreqLabel) + + self.PoweredFreqCombo = QComboBox(0,self.MainsPoweredBox,"PoweredFreqCombo") + layout13_2_2.addWidget(self.PoweredFreqCombo) + MainsPoweredBoxLayout.addLayout(layout13_2_2) + PowerManagerUILayout.addWidget(self.MainsPoweredBox) + + self.BatteryBox = QGroupBox(self,"BatteryBox") + self.BatteryBox.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Fixed,0,0,self.BatteryBox.sizePolicy().hasHeightForWidth())) + self.BatteryBox.setColumnLayout(0,Qt.Vertical) + self.BatteryBox.layout().setSpacing(6) + self.BatteryBox.layout().setMargin(11) + BatteryBoxLayout = QVBoxLayout(self.BatteryBox.layout()) + BatteryBoxLayout.setAlignment(Qt.AlignTop) + + layout16 = QHBoxLayout(None,0,6,"layout16") + + self.BatteryBrightnessLabel = QLabel(self.BatteryBox,"BatteryBrightnessLabel") + layout16.addWidget(self.BatteryBrightnessLabel) + + self.BatteryBrightnessSlider = QSlider(self.BatteryBox,"BatteryBrightnessSlider") + self.BatteryBrightnessSlider.setMouseTracking(1) + self.BatteryBrightnessSlider.setMaxValue(7) + self.BatteryBrightnessSlider.setPageStep(1) + self.BatteryBrightnessSlider.setOrientation(QSlider.Horizontal) + self.BatteryBrightnessSlider.setTickmarks(QSlider.Both) + layout16.addWidget(self.BatteryBrightnessSlider) + BatteryBoxLayout.addLayout(layout16) + + layout14 = QGridLayout(None,1,1,0,6,"layout14") + + self.BatteryIdleCombo = QComboBox(0,self.BatteryBox,"BatteryIdleCombo") + + layout14.addWidget(self.BatteryIdleCombo,1,4) + + self.BatteryIdleLabel = QLabel(self.BatteryBox,"BatteryIdleLabel") + + layout14.addWidget(self.BatteryIdleLabel,1,2) + + self.BatteryCriticalCombo = QComboBox(0,self.BatteryBox,"BatteryCriticalCombo") + + layout14.addWidget(self.BatteryCriticalCombo,0,4) + + self.BatteryCriticalLabel = QLabel(self.BatteryBox,"BatteryCriticalLabel") + + layout14.addMultiCellWidget(self.BatteryCriticalLabel,0,0,1,2) + + self.BatteryIdleTime = QSpinBox(self.BatteryBox,"BatteryIdleTime") + + layout14.addWidget(self.BatteryIdleTime,1,3) + + self.CriticalRemainTime = QSpinBox(self.BatteryBox,"CriticalRemainTime") + + layout14.addWidget(self.CriticalRemainTime,0,3) + spacer12_3 = QSpacerItem(28,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + layout14.addItem(spacer12_3,0,0) + spacer12_3_2 = QSpacerItem(50,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + layout14.addMultiCell(spacer12_3_2,1,1,0,1) + BatteryBoxLayout.addLayout(layout14) + + layout13_2 = QHBoxLayout(None,0,6,"layout13_2") + spacer12_3_2_2_3 = QSpacerItem(200,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + layout13_2.addItem(spacer12_3_2_2_3) + + self.BatteryFreqLabel = QLabel(self.BatteryBox,"BatteryFreqLabel") + layout13_2.addWidget(self.BatteryFreqLabel) + + self.BatteryFreqCombo = QComboBox(0,self.BatteryBox,"BatteryFreqCombo") + layout13_2.addWidget(self.BatteryFreqCombo) + BatteryBoxLayout.addLayout(layout13_2) + PowerManagerUILayout.addWidget(self.BatteryBox) + + self.LaptopLidRadios = QButtonGroup(self,"LaptopLidRadios") + self.LaptopLidRadios.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Fixed,0,0,self.LaptopLidRadios.sizePolicy().hasHeightForWidth())) + self.LaptopLidRadios.setFrameShape(QButtonGroup.GroupBoxPanel) + self.LaptopLidRadios.setColumnLayout(0,Qt.Vertical) + self.LaptopLidRadios.layout().setSpacing(5) + self.LaptopLidRadios.layout().setMargin(11) + LaptopLidRadiosLayout = QHBoxLayout(self.LaptopLidRadios.layout()) + LaptopLidRadiosLayout.setAlignment(Qt.AlignTop) + + self.laptopClosedNone = QRadioButton(self.LaptopLidRadios,"laptopClosedNone") + LaptopLidRadiosLayout.addWidget(self.laptopClosedNone) + + self.laptopClosedBlank = QRadioButton(self.LaptopLidRadios,"laptopClosedBlank") + LaptopLidRadiosLayout.addWidget(self.laptopClosedBlank) + + self.laptopClosedSuspend = QRadioButton(self.LaptopLidRadios,"laptopClosedSuspend") + LaptopLidRadiosLayout.addWidget(self.laptopClosedSuspend) + + self.laptopClosedHibernate = QRadioButton(self.LaptopLidRadios,"laptopClosedHibernate") + LaptopLidRadiosLayout.addWidget(self.laptopClosedHibernate) + + self.laptopClosedShutdown = QRadioButton(self.LaptopLidRadios,"laptopClosedShutdown") + LaptopLidRadiosLayout.addWidget(self.laptopClosedShutdown) + spacer12_2 = QSpacerItem(213,20,QSizePolicy.Expanding,QSizePolicy.Minimum) + LaptopLidRadiosLayout.addItem(spacer12_2) + PowerManagerUILayout.addWidget(self.LaptopLidRadios) + spacer11 = QSpacerItem(31,80,QSizePolicy.Minimum,QSizePolicy.Expanding) + PowerManagerUILayout.addItem(spacer11) + + self.languageChange() + + self.resize(QSize(505,374).expandedTo(self.minimumSizeHint())) + self.clearWState(Qt.WState_Polished) + + + def languageChange(self): + self.setCaption(i18n("PowerManagerUI")) + self.GeneralSettingsBox.setTitle(i18n("General Settings")) + self.lockScreenOnResume.setText(i18n("Lock screen on resume")) + self.MainsPoweredBox.setTitle(i18n("Mains Powered")) + self.PoweredBrightnessLabel.setText(i18n("Brightness")) + QWhatsThis.add(self.PoweredBrightnessSlider,i18n("With this slider you can set the brightness when the system is plugged into the socket outlet")) + self.PoweredIdleLabel.setText(i18n("When the system is idle for more than")) + self.PoweredIdleTime.setPrefix(QString.null) + self.PoweredIdleTime.setSuffix(i18n(" min")) + QWhatsThis.add(self.PoweredIdleTime,i18n("To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action.")) + self.PoweredFreqLabel.setText(i18n("CPU frequency scaling policy")) + self.BatteryBox.setTitle(i18n("Battery Powered")) + self.BatteryBrightnessLabel.setText(i18n("Brightness")) + QWhatsThis.add(self.BatteryBrightnessSlider,i18n("This slider controls the brightness when the system runs on batteries")) + self.BatteryIdleLabel.setText(i18n("When the system is idle for more than")) + self.BatteryCriticalLabel.setText(i18n("When battery remaining time drops below")) + self.BatteryIdleTime.setPrefix(QString.null) + self.BatteryIdleTime.setSuffix(i18n(" min")) + QWhatsThis.add(self.BatteryIdleTime,i18n("To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action.")) + self.CriticalRemainTime.setPrefix(QString.null) + self.CriticalRemainTime.setSuffix(i18n(" min")) + QWhatsThis.add(self.CriticalRemainTime,i18n("To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action.")) + self.BatteryFreqLabel.setText(i18n("CPU frequency scaling policy")) + self.LaptopLidRadios.setTitle(i18n("When Laptop Lid Closed")) + self.laptopClosedNone.setText(i18n("Do nothing")) + self.laptopClosedBlank.setText(i18n("Lock screen")) + self.laptopClosedSuspend.setText(i18n("Suspend")) + QToolTip.add(self.laptopClosedSuspend,i18n("Suspend to Memory")) + QWhatsThis.add(self.laptopClosedSuspend,i18n("Suspend is a sleep state, the system will consume only very little energy when suspended")) + self.laptopClosedHibernate.setText(i18n("Hibernate")) + QToolTip.add(self.laptopClosedHibernate,i18n("Suspend to Disk")) + QWhatsThis.add(self.laptopClosedHibernate,i18n("Hibernate or \"Suspend to Disk\" is a deep sleepstate, allowing the system to power off completely")) + self.laptopClosedShutdown.setText(i18n("Shutdown")) + QToolTip.add(self.laptopClosedShutdown,i18n("Halt the machine")) + + +if __name__ == "__main__": + appname = "" + description = "" + version = "" + + KCmdLineArgs.init (sys.argv, appname, description, version) + a = KApplication () + + QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()")) + w = PowerManagerUI() + a.setMainWidget(w) + w.show() + a.exec_loop() diff --git a/powermanager/guidance_power_manager_ui.ui b/powermanager/guidance_power_manager_ui.ui new file mode 100644 index 0000000..77aa884 --- /dev/null +++ b/powermanager/guidance_power_manager_ui.ui @@ -0,0 +1,530 @@ + +PowerManagerUI + + + PowerManagerUI + + + + 0 + 0 + 505 + 374 + + + + PowerManagerUI + + + true + + + + unnamed + + + + GeneralSettingsBox + + + General Settings + + + + unnamed + + + + lockScreenOnResume + + + Lock screen on resume + + + + + + + MainsPoweredBox + + + + 5 + 0 + 0 + 0 + + + + Mains Powered + + + + unnamed + + + + layout17 + + + + unnamed + + + + PoweredBrightnessLabel + + + Brightness + + + + + PoweredBrightnessSlider + + + true + + + true + + + 7 + + + 1 + + + 1 + + + Horizontal + + + Both + + + 0 + + + With this slider you can set the brightness when the system is plugged into the socket outlet + + + + + + + layout13 + + + + unnamed + + + + spacer12_3_2_2 + + + Horizontal + + + Expanding + + + + 200 + 20 + + + + + + PoweredIdleLabel + + + When the system is idle for more than + + + + + PoweredIdleTime + + + + + + min + + + To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action. + + + + + PoweredIdleCombo + + + + + + + layout13_2_2 + + + + unnamed + + + + spacer12_3_2_2_3_2 + + + Horizontal + + + Expanding + + + + 200 + 20 + + + + + + PoweredFreqLabel + + + CPU frequency scaling policy + + + + + PoweredFreqCombo + + + + + + + + + BatteryBox + + + + 5 + 0 + 0 + 0 + + + + Battery Powered + + + + unnamed + + + + layout16 + + + + unnamed + + + + BatteryBrightnessLabel + + + Brightness + + + + + BatteryBrightnessSlider + + + true + + + 7 + + + 1 + + + Horizontal + + + Both + + + This slider controls the brightness when the system runs on batteries + + + + + + + layout14 + + + + unnamed + + + + BatteryIdleCombo + + + + + BatteryIdleLabel + + + When the system is idle for more than + + + + + BatteryCriticalCombo + + + + + BatteryCriticalLabel + + + When battery remaining time drops below + + + + + BatteryIdleTime + + + + + + min + + + To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action. + + + + + CriticalRemainTime + + + + + + min + + + To prevent data loss or other damage, you can have the system suspend or hibernate, so you don't run accidentally out of battery power. Configure the number of minutes below which the machine will run the configured action. + + + + + spacer12_3 + + + Horizontal + + + Expanding + + + + 28 + 20 + + + + + + spacer12_3_2 + + + Horizontal + + + Expanding + + + + 50 + 20 + + + + + + + + layout13_2 + + + + unnamed + + + + spacer12_3_2_2_3 + + + Horizontal + + + Expanding + + + + 200 + 20 + + + + + + BatteryFreqLabel + + + CPU frequency scaling policy + + + + + BatteryFreqCombo + + + + + + + + + LaptopLidRadios + + + + 5 + 0 + 0 + 0 + + + + GroupBoxPanel + + + When Laptop Lid Closed + + + + unnamed + + + 5 + + + + laptopClosedNone + + + Do nothing + + + + + laptopClosedBlank + + + Lock screen + + + + + laptopClosedSuspend + + + Suspend + + + Suspend to Memory + + + Suspend is a sleep state, the system will consume only very little energy when suspended + + + + + laptopClosedHibernate + + + Hibernate + + + Suspend to Disk + + + Hibernate or "Suspend to Disk" is a deep sleepstate, allowing the system to power off completely + + + + + laptopClosedShutdown + + + Shutdown + + + Halt the machine + + + + + spacer12_2 + + + Horizontal + + + Expanding + + + + 213 + 20 + + + + + + + + spacer11 + + + Vertical + + + Expanding + + + + 31 + 80 + + + + + + + diff --git a/powermanager/hal-test.py b/powermanager/hal-test.py new file mode 100644 index 0000000..f9ef90c --- /dev/null +++ b/powermanager/hal-test.py @@ -0,0 +1,35 @@ +import dbus + +bus = dbus.SystemBus() +hal_manager_obj = bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager") +hal_manager = dbus.Interface(hal_manager_obj, "org.freedesktop.Hal.Manager") +#device_names = hal_manager.GetAllDevices() + +ac = hal_manager.FindDeviceByCapability("ac_adapter") + +#all_devices = hal_manager.GetAllDevices() + +#for n in device_names: print n +#obj = bus.get_object("org.freedesktop.Hal", u'/org/freedesktop/Hal/devices/acpi_AC') +#obj.GetAllProperties() + +name = ac[0] +device_dbus_obj = bus.get_object("org.freedesktop.Hal" ,ac[0]) +properties = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + +try: + parent_name = properties["info.parent"] +except KeyError: + # no parent, must be parent of virtual_root + parent_name = "/" +except TypeError: + print "Error: no properties for device %s"%name + #continue +print properties['ac_adapter.present'] +#for p in properties: +# print p, " :: ", properties[p] +if properties['ac_adapter.present']: + print "plugged in" +else: + print "unplugged" + diff --git a/powermanager/notify.py b/powermanager/notify.py new file mode 100644 index 0000000..869f064 --- /dev/null +++ b/powermanager/notify.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'notify.ui' +# +# Created: Thu Apr 10 00:50:39 2008 +# by: The PyQt User Interface Compiler (pyuic) 3.17.4 +# +# WARNING! All changes made in this file will be lost! + + +import sys +from qt import * +from kdecore import KCmdLineArgs, KApplication +from kdecore import i18n +from kdeui import * + + +class NotifyWidget(QWidget): + def __init__(self,parent = None,name = None,fl = 0): + QWidget.__init__(self,parent,name,fl) + + if not name: + self.setName("NotifyWidgetUI") + + self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.MinimumExpanding,0,0,self.sizePolicy().hasHeightForWidth())) + self.setBaseSize(QSize(0,0)) + + NotifyWidgetUILayout = QGridLayout(self,1,1,11,6,"NotifyWidgetUILayout") + + self.Icon = QLabel(self,"Icon") + self.Icon.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.Icon.sizePolicy().hasHeightForWidth())) + self.Icon.setScaledContents(1) + + NotifyWidgetUILayout.addMultiCellWidget(self.Icon,0,1,0,0) + + self.Text = QLabel(self,"Text") + + NotifyWidgetUILayout.addWidget(self.Text,1,1) + + self.Caption = QLabel(self,"Caption") + + NotifyWidgetUILayout.addWidget(self.Caption,0,1) + + self.languageChange() + + self.resize(QSize(151,60).expandedTo(self.minimumSizeHint())) + self.clearWState(Qt.WState_Polished) + + + def languageChange(self): + self.setCaption(i18n("Form3")) + self.Text.setText(QString.null) + self.Caption.setText(i18n("Powermanager:")) + + +if __name__ == "__main__": + appname = "" + description = "" + version = "" + + KCmdLineArgs.init (sys.argv, appname, description, version) + a = KApplication () + + QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()")) + w = NotifyWidget() + a.setMainWidget(w) + w.show() + a.exec_loop() diff --git a/powermanager/notify.ui b/powermanager/notify.ui new file mode 100644 index 0000000..ebf2950 --- /dev/null +++ b/powermanager/notify.ui @@ -0,0 +1,75 @@ + +NotifyWidget + + + NotifyWidgetUI + + + + 0 + 0 + 151 + 60 + + + + + 3 + 3 + 0 + 0 + + + + + 0 + 0 + + + + Form3 + + + + unnamed + + + + Icon + + + + 0 + 0 + 0 + 0 + + + + + + + true + + + + + Text + + + + + + + + Caption + + + <b>Powermanager:</b> + + + + +QPixmap + + diff --git a/powermanager/powermanage.py b/powermanager/powermanage.py new file mode 100644 index 0000000..db62e8c --- /dev/null +++ b/powermanager/powermanage.py @@ -0,0 +1,606 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +########################################################################### +# Copyright (C) 2006 by Sebastian Kügler +# +# +# Copyright: See COPYING file that comes with this distribution +# +########################################################################### +# An API for changing the powerstate of a notebook + +import dbus +import dbus.glib +import os, time +from dcopext import DCOPClient, DCOPApp # Used for kscreensaver +import xf86misc + +DEBUG = False + +def debug(msg): + """ Print debug message to terminal. """ + if DEBUG: + print msg + +# Default values for actions when battery runs out. +BATTERY_CRITICAL_MINUTES=5 + +# Only do an emergency suspend if charge level percentage is below ... +CHARGE_LEVEL_THRESHOLD = 10 + +isroot = os.environ["USER"] == "root" + +# Send suspend / hibernate commands to HAL or use Sx_COMMANDS +SUSPEND_USE_HAL = True + +# Show the cpu frequency widgets in the tooltip? +SHOW_CPUFREQ = True + + +# Command to initiate suspend-to-disk when not using HAL +S4_COMMAND = "/usr/local/bin/hibernate" +# Command to initiate suspend-to-ram when not using HAL +S3_COMMAND = "/usr/local/bin/s2ram" + +# Override isLaptop method +#IS_LAPTOP = True + +def _readValue(filename, line=0): + """ Reads a single value from the first line of a file. """ + fhandle = open(filename) + value = fhandle.readlines()[line][:-1] + fhandle.close() + return value + +class PowerManage: + """ Class providing low-level power managerment functionality. """ + + def __init__(self): + # (En|Dis)able using hdparm to set disk timeout + self.USE_HDPARM = True + # (En|Dis)able using laptop_mode to make the disk spin up less often + self.USE_LAPTOP_MODE = True + # (En|Dis)able using cpufreq to control cpu frequency scaling + self.USE_CPUFREQ = True + # (En|Dis)able using wireless adapter powermanagement (causes lag in network connection) + self.USE_WI_PM = True + # (En|Dis)able using display powermanagement + self.USE_DPMS = True + # (En|Dis)able using display brightness switching + self.USE_DISPLAY = True + # (En|Dis)able screensaver blankonly + self.SCREENSAVER_BLANKONLY = True + + + try: + xg = xf86misc.XF86Server() + self.xscreen = xg.getDefaultScreen() + except xf86misc.XF86Error: + print "Problem connecting to X server for idletime detection." + # Currently only used in the test method + self.display_dark = 0.5 + self.display_light = 1 + + # Some status initialisations + self.lowBatteryState = False + self.warningBatteryState = False + self.criticalBatteryState = False + + self.criticalBatteryState = False + self.lidClosedState = False + + # What does HAL support on this machine + self.hasBrightness = False + self.hasAC = False + self.hasLid = False + self.hasBattery = False + self.hasCpuFreqGovernors = False + + # Used to track if the previous check reported a battery to determine + # if we want to fire a notice "battery removed|plugged in" + self.wasOnBattery = False + self._initHAL() + self._initBrightness() + self._initBattery() + self._initAc() + self._initLid() + self._checkSuspend() + self._checkCpuCapabilities() + self._findDisks() + + def checkHAL(self): + """ Handle HAL and DBus restarts """ + try: + self.hal_manager.FindDeviceByCapability("") + except dbus.DBusException, e: + if str(e) == 'org.freedesktop.DBus.Error.Disconnected: Connection is closed' \ + or str(e) == 'org.freedesktop.DBus.Error.Disconnected: Connection was disconnected before a reply was received': + # DBus doesn't support on-the-fly restart + print "connection with DBus lost, please restart the display manager" + return + + if os.system("ps aux|grep [h]ald-runner") == 0: + print "connection with HAL lost, trying to reconnect" + self._initHAL() + self._initBrightness() + self._initBattery() + self._initAc() + self._initLid() + self._checkSuspend() + self._checkCpuCapabilities() + else: + print "HAL is not running" + + def isLaptop(self): + """ Detect if system is laptop. """ + try: + return IS_LAPTOP + except NameError: + pass + self.computerObject = self.bus.get_object("org.freedesktop.Hal", + u'/org/freedesktop/Hal/devices/computer') + properties = self.computerObject.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + # formfactor sometimes (ppc) also reports "unknown" for laptops + # workaround: consider laptop anything with primary battery (see LP #64053) + return properties["system.formfactor"] == "laptop" or self.hasBattery + + def _findDisks(self): + """ Scan /sys/block for non-removable and non-ramdisks, used for hdparm actions, + currently not implemented in the powermanager frontend. """ + self.disks = [] + blk_path = "/sys/block/" + for d in os.listdir(blk_path): + # No RAM disks, no DM-RAID + if d.startswith("ram") or d.startswith("dm"): + continue + fhandle = open(blk_path+d+"/removable") + if fhandle.readlines()[0][:-1] == "0": + self.disks.append(d) + debug("Detected disks: "+" ".join(self.disks)) + + def onBattery(self): + """ Find out if we're on AC or on battery using HAL. """ + if not self.hasAC: + print "No AC adapter found - assume that we are on batteries." + return False + properties = self.acObject.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + if properties.has_key("ac_adapter.present"): + return not properties['ac_adapter.present'] + else: + print "Error: ac_adapter has no property \"present\"" + return False + + def _initBattery(self): + """ Looks for a battery in HAL. """ + batteryDevices = self.hal_manager.FindDeviceByCapability("battery") + self.batteries = {} + self.batteryIsPresent = {} + + numBatt = 0 + for batt in batteryDevices: + battObj = self.bus.get_object("org.freedesktop.Hal", batt) + properties = battObj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + if properties['battery.type'] != "primary": + continue + self.batteries[numBatt] = battObj + self.batteryIsPresent[numBatt] = properties['battery.present'] + numBatt += 1 + + if numBatt > 0: + self.hasBattery = True + else: + self.hasBattery = False + print "No battery found." + + def getBatteryState(self,batt): + """ Read battery status from HAL and return + (battery state, charge percentage, remaining seconds). + """ + try: + properties = self.batteries[batt].GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + except dbus.DBusException: + print "problem getting battery state from dbus." + return "not present", 0, 0, 0, 0, 0 + + if not properties['battery.present']: + return "not present", 0, 0, 0, 0, 0 + else: + current = full = level = remain = rate = 0 + if properties.has_key("battery.charge_level.current"): + current = properties["battery.charge_level.current"] + if properties.has_key("battery.charge_level.last_full"): + full = properties["battery.charge_level.last_full"] + + if properties["battery.rechargeable.is_charging"]: + state = "charging" + elif properties["battery.rechargeable.is_discharging"]: + if self.onBattery(): + state = "discharging" + else: + state = "charged" + elif not properties["battery.rechargeable.is_discharging"] \ + and not properties["battery.rechargeable.is_charging"]: + if current == 0: + state = "empty" + else: + state = "charged" + else: + print "Unknown battery state ..." + + # Sometimes, HAL doesn't report the percentage, but we can compute that ourselves anyway + if properties.has_key("battery.charge_level.percentage"): + level = properties["battery.charge_level.percentage"] + elif current > 0 and full > 0: + level = current / full + + if state in ("charging","discharging"): + if properties.has_key("battery.remaining_time"): + remain = properties["battery.remaining_time"] + if properties.has_key("battery.charge_level.rate"): + rate = properties["battery.charge_level.rate"] + + return state, level, remain, rate, current, full + + def showInfo(self): + """ Outputs some random information to show that it does not work yet. """ + print "OnBattery:", self.onBattery() + print "CPUs:", len(self.cpus) + + def _initHAL(self): + """ Initialise HAL client to be used later. """ + self.bus = dbus.SystemBus() + hal_manager_obj = self.bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager") + self.hal_manager = dbus.Interface(hal_manager_obj, "org.freedesktop.Hal.Manager") + + def _initLid(self): + """ Find out if there's a Lid device. """ + lidDevice = self.hal_manager.FindDeviceStringMatch("button.type", "lid") + if len(lidDevice) >= 1: + self.hasLid = True + self.lidObject = self.bus.get_object("org.freedesktop.Hal" ,lidDevice[0]) + + def _initAc(self): + """ Search HAL for detecting if power is plugged in. """ + acDevice = self.hal_manager.FindDeviceByCapability("ac_adapter") + if len(acDevice) >= 1: + self.hasAC = True + self.acObject = self.bus.get_object("org.freedesktop.Hal" ,acDevice[0]) + + def _checkSuspend(self): + """ Ask HAL whether we can suspend / hibernate. """ + if SUSPEND_USE_HAL: + self.computerObject = self.bus.get_object("org.freedesktop.Hal", + u'/org/freedesktop/Hal/devices/computer') + properties = self.computerObject.GetAllProperties( + dbus_interface="org.freedesktop.Hal.Device") + self.canSuspend = properties["power_management.can_suspend"] + self.canHibernate = properties["power_management.can_hibernate"] + else: + self.canSuspend = self.canHibernate = True + + def _initBrightness(self): + """ Search HAL for a screen with brightness controls.""" + + brightnessDevice = self.hal_manager.FindDeviceByCapability("laptop_panel") + + if len(brightnessDevice) >= 1: + self.hasBrightness = True + self.brightnessObject = self.bus.get_object("org.freedesktop.Hal", brightnessDevice[0]) + self.brightness_properties = self.brightnessObject.GetAllProperties( + dbus_interface="org.freedesktop.Hal.Device") + try: + self.brightness_levels = self.brightness_properties[u'laptop_panel.num_levels'] + except KeyError,e: + self.hasBrightness = False + return 0 # Really don't know what to do here, but don't crash in any case. + try: + self.old_b = self.brightness_levels[-1] # Setting cached brightness value to brightest + except TypeError,e: + return 0 # Really don't know what to do here, but don't crash in any case. + + def getBrightness(self): + """ Read brightness from HAL. """ + if not self.hasBrightness: + debug("Brightness setting not supported.") + return + try: + b = self.brightnessObject.GetBrightness( + dbus_interface="org.freedesktop.Hal.Device.LaptopPanel") + except dbus.DBusException, e: + # Sometimes, right after resume, the HAL call + # fails, in that case, we return the last value + # and hope that it goes well next time. + print "Warning: in getBrightness(): ", e + # try and return the old brightness setting, but don't die in any case: + try: + return self.old_b + except AttributeError, errmsg: + return + self.old_b = b + return b + + def adjustBrightness(self, level): + """ Adjust the brightness via HAL. """ + if not self.hasBrightness: + debug("Brightness setting not supported.") + return + try: + self.brightnessObject.SetBrightness(level, + dbus_interface="org.freedesktop.Hal.Device.LaptopPanel") + except dbus.DBusException, e: + print e + + def _checkCpuCapabilities(self): + """ Find out the number of CPUs / cores, check which governors are avaible.""" + cpufreq_dir = "/sys/devices/system/cpu" + self.cpus = [] + for cpu in os.listdir(cpufreq_dir): + if cpu.startswith('cpu') and cpu != 'cpuidle': + self.cpus.append(cpu) + self.cpus.sort() + + # Map our policies to cpufreq governors. + self.cpu_policy = {} + self.cpu_policy['dynamic/ac'] = [] + self.cpu_policy['dynamic/battery'] = [] + self.cpu_policy['powersave'] = [] + self.cpu_policy['performance'] = [] + + try: + comp_obj = self.bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/devices/computer') + self.cpufreq = dbus.Interface(comp_obj, 'org.freedesktop.Hal.Device.CPUFreq') + self.governor_available = self.cpufreq.GetCPUFreqAvailableGovernors() + except dbus.DBusException: + return + self.hasCpuFreqGovernors = True + + if 'ondemand' in self.governor_available: + self.cpu_policy['dynamic/ac'].append('ondemand') + self.cpu_policy['dynamic/battery'].append('ondemand') + if 'conservative' in self.governor_available: + self.cpu_policy['dynamic/ac'].append('conservative') + self.cpu_policy['dynamic/battery'].insert(0,'conservative') + if 'userspace' in self.governor_available: + self.cpu_policy['dynamic/ac'].append('userspace') + self.cpu_policy['dynamic/battery'].append('userspace') + if 'powersave' in self.governor_available: + self.cpu_policy['powersave'].append('powersave') + if 'performance' in self.governor_available: + self.cpu_policy['performance'].append('performance') + + def getSupportedCpuPolicies(self): + """ Report a list of supported CPU policies """ + policies = [] + if len(self.cpu_policy['dynamic/ac']) > 0: + policies.append('dynamic') + if len(self.cpu_policy['powersave']) > 0: + policies.append('powersave') + if len(self.cpu_policy['performance']) > 0: + policies.append('performance') + return policies + + def getCpuPolicy(self): + """ Translate current CPU frequency governor into policy """ + if not self.USE_CPUFREQ or not self.hasCpuFreqGovernors: + return "" + gov = self.cpufreq.GetCPUFreqGovernor() + for policy in self.cpu_policy.keys(): + if gov in self.cpu_policy[policy]: + return policy.split('/')[0] # strip ac or battery off + return gov ## return as-is - no conversion + + def setCpuPolicy(self,policy): + """ Using cpufreq governors. Mode is powersave, dynamic or performance. We're assuming that + the available governors are the same for all CPUs. This method changes the cpufreq + governor on all CPUs to a certain policy.""" + if not self.USE_CPUFREQ or not self.hasCpuFreqGovernors: + return False + + if policy == "dynamic": + if self.onBattery(): + policy = "dynamic/battery" + else: + policy = "dynamic/ac" + + for gov in self.cpu_policy[policy]: + try: + self.cpufreq.SetCPUFreqGovernor(gov) + return True + except dbus.DBusException: + pass + return False # no of governor worked + + def cpuIsOnline(self,cpu): + """ Check if cpu is online. CPU0 is always online, CPU1 might be unplugged. Since + /sys/devices/system/cpu/$cpu/cpufreq is not readable for normal users, we just + check for the cpufreq subdir (which is where it's really needed anyway). + """ + if cpu == "cpu0": return True + else: return os.path.isdir("/sys/devices/system/cpu/"+cpu+"/cpufreq") + + def getCpuState(self,cpu): + """ Reads the status of a CPU from /sys. """ + state = {} + state['online'] = self.cpuIsOnline(cpu) + if not state['online']: + debug("getCpuState: "+cpu+" is offline") + return state + try: + state['cpu'] = cpu + state['cur'] = int(_readValue("/sys/devices/system/cpu/"+cpu+"/cpufreq/scaling_cur_freq"))/1000 + state['governor'] = _readValue("/sys/devices/system/cpu/"+cpu+"/cpufreq/scaling_governor") + state['driver'] = _readValue("/sys/devices/system/cpu/"+cpu+"/cpufreq/scaling_driver") + state['steps'] = [] + freqs = _readValue("/sys/devices/system/cpu/"+cpu+"/cpufreq/scaling_available_frequencies") + except IOError: + # CPUFREQ has gone away, let's disable it. + state['online'] = False + return state + for v in freqs.split(): + state['steps'].append(int(v)/1000) + state['max'] = max(state['steps']) + state['min'] = min(state['steps']) + debug(state) + return state + + def getLidClosedState(self): + """ Returns True if the lid is currently closed, or False if it isn't. """ + try: + properties = self.lidObject.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") + return properties["button.state.value"] + except (KeyError, dbus.DBusException): + return False + + def setPowerSave(self, state): + # No SetPowerSave in Ubuntu's HAL + try: + self.computerObject.SetPowerSave(state, + dbus_interface="org.freedesktop.Hal.Device.SystemPowerManagement") + except dbus.DBusException, e: + print "Warning: While setting SystemPowerManagement to ", state, ": ", + print e + + def blankScreen(self): + """ Call dpms to switch off the screen immediately. """ + os.system('xset dpms force standby') + + def setScreensaverBlankOnly(self,blankonly): + """ Switches a screensaver to blankonly, so cpu hungry screensavers will not drain the poor + battery.""" + # create a new DCOP-Client: + client = DCOPClient() + # connect the client to the local DCOP-server: + client.attach() + # create a DCOP-Application-Object to talk to amarok: + kdesktop = DCOPApp('kdesktop', client) + # call a DCOP-function: + ok, foo = kdesktop.KScreensaverIface.setBlankOnly(blankonly) + if not ok: + debug("Failed to set kdesktop screensaver to blankonly.") + return False + return True + + def getIdleSeconds(self): + """ Get idle seconds from X server. """ + return self.xscreen.getIdleSeconds() + + def resetIdleSeconds(self): + """ Reset idle seconds. """ + return self.xscreen.resetIdleSeconds() + + def test(self): + """ Try all kinds of stuff and see what breaks.""" + print "Trying to adjust brightness ..." + bright = self.getBrightness() + self.adjustBrightness(2) + time.sleep(1) + self.adjustBrightness(bright) + print " ... OK." + + if self.USE_CPUFREQ: + print "Reading speeds from cpufreq..." + for cpu in self.cpus: + print self.getCpuState(cpu) + print "Report supported cpufreq policies..." + for policy in self.cpu_policy.keys(): + print "Policy:", policy, "=", self.cpu_policy[policy] + + print "Trying all cpufreq policies ..." + orig_pol = self.getCpuPolicy() + for pol in self.cpu_policy.keys(): + print ". ", pol + self.setCpuPolicy(pol) + self.setCpuPolicy(orig_pol) + print "... OK." + else: + print "Skipping CPUFREQ: USE_CPUFREQ = False" + + if self.SCREENSAVER_BLANKONLY: + if self.setScreensaverBlankOnly(True): + debug("Manipulating screensaver seems to work well.") + else: + debug("Manipulating screensaver seems broken.") + + if isroot: + print "Trying to use Disk powermanagement and laptop_mode" + self.setDiskPM(True) + time.sleep(1) + self.setDiskPM(False) + print "...OK" + else: + print "Skipping DiskPM, not root." + + if self.hasLid: + if self.getLidClosedState(): + print "Lid is closed." + else: + print "Lid is currently open." + + def setDiskPM(self,on=True): + """ Switches on laptop_mode and sets disks to advanced powermanagement.""" + if self.USE_LAPTOP_MODE: + # Check if laptop_mode exists: + laptop_mode = "/proc/sys/vm/laptop_mode" + if not os.path.isfile(laptop_mode): + self.USE_LAPTOP_MODE = False + debug("Laptop mode not supported, no "+laptop_mode) + else: + fhandle = open(laptop_mode,"w") + if on: val = 1 + else: val = 0 + fhandle.write(str(val)) + fhandle.close() + + if self.USE_HDPARM: + # Set disks to advanced PM + for disk in self.disks: + if on: + # Switch on advanced powermanagement + cmd = "hdparm -B1 /dev/"+disk+" > /dev/null" + else: + # Switch off advanced powermanagement + cmd = "hdparm -B255 /dev/"+disk+" > /dev/null" + if os.system(cmd) != 0: + self.USE_HDPARM = False + print "Switching advanced powermanagement failed, not using hdparm anymore" + + def suspend(self): + """ Run a suspend command, either via HAL or script. """ + if SUSPEND_USE_HAL: + try: + self.computerObject.Suspend(0, dbus_interface="org.freedesktop.Hal.Device.SystemPowerManagement") + except dbus.DBusException: + pass #we get a DBusException: No reply within specified time + else: + self._sleepMode(S3_COMMAND) + + def hibernate(self): + """ Implements suspend to disk (S4). """ + if SUSPEND_USE_HAL: + try: + self.computerObject.Hibernate(dbus_interface="org.freedesktop.Hal.Device.SystemPowerManagement") + except dbus.DBusException: + pass #we get a DBusException: No reply within specified time + else: + self._sleepMode(S4_COMMAND) + + def _sleepMode(self, command): + """ Send the system into S3 or S4 not using HAL. """ + debug("Initiating a sleep cycle") + if os.system(command) != 0: + print "sleepmode failed. ("+command+")" + return False + debug("Everything is dandy") + return True + + def shutdown(self): + """ Shutdown the system via HAL. """ + self.computerObject.Shutdown(dbus_interface="org.freedesktop.Hal.Device.SystemPowerManagement") + + +if __name__ == "__main__": + """ Run some tests, used for debugging.""" + pman = PowerManage() + pman.showInfo() + pman.test() + diff --git a/powermanager/powermanager_ui.ui b/powermanager/powermanager_ui.ui new file mode 100644 index 0000000..1fc9cfb --- /dev/null +++ b/powermanager/powermanager_ui.ui @@ -0,0 +1,924 @@ + +Powermanager + + + Powermanager + + + + 0 + 0 + 568 + 600 + + + + Powermanager + + + true + + + + unnamed + + + 11 + + + 6 + + + + tabWidget + + + + Widget8 + + + Power Schemes + + + + unnamed + + + + PerformanceGroup + + + Performance + + + + unnamed + + + + layout4 + + + + unnamed + + + + textLabel1 + + + Brightness + + + + + spacer3 + + + Horizontal + + + Expanding + + + + 551 + 21 + + + + + + + + PerrformanceBrightnessSlider + + + 10 + + + Horizontal + + + Both + + + Control the brightness setting in the active scheme + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + layout6 + + + + unnamed + + + + textLabel2 + + + When system is inactive for + + + + + PerformanceMinutesSpin + + + + + textLabel3 + + + minutes ... + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 311 + 21 + + + + + + + + layout3 + + + + unnamed + + + + PerformanceNothing + + + Do nothing + + + + + PerformanceFade + + + Fade display + + + + + PerformanceSleepmode + + + Initiate sleepmode + + + + + + Hibernate (S4) + + + + + Standby (S3) + + + + PerformanceSleepmodeCombo + + + + + + + + + AutomaticGroup + + + Automatic + + + + unnamed + + + + layout4_3 + + + + unnamed + + + + textLabel1_3 + + + Brightness + + + + + spacer3_3 + + + Horizontal + + + Expanding + + + + 551 + 21 + + + + + + + + AutomaticBrightnessSlider + + + 10 + + + Horizontal + + + Both + + + Control the brightness setting in the active scheme + + + + + line1_3 + + + HLine + + + Sunken + + + Horizontal + + + + + layout6_3 + + + + unnamed + + + + textLabel2_3 + + + When system is inactive for + + + + + AutomaticMinutesSpin + + + + + textLabel3_3 + + + minutes ... + + + + + spacer4_3 + + + Horizontal + + + Expanding + + + + 311 + 21 + + + + + + + + layout3_3 + + + + unnamed + + + + AutomaticNothing + + + Do nothing + + + + + AutomaticFade + + + Fade display + + + + + AutomaticSleepmode + + + Initiate sleepmode + + + + + + Hibernate (S4) + + + + + Standby (S3) + + + + AutomaticSleepmodeCombo + + + + + + + + + PowersaveGroup + + + Powersave + + + + unnamed + + + + layout4_2 + + + + unnamed + + + + textLabel1_2 + + + Brightness + + + + + spacer3_2 + + + Horizontal + + + Expanding + + + + 551 + 21 + + + + + + + + PowersaveBrightnessSlider + + + 10 + + + Horizontal + + + Both + + + Control the brightness setting in the active scheme + + + + + line1_2 + + + HLine + + + Sunken + + + Horizontal + + + + + layout6_2 + + + + unnamed + + + + textLabel2_2 + + + When system is inactive for + + + + + PowersaveMinutesSpin + + + + + textLabel3_2 + + + minutes ... + + + + + spacer4_2 + + + Horizontal + + + Expanding + + + + 311 + 21 + + + + + + + + layout3_2 + + + + unnamed + + + + PowersaveNothing + + + Do nothing + + + + + PowersaveFade + + + Fade display + + + + + PowersaveSleepmode + + + Initiate sleepmode + + + + + + Hibernate (S4) + + + + + Standby (S3) + + + + PowersaveSleepmodeCombo + + + + + + + + + + + Widget9 + + + Events + + + + unnamed + + + + spacer9 + + + Vertical + + + Expanding + + + + 520 + 260 + + + + + + spacer10 + + + Horizontal + + + Expanding + + + + 110 + 80 + + + + + + layout17 + + + + unnamed + + + + + Do nothing + + + + + Switch to Performance + + + + + Switch to Automatic + + + + + Switch to Powersave + + + + + Suspend (S3) + + + + + Hibernate (S4) + + + + ACPluggedinCombo + + + + + layout16 + + + + unnamed + + + + textLabel7 + + + When battery power is below + + + + + BatteryLowPercentage + + + + + textLabel8 + + + % + + + + + + + textLabel4 + + + When AC adapter is removed + + + + + + Do nothing + + + + + Switch to Performance + + + + + Switch to Automatic + + + + + Switch to Powersave + + + + + Suspend (S3) + + + + + Hibernate (S4) + + + + BatteryLowCombo + + + + + textLabel6 + + + When AC adapter is plugged in + + + + + + Do nothing + + + + + Switch to Performance + + + + + Switch to Automatic + + + + + Switch to Powersave + + + + + Suspend (S3) + + + + + Hibernate (S4) + + + + LidCloseCombo + + + + + textLabel5 + + + When the lid is closed + + + + + + Do nothing + + + + + Switch to Performance + + + + + Switch to Automatic + + + + + Switch to Powersave + + + + + Suspend (S3) + + + + + Hibernate (S4) + + + + ACRemovedCombo + + + + + + + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + buttonHelp + + + &Help + + + F1 + + + true + + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + buttonCancel + + + &Cancel + + + + + + true + + + + + + + + + buttonOk + clicked() + Powermanager + accept() + + + buttonCancel + clicked() + Powermanager + reject() + + + + diff --git a/powermanager/recompile-ui-files b/powermanager/recompile-ui-files new file mode 100644 index 0000000..170797c --- /dev/null +++ b/powermanager/recompile-ui-files @@ -0,0 +1,6 @@ +#!/bin/bash +pyuic -tr i18n tooltip.ui -o tooltip.py +pyuic -tr i18n guidance_power_manager_ui.ui -o guidance_power_manager_ui.py +pyuic -tr i18n notify.ui -o notify.py + + diff --git a/powermanager/tooltip.py b/powermanager/tooltip.py new file mode 100644 index 0000000..37b62f3 --- /dev/null +++ b/powermanager/tooltip.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'tooltip.ui' +# +# Created: Thu Apr 10 00:50:39 2008 +# by: The PyQt User Interface Compiler (pyuic) 3.17.4 +# +# WARNING! All changes made in this file will be lost! + + +import sys +from qt import * +from kdecore import KCmdLineArgs, KApplication +from kdecore import i18n +from kdeui import * + +from kdeui import * + +class ToolTip(QWidget): + def __init__(self,parent = None,name = None,fl = 0): + QWidget.__init__(self,parent,name,fl) + + if not name: + self.setName("ToolTip") + + self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.MinimumExpanding,0,0,self.sizePolicy().hasHeightForWidth())) + self.setMinimumSize(QSize(240,0)) + self.setBaseSize(QSize(200,0)) + + ToolTipLayout = QVBoxLayout(self,0,6,"ToolTipLayout") + + self.languageChange() + + self.resize(QSize(300,80).expandedTo(self.minimumSizeHint())) + self.clearWState(Qt.WState_Polished) + + + def languageChange(self): + self.setCaption(i18n("Form1")) + + + def ToolTip_destroyed(self,a0): + print "ToolTip.ToolTip_destroyed(QObject*): Not implemented yet" + +if __name__ == "__main__": + appname = "" + description = "" + version = "" + + KCmdLineArgs.init (sys.argv, appname, description, version) + a = KApplication () + + QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()")) + w = ToolTip() + a.setMainWidget(w) + w.show() + a.exec_loop() diff --git a/powermanager/tooltip.ui b/powermanager/tooltip.ui new file mode 100644 index 0000000..8d7640d --- /dev/null +++ b/powermanager/tooltip.ui @@ -0,0 +1,53 @@ + +ToolTip +Python:from kdeui import * + + + ToolTip + + + + 0 + 0 + 300 + 80 + + + + + 3 + 3 + 0 + 0 + + + + + 240 + 0 + + + + + 200 + 0 + + + + Form1 + + + + unnamed + + + 0 + + + + + ToolTip_destroyed( QObject * ) + +QPixmap + + diff --git a/review_guidance_malaga.pdf b/review_guidance_malaga.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a4ef5ec6a0f4c54f324ccdc4856c4a6c608f2547 GIT binary patch literal 393561 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

)P7dZzhf* zds@LRvN%*40z5F!(ujzNwJrc2XNr8>Otn=F`E+)xfoMG8#>vUaqnV<*hG>x~G4A9H zogbO;L;Z0|PWXc?$K$vS`B2}@EG!_qH&YW5APLPKqfd3$eJ%vZ$@4pW9-L2c^Fw)E z`YOGhN5MI&#l>c+1S-1oo8EN0{zxp zs`^^d_suXgG_)GsL^TlbGgBFerq|_9sBT74ar^#O{rS&!6tkh;RHNO>>+{2m&UYX% zj1qLU1HyYRHF+NF9!{dayS;67KYMz4bOBa{;fMZv=M;QjnsqR^VJw5hG)UzhPlM{d z@nHcHhD@uwDXa?L9|GQAAJ*2^*jQPEf`ct4vgH~+;o~poOQ6z}EK;pVpcFrgss5q@ z0~UH{akIc`#d@aw8Oh78$-)I2b@}Wc2r%>Fdj5n|%55 z<<@=i{D%Iu>%HCU=BKy0`Tkf2mr<;g!`5tx3JeU)E(&h>-Up9e2Jng;4I;q9!GTPV zc$B6+nNX1Czh7u{s?uI>NNsA0i9rWyN=m}z6CS(ZthKtW)LEb=Re=xS`Topi=GPeC zMLT34pUXbd+()U!rKK-MAF^6nc*<)f1;Hcxzdc%5w#%e2b#``w<2Q#iG>C#GkxD8W zK(9ip8T_TDyqw`$lv;4UBchNt9>SlUlEK=4m!qcsj1?jXNfD72i~zFGc#2&FjbTb z%;U1o36@T^nol`J=Yvm_M<1o1Y7TJHLdIRuTszOa3%X6eT}9ULsen6fVJ%)5Hsrq7 zMT0_}6C9sfwsUeqy5m%X1tkl_j+Yhi(G^j}kw0u$4NjDW^jH%gTQYJwBmEuj`C=Q? zX_X|MslVi8|0C}x-+pE3x%O0~0oA{L9+xM~IKdit_vdPTtWWyV?l@J^i|-3cO+u^- z9UUF50aB@{e}SSj|f?z2#}yn+K>t1{L z1d1fj$cZcDD)%&HKjOfKekB39N|f@<+wIStbtb{g5Xse)Wy<>2UthS!6txdKaGjxX z?7n&9L#sazO4ua8gYrz!n{j!5sVI-tRI=$Z600}j9dH=x?0q}qSie7(+-o1U6&R1g zcedgYtEN!-=c9%&lN#!UP&xm_sk4DtZYvi55Wu3BeL?FYJ~qBAOBe_bb#7h zS&odEp0k_ZOO-#)kZ0T5SLYdx(tD%WdDf^blQdfRh?Sz`9BpN9UqMG%)>@@Y1-}`J zKSpmbiiCg+X9Vu~+BR|=;1?Fwbx>6)S-cb3`lg>|QiN zW%o|NdHWHbWKuyOrH?`U>ut{VUQXf#k&L2xs|P{@5jjfMrT^_CB!D?m8FPSGG^*ct zSF?0s=_T|cWz0sE^PM;;<5}&TNayi+@f1rh!Msua^%vS&gi88A=2Fj9D^1uB6U!R# z?HXAWtnrTUuSn@MFtbYKy@;wN93hjpmHNy=llf}D6)m2Kk&Nfu&J;DR2a&Oxz^YfUz(iO>aLqL;XX7noJH&J+D-&p+XN5_y8iD;+(3nKcxR zOmrk1TZW<~U&-<|+xD(Y29LgWhcf;j?U<=*=sjE=2Caq(xO!YUZmJl$-R(`qI~RsA zGn>1br8$uQAWu81>qx8?{8YA&2H%r^@eAprGe{S-EmJt z4%}__YrK}9NbbQ@LrYWhM}1%*1nuX4nOL#gRuA;IXyYcVs(yFU_-~nWWf}_V}!0 zSQ&-6U}B3y20sXaDf$tp`fIvO7(bi&L`F*p9>mgFV_LF>FyYb9Y;${i*XDk5P5=-y zQ;xVJ;0(C@5)~`>v^ce-c9wwnI?uoa3^ZK+CP+K7y6S}dv#&1>DR9zk%IJlsga1Zd#Tf9aNZB)f&dj#TsL2n zA?04I?5fZ?DlPe;W4HNU31BX(m4#hATND70wSrf#@w_Y2#R0yq-~z|zEckActh6*d zEWqZS$^yYno1pIe>vp+(O-)URp&}9qOa*PF<{~%DiohagJo4IL961&uy_~|omf2e{!o$N8 zjlIade{f)7Yr9$?g;ChFu{5n*;=0`zQQgP-`Lm;=qvB~bnVqV^_tk@N7>#nx+$TD+ zJ=_h=+F8en1e4o%>m1#~CUX|&Q4lEnQ}pU6s^6PxZKuRHt%vaa_m0+T6;DrYQcYpu z51ZS|4K{$PC}T-(;8hHz4D!OVpV_%gqaS5Q!7??@Q-xMEz~^!k;_hM)>_|V0ViOu~ zqZ&`+s%wgNo8r2=yF&~oY#*V z3Cd04+v@`m;b*|g3@ZNT2F3xAjQW`iRK;Vm3j+pEm@-+kP#AVpoCyslDQMF89}W@7 z{g+jORXOKhy!Na{X30}(rP;-elnZi>W+bwOjIl|yhX-?+83LDb`Kw6S6_Oo5;Tl*m zFM>tj`T4nx0nRQB;}91SgIbMJzIc!SnsHH z0r5E#uVP?j)XJ$rpMsZ5wSoQAE;TgV?vK}dU`99%soyQi#l@w&e}(nW2Mg6ffvo*K zzB}E@SYW#gw})zI?wlFO$whxDgA3@Dm6a5NLU8luKhhEtq3p7G-6Bn36~EqX;ff&@ z0(J;clS|>oDz)mw4Gg+`9?q4Nlqgi_lJ3t|nyhAiy*}MV#l)D66og|tG{c8X!=VBD z2f)^N^X%{KxgCsWNyHlB3l!f$UiuBA{(k6q&3=YipyB1k595XZdVe@YUKrP{NT;d* zMAH=(Bqw4py#_B#4|;jE-bzJEDx#s`E|tYZa{q8DpOKM~6axsR_G)nvP&4XR+nScm zygcsjZ+=?Z18nb`MgH9rA4U8id$VCfpP?oGgn#Xs;`)4>7BHA_@I`O}^5z;bI4H=F zMO@viT2d6)5g4Kq`_C>?IFXaa>D&ZEQ>Z8?FjG^*qC2W)MF3{hBff(*_5|_cpB(;x zlc(TMRR>eCJ@K!`bv??65m@DX5el$8xJk(EUP zYkZ2;SC-Bz!;(l_z)tGlG3Oi^6fgmZNS8h@?7xqed&@i^9iF~=!w%@RlBd}bd|oDV z?Aac1nQ!)alzR8ZbYB`_W?-e|=BDA`NLnS6k(LfB2?y-ZZ@n4(<1KNg^hQvu_erYz z%}zV2rkoDzx^zjLle1r}ethk#`T1Lzc;jcnfdI4jlYSeq@4ogWZBwe>L2v~SI@SQ^ zvwOw6*93`h=FFW$=Ue>m-yhOsVLm~oLx6~c{Gm~@$tbyI5@aTV<7yk4SA0r zbA$u`Vi>^%%~@h9H#Yh$KM4|dqiz1oWT^c{x);FhS_qseA2ARFO>Df{7nZf0^+SS~ zMFJ%RNlCtG>gyBPG8zfopDxuUP%9|Qb&D=~_+ECyUUWRH{|=c(10J; zuUB8IOVrA?(@Ns!o2T4_N>n0P0k3OZ!@Jv~8Sp(>YjFXstYnPW015f*SDVoqiK?&K z{a<-M$LyA+m}K#JPzfo)+DM9u_Hu1%_DWA)M7YGlt_YJ87LLWcFvtJ^h=|B22yq`u z#&w(bcK6SG&$hONh=2yZk7Y|52bCS>jdL?s@ejl)?edvzP7i?rnwB&S10UcbA|jMa zZo7kFU4cg@C$O-vY|BLf;B(*CV2z21$>6jb{u3dkqqCx3q0M2xnyP;?qaftw>`cb< zTY)4#J3G6igjOu|KWNW4MO0MOehXMipWDMPg5`*Mq4meDyIf3o8b&YeRQV>pp;sV9 zVlDz-|4DkERgTSeeHGTtS0JNR5&fA z$w5=o5-hjH#l>}X0gm5lh*sLY*lg*-QHeW!ADuNcH0W~1A-HY8o`=ZE4a8*$qWc{1 z=Bqh4#RpUG>fyol`7;S>I0~Wkt2+1GW(YjS>FFuSO;0G|H}b#cR_V4(H`pwIW%7sF zy^$2Pg-|Biw|#CCqk%(Q190xZ7)=2vR(A8A^d-=0sb7PL(!eYJ_@3b|-QoD|z)`m4 zIKlJEXtjlJ%7VWqRu7)nYw6+*l33GTii|RrScod;?BV)4&xN6B|DeSv_hXyPVmG6H zd#dR)&t$n4+x3|aXZ%_~-y^Y;By zi7zEP$U9Bqjy3Ao4ts8dUpt#KR{7agIBJ$`ThrK>>@}cbFaNS2zv4O8N=TQP7nam**XtiJjF9{3yA{3&hU%a$;Z*$pVmH zUtK{sejzqQC@Ls0H8m9$A&dL4M}jfW_HeN=F+MIYEsFpL_o4ZV*C52tDIS=WN^mNv z^I_cwoCPB0_pEMe;u-=6*KjwKv_Jm-{@@I3HLvmNL6L{~#d-&$cKrv-a-Ak1FSN$# zYzanvo#{}#$A({uX-}!cUm~W;_W85olN2Xd8EU71Y1k+hy*iDvtQF{wG&k_o!|O+R zao&9YwTZTzH}Ebgdj#^mjjZ}0l7#!s!;6Gp^&fF12CCunIR8ym_VXu66O}0$1CL>D z{Er_WghwVOU`@NR(XUO6jlqQ2&9E{T%mAXcDJuMbBqV8{T)vcbv@2)#yJ= zWc>1-=?{}l7bt4?-uRFoTd)*UcMM^S%lt%g=ZvSEkZ_@OMs`54z4mD81`|0#;YmZm zu&CcutKlABA2z@VIfcPMmFi(|Hud`Q@*5#G!vOd+#%dt%a9Y~l9uIOLRX3K!2y}a# z%0YP&6Q)?wnd(>lnZjZ&fYCvgtcFAGWStwQATU8L)2Qm9I@#gFdyaO7mktw31TT1R;UeJ4pfmnkq zu36xG*5UBb5JY)gY&RS0kuj#QQ=B7!E!|^9OL+^>uj{wuZThjzze}%Xt!$ua@0SY$ z4QuDdw=cUl6civX$?3wfxa`h1Iu}Gbl{cq7LLco0d&n|l&qEx#vo#4Kg84rq{t#O= zs(2$YI$+iKI0eedbZV{l@7+&k6nc5+5_+21i(Iy#Ot0!6XGwG{MFpwCiR(2HzttjE z9)8|gs=dJir8rNYMB`yY@H?!%)|f5WOP2LUKM z{OiLxc`Wis^MnGHFfd|mkOeaXJkY+7LlPE_LfZAs^H+9&#XL7E2dY?JS z+XT4icPu$Q>w}(AvrWyMIslP!Jx;bWX#@({Fe|6rk0cD|up>KT%jY)p?ha=)$WjU6 z&zrf+2XNXqj0c*HG(6bo`1dE{DiVYjFd>D*tcWf*GS`R(=KGl8lGF0sYb$=2vBkxQ ztvX+7WVnHZT~Cg^xiZPH%oTi&R}17RbApJI#!Zqu_kV`i>SBs=pbPhRlTA&rC=2%s zf9+Wuv=5H^LGT!M{g{nenSg>}3D(GOM%)!^aYrvTmG_-h18->+?WSY2N&ws== z{BXTb{4IqojjgqfBFjl=p|j=feU;aH52W@9z(jxGpEMUZQlUrn4b~txQa2KNJD#au zrrmgeNr_9(<5mfxLJkZCR5)e0_^&5e>aeeaJrsD|TSslDMCYqz{74L?^6+Cd>t*>q z84?5;5)b-}NktkGTz%igkMgW}k|}nKQh;LW@dRBV* z=%@rG$j0E1=gBRA{(SM zWcEs>R>~=-(e>`VlcW20!Va^yV>X#`=2ugT+v1T~y3bnbpRf`LKgHjt zXcTnpkVi2tq?EVWcRZ5(Gt&=~k!*A%)f_Ra zG&m4?ys0jL2rpkB2rflcX*+azP4tSQOrY}cr`_*#zXMmMTzb|cgtAj@_7Bgo(zCS~ z;>;gme(86j)mYvwO;N#0j0rZ3zIYT5{;k#aIGuJy5kLP@qjT~$1GRjKRA2u76Hr#i z>#Uql!356h?WQ_ZZ?r#me}UboOA$fkSv|RJ= zZ5(xD*QLwmJAjtY@{C=v{EG+Rvt#gDTo(spmBs`Hi5-v+7Y2QIQ;CVl-We26pS<9B zNpW$3FH12xPgtqS_BQUI`@#@1-#H=wme0QLWHhD3`o;Cgv-tc4f!2|SmXKSG=vUSK zo8Pmq)H(GhP3xA2PiZ4rt+7isR&^;;a7aDi($)-dX|r+fb?&&$RDT&7np63+Z`F-b z(m{S&SycsB-8a3qb~=yMlA4rNSD+LHI#}uW`T2nhf{_#@B_-tI(Vh=$9>~bZC7s<% z95(a9L+5L45@KR;vL&j^O;!A#ZM*zmjZI9JSdSba_+6cykHNJfFpok)LIM&CRcd|! z`rM!XC+6Nds>-fwA5}z!*cat3-&^bfAQdoPYYe0TQh9h^{_4cQ42M?9#8zf@!6G za;JN-f^4VfSK}a~E-Eetx#XObjd@oLeOOr7Ge)cFFSqF^6RjPGa$jUq4L?rIk_@k| ztn`xuUWQj)oW`T}umzdi+089JHntrKeX99csO(mDJfl`rWTddy13*A$r~@>vdVoBl zxX@m9FX)V$CiXkyD~C7pW@BE2mls|-V^tSB@54ha1h#5&e!QD)hO8#9aUS*~OKk9B zOc^~lgSOobi#D_p z!gZf@ekVUQRoA}q!cAe9^9)e7*>z1#O=V>)7ly_cOF6~G7KQ4?bK&d`OrOxG=fmp_ zpak6J7Ey?&xUy@ZU}%GlGsZEHkprH7JwX^Hu$ZczY4Q?0=FAixh0GY~&TPLleYQ1# zQ{UCuCi&!Ae$2V6@WOG3ObJ(?|BlByL>CFF3YYzrVK*_o2TIf#c%6Ru41Wj&X(&!Z zb))Mcf-->M#RUa2ptpcMzsv- zPJaQNxc*N@ocijd;%fspk~%nR$AHx6ln9wSqR~^d_vU#%1!%E zzxG%AX!|L#L`6kGhIcz&agycx*QnwbQSV6F^>B^d@D416YV_+`upe`AIfHC^dNvSg zUUQ7FBo6DzROmj@=VIIDBO)ue8$3X^Dz2v5#nR!&>0^V!5oXi#X8OvfaTP#lBrGyM zhhM7g2%+E7+<&8((*kk}4Hfm1N(YPZ-B(@M#L(C8OQ!78H$J6Y=8=-IY!DQ+&@gkc zpF)SC@sf!+$MlzqAihn!lXl;(MlU&-wG8jL-EPP0NVm1I@oq=I_2Ka?^k-*!j;{Kd z@+mwm*!Qj`zmeVk^|RJ4KzrExBl}xCe0*dSuBZv&0$N(yp{JJsppx@C%B`!Yyb3R? zlE#h@O?^bN;SSyxWE(h|sBi2X9gWUe=z)h?_Ue7It$Jnr$<25}rnXp@^sTuRug^7G zTS~z_H=*(PmV4PdPx62g!?>4Z?WKW1W>64HC)GKZa@5z-Y*y$8;=IQXN1dCQqjVdb zSsG53mzTrI_Bo7{BRPwMdBV!QRpX}=M;NcYZPq4lnEi&wm zBP1eP8GU9k|IJ_U^5CHZUV^BScqI3*s3u~y)vUmyM?&IIuIl_4h=Tyy5!szmVbvz$*4CMi$zTUpk5#-i6054%K}qENaA zzIrTs>#rZ5#u{mBX+;VmN4yv0*_y2K#pP<)!GEf&%Gf-Y(AenF9mnKt#KFN~A4Y!! zTt>mNrY#ZvTTG}BYa&ovF$*T`Xc?|odKN;zr|1$pPIF8x9kup3R^U&Jlk?`=Q%i`w z`1P{JvdVPmy(^dLU^XF}k&yWK*^4xRlsDh3{pl43VIFs~a(%S;C56S?w`jiCfSFgV z_>5fn`SYXthDb_D))H|q=U>D0ckbNzVn;hFvhK|DvNhv(XEFzypoK{SbOK)&61*GiNH(tG3HWTi6(z+JaWLuu>P zId)925(TS$N8i%GLc@D3X^3yOh!UaPb3-1@!zwxq-|ER0zp zO(0$d0y0c~KFgh1tJQ@C*X6Fm{Z*Zr=@Od;IvFJbFLY|WPVTtqw1njE?GK_fG3Lkf zRl)!pEa-LR6CK?h^e}9Do3r_mX1K-d*QtR$1^oCB@G4amDpyv*IQTGnCK7;HAU|ZoX!E3Xk-E>_yxfj5fffW4CGVIqYUm`0xb2dq1#N# z`wN>e$2WEh)tALJ-8$?y&==GKts=5Pb%WpIQf&nf2m07R zB>+7d>U)_PJ+G&WmD8!k^wRU5q%8*z{eCOfHDPpA@lox2yk1x{es!|=uyz=?`T8qC z0VT`+D&Rk9;lEyZogV8~f1)YqTHD(#(m@zfif7+*n)k+T-Ii@dxz;x92NDiJYJy)dy; z2g^*P_3@wgP!1C|a+X7dygnv=*3mL>+D4e?t%sq0&M1mz0qf^)mrYSte41uoFp0Uf zzn1oRNY4T}6`#fTB5tATh)-?nvxyOM@5bXVkq*|Uu6t@j%+d@^GGN0Y{iP4i%42BQ zrZpv|{Jk$Ekjw< z&T4xMdl?ZsITkW=(NL2ZW*~bsh)ySggaD#;ktpZoV-i{UA9Fyc#b+z$M5V~5dA}4? ze^`5!CV^5j_yvMTDtQ|xSy2SrtsD$kwTG(z_=zy6!xWIU9z4mk2eA()2{G8|_5v`Q zLjur3cnt~13mx3WsK&z1?$CD6WlaOb=0OaJ@$V-mC&Io+jr+a4_c&8PF-TlG62e2} zz0e{;Gwb|$SS?pBaba!k9_20|-QKbO3Rd+7Qa?}~j){vys{&E~J zclD_^=s4!T9LAU}BU6YaciUjOZd@G8?<`0J=vZOy?|8tWupux>JXt32Ca{%*QiCxj zaA<(}l(2O6S#uHGZz+)=#j`A}aYn#IAfEBm6cUW?m=3<__pxZLm6 z&8p8KOL&B~a`DuP4QPLV(9jod5G}L^eLV~-NnvQ1I9|U0LGIqakQFZAXzxg!=$SG2 z)8mZ8VsiKo0VVxk1QcSGcJ;+BP%W$}#6QUv5;Ol5q5r$TT+g2MqI)A_()e+m021@^ z3T|$$A&cDhJ{xNm1UnK^M+t1_+nr z#+XGVwAp9`CLBj(rSLgscC(+fN?59O%<@D}y5! z9+~=#T2sPi$AR0zG??XZ8-dO7XIBK;jG%r@4yzw8f&astk)KVmlEH)d1V;X(keo7U zn1k&nQ3*w!x=%@z6H+j*`2fsSI=B&5dw*w_sR7dGGT2Xn`mwbyX-*Z~qrfWNz7}8h zu0F2mWrzf6!@WExsXKpG6(3p3=(R-zf$*(rcUdS2vqac>46NMl{MlWS-BxZ4@l_XW zg4{G;RxP{q3n(B?XNre_>cIHONUUevgN_F`scY_w#qS^HGsGUVu(A%0jn#bqEEHz? z!P$n929rBi6i#!Z?gG=T0&7}6e0ZUx6j@PW2Y?E0-(Vr5Ryh?VB_J}{j6$ob9HgaD zNFpKHl<-;p#}f@o-(~a>1Byv46SWGvqR*crT<<+$amE9}2+;a}UK~7M?F}Vve1%vC zOEC^?p3egb3-FYYl+cfyR?M+7xUxPH;ba)RG6bz(N{4A4gyk46S+l?f4kpL(&0(-z zN{EfcEvo-?esLfheSYEU>I&Fr3D~>XG7@s<@&aC*Z?T1A+F8;x5AKcs@;PvKeVNL2 z44nM)L8VDX*em=2JL-xtaf#jS!2Gdo0s4VC$!}jqP0HXBM)YX z*f9|Ez1JfGl>G+J!JFiAt|XYXd|Jc>4hJSSfEmbN+JIGpKH%W3ErvfmfZTerlJ3wC zYs(*OvU8jM4>rl4mr;^s@g6n26aO16c#zGmmWxB+E_kayAO%QmZ$JugRA0zMpRmJz z`OG?0BvBb=!NBq4Y?R?P6%7oH?MxQeTUp0U(&A_{!5RZA(Swz~phOQxLHY9qF%UfU zq&M8ug=1P+|vt8WRjon4t(a(f0EH1^ZAI!!z0qzLkX*@xiAB zT-(=MCbCT;3!|E!m9Wb&!spj>C=M_OI3Ec&hc1?C*ysZeF-JShaMccOT>p2G-~0}y z^VwLgqBiB6@@-h;Meu0C2C*G>y}<3>a|>WA?KIvCI`4pSS~TAdKN*w(!Up%pFzl*$ z!EzryjZGXuvc&YV`eG?sfmx@n5+o;Bf)~3WG}PHHqtZuw{K#X_76{5rWBvURzw-W? zql$H8l%$q3byZ18FFlNLWJuw!1X|m%1cFr^YNaUzqN1vbcpqep6S*&vr0PTifNzhX zS0N!K)erpn#paHp1S>R@5Mp7WqpXY|S?ytHXqW?PU?Z3^uiy`xc7=!b$IsEY;1igC z1~_}=bkIgsooNKuZjWx!D92RqoBPga`Rt@L<%($LU+ zOh}068OV}s4a5FkJm_HqzZS5=NE9F*v`Wpmzc;6m#Y`W>f zz9HHF(P~ave;}kN8G%_|N=6h|DdHKQAbd97*gnh~!3<1-6qwB+$x=UcmBRDNbf8+c zq<|wjg!cM(u=K&4ADP=$ZUa8+(oZ1Ie!*Ph`zO!e*4oH|z7sqp&fLMzDRl_zUi;iM@#(wN}coRvZ_&j7={|6rNP%FpG}y^IKfjblZLKwuZe zVKVEj)Z8*qn7lC3IcA`J-fjtN?xM=-sBLV~cK148} z{ZG2_Bt=SQ$iXPHYVc+xy{N(a4O@G#g{^a1ZR0bjoxc)A1FNP$-f;so&Axmkg!V8M z0cD|$T5kJFds*8XSeX8@L;c@zIt&5^wE$l9@q8AewOmchvXY5U=P0;=$B*er^Z z3@5WNHPr$W8(Ic|M#;S!0nd3fG&$+olfa^y`-}}KCkTke(3cXnrM?{AUXKs*LLmsqKo$dA|RF_O6UQ+*|yK!r#7P zS4TWU&`0C8oaTy>K8mG^p47MzFO5Qs(%Y!ug9+;ikTlra+s}UegVL;k8bp@;j}8e4 zcMskgG><}T)5YLu0s%;c`7}q0DT);T({BYY0mgy5?wfHXHp$Ma)Pj8u2KC0t!`76E z+rlSp%sr6z!2-kdg#oj?JctDmek%zEd?2qxu9U)f#jw&+CI{;e#jvt=fCEE?LI$cx zo;bI_BL5(@w@WB8Ja{G+B!=~^;Hw(}!R+XaJ#@NAH2hUHzYCeO9H)6i4eAOH} zU)ZcHn#@DdZiRCa_|SW81$ynzRD1I5V4d*qO)7uW2~VEaFT}IEo}KB`I~-$#WwKg0 zZJLhM-5@o+PnAyOEH!)S$+zgXyPAWEobwJw`KD0NfB~ghvNfwqV*ad)4>JhYZ z96o60f2McwjK<2>H=9q^MTvmEjc{B(N>1vc+=zesN!18DiCYkfD8061xX@}v)fe#qbU^wu`hHvsVSetKhOwFpk2bC-E@q@1+fHnCKc7RRaAUJS9V21hs z7-^F*dJxtFAAA@ThbPT&$VY7QD$8CLb{}FeGTH(zR<-bVZkc+p#;OdKNBRO^yv&}x zx7zLiH)6yyPwGHm0=lBwTwwz*CA7_A?tyv61+c0LHr>y=H=mttS)=DDfx63Zr8&$x zfmeS750w3zsr+t}m5~9S{sI48$Mf!v!539E#Ngu}~zdkz$WfsXxId6b=?1>nR8wB?uPGe=vSBt6^|f z7A1NIF{kCN=bu>3jqw>ZKRaxw+m4QoIsz_QwL3O8_UF%^q&)WNL_kAL&%j{o=x90n zl|Rdnp}0~wr%?oj0d6CIQUj3+u##=K6-b;_;whh0nh{yx^dTW4HfxtONPIp+MU5&w z00bUT#K?&p4-o@Bt2f9a{0+KQwM~6x-2Aa`!hijZk}=XIKt@kbkeb?mfJ;qceFD%a zE!f?iou<2UEmW(~3Ig8$pbj;SbtaFT#9$1DMrIks|KdCdNq~xOGMU>hT?2^i0R9MN zmFYvULqaF$Z}Peh!G9W+jK`!sUGIeT$jHpBfJp0ef0Rs4s9ZZztSu4(pzJKGsj-?Ur|$3h{#}9{3O;&WEIzYp z{8n+@LCFUmMyzLL0fTfSTZG#YJRhC1UL?&)fxF!s+RGw6GHlQYo|&9L=*sSW!66{& z1%ikffKjONW*XhF`rBp{L2^QQler-JMvpxJfBgkg97IGA^kOKW_F`~lbJ3(RqIT%_ zLm2RAhf$ONGOHUf`~Giu@+$m>0MmzY951hJFdLQOfg67PA3kK%E142nIR%mvFdP1c zCiM`7!qMN(pLhG8s!rA`z#<-z*TmCDd~q>KJ=p<{s^_7iX!#tb0>R?R1dkTjeZb$cwF9;(3KKZ4fa=l0Oj1t$zr&Uj*g2w zW(=1fY-oP9vf%n40EDK@-x5dW9Xufc=T)JZD$nqScy(yLBV72jA@OQHG2a^twIV+^*=+G9}Ep6eLGe%0|cQE6BFk>0}Fxv&Ym7Q z1%BppMEaB=8`mRv;~8!{3o+D=MUhN z!H0Jn4AzL<3?}f>W%ppe4W~YP@C`=3kPQ*uEK-0kDkFk744%`Pr9CiVx%nH)qWcg2 zh5sg;W4htIVD=RK7pwk0(&c!&ky3K2mOy4@!Bx)qhG@C@GD=%;u&y3RG2Jj4Jo+p<0tFS>A&p$8}+^uW^`kOo})VwJeL=B>ux760D%vHsl~gs&@4Utz~4 z+&1l)-rZ1-va*1jw>J2jr5UW%yq;5b7cn!@#K9Nb#}I>w%KlVD#&Wt}xLwrXD|W9B zR&6dYM4`Ymaa5*OX}2^J4F9c*Oh^Cn^OpV3?gJvQN5JvU7V`fdn-DX)u{ZB`#_ zvPjeTLT;5kJ;zuZs<}?I!ygf7>bHV6=lrw700(@B0YGO`-Vk8yuZ~FtMsth#$Tsm) z(COt^xZ+avj>yNAt4))%G&7D){XKb+n1P%uD<{aF)0G;w$pBbq?F3&Z^M}p!mNi}1 zVkwi3A#P+o%TJGxg5ik2h|N3ktgN;R3o3!c^bZ|4s2EXUAn9+SvpJ`yX!-<^5I?|Q z5>!$o0WrpecPGBQVjnwtu$GID6$#t{!cn`yoU)~*u&oRYzNADuh$WKn6zX>T@875@ zh7+$CgTi1b7aVqvzb6==)4{RMPfwSMi)(EkU=qtDQ>0b!rXI-v-7 z#3JiO@*J>pq*x?sP9QL{y#)eqO$Q7p0i6nC5BONfmpq64@g9`xyHeW;geT`O;!F*$ z`H?2W8olvyPCGx@pFan&n#x%JO||I|4G+&pr6TWDbeVmzg>bN*I&crvnhuu;gbA0} z{;l5#oXp*^vf2TA6M#z3T{JwQ-Utd&pstqeP35vNSm{ZW3CXB@Np(d4W~Xd`8EF9V93rF7rlA<;@c00#;&xms-VD zp2-0E-uCyB^iNUp9C0u>IHEM)b`^VI14Mm8`hn$F_AwkGZRCptk`ETc*JI#$-+#st zl$&H2|N0Y7(0I%G`?r%U(#b%gVbd&wd$gOcAYMz=s^XLG%E0eRmzc@CJKEVaHf~># zmD;}W*HH>{kJ_neDrt!ibO%pDK2WH@3rDRTOJqLiG9oTZ31L#}#PjRxC_7iO@C{hd zeG?a70ZFQcxfP-?Au6?*O@Jn|tz6GHP6YqQBH;RoM7kniy7y3L@8JaBm)mkEf9v5YES?lD^j-RZiqa#{ znXi0W5|g9I9uUGeRr%)$Epu|xKO)51ZOXh}gHjqkEa{eg#-(DIs5G$GCgY`+%tK6o z6Gk$07a-IHY;<`oZS~6Mk*s$N4pf9&9{lx~zK*b<7nw;u1eAhYsy_eT&>Py5JpKje z{cVHbL<)UC!Q8aGWv-{E>qCgb@&ai7QUWuq92o!F+Q@K~nyTx|jax#4y$Ur!@&xy3 zko5X15{e*@`1^q9X_Nn6_O5g%A#h5;z?Z-;p&%LS>gvmw;o(b4KLp0{&nu_t#eiKq z7Ss6pePQagffpos*ou-@*3LSt_e=5v|{zm ztuiatJwhl0DvYCT-bWImeG`fjkKz*5(z{puS;3%WXFiIHOQe-hp;K`;d*%;Wh~mD| zEv}vv6dIqE1wSe*bW2FsUe3TeK`hjBn;iYpcu~{b9ThrTIHHb>hMw3Z`E z!oemp9L~vF3-i29QNWG&fUwniNrgP9;!clEyUN_N_|J;Rsq?hqv zV1~!*wxrF3;4{^`uw^ZTO{Ju#2d2Lkgtd$)FEO|7p~bWIB2AHokspcAjp?M$eueHK z*RxN1BV2D2qvoJwa^d08Qy-!1=M6HTx2w-+O-vo4G5h;lYqa*w6Wqti^?~dKl}kx8 z8g{aAw{=;i7Sy>>Ku+(?-kH)@lSsiR6HI71;wM2ZxL1W*a4 zjrX9huf-$hRiNtlWV+rtn8ZRCt*vra#g^;izXfRp`91e4>$oIH?&FkvghYMf>QZ9H z)M^Sv3{-RBQr9Vq|3SV~Z^di=bx!ruAaoK9=yTkjlwEE;pbC2F)G$RzaAEK@w>P)W{tP|rvz2m7nP2vE zunp0&l=Q3S$P_}s@LPdu+&jfQ7D*S4dlme=BAb{)6f|2`X(3G)wteR)jg~1t8&`#( zVJROuEKn7!p}jK=zigc_pw2P)XHL#abT1Ua$^yd&N@Xd$j_(%Fh({Y+U2A;QCVUHT zp?2I3kj@?@<0!s18DgrOYVPp9=AYi*q{hEae`Xt(g+;0-fS7FmY%SHW&Z>k&we?QH7^j;^;-+~ia=NTA{N!Qx6t0?w7`0)@FUiqUC*|j$wPPL$2sc*Q_s>_!Bc)?T`)$5nVI(wFJX+z>Gh)Tc zdx2129L8Gq_L46m!Ku&q=N55ll%}rb+a!`O6vj8w5)wu_Gkf}Z9ruUwpc>LL)cnO%9Hj@M1w1xy5eIh3%?5qcS@$EluX=JW?i4VxFm#Tk0~kZ zhh5K>bz&_qPWDvE1yX^AaQg5tRk1IBkU*1fbdl`Y%VckskDH`92CMZ}C(=dqPfLq+ zFe_Ts%Mh7gV04{ENiS7R zRW)-Y@om@$Sh+P=MbXHjNuCLf_`F&MeRV0v?XKDAo#0MJX(_@dN-A|ni%HjQ?>GC0 zpBE(9Zn@BE^B+Os3SW*OZzGJeiqOt!@WN}SeT`)MJ`YGSkXl{aDfHAc48`EN8A36A z2LzN+WZ&}{p^}uAL5ic$W8$pR;h>#y3ke+BAJi1`OkzuchBgYj_Z7#xC|6ppr z#zJ+G!M9?MMZgqRiSwZHe&@3GpjhCcbdO(5ba-3ZyEpa{PaJyemVShCk5nBVbpL`J zCMMeDf8D7LZ{HJlv=k9p_z-tA*Ox!~ahz|I*QBft0c>??@N*nF$YoaMxEDVGn?|bL zpQgfYoFT3`uD0Y#Z0-aU>Zm2(9wNQ0k--|1!WbkqO6yyF!pq*HO%U0I14eEX>0$9t zA2QKzWL=QY*Q{D8_NuO}UuG7iIQPV*AAkKYt{pWt$iSXp(bFpOVq#uq{3Uy;DH36- zB@Zo5P}dXc;h3H3K1Ety+NUfmD4wHieH?Fd?`vyXeRl5Isx_{vy~r%g|9!qeRn#VA z@biGn%00&WI@sKcVzUDQO-or;qci9VnP46 z*+}b{6;1Z*lCNcbVR&5NB!xGlLorE&c2?E!;> zMy)wPQPJ%SzQc2zZpUCh2Nd(TlxV^1N3)M+^p9RnEq&(s5Ez&)i6f(?J}NVAh3cO< zSRULw`8ty=9U8G4@H?T~uFt@UWq)K(C)-DCSz@bbnyG0B<@+x0oR@-(Ep{c3um2B- z$FaqcF3u2-hy?hsy$!o#qI+mCdn+$$$p5`xTs6dj8huN zo!G(}tMBHa^dH$fFihh|YBNN5`VY9=s zQHhyiCi;yn3uRxg0x${x*}oK>zoMO(7<|FNB$m+mF&eoNipQJLK#bc85N zj|m_0ocpG|MK#ynKPGrZ6K*lpAZ%c8gk!veo?T>G#_LVgC@^b$tM0Z!m9UJ}(7D(? zrwBU5w`GxFb45o1X}${**`ym^#2aD00PTD3@>+bOg1Z$Jd0x+~rGw+}Y;;c}528NDTPFlkBW4tgw&49~iI$EB@k)dh&_xn6(vdNh6Y9rG%#9ePCld(&%hTWf|M)fc(y`le zb$ouvr~h+&U_B2XA6AZkMhAXGAnY0oH^;w220t4Vc9H$rzu)`MS?B*Az0PuT-1+}E zXRpJ5{J-pq1$M^i|LL?tf&ZMozWiz`n1u7bhu*l?+N#iKor40J+-qq4!{2LfTF4`a z{kSMR_jV-thtvjteWG&cBS1FrEpvNGt(O05xT^Sl-?7s*I>QCo<;mRpiNyJ#($)a0 z_f2caW+!{+=k?vQ0+*g%PVTi$Y^~jS!u((8FSMmXhg$Wy7H1OlEKFk#$HW>s6LWEB;7cM$Z#-=HDt;|6z@Siw-mf+Gr5ROe zbbmQb!%i%uT=VOz5yDm8_Dx!dOF|CY_(f!XcB^J-2l^D2II` zzsi+h9XILeATw zd~1Dy*qzcnT`l^1db1$|(cQbp1sEw|vo6!ED*Hs?tda{v3lY70Vd|e7P0NL%yc5^o zNJKHcOvf9}NhhJ~teX|F4caU#A1sbmbr(nDwZF4NdMsig+}P&kfyl$iFCrZBF)=ON z3Qx%lPvO;<+V91PE&?o3(kI?;P|^*NOel#nW=C(oJhgsF-9|I0md(k0aq6lQGfy#w zhC+ARPq!GCN|_aewDk5Cglfu(y+J73?D3OSCSB(Jp0)3VsZzc<`*@Bk`>fjZ=~3NH zK5Utp%g^~5=k@g{yUR_GEo%JwedA{lDgDqJVva`erb$yV39U{p5wafzV618gg^=i< z1i#s}khQ2!#7$P7BCP&0b9ZK7PU%qm%5u&CjpswZudAOWmhhb;c^{((X;K#hn(o{D zKM;2|@j{nWHotjv=G${q{?Jmhm%y}d9-K3C-q4>!D;E{iq#eTGB$OpIRed{$T8xjAiQ>pf8)G}z4 zAF5GqzihT;naDq(Y1{r0Tj4{(uOqBH!tlv+$OZ?!4AtJ6o+VD0{GHIqZKJMe&OKY) zY;2q*1Nk0z$+JO6(lrL3(w8||(F+t&cznWQ(u}V5ubuBwa%NA(V*fywQ9|+IHLz{y zc_f4oO{FPx8=-LPexABG6t$VILMgw)KxRgiHav$)4)d8V(lYb2G`0J)-D7l|`aS;Q z(W$mf+R}x0-l+}*PTlR`_qU#|EzG*DB%CAVKdz`WP(8EARFz#%5g#8~oYYMSx& z=exbceg-X*Xag@4+IjHXFv>4Qkn>`h?d{Vulb0sFgpqo}$G^TsZq}n3ryMT5ou&eI zvwo+bb4#W5l#A{SnPffD)$N7Aleoj$6>jnL;AMKso$t+Ntb{c3M^cZ5?!u-&IeL{zk|2#si&dFyH!Z@o{4E=lVSVD;Yx-a9Nsg2 zM~d;)ceO19fycjbmyaf%SeFLH&2JNPKZ-!}InK|}kdmg+lQ9YrPipq=G8Ky9beABR zh>69E9yT?U5ATzT6WwpGOgdCsn&{iCRd5|d+*?=;KX#HuJaUmjR{yFKc5SnZmYsTi zwYsY!o}KkAmz>>;q0FNQqD~~@(%dbf5M?{Ck|5{Q91zet$fL4OSNxI#CEqqsAgfTP zxo`#JTfOBsQTef^Tn)VDOuPQB0yU)#h&mE9tD&sOm~VMrs?+XA2-*)RK2x<%?^g3w zSv5zblJOjS_-@njq^L}ewr#g3l}!FzAu^y~`i|sS5j{ZH*Y_B$O7)pjTx@>iM&0g! zgDQ5%m9BV3N&nEjA1;$KT!jY_)kcO%X4Cv0{$Z%@8QLzso>qR6`HMQ~We~qU+eztP z`tpRFoMiVSb@bindPU5~m5vxI3r@dh?j0XgTZ?4RkbWamp(!k(liZXIGOS=pS3tRP zGk9&+KmTQ-u3Fj3^3oB4beh+oS)RezzfC`z-4RCV%TIb)I_oy;o3yo{Sj^x>IlURE z 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∋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%0D5IbdPSWt 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

=KllwaR&xDxneY1a`b-8o!|&7FlG-Rxe;xSw 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%0AS866PJuW%2C-n9`Qt}+d7#^yl8hYf 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