Script converting hal fdi to InputClass sections
Dan Nicholson
dbn.lists at gmail.com
Sat Apr 10 11:22:59 PDT 2010
Now that the server has moved to using udev on linux, we have many
configurations that were working using hal fdi files that will no longer
be applied. The proper solution is to convert these configurations to
using InputClass sections. I've taken a script that Martin Pitt wrote
for parsing media player info from fdi files and converted it to
generating InputClass sections. See the attached script. My python
skills suck, so it might be suboptimal in a lot of ways.
One of the things I noticed when working on this is that some of the hal
matches don't have corresponding InputClass Match* entries. Here are a
couple things I'd like to add to make this transition easier.
MatchOS - This affects the generic rules that the server installs on all
platforms. Our current x11-input.fdi allows evdev to be the driver only
on linux. It would be nice to add 'MatchOS "linux"' to 10-evdev.conf so
that it could be installed unconditionally.
PnP id matching - Since the serial devices don't export a name, most of
the matching is done on the PnP ID. Here's the wacom fdi file:
http://cgit.freedesktop.org/~whot/xf86-input-wacom/tree/fdi/wacom.fdi
I'd like to add a MatchPnPID entry. Matching could be substring or glob.
By the same token, it would be nice to match USB or PCI IDs, too. Maybe
you could have three tags (MatchPnPID, MatchUSBID, MatchPCIID) or a
magic MatchBusID entry.
It would be nice to put this script in the server repo, but it's GPLv2+.
Martin, would you be opposed to relicensing your contributions to X11 or
dual licensing? The original script is here:
http://cgit.freedesktop.org/~teuf/media-player-info/tree/tools/fdi2mpi.py
Let me know what you guys think.
--
Dan
-------------- next part --------------
#!/usr/bin/python
#
# Convert xorg keys from hal FDIs files to xorg.confInputClass sections.
#
# (C) 2010 Dan Nicholson
# (C) 2009 Canonical Ltd.
# Author: Dan Nicholson <dbn.lists at gmail.com>
# Author: Martin Pitt <martin.pitt at ubuntu.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.
#
# keymap is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with keymap; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
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',
# Keys below aren't supported yet
'/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)
if value:
value += '|' + v
else:
value = v
if match == 'MatchOS':
value = value.lower()
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
def print_section(matches, driver, ignore, options):
''' Print a valid InputClass section to stdout'''
print 'Section "InputClass"'
print '\tIdentifier "xxx"'
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))
More information about the xorg-devel
mailing list