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.
904 lines
32 KiB
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()
|
|
|