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.
tde-guidance/displayconfig/xorgconfig.py

904 lines
32 KiB

#!/usr/bin/python
###########################################################################
# xorgconfig.py - description #
# ------------------------------ #
# begin : Wed Feb 9 2004 #
# copyright : (C) 2005 by Simon Edwards #
# email : simon@simonzone.com #
# #
###########################################################################
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
###########################################################################
import csv
import codecs
import locale
"""
General usage:
import xorgconfig
config = readConfig("/etc/X11/xorg.conf")
input_devices = config.getSections("InputDevice")
print input_devices[0].driver
options = input_devices[0].options
for option in options:
# option is of type OptionLine.
print option._row[0],
if len(option._row)>=2:
print "=>",option._row[1]
# Add line: Option "XkbModel" "pc105"
options.append( options.makeLine("Comment text",["XkbModel" "pc105"]) )
Refactor plan
=============
New usage:
import xorgconfig
config = readConfig("/etc/X11/xorg.conf")
input_devices = config.section.InputDevice
print input_devices[0].driver
options = input_devices[0].options
for option in options:
# option is of type OptionLine.
print option[1],
if len(option)>=3:
print "=>",option[2]
module_section = config.section.module[0]
module_section.append(["load","i2c"])
assert module_section.existsLoad("i2c")
module_section.removeLoad("i2c")
device_section = config.section.device[0]
if device_section.busid is not None:
print "Found busid:",device_section.busid
* direct references to myline._row should be removed.
* A ConfigLine should be a subclass of List. With line[i] accessing the
parts of the line.
* the order of the makeLine() parameters should be reversed.
* it should be possible to directly append a list or tuple that represents
a line to a section.
"""
############################################################################
class ConfigLine(object):
"""Represents one line from the Xorg.conf file.
Each part of the line is printed without quotes.
"""
def __init__(self,comment,row):
self._row = [item for item in row if item!='']
self._comment = comment
def toString(self,depth=0):
caprow = self._row
if len(caprow) > 0:
caprow[0] = caprow[0].capitalize()
string = ('\t' * (depth/2)) + ' ' * (depth%1) + '\t'.join([unicode(item) for item in caprow])
if self._comment is not None:
string += '#' + self._comment
return string + '\n'
############################################################################
class ConfigLineQuote(ConfigLine):
"""Represents one line from the Xorg.conf file.
The first item in the line is not quoted, but the remaining items are.
"""
def toString(self,depth=0):
string = ('\t' * (depth/2) + ' ' * (depth%1))
if len(self._row)!=0:
string += self._row[0].capitalize()
if len(self._row)>1:
if len(self._row[0]) < 8:
string += '\t'
string += '\t"' + '"\t"'.join([unicode(item) for item in self._row[1:]]) + '"'
if self._comment is not None:
string += '#' + self._comment
return string + '\n'
############################################################################
class OptionLine(ConfigLineQuote):
def __init__(self,comment,row):
arg = ['option']
arg.extend(row)
ConfigLineQuote.__init__(self,comment,arg)
############################################################################
class ConfigList(list):
def toString(self,depth=0):
string = ""
for item in self:
string += item.toString(depth)
return string
############################################################################
class OptionList(ConfigList):
name = "option"
def __setitem__(self,key,value):
list.__setitem__(self,key,value)
def makeLine(self,comment,row):
return OptionLine(comment,row)
def appendOptionRow(self,row):
self.append(self.makeLine(None,row))
def removeOptionByName(self,name):
name = name.lower()
i = 0
while i < len(self):
if self[i]._row[1].lower()==name:
del self[i]
else:
i += 1
def getOptionByName(self,name):
name = name.lower()
for item in self:
try:
if item._row[1].lower()==name:
return item
except IndexError:
pass
return None
############################################################################
class ScreenConfigLine(ConfigLine):
def __init__(self,comment,row):
arg = ["screen"]
arg.extend(row)
ConfigLine.__init__(self,comment,arg)
def toString(self,depth=0):
string = (' ' * depth)
try: # Keep on building up the string until the IndexError is thrown.
string += self._row[0]
i = 1
if self._row[i].isdigit():
string += ' ' + self._row[i]
i += 1
string += ' "' + self._row[i] + '"'
i += 1
while True:
item = self._row[i].lower()
if item in ['rightof','leftof','above','below']:
string += ' %s "%s"' % (item, self._row[i+1])
i += 1
elif item=='absolute':
string += ' %s %d %d' % (item, self._row[i+1], self._row[i+2])
i += 2
elif item.isdigit():
i += 1
string += ' %s %s' % (item,self._row[i])
i += 1
except IndexError: pass
if self._comment is not None:
string += ' #' + self._comment
return string + '\n'
############################################################################
class ScreenConfigList(ConfigList):
name = "screen"
def __setitem__(self,key,value):
list.__setitem__(self,key,value)
def makeLine(self,comment,row):
return ScreenConfigLine(comment,row)
############################################################################
class ConfigContainer(object):
"""Acts as a container for ConfigLines and other ConfigContainers.
Is used for representing things like the whole config file, sections
and subsections inside the file.
"""
def __init__(self):
self._contents = []
def append(self,item):
assert (item is not None)
self._contents.append(item)
def remove(self,item):
self._contents.remove(item)
def toString(self,depth=0):
string = ''
for item in self._contents:
string += item.toString(depth+1)
return string
def makeSection(self,comment,row):
return Section(comment,row)
def isSection(self,name):
lname = name.lower()
return lname=='section'
def isEndSection(self,name):
return False
def makeLine(self,comment,row):
return ConfigLine(comment,row)
def isListAttr(self,name):
lname = name.lower()
return lname in self._listattr
def makeListAttr(self,comment,row):
listobj = self.__getattr__(row[0].lower())
listobj.append( listobj.makeLine(comment,row[1:]) )
def getSections(self,name):
"""Get all sections having the given name.
Returns a list of ConfigContainer objects.
"""
name = name.lower()
sections = []
for item in self._contents:
try:
if isinstance(item,ConfigContainer) and item._name.lower()==name:
sections.append(item)
except IndexError: pass
return sections
def __getattr__(self,name):
if not name.startswith("_"):
lname = name.lower()
if lname in self._listattr:
# Lookup list attributes.
for item in self._contents:
if isinstance(item,ConfigList) and item.name==lname:
return item
else:
listitem = self._listattr[lname]()
self._contents.append(listitem)
return listitem
else:
for item in self._contents:
try:
if isinstance(item,ConfigLine) and item._row[0].lower()==lname:
return item._row[1]
except IndexError: pass
if lname in self._attr or lname in self._quoteattr:
return None
raise AttributeError, name
def __setattr__(self,name,value):
if name.startswith('_'):
return super(ConfigContainer,self).__setattr__(name,value)
lname = name.lower()
for item in self._contents:
try:
if isinstance(item,ConfigLine) and item._row[0].lower()==lname:
item._row[1] = value
break
except IndexError: pass
else:
if lname in self._attr or lname in self._quoteattr:
line = self.makeLine(None,[name,value])
self.append(line)
else:
raise AttributeError, name
def clear(self):
self._contents = []
def getRow(self,name):
if not name.startswith("_"):
lname = name.lower()
for item in self._contents:
try:
if isinstance(item,ConfigLine) and item._row[0].lower()==lname:
return item._row[1:]
except IndexError: pass
if name in self._attr or name in self._quoteattr:
# is a valid name, just has no real value right now.
return None
raise AttributeError, name
############################################################################
class Section(ConfigContainer):
"""Represents a Section in the config file.
"""
# List of config line types allowed inside this section.
# A list of strings naming lines that need to be stored in ConfigLine objects.
_attr = []
# A list of strings naming the lines that need to be stored in ConfigLineQuote objects.
# This is often overridden in subclasses.
_quoteattr = []
_listattr = {}
def __init__(self,comment,row):
ConfigContainer.__init__(self)
self._name = row[1]
self._comment = comment
def __show__(self):
""" For debugging """
for a in self._attr:
print self._name, "Attribute:", a
for a in self._quoteattr:
print self._name, "QuoteAttribute:", a
for a in self._listattr:
print self._name, "ListAttr:", a
def isSection(self,name):
return name.lower()=='subsection'
def isEndSection(self,name):
return name.lower()=='endsection'
def makeLine(self,comment,row):
try:
lname = row[0].lower()
if lname in self._quoteattr:
return ConfigLineQuote(comment,row)
if lname in self._attr:
return ConfigLine(comment,row)
return None
except IndexError:
pass
return ConfigContainer.makeLine(self,comment,row)
def toString(self,depth=0):
if self._comment is None:
return '%sSection "%s"\n%s%sEndSection\n' % \
(' ' * depth, self._name, ConfigContainer.toString(self,depth+1), ' ' * depth)
else:
return '%sSection "%s" # %s\n%s%sEndSection\n' % \
(' ' * depth, self._name, self._comment, ConfigContainer.toString(self,depth+1), ' ' * depth)
############################################################################
class SubSection(Section):
def isSection(self,name):
return False
def isEndSection(self,name):
return name.lower()=='endsubsection'
def toString(self,depth=0):
return '%sSubSection "%s"\n%s%sEndSubSection\n' % \
('\t' * (depth/2) + ' ' * (depth%1), self._name, ConfigContainer.toString(self,depth+1), '\t' * (depth/2) + ' ' * (depth%1))
############################################################################
class DeviceSection(Section):
_attr = ["endsection","dacspeed","clocks","videoram","biosbase","membase", \
"iobase","chipid","chiprev","textclockfreq","irq","screen"]
_quoteattr = ["identifier","vendorname","boardname","chipset","ramdac", \
"clockchip","card","driver","busid"]
_listattr = {"option" : OptionList}
############################################################################
class DriSection(Section):
_attr = ["group","buffers","mode"]
def makeLine(self,comment,row):
try:
lname = row[0].lower()
if lname=="group" and not row[1].isdigit():
return ConfigLineQuote(comment,row)
except IndexError:
pass
return Section.makeLine(self,comment,row)
############################################################################
class ExtensionsSection(Section):
_listattr = {"option" : OptionList}
############################################################################
class FilesSection(Section):
_quoteattr = ["fontpath","rgbpath","modulepath","inputdevices","logfile"]
def makeLine(self,comment,row):
return ConfigLineQuote(comment,row)
############################################################################
class ModuleSection(Section):
_quoteattr = ["load","loaddriver","disable"]
def makeSection(self,comment,row):
return ModuleSubSection(comment,row)
def allowModule(self,modname):
killlist = []
for item in self._contents:
try:
if isinstance(item,ConfigLineQuote) \
and item._row[0].lower()=='disable' \
and item._row[1]==modname:
killlist.append(item)
except IndexError: pass
for item in killlist:
self._contents.remove(item)
def removeModule(self,modname):
killlist = []
for item in self._contents:
try:
if isinstance(item,ConfigLineQuote) \
and item._row[0].lower()=='load' \
and item._row[1]==modname:
killlist.append(item)
except IndexError: pass
for item in killlist:
self._contents.remove(item)
def disableModule(self,modname):
self.removeModule(modname)
self._contents.append(ConfigLineQuote(None,['disable',modname]))
def addModule(self,modname):
self.removeModule(modname)
self._contents.append(ConfigLineQuote(None,['load',modname]))
############################################################################
class ModuleSubSection(SubSection):
_listattr = {"option" : OptionList}
############################################################################
class ModeSection(Section):
_attr = ["dotclock","htimings","vtimings","hskew","bcast","vscan"]
_quoteattr = ["flags"]
def __init__(self,comment,row):
Section.__init__(self,comment,row)
self._name = row[1]
def isEndSection(self,name):
return name.lower()=='endmode'
def toString(self,depth=0):
if self._comment is None:
return '%sMode "%s"\n%s%sEndMode\n' % \
(' ' * depth, self._name, ConfigContainer.toString(self,depth+1), ' ' * depth)
else:
return '%sMode "%s" # %s\n%s%sEndMode\n' % \
(' ' * depth, self._name, self._comment, ConfigContainer.toString(self,depth+1), ' ' * depth)
############################################################################
class ModeList(ConfigList):
name = "mode"
def __setitem__(self,key,value):
list.__setitem__(self,key,value)
def makeLine(self,comment,row):
return ModeLine(comment,row)
############################################################################
class ModeLineList(ConfigList):
name = "modeline"
def __setitem__(self,key,value):
list.__setitem__(self,key,value)
def makeLine(self,comment,row):
return ModeLineConfigLine(comment,row)
############################################################################
class MonitorSection(Section):
_attr = ["displaysize","horizsync","vertrefresh","gamma"]
_quoteattr = ["identifier","vendorname","modelname","usemodes"]
_listattr = {"option" : OptionList, "mode" : ModeList, "modeline" : ModeLineList}
def makeLine(self,comment,row):
return Section.makeLine(self,comment,row)
def isSection(self,name):
lname = name.lower()
return lname=='mode'
def isEndSection(self,name):
return name.lower()=='endsection'
def makeSection(self,comment,row):
if row[0].lower()=='mode':
return ModeSection(comment,row)
else:
return Section.makeSection(self,comment,row)
############################################################################
class ModeLineConfigLine(ConfigLine):
def toString(self,depth=0):
string = (' ' * depth)+"modeline "
if len(self._row)>0:
string += ' "' + self._row[0] + '"'
if len(self._row)>1:
string += ' ' + ' '.join([unicode(item) for item in self._row[1:]])
if self._comment is not None:
string += '#' + self._comment
return string + '\n'
############################################################################
class ModesSection(MonitorSection):
# Like a MonitorSection, only smaller.
_attr = ["modeline"]
_quoteattr = ["identifier"]
############################################################################
class PointerSection(Section):
_attr = ["emulate3timeout","baudrate","samplerate","resolution",\
"devicename","buttons"]
_quoteattr = ["protocol","device","port","emulate3buttons","chordmiddle",\
"cleardtr","clearrts","zaxismapping","alwayscore"]
############################################################################
class ScreenSection(Section):
_attr = ["screenno","defaultcolordepth","defaultdepth","defaultbpp","defaultfbbpp"]
_quoteattr = ["identifier","driver","device","monitor","videoadaptor","option"]
_listattr = {"option" : OptionList}
def makeSection(self,comment,row):
if row[1].lower()=='display':
return DisplaySubSection(comment,row)
return SubSection(comment,row)
############################################################################
class DisplaySubSection(SubSection):
_attr = ["viewport","virtual","black","white","depth","fbbpp","weight"]
_quoteattr = ["modes","visual","option"]
_listattr = {"option" : OptionList}
############################################################################
class ServerFlagsSection(Section):
_quoteattr = ["notrapsignals","dontzap","dontzoom","disablevidmodeextension",\
"allownonlocalxvidtune","disablemodindev","allownonlocalmodindev","allowmouseopenfail", \
"blanktime","standbytime","suspendtime","offtime","defaultserverlayout"]
_listattr = {"option" : OptionList}
############################################################################
class ServerLayoutSection(Section):
_attr = []
_quoteattr = ["identifier","inactive","inputdevice","option"]
_listattr = {"option" : OptionList, "screen" : ScreenConfigList}
############################################################################
class InputDeviceSection(Section):
_quoteattr = ["identifier","driver"]
_listattr = {"option" : OptionList}
############################################################################
class KeyboardSection(Section):
_attr = ["autorepeat","xleds"]
_quoteattr = ["protocol","panix106","xkbkeymap","xkbcompat","xkbtypes",\
"xkbkeycodes","xkbgeometry","xkbsymbols","xkbdisable","xkbrules",\
"xkbmodel","xkblayout","xkbvariant","xkboptions","vtinit","vtsysreq",\
"servernumlock","leftalt","rightalt","altgr","scrolllock","rightctl"]
############################################################################
class VendorSection(Section):
_attr = []
_quoteattr = ["identifier"]
_listattr = {"option" : OptionList}
def isSection(self,name): return False
############################################################################
class VideoAdaptorSection(Section):
_attr = []
_quoteattr = ["identifier","vendorname","boardname","busid","driver"]
_listattr = {"option" : OptionList}
def makeSection(self,comment,row):
return VideoPortSection(comment,row)
############################################################################
class VideoPortSection(SubSection):
_attr = []
_quoteattr = ["identifier"]
_listattr = {"option" : OptionList}
############################################################################
class XorgConfig(ConfigContainer):
_sectiontypes = { \
'device': DeviceSection,
'dri': DriSection,
'extensions': ExtensionsSection,
'files': FilesSection,
'inputdevice': InputDeviceSection,
'keyboard': KeyboardSection,
'modes': ModesSection,
'monitor': MonitorSection,
'module': ModuleSection,
'pointer': PointerSection,
'serverflags': ServerFlagsSection,
'serverlayout': ServerLayoutSection,
'screen': ScreenSection,
'videoadaptor': VideoAdaptorSection}
def makeSection(self,comment,row):
lname = row[1].lower()
try:
return self._sectiontypes[lname](comment,row)
except KeyError:
return ConfigContainer.makeSection(self,comment,row)
def toString(self,depth=-1):
return ConfigContainer.toString(self,depth)
def writeConfig(self,filename):
try:
encoding = locale.getpreferredencoding()
except locale.Error:
encoding = 'ANSI_X3.4-1968'
fhandle = codecs.open(filename,'w',locale.getpreferredencoding())
fhandle.write(self.toString())
fhandle.close()
def createUniqueIdentifier(self,stem="id"):
"""Create a unique identifier for a section
"""
# Build a list of used identifiers
used_identifiers = []
for name in ['monitor','videoadaptor','inputdevice','serverlayout','device','screen']:
for section in self.getSections(name):
if section.identifier is not None:
used_identifiers.append(section.identifier)
# Generate a identifier that is not in use.
i = 1
while (stem+str(i)) in used_identifiers:
i += 1
return stem+str(i)
############################################################################
def addxorg(context, stack):
# Add minimal xorg.conf if it's missing
rows = [[None, [u'Section', u'Device']], [None, [u'Identifier', u'Configured Video Device']], \
[None, [u'EndSection']], [None, [u'Section', u'Monitor']], \
[None, [u'Identifier', u'Configured Monitor']], \
[None, [u'EndSection']], [None, [u'Section', u'Screen']], \
[None, [u'Identifier', u'Default Screen']], \
[None, [u'Monitor', u'Configured Monitor']], [None, [u'EndSection']], \
[None, [u'Section', u'ServerLayout']], \
[None, [u'Identifier', u'Default Layout']], \
[None, [u'screen', u'Default Screen']], \
[None, [u'EndSection']]]
for data in rows:
rowcomment = data[0]
row = data[1]
try:
first = row[0].lower()
if context.isSection(first):
section = context.makeSection(rowcomment,row)
context.append(section)
stack.append(context)
context = section
context_class = context.__class__
elif context.isEndSection(first):
context = stack.pop()
elif context.isListAttr(first):
context.makeListAttr(rowcomment,row)
else:
newline = context.makeLine(rowcomment,row)
if newline is None:
raise ParseException,"Unknown line type '%s' on line %i" % (first,line)
context.append(newline)
except IndexError:
context.append(ConfigLine(rowcomment,row))
return context, section, stack, first
############################################################################
def addServerLayout(context, section, stack, first):
# Add empty server layout section to xorg.conf if it's missing
rows = [[None, [u'Section', u'ServerLayout']], \
[None, [u'Identifier', u'Default Layout']], \
[None, [u'screen', u'0', u'Default Screen', u'0', u'0']], \
[None, [u'Inputdevice', u'Generic Keyboard']], \
[None, [u'Inputdevice', u'Configured Mouse']], \
[None, []], ["Uncomment if you have a wacom tablet", []], \
["InputDevice \"stylus\" \"SendCoreEvents\"", []], \
[" InputDevice \"cursor\" \"SendCoreEvents\"", []], \
[" InputDevice \"eraser\" \"SendCoreEvents\"", []], \
[None, [u'Inputdevice', u'Synaptics Touchpad']], [None, [u'EndSection']]]
for data in rows:
rowcomment = data[0]
row = data[1]
try:
first = row[0].lower()
if context.isSection(first):
section = context.makeSection(rowcomment,row)
context.append(section)
stack.append(context)
context = section
context_class = context.__class__
elif context.isEndSection(first):
context = stack.pop()
elif context.isListAttr(first):
context.makeListAttr(rowcomment,row)
else:
newline = context.makeLine(rowcomment,row)
if newline is None:
raise ParseException,"Unknown line type '%s' on line %i" % (first,line)
context.append(newline)
except IndexError:
context.append(ConfigLine(rowcomment,row))
return context, section, stack, first
############################################################################
def readConfig(filename, check_exists=False):
context = XorgConfig()
stack = []
line = 1
hasserverlayout = False
hasxorg = True
try:
import os
try:
if os.path.isfile('/etc/X11/xorg.conf'):
if os.path.getsize(filename) == 0:
raise IOError, "xorg.conf is empty - making up config"
else:
raise IOError, "xorg.conf is empty - making up config"
except OSError, errmsg:
raise IOError, errmsg
for row in XorgconfCVSReader(filename=filename).readlines():
try:
first = row[0].lower()
if context.isSection(first):
section = context.makeSection(row.comment,row)
if section._name == 'ServerLayout':
hasserverlayout = True
context.append(section)
stack.append(context)
context = section
context_class = context.__class__
elif context.isEndSection(first):
context = stack.pop()
elif context.isListAttr(first):
context.makeListAttr(row.comment,row)
else:
newline = context.makeLine(row.comment,row)
if newline is None:
raise ParseException,"Unknown line type '%s' on line %i" % (first,line)
context.append(newline)
except IndexError:
context.append(ConfigLine(row.comment,row))
line += 1
except IOError, errmsg:
ermsg = str(errmsg)
print "IOError", ermsg, " - will create xorg.conf if possible."
if ermsg[:9] == "[Errno 2]": # No such file or directory:
hasxorg = False
addxorg(context, stack)
try:
xorgfile = open(filename, 'a')
xorgfile.close()
except IOError, errmsg:
ermsg = str(errmsg)
if ermsg[:9] == "[Errno 13]": #Permission denied:
pass
# Since we aren't root, changes can't be made anyway.
elif ermsg[:9] == "xorg.conf": # xorg.conf exists, but is empty
hasxorg = False
addxorg(context, stack)
if len(stack)!=0:
raise ParseException,"Unexpected end of file on line %i" % line
if not hasserverlayout and hasxorg:
addServerLayout(context, section, stack, first)
if check_exists:
return context, hasxorg
else:
return context
############################################################################
class ParseException(Exception):
def __init__(self,*args):
Exception.__init__(self,*args)
############################################################################
def toBoolean(value):
return unicode(value).lower() in ['on','true','1','yes']
############################################################################
# Our own class for reading CSV file. This version supports unicode while
# standard Python (2.4) version doesn't. Hence the need for this class.
#
class XorgconfCVSReader(object):
def __init__(self,filename=None, text=None):
assert filename is not None or text is not None
STATE_DELIMITER = 0
STATE_ITEM = 1
STATE_QUOTE = 2
QUOTE = '"'
LINE_COMMENT = '#'
class CommentList(list):
def __init__(self):
list.__init__(self)
self.comment = None
if filename is not None:
try:
loc = locale.getpreferredencoding()
except locale.Error:
loc = 'ANSI_X3.4-1968'
fhandle = codecs.open(filename,'r',loc,'replace')
source_lines = fhandle.readlines()
fhandle.close()
else:
source_lines = text.split('\n')
self.lines = []
for line in source_lines:
if len(line)!=0 and line[-1]=='\n':
line = line[:-1]
state = STATE_DELIMITER
row = CommentList()
item = None
for i in range(len(line)):
c = line[i]
if state==STATE_DELIMITER:
if not c.isspace():
if c==QUOTE:
item = []
state = STATE_QUOTE
elif c==LINE_COMMENT:
row.comment = line[i+1:]
break
else:
item = []
item.append(c)
state = STATE_ITEM
elif state==STATE_ITEM:
if c.isspace():
row.append(u''.join(item))
state = STATE_DELIMITER
item = None
else:
item.append(c)
elif state==STATE_QUOTE:
if c==QUOTE:
row.append(u''.join(item))
state = STATE_DELIMITER
item = None
else:
item.append(c)
if item is not None:
row.append(u''.join(item))
self.lines.append(row)
def readlines(self):
return self.lines
############################################################################
if __name__=='__main__':
import sys
if len(sys.argv)==2:
filename = sys.argv[1]
else:
filename = "/etc/X11/xorg.conf"
print "Reading",filename
c = readConfig(filename)
print c.toString()