You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3231 lines
129 KiB
3231 lines
129 KiB
#!/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 i<len(x_live_screens) and self.getScreenRole(screen)!=XSetup.ROLE_UNUSED:
|
|
screen._setXLiveScreen(x_live_screens[i])
|
|
i += 1
|
|
|
|
# Ensure that all of the screen roles have been handed out.
|
|
if self.primary_screen is None:
|
|
for screen in self.getAllScreens():
|
|
if screen is not self.secondary_screen:
|
|
self.primary_screen = screen
|
|
break
|
|
if self.secondary_screen is None:
|
|
for screen in self.getAllScreens():
|
|
if screen is not self.primary_screen:
|
|
self.secondary_screen = screen
|
|
break
|
|
|
|
self._finalizeInit()
|
|
|
|
if not self.hasxorg and not secondtry:
|
|
"""No xorg.conf, so we need to write a temporary one and reload from that one"""
|
|
self.writeXorgConfig('/tmp/xorg.conf.displayconfig')
|
|
self.__init__(xorg_config_filename='/tmp/xorg.conf.displayconfig',secondtry=True)
|
|
return
|
|
|
|
def _finalizeInit(self):
|
|
for gfxcard in self.gfxcards:
|
|
gfxcard._finalizeInit()
|
|
|
|
# Check the 'layout' on the gfx cards to detect optimised configs.
|
|
gfxcard = self.primary_screen._getGfxCard()
|
|
if gfxcard._getDetectedLayout()!=XSetup.LAYOUT_SINGLE:
|
|
# Something interesting is going on on this gfx card. The
|
|
# secondary screen is in this case going to actually be the
|
|
# other 'head' on this gfx card.
|
|
if gfxcard.getScreens()[0] is not self.primary_screen:
|
|
self.secondary_screen = gfxcard.getScreens()[0]
|
|
else:
|
|
self.secondary_screen = gfxcard.getScreens()[1]
|
|
|
|
self.orientation = gfxcard._getDetectedDualheadOrientation()
|
|
self.setLayout(gfxcard._getDetectedLayout())
|
|
|
|
self.setLayout(self.layout) # Propogate any XINERAMA_SINGLE stuff out to the gfxcards.
|
|
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 _matchMonitor(self,monitor_section):
|
|
monitor_model_db = GetMonitorModelDB()
|
|
|
|
model_name = monitor_section.modelname
|
|
if monitor_model_db.getMonitorByName(model_name) is not None:
|
|
monitor_model = monitor_model_db.getMonitorByName(model_name)
|
|
else:
|
|
|
|
if monitor_section.getRow('horizsync') is not None and monitor_section.getRow('vertrefresh') is not None:
|
|
# Create a monitor object for the monitor in the xconfig.
|
|
# It is probably a Plug N Play monitor and as such doesn't
|
|
# appear in our monitor DB.
|
|
monitor_model = monitor_model_db.newCustomMonitor(name=model_name)
|
|
monitor_model.setType(MonitorModel.TYPE_PLUGNPLAY)
|
|
|
|
if monitor_section.vendorname is not None:
|
|
monitor_model.setManufacturer(monitor_section.vendorname)
|
|
|
|
monitor_model.setHorizontalSync(' '.join(monitor_section.getRow('horizsync')))
|
|
monitor_model.setVerticalSync(' '.join(monitor_section.getRow('vertrefresh')))
|
|
|
|
else:
|
|
# Try detecting the monitor.
|
|
monitor_model = monitor_model_db.detect()
|
|
|
|
monitor_model.setDpms("dpms" in monitor_section.option)
|
|
|
|
return monitor_model
|
|
|
|
def _decodeServerLayoutScreens(self,screens_lines):
|
|
primary_name = None
|
|
secondary_name = None
|
|
layout = XSetup.LAYOUT_SINGLE
|
|
position = XSetup.POSITION_LEFTOF
|
|
|
|
for line in screens_lines:
|
|
try:
|
|
i = 1
|
|
if line._row[i].isdigit(): # Skip any screen ID number.
|
|
i += 1
|
|
|
|
screen_name1 = line._row[i]
|
|
if primary_name is None:
|
|
primary_name = screen_name1
|
|
elif secondary_name is None:
|
|
secondary_name = screen_name1
|
|
layout = XSetup.LAYOUT_CLONE
|
|
i += 1
|
|
|
|
if line._row[i].isdigit():
|
|
# Skip absolute coords.
|
|
i += 2
|
|
else:
|
|
if line._row[i].lower()=='absolute':
|
|
# Skip the absolute keyword and coords
|
|
i += 3
|
|
|
|
screen_name2 = line._row[i+1]
|
|
secondary_name = screen_name2
|
|
|
|
position = {
|
|
'leftof': XSetup.POSITION_LEFTOF,
|
|
'rightof': XSetup.POSITION_RIGHTOF,
|
|
'above': XSetup.POSITION_ABOVE,
|
|
'below': XSetup.POSITION_BELOW
|
|
}[line._row[i].lower()]
|
|
|
|
layout = XSetup.LAYOUT_DUAL
|
|
|
|
if screen_name1!=primary_name:
|
|
# Swap the screens around. The primary wasn't given first on this
|
|
# dualhead screen line.
|
|
secondary_name = screen_name1
|
|
position = {
|
|
XSetup.POSITION_LEFTOF: XSetup.POSITION_RIGHTOF,
|
|
XSetup.POSITION_RIGHTOF: XSetup.POSITION_LEFTOF,
|
|
XSetup.POSITION_ABOVE: XSetup.POSITION_BELOW,
|
|
XSetup.POSITION_BELOW: XSetup.POSITION_ABOVE
|
|
}[position]
|
|
|
|
except IndexError:
|
|
pass
|
|
except KeyError:
|
|
pass
|
|
|
|
return (primary_name, secondary_name, layout, position)
|
|
|
|
def _detectGfxCards(self,pci_bus):
|
|
"""Scans the PCI bus for graphics cards.
|
|
|
|
Returns a list of (PCI_ID, PCIDevice, GfxCard) tuples."""
|
|
self.gfx_card_db = GetGfxCardModelDB()
|
|
vesa_model = "VESA driver (generic)"
|
|
|
|
# Look for a gfxcard.
|
|
found_list = []
|
|
for pci_device in pci_bus.devices:
|
|
if pci_device.isGfxCard():
|
|
pci_id = "PCI:%i:%i:%i" % (pci_device.pci_bus, pci_device.pci_device, pci_device.pci_function)
|
|
model = None
|
|
try:
|
|
cardname = pci_device.getModule()
|
|
if not pci_device.isModuleXorgDriver():
|
|
cardname = vesa_model
|
|
model = self.gfx_card_db.getGfxCardModelByName(cardname)
|
|
except KeyError:
|
|
model = self.gfx_card_db.getGfxCardModelByName(vesa_model)
|
|
found_list.append( (pci_id,pci_device,model) )
|
|
|
|
return found_list
|
|
|
|
def _canonicalPCIBusID(self,bus_id):
|
|
try:
|
|
parts = bus_id.split(":")
|
|
if parts[0].lower()!="pci":
|
|
return None
|
|
bus = int(parts[1])
|
|
device = int(parts[2])
|
|
function = int(parts[3])
|
|
return "PCI:%i:%i:%i" % (bus,device,function)
|
|
except IndexError:
|
|
return None
|
|
except ValueError:
|
|
return None
|
|
except AttributeError:
|
|
return None
|
|
|
|
def _isNVidiaCardDualhead(self,PCIDeviceObject):
|
|
"""
|
|
PCIDevice - ScanPCI.PCIDevice
|
|
|
|
Returns true if the given nVidia PCI device ID supports dualhead.
|
|
"""
|
|
# From Xorg source xc/programs/Xserver/hw/xfree86/drivers/nv/nv_setup.c
|
|
# See line "pNv->twoHeads = "
|
|
#
|
|
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<len_modes: # interlace the two sets of indexes.
|
|
if higher<len_modes:
|
|
mode_indexes.append(higher)
|
|
higher += 1
|
|
if lower>=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()
|