1706f2543Smrg#!/usr/bin/python 2706f2543Smrg# 3706f2543Smrg# Convert xorg keys from hal FDIs files to xorg.conf InputClass sections. 4706f2543Smrg# Modified from Martin Pitt's original fdi2mpi.py script: 5706f2543Smrg# http://cgit.freedesktop.org/media-player-info/tree/tools/fdi2mpi.py 6706f2543Smrg# 7706f2543Smrg# (C) 2010 Dan Nicholson 8706f2543Smrg# (C) 2009 Canonical Ltd. 9706f2543Smrg# Author: Dan Nicholson <dbn.lists@gmail.com> 10706f2543Smrg# Author: Martin Pitt <martin.pitt@ubuntu.com> 11706f2543Smrg# 12706f2543Smrg# Permission is hereby granted, free of charge, to any person obtaining a copy 13706f2543Smrg# of this software and associated documentation files (the "Software"), to 14706f2543Smrg# deal in the Software without restriction, including without limitation the 15706f2543Smrg# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16706f2543Smrg# sell copies of the Software, and to permit persons to whom the Software is 17706f2543Smrg# fur- nished to do so, subject to the following conditions: 18706f2543Smrg# 19706f2543Smrg# The above copyright notice and this permission notice shall be included in 20706f2543Smrg# all copies or substantial portions of the Software. 21706f2543Smrg# 22706f2543Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23706f2543Smrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24706f2543Smrg# FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25706f2543Smrg# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26706f2543Smrg# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- 27706f2543Smrg# NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28706f2543Smrg 29706f2543Smrgimport sys, xml.dom.minidom 30706f2543Smrg 31706f2543Smrg# dict converting <match> tags to Match* entries 32706f2543Smrgmatch_table = { 33706f2543Smrg 'info.product': 'MatchProduct', 34706f2543Smrg 'input.product': 'MatchProduct', 35706f2543Smrg 'info.vendor': 'MatchVendor', 36706f2543Smrg 'input.vendor': 'MatchVendor', 37706f2543Smrg 'info.device': 'MatchDevicePath', 38706f2543Smrg 'linux.device_file': 'MatchDevicePath', 39706f2543Smrg '/org/freedesktop/Hal/devices/computer:system.kernel.name': 'MatchOS', 40706f2543Smrg '@info.parent:pnp.id': 'MatchPnPID', 41706f2543Smrg} 42706f2543Smrg 43706f2543Smrg# dict converting info.capabilities list to Match* entries 44706f2543Smrgcap_match_table = { 45706f2543Smrg 'input.keys': 'MatchIsKeyboard', 46706f2543Smrg 'input.keyboard': 'MatchIsKeyboard', 47706f2543Smrg 'input.keypad': 'MatchIsKeyboard', 48706f2543Smrg 'input.mouse': 'MatchIsPointer', 49706f2543Smrg 'input.joystick': 'MatchIsJoystick', 50706f2543Smrg 'input.tablet': 'MatchIsTablet', 51706f2543Smrg 'input.touchpad': 'MatchIsTouchpad', 52706f2543Smrg 'input.touchscreen': 'MatchIsTouchscreen', 53706f2543Smrg} 54706f2543Smrg 55706f2543Smrgdef device_glob(path): 56706f2543Smrg '''Convert a contains device path to a glob entry''' 57706f2543Smrg if path[0] != '/': 58706f2543Smrg path = '*' + path 59706f2543Smrg return path + '*' 60706f2543Smrg 61706f2543Smrgdef parse_match(node): 62706f2543Smrg '''Parse a <match> tag to a tuple with InputClass values''' 63706f2543Smrg match = None 64706f2543Smrg value = None 65706f2543Smrg booltype = False 66706f2543Smrg 67706f2543Smrg # see what type of key we have 68706f2543Smrg if node.attributes.has_key('key'): 69706f2543Smrg key = node.attributes['key'].nodeValue 70706f2543Smrg if key in match_table: 71706f2543Smrg match = match_table[key] 72706f2543Smrg elif key == 'info.capabilities': 73706f2543Smrg booltype = True 74706f2543Smrg 75706f2543Smrg # bail out now if it's unrecognized 76706f2543Smrg if not match and not booltype: 77706f2543Smrg return (match, value) 78706f2543Smrg 79706f2543Smrg if node.attributes.has_key('string'): 80706f2543Smrg value = node.attributes['string'].nodeValue 81706f2543Smrg elif node.attributes.has_key('contains'): 82706f2543Smrg value = node.attributes['contains'].nodeValue 83706f2543Smrg if match == 'MatchDevicePath': 84706f2543Smrg value = device_glob(value) 85706f2543Smrg elif booltype and value in cap_match_table: 86706f2543Smrg match = cap_match_table[value] 87706f2543Smrg value = 'yes' 88706f2543Smrg elif node.attributes.has_key('string_outof'): 89706f2543Smrg value = node.attributes['string_outof'].nodeValue.replace(';','|') 90706f2543Smrg elif node.attributes.has_key('contains_outof'): 91706f2543Smrg all_values = node.attributes['contains_outof'].nodeValue.split(';') 92706f2543Smrg for v in all_values: 93706f2543Smrg if match == 'MatchDevicePath': 94706f2543Smrg v = device_glob(v) 95706f2543Smrg elif match == 'MatchPnPID' and len(v) < 7: 96706f2543Smrg v += '*' 97706f2543Smrg if value: 98706f2543Smrg value += '|' + v 99706f2543Smrg else: 100706f2543Smrg value = v 101706f2543Smrg 102706f2543Smrg return (match, value) 103706f2543Smrg 104706f2543Smrgdef parse_options(node): 105706f2543Smrg '''Parse the x11_* options and return InputClass entries''' 106706f2543Smrg driver = '' 107706f2543Smrg ignore = False 108706f2543Smrg options = [] 109706f2543Smrg for n in node.childNodes: 110706f2543Smrg if n.nodeType != xml.dom.minidom.Node.ELEMENT_NODE: 111706f2543Smrg continue 112706f2543Smrg 113706f2543Smrg tag = n.tagName 114706f2543Smrg key = n.attributes['key'].nodeValue 115706f2543Smrg value = '' 116706f2543Smrg 117706f2543Smrg if n.hasChildNodes(): 118706f2543Smrg content_node = n.childNodes[0] 119706f2543Smrg assert content_node.nodeType == xml.dom.Node.TEXT_NODE 120706f2543Smrg value = content_node.nodeValue 121706f2543Smrg 122706f2543Smrg if tag == 'match': 123706f2543Smrg continue 124706f2543Smrg assert tag in ('addset', 'merge', 'append', 'remove') 125706f2543Smrg 126706f2543Smrg if tag == 'remove' and key == 'input.x11_driver': 127706f2543Smrg ignore = True 128706f2543Smrg elif key == 'input.x11_driver': 129706f2543Smrg driver = value 130706f2543Smrg elif key.startswith('input.x11_options.'): 131706f2543Smrg option = key.split('.', 2)[2] 132706f2543Smrg options.append((option, value)) 133706f2543Smrg 134706f2543Smrg return (driver, ignore, options) 135706f2543Smrg 136706f2543Smrgdef is_match_node(node): 137706f2543Smrg '''Check if a node is a <match> element''' 138706f2543Smrg return node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and \ 139706f2543Smrg node.tagName == 'match' 140706f2543Smrg 141706f2543Smrgdef parse_all_matches(node): 142706f2543Smrg '''Parse a x11 match tag and any parents that don't supply their 143706f2543Smrg own options''' 144706f2543Smrg matches = [] 145706f2543Smrg 146706f2543Smrg while True: 147706f2543Smrg (key, value) = parse_match(node) 148706f2543Smrg if key and value: 149706f2543Smrg matches.append((key, value)) 150706f2543Smrg 151706f2543Smrg # walk up to a parent match node 152706f2543Smrg node = node.parentNode 153706f2543Smrg if node == None or not is_match_node(node): 154706f2543Smrg break 155706f2543Smrg 156706f2543Smrg # leave if there other options at this level 157706f2543Smrg children = set([n.tagName for n in node.childNodes 158706f2543Smrg if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 159706f2543Smrg if children & set(['addset', 'merge', 'append']): 160706f2543Smrg break 161706f2543Smrg 162706f2543Smrg return matches 163706f2543Smrg 164706f2543Smrg# stupid counter to give "unique" rule names 165706f2543Smrgnum_sections = 1 166706f2543Smrgdef print_section(matches, driver, ignore, options): 167706f2543Smrg '''Print a valid InputClass section to stdout''' 168706f2543Smrg global num_sections 169706f2543Smrg print 'Section "InputClass"' 170706f2543Smrg print '\tIdentifier "Converted Class %d"' % num_sections 171706f2543Smrg num_sections += 1 172706f2543Smrg for m, v in matches: 173706f2543Smrg print '\t%s "%s"' % (m, v) 174706f2543Smrg if driver: 175706f2543Smrg print '\tDriver "%s"' % driver 176706f2543Smrg if ignore: 177706f2543Smrg print '\tOption "Ignore" "yes"' 178706f2543Smrg for o, v in options: 179706f2543Smrg print '\tOption "%s" "%s"' % (o, v) 180706f2543Smrg print 'EndSection' 181706f2543Smrg 182706f2543Smrgdef parse_fdi(fdi): 183706f2543Smrg '''Parse x11 matches from fdi''' 184706f2543Smrg # find all <match> leaf nodes 185706f2543Smrg num = 0 186706f2543Smrg for match_node in fdi.getElementsByTagName('match'): 187706f2543Smrg children = set([n.tagName for n in match_node.childNodes 188706f2543Smrg if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 189706f2543Smrg 190706f2543Smrg # see if there are any options at this level 191706f2543Smrg (driver, ignore, options) = parse_options(match_node) 192706f2543Smrg if not driver and not ignore and not options: 193706f2543Smrg continue 194706f2543Smrg 195706f2543Smrg matches = parse_all_matches(match_node) 196706f2543Smrg if num > 0: 197706f2543Smrg print 198706f2543Smrg print_section(matches, driver, ignore, options) 199706f2543Smrg num += 1 200706f2543Smrg 201706f2543Smrgfor f in sys.argv[1:]: 202706f2543Smrg parse_fdi(xml.dom.minidom.parse(f)) 203