1*4882a593Smuzhiyun#!/usr/bin/python 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Convert xorg keys from hal FDIs files to xorg.conf InputClass sections. 4*4882a593Smuzhiyun# Modified from Martin Pitt's original fdi2mpi.py script: 5*4882a593Smuzhiyun# http://cgit.freedesktop.org/media-player-info/tree/tools/fdi2mpi.py 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# (C) 2010 Dan Nicholson 8*4882a593Smuzhiyun# (C) 2009 Canonical Ltd. 9*4882a593Smuzhiyun# Author: Dan Nicholson <dbn.lists@gmail.com> 10*4882a593Smuzhiyun# Author: Martin Pitt <martin.pitt@ubuntu.com> 11*4882a593Smuzhiyun# 12*4882a593Smuzhiyun# Permission is hereby granted, free of charge, to any person obtaining a copy 13*4882a593Smuzhiyun# of this software and associated documentation files (the "Software"), to 14*4882a593Smuzhiyun# deal in the Software without restriction, including without limitation the 15*4882a593Smuzhiyun# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16*4882a593Smuzhiyun# sell copies of the Software, and to permit persons to whom the Software is 17*4882a593Smuzhiyun# fur- nished to do so, subject to the following conditions: 18*4882a593Smuzhiyun# 19*4882a593Smuzhiyun# The above copyright notice and this permission notice shall be included in 20*4882a593Smuzhiyun# all copies or substantial portions of the Software. 21*4882a593Smuzhiyun# 22*4882a593Smuzhiyun# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23*4882a593Smuzhiyun# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24*4882a593Smuzhiyun# FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25*4882a593Smuzhiyun# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26*4882a593Smuzhiyun# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- 27*4882a593Smuzhiyun# NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28*4882a593Smuzhiyun 29*4882a593Smuzhiyunimport sys, xml.dom.minidom 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun# dict converting <match> tags to Match* entries 32*4882a593Smuzhiyunmatch_table = { 33*4882a593Smuzhiyun 'info.product': 'MatchProduct', 34*4882a593Smuzhiyun 'input.product': 'MatchProduct', 35*4882a593Smuzhiyun 'info.vendor': 'MatchVendor', 36*4882a593Smuzhiyun 'input.vendor': 'MatchVendor', 37*4882a593Smuzhiyun 'info.device': 'MatchDevicePath', 38*4882a593Smuzhiyun 'linux.device_file': 'MatchDevicePath', 39*4882a593Smuzhiyun '/org/freedesktop/Hal/devices/computer:system.kernel.name': 'MatchOS', 40*4882a593Smuzhiyun '@info.parent:pnp.id': 'MatchPnPID', 41*4882a593Smuzhiyun} 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun# dict converting info.capabilities list to Match* entries 44*4882a593Smuzhiyuncap_match_table = { 45*4882a593Smuzhiyun 'input.keys': 'MatchIsKeyboard', 46*4882a593Smuzhiyun 'input.keyboard': 'MatchIsKeyboard', 47*4882a593Smuzhiyun 'input.keypad': 'MatchIsKeyboard', 48*4882a593Smuzhiyun 'input.mouse': 'MatchIsPointer', 49*4882a593Smuzhiyun 'input.joystick': 'MatchIsJoystick', 50*4882a593Smuzhiyun 'input.tablet': 'MatchIsTablet', 51*4882a593Smuzhiyun 'input.touchpad': 'MatchIsTouchpad', 52*4882a593Smuzhiyun 'input.touchscreen': 'MatchIsTouchscreen', 53*4882a593Smuzhiyun} 54*4882a593Smuzhiyun 55*4882a593Smuzhiyundef device_glob(path): 56*4882a593Smuzhiyun '''Convert a contains device path to a glob entry''' 57*4882a593Smuzhiyun if path[0] != '/': 58*4882a593Smuzhiyun path = '*' + path 59*4882a593Smuzhiyun return path + '*' 60*4882a593Smuzhiyun 61*4882a593Smuzhiyundef parse_match(node): 62*4882a593Smuzhiyun '''Parse a <match> tag to a tuple with InputClass values''' 63*4882a593Smuzhiyun match = None 64*4882a593Smuzhiyun value = None 65*4882a593Smuzhiyun booltype = False 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun # see what type of key we have 68*4882a593Smuzhiyun if node.attributes.has_key('key'): 69*4882a593Smuzhiyun key = node.attributes['key'].nodeValue 70*4882a593Smuzhiyun if key in match_table: 71*4882a593Smuzhiyun match = match_table[key] 72*4882a593Smuzhiyun elif key == 'info.capabilities': 73*4882a593Smuzhiyun booltype = True 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun # bail out now if it's unrecognized 76*4882a593Smuzhiyun if not match and not booltype: 77*4882a593Smuzhiyun return (match, value) 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun if node.attributes.has_key('string'): 80*4882a593Smuzhiyun value = node.attributes['string'].nodeValue 81*4882a593Smuzhiyun elif node.attributes.has_key('contains'): 82*4882a593Smuzhiyun value = node.attributes['contains'].nodeValue 83*4882a593Smuzhiyun if match == 'MatchDevicePath': 84*4882a593Smuzhiyun value = device_glob(value) 85*4882a593Smuzhiyun elif booltype and value in cap_match_table: 86*4882a593Smuzhiyun match = cap_match_table[value] 87*4882a593Smuzhiyun value = 'yes' 88*4882a593Smuzhiyun elif node.attributes.has_key('string_outof'): 89*4882a593Smuzhiyun value = node.attributes['string_outof'].nodeValue.replace(';','|') 90*4882a593Smuzhiyun elif node.attributes.has_key('contains_outof'): 91*4882a593Smuzhiyun all_values = node.attributes['contains_outof'].nodeValue.split(';') 92*4882a593Smuzhiyun for v in all_values: 93*4882a593Smuzhiyun if match == 'MatchDevicePath': 94*4882a593Smuzhiyun v = device_glob(v) 95*4882a593Smuzhiyun elif match == 'MatchPnPID' and len(v) < 7: 96*4882a593Smuzhiyun v += '*' 97*4882a593Smuzhiyun if value: 98*4882a593Smuzhiyun value += '|' + v 99*4882a593Smuzhiyun else: 100*4882a593Smuzhiyun value = v 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun return (match, value) 103*4882a593Smuzhiyun 104*4882a593Smuzhiyundef parse_options(node): 105*4882a593Smuzhiyun '''Parse the x11_* options and return InputClass entries''' 106*4882a593Smuzhiyun driver = '' 107*4882a593Smuzhiyun ignore = False 108*4882a593Smuzhiyun options = [] 109*4882a593Smuzhiyun for n in node.childNodes: 110*4882a593Smuzhiyun if n.nodeType != xml.dom.minidom.Node.ELEMENT_NODE: 111*4882a593Smuzhiyun continue 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun tag = n.tagName 114*4882a593Smuzhiyun key = n.attributes['key'].nodeValue 115*4882a593Smuzhiyun value = '' 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun if n.hasChildNodes(): 118*4882a593Smuzhiyun content_node = n.childNodes[0] 119*4882a593Smuzhiyun assert content_node.nodeType == xml.dom.Node.TEXT_NODE 120*4882a593Smuzhiyun value = content_node.nodeValue 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun if tag == 'match': 123*4882a593Smuzhiyun continue 124*4882a593Smuzhiyun assert tag in ('addset', 'merge', 'append', 'remove') 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun if tag == 'remove' and key == 'input.x11_driver': 127*4882a593Smuzhiyun ignore = True 128*4882a593Smuzhiyun elif key == 'input.x11_driver': 129*4882a593Smuzhiyun driver = value 130*4882a593Smuzhiyun elif key.startswith('input.x11_options.'): 131*4882a593Smuzhiyun option = key.split('.', 2)[2] 132*4882a593Smuzhiyun options.append((option, value)) 133*4882a593Smuzhiyun 134*4882a593Smuzhiyun return (driver, ignore, options) 135*4882a593Smuzhiyun 136*4882a593Smuzhiyundef is_match_node(node): 137*4882a593Smuzhiyun '''Check if a node is a <match> element''' 138*4882a593Smuzhiyun return node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and \ 139*4882a593Smuzhiyun node.tagName == 'match' 140*4882a593Smuzhiyun 141*4882a593Smuzhiyundef parse_all_matches(node): 142*4882a593Smuzhiyun '''Parse a x11 match tag and any parents that don't supply their 143*4882a593Smuzhiyun own options''' 144*4882a593Smuzhiyun matches = [] 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun while True: 147*4882a593Smuzhiyun (key, value) = parse_match(node) 148*4882a593Smuzhiyun if key and value: 149*4882a593Smuzhiyun matches.append((key, value)) 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun # walk up to a parent match node 152*4882a593Smuzhiyun node = node.parentNode 153*4882a593Smuzhiyun if node == None or not is_match_node(node): 154*4882a593Smuzhiyun break 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun # leave if there other options at this level 157*4882a593Smuzhiyun children = set([n.tagName for n in node.childNodes 158*4882a593Smuzhiyun if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 159*4882a593Smuzhiyun if children & set(['addset', 'merge', 'append']): 160*4882a593Smuzhiyun break 161*4882a593Smuzhiyun 162*4882a593Smuzhiyun return matches 163*4882a593Smuzhiyun 164*4882a593Smuzhiyun# stupid counter to give "unique" rule names 165*4882a593Smuzhiyunnum_sections = 1 166*4882a593Smuzhiyundef print_section(matches, driver, ignore, options): 167*4882a593Smuzhiyun '''Print a valid InputClass section to stdout''' 168*4882a593Smuzhiyun global num_sections 169*4882a593Smuzhiyun print 'Section "InputClass"' 170*4882a593Smuzhiyun print '\tIdentifier "Converted Class %d"' % num_sections 171*4882a593Smuzhiyun num_sections += 1 172*4882a593Smuzhiyun for m, v in matches: 173*4882a593Smuzhiyun print '\t%s "%s"' % (m, v) 174*4882a593Smuzhiyun if driver: 175*4882a593Smuzhiyun print '\tDriver "%s"' % driver 176*4882a593Smuzhiyun if ignore: 177*4882a593Smuzhiyun print '\tOption "Ignore" "yes"' 178*4882a593Smuzhiyun for o, v in options: 179*4882a593Smuzhiyun print '\tOption "%s" "%s"' % (o, v) 180*4882a593Smuzhiyun print 'EndSection' 181*4882a593Smuzhiyun 182*4882a593Smuzhiyundef parse_fdi(fdi): 183*4882a593Smuzhiyun '''Parse x11 matches from fdi''' 184*4882a593Smuzhiyun # find all <match> leaf nodes 185*4882a593Smuzhiyun num = 0 186*4882a593Smuzhiyun for match_node in fdi.getElementsByTagName('match'): 187*4882a593Smuzhiyun children = set([n.tagName for n in match_node.childNodes 188*4882a593Smuzhiyun if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun # see if there are any options at this level 191*4882a593Smuzhiyun (driver, ignore, options) = parse_options(match_node) 192*4882a593Smuzhiyun if not driver and not ignore and not options: 193*4882a593Smuzhiyun continue 194*4882a593Smuzhiyun 195*4882a593Smuzhiyun matches = parse_all_matches(match_node) 196*4882a593Smuzhiyun if num > 0: 197*4882a593Smuzhiyun print 198*4882a593Smuzhiyun print_section(matches, driver, ignore, options) 199*4882a593Smuzhiyun num += 1 200*4882a593Smuzhiyun 201*4882a593Smuzhiyunfor f in sys.argv[1:]: 202*4882a593Smuzhiyun parse_fdi(xml.dom.minidom.parse(f)) 203