203 lines
6.8 KiB
Python
203 lines
6.8 KiB
Python
|
#!/usr/bin/python
|
||
|
#
|
||
|
# Convert xorg keys from hal FDIs files to xorg.conf InputClass sections.
|
||
|
# Modified from Martin Pitt's original fdi2mpi.py script:
|
||
|
# http://cgit.freedesktop.org/media-player-info/tree/tools/fdi2mpi.py
|
||
|
#
|
||
|
# (C) 2010 Dan Nicholson
|
||
|
# (C) 2009 Canonical Ltd.
|
||
|
# Author: Dan Nicholson <dbn.lists@gmail.com>
|
||
|
# Author: Martin Pitt <martin.pitt@ubuntu.com>
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
# of this software and associated documentation files (the "Software"), to
|
||
|
# deal in the Software without restriction, including without limitation the
|
||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||
|
# fur- nished to do so, subject to the following conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included in
|
||
|
# all copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
# FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
|
||
|
# NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
import sys, xml.dom.minidom
|
||
|
|
||
|
# dict converting <match> tags to Match* entries
|
||
|
match_table = {
|
||
|
'info.product': 'MatchProduct',
|
||
|
'input.product': 'MatchProduct',
|
||
|
'info.vendor': 'MatchVendor',
|
||
|
'input.vendor': 'MatchVendor',
|
||
|
'info.device': 'MatchDevicePath',
|
||
|
'linux.device_file': 'MatchDevicePath',
|
||
|
'/org/freedesktop/Hal/devices/computer:system.kernel.name': 'MatchOS',
|
||
|
'@info.parent:pnp.id': 'MatchPnPID',
|
||
|
}
|
||
|
|
||
|
# dict converting info.capabilities list to Match* entries
|
||
|
cap_match_table = {
|
||
|
'input.keys': 'MatchIsKeyboard',
|
||
|
'input.keyboard': 'MatchIsKeyboard',
|
||
|
'input.keypad': 'MatchIsKeyboard',
|
||
|
'input.mouse': 'MatchIsPointer',
|
||
|
'input.joystick': 'MatchIsJoystick',
|
||
|
'input.tablet': 'MatchIsTablet',
|
||
|
'input.touchpad': 'MatchIsTouchpad',
|
||
|
'input.touchscreen': 'MatchIsTouchscreen',
|
||
|
}
|
||
|
|
||
|
def device_glob(path):
|
||
|
'''Convert a contains device path to a glob entry'''
|
||
|
if path[0] != '/':
|
||
|
path = '*' + path
|
||
|
return path + '*'
|
||
|
|
||
|
def parse_match(node):
|
||
|
'''Parse a <match> tag to a tuple with InputClass values'''
|
||
|
match = None
|
||
|
value = None
|
||
|
booltype = False
|
||
|
|
||
|
# see what type of key we have
|
||
|
if node.attributes.has_key('key'):
|
||
|
key = node.attributes['key'].nodeValue
|
||
|
if key in match_table:
|
||
|
match = match_table[key]
|
||
|
elif key == 'info.capabilities':
|
||
|
booltype = True
|
||
|
|
||
|
# bail out now if it's unrecognized
|
||
|
if not match and not booltype:
|
||
|
return (match, value)
|
||
|
|
||
|
if node.attributes.has_key('string'):
|
||
|
value = node.attributes['string'].nodeValue
|
||
|
elif node.attributes.has_key('contains'):
|
||
|
value = node.attributes['contains'].nodeValue
|
||
|
if match == 'MatchDevicePath':
|
||
|
value = device_glob(value)
|
||
|
elif booltype and value in cap_match_table:
|
||
|
match = cap_match_table[value]
|
||
|
value = 'yes'
|
||
|
elif node.attributes.has_key('string_outof'):
|
||
|
value = node.attributes['string_outof'].nodeValue.replace(';','|')
|
||
|
elif node.attributes.has_key('contains_outof'):
|
||
|
all_values = node.attributes['contains_outof'].nodeValue.split(';')
|
||
|
for v in all_values:
|
||
|
if match == 'MatchDevicePath':
|
||
|
v = device_glob(v)
|
||
|
elif match == 'MatchPnPID' and len(v) < 7:
|
||
|
v += '*'
|
||
|
if value:
|
||
|
value += '|' + v
|
||
|
else:
|
||
|
value = v
|
||
|
|
||
|
return (match, value)
|
||
|
|
||
|
def parse_options(node):
|
||
|
'''Parse the x11_* options and return InputClass entries'''
|
||
|
driver = ''
|
||
|
ignore = False
|
||
|
options = []
|
||
|
for n in node.childNodes:
|
||
|
if n.nodeType != xml.dom.minidom.Node.ELEMENT_NODE:
|
||
|
continue
|
||
|
|
||
|
tag = n.tagName
|
||
|
key = n.attributes['key'].nodeValue
|
||
|
value = ''
|
||
|
|
||
|
if n.hasChildNodes():
|
||
|
content_node = n.childNodes[0]
|
||
|
assert content_node.nodeType == xml.dom.Node.TEXT_NODE
|
||
|
value = content_node.nodeValue
|
||
|
|
||
|
if tag == 'match':
|
||
|
continue
|
||
|
assert tag in ('addset', 'merge', 'append', 'remove')
|
||
|
|
||
|
if tag == 'remove' and key == 'input.x11_driver':
|
||
|
ignore = True
|
||
|
elif key == 'input.x11_driver':
|
||
|
driver = value
|
||
|
elif key.startswith('input.x11_options.'):
|
||
|
option = key.split('.', 2)[2]
|
||
|
options.append((option, value))
|
||
|
|
||
|
return (driver, ignore, options)
|
||
|
|
||
|
def is_match_node(node):
|
||
|
'''Check if a node is a <match> element'''
|
||
|
return node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and \
|
||
|
node.tagName == 'match'
|
||
|
|
||
|
def parse_all_matches(node):
|
||
|
'''Parse a x11 match tag and any parents that don't supply their
|
||
|
own options'''
|
||
|
matches = []
|
||
|
|
||
|
while True:
|
||
|
(key, value) = parse_match(node)
|
||
|
if key and value:
|
||
|
matches.append((key, value))
|
||
|
|
||
|
# walk up to a parent match node
|
||
|
node = node.parentNode
|
||
|
if node == None or not is_match_node(node):
|
||
|
break
|
||
|
|
||
|
# leave if there other options at this level
|
||
|
children = set([n.tagName for n in node.childNodes
|
||
|
if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE])
|
||
|
if children & set(['addset', 'merge', 'append']):
|
||
|
break
|
||
|
|
||
|
return matches
|
||
|
|
||
|
# stupid counter to give "unique" rule names
|
||
|
num_sections = 1
|
||
|
def print_section(matches, driver, ignore, options):
|
||
|
'''Print a valid InputClass section to stdout'''
|
||
|
global num_sections
|
||
|
print 'Section "InputClass"'
|
||
|
print '\tIdentifier "Converted Class %d"' % num_sections
|
||
|
num_sections += 1
|
||
|
for m, v in matches:
|
||
|
print '\t%s "%s"' % (m, v)
|
||
|
if driver:
|
||
|
print '\tDriver "%s"' % driver
|
||
|
if ignore:
|
||
|
print '\tOption "Ignore" "yes"'
|
||
|
for o, v in options:
|
||
|
print '\tOption "%s" "%s"' % (o, v)
|
||
|
print 'EndSection'
|
||
|
|
||
|
def parse_fdi(fdi):
|
||
|
'''Parse x11 matches from fdi'''
|
||
|
# find all <match> leaf nodes
|
||
|
num = 0
|
||
|
for match_node in fdi.getElementsByTagName('match'):
|
||
|
children = set([n.tagName for n in match_node.childNodes
|
||
|
if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE])
|
||
|
|
||
|
# see if there are any options at this level
|
||
|
(driver, ignore, options) = parse_options(match_node)
|
||
|
if not driver and not ignore and not options:
|
||
|
continue
|
||
|
|
||
|
matches = parse_all_matches(match_node)
|
||
|
if num > 0:
|
||
|
print
|
||
|
print_section(matches, driver, ignore, options)
|
||
|
num += 1
|
||
|
|
||
|
for f in sys.argv[1:]:
|
||
|
parse_fdi(xml.dom.minidom.parse(f))
|