commit 19ae07d0d443ff8b777f46bcbe97119483356bfd Author: tpearson Date: Sat Mar 13 05:43:39 2010 +0000 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 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 0000000..7561140 Binary files /dev/null and b/displayconfig/energystar.png differ diff --git a/displayconfig/execwithcapture.py b/displayconfig/execwithcapture.py new file mode 100644 index 0000000..10c8e23 --- /dev/null +++ b/displayconfig/execwithcapture.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +import os +import select + +############################################################################ +def ExecWithCapture(command, argv, searchPath = 0, root = '/', stdin = 0, + catchfd = 1, closefd = -1): + + 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) + 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 diff --git a/displayconfig/extramodes b/displayconfig/extramodes new file mode 100644 index 0000000..94dec61 --- /dev/null +++ b/displayconfig/extramodes @@ -0,0 +1,39 @@ +// +// Extra modes to include as default modes in the X server. +// +// Based on Xorg's xc/programs/Xserver/hw/xfree86/etc/extramodes file. +// The mode names have been changed to include the refresh rate. +// + +# 832x624 @ 75Hz (74.55Hz) (fix if the official/Apple spec is different) hsync: 49.725kHz +ModeLine "832x624@75" 57.284 832 864 928 1152 624 625 628 667 -Hsync -Vsync + +# 1280x960 @ 60.00 Hz (GTF) hsync: 59.64 kHz; pclk: 102.10 MHz +Modeline "1280x960@60" 102.10 1280 1360 1496 1712 960 961 964 994 -HSync +Vsync + +# 1280x960 @ 75.00 Hz (GTF) hsync: 75.15 kHz; pclk: 129.86 MHz +Modeline "1280x960@75" 129.86 1280 1368 1504 1728 960 961 964 1002 -HSync +Vsync + +# 1152x768 @ 54.8Hz (Titanium PowerBook) hsync: 44.2kHz +ModeLine "1152x768@54" 64.995 1152 1178 1314 1472 768 771 777 806 +hsync +vsync + +# 1400x1050 @ 60Hz (VESA GTF) hsync: 65.5kHz +ModeLine "1400x1050@60" 122.0 1400 1488 1640 1880 1050 1052 1064 1082 +hsync +vsync + +# 1400x1050 @ 75Hz (VESA GTF) hsync: 82.2kHz +ModeLine "1400x1050@75" 155.8 1400 1464 1784 1912 1050 1052 1064 1090 +hsync +vsync + +# 1600x1024 @ 60Hz (SGI 1600SW) hsync: 64.0kHz +Modeline "1600x1024@60" 106.910 1600 1620 1640 1670 1024 1027 1030 1067 -hsync -vsync + +# 1920x1440 @ 85Hz (VESA GTF) hsync: 128.5kHz +Modeline "1920x1440@85" 341.35 1920 2072 2288 2656 1440 1441 1444 1512 -hsync +vsync + +# 2048x1536 @ 60Hz (VESA GTF) hsync: 95.3kHz +Modeline "2048x1536@60" 266.95 2048 2200 2424 2800 1536 1537 1540 1589 -hsync +vsync + +# 2048x1536 @ 75Hz (VESA GTF) hsync: 120.2kHz +Modeline "2048x1536@75" 340.48 2048 2216 2440 2832 1536 1537 1540 1603 -hsync +vsync + +# 2048x1536 @ 85Hz (VESA GTF) hsync: 137.0kHz +Modeline "2048x1536@85" 388.04 2048 2216 2440 2832 1536 1537 1540 1612 -hsync +vsync diff --git a/displayconfig/infimport.py b/displayconfig/infimport.py new file mode 100755 index 0000000..f51475d --- /dev/null +++ b/displayconfig/infimport.py @@ -0,0 +1,297 @@ +#!/usr/bin/python +# +# Based on inf2mondb.py from RedHat +# +# originally by Matt Wilson +# option parsing and database comparison by Fred New +# ini parsing completely rewritten by Matt Domsch 2006 +# +# Copyright 2002 Red Hat, Inc. +# Copyright 2006 Dell, Inc. +# Copyright 2007 Sebastian Heinlein +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# You should have received a copy of the GNU Library Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" +Provides an importer for Microsoft Windows monitor descriptions + +The code can be used as a python module for or as a script to add new monitor +definitions to a monitor database. + +In code example: Read the list of monitors from an inf file. + +import infimport +monitors = infimport.get_monitors_from_inf(PATH) + +Script example: To check for monitors of an inf file that are not yet in the database. + +./infimport.py MONITORS.inf /usr/share/hwdata/MonitorsDB +""" + +import sys +import string +import re +import ConfigParser +import os + +import logging + +logging.basicConfig() +log = logging.getLogger("infimport") +#log.setLevel(logging.DEBUG) +log.setLevel(logging.INFO) + +# this is a class to deal with various file line endings and leading whitespace +# converts all \r line endings to \n. +# It also strips leading whitespace. +# NOTE: be sure to always return _something_, even if it is just "\n", or we +# break the file API. (nothing == eof) +class myFile(object): + def __init__(self, *args): + self.fd = open(*args) + + def close(self): + return self.fd.close() + + def readline(self, *args): + line = self.fd.readline(*args) + line = line.replace('\r', '\n') + line = line.replace('\n\n', '\n') + line = line.lstrip(" \t") + return line + + +# we will use this to override default option parsing in ConfigParser to handle +# Microsoft-style "INI" files. (Which do not necessarily have " = value " after +# the option name +OPTCRE = re.compile( + r'(?P