fdi2iclass.py revision 706f2543
11.2.4.1Stls#!/usr/bin/python 21.1Srkujawa# 31.1Srkujawa# Convert xorg keys from hal FDIs files to xorg.conf InputClass sections. 41.1Srkujawa# Modified from Martin Pitt's original fdi2mpi.py script: 51.1Srkujawa# http://cgit.freedesktop.org/media-player-info/tree/tools/fdi2mpi.py 61.1Srkujawa# 71.1Srkujawa# (C) 2010 Dan Nicholson 81.1Srkujawa# (C) 2009 Canonical Ltd. 91.1Srkujawa# Author: Dan Nicholson <dbn.lists@gmail.com> 101.1Srkujawa# Author: Martin Pitt <martin.pitt@ubuntu.com> 111.1Srkujawa# 121.1Srkujawa# Permission is hereby granted, free of charge, to any person obtaining a copy 131.1Srkujawa# of this software and associated documentation files (the "Software"), to 141.1Srkujawa# deal in the Software without restriction, including without limitation the 151.1Srkujawa# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 161.1Srkujawa# sell copies of the Software, and to permit persons to whom the Software is 171.1Srkujawa# fur- nished to do so, subject to the following conditions: 181.1Srkujawa# 191.1Srkujawa# The above copyright notice and this permission notice shall be included in 201.1Srkujawa# all copies or substantial portions of the Software. 211.1Srkujawa# 221.1Srkujawa# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 231.1Srkujawa# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 241.1Srkujawa# FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 251.1Srkujawa# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 261.1Srkujawa# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- 271.1Srkujawa# NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 281.1Srkujawa 291.1Srkujawaimport sys, xml.dom.minidom 301.1Srkujawa 311.1Srkujawa# dict converting <match> tags to Match* entries 321.1Srkujawamatch_table = { 331.1Srkujawa 'info.product': 'MatchProduct', 341.1Srkujawa 'input.product': 'MatchProduct', 351.1Srkujawa 'info.vendor': 'MatchVendor', 361.1Srkujawa 'input.vendor': 'MatchVendor', 371.1Srkujawa 'info.device': 'MatchDevicePath', 381.1Srkujawa 'linux.device_file': 'MatchDevicePath', 391.1Srkujawa '/org/freedesktop/Hal/devices/computer:system.kernel.name': 'MatchOS', 401.1Srkujawa '@info.parent:pnp.id': 'MatchPnPID', 411.1Srkujawa} 421.1Srkujawa 431.1Srkujawa# dict converting info.capabilities list to Match* entries 441.1Srkujawacap_match_table = { 451.1Srkujawa 'input.keys': 'MatchIsKeyboard', 461.1Srkujawa 'input.keyboard': 'MatchIsKeyboard', 471.1Srkujawa 'input.keypad': 'MatchIsKeyboard', 481.1Srkujawa 'input.mouse': 'MatchIsPointer', 491.1Srkujawa 'input.joystick': 'MatchIsJoystick', 501.1Srkujawa 'input.tablet': 'MatchIsTablet', 511.1Srkujawa 'input.touchpad': 'MatchIsTouchpad', 521.1Srkujawa 'input.touchscreen': 'MatchIsTouchscreen', 531.1Srkujawa} 541.1Srkujawa 551.2.4.1Stlsdef device_glob(path): 561.2.4.1Stls '''Convert a contains device path to a glob entry''' 571.1Srkujawa if path[0] != '/': 581.1Srkujawa path = '*' + path 591.1Srkujawa return path + '*' 601.1Srkujawa 611.1Srkujawadef parse_match(node): 621.1Srkujawa '''Parse a <match> tag to a tuple with InputClass values''' 631.1Srkujawa match = None 641.1Srkujawa value = None 651.1Srkujawa booltype = False 661.1Srkujawa 671.1Srkujawa # see what type of key we have 681.1Srkujawa if node.attributes.has_key('key'): 691.1Srkujawa key = node.attributes['key'].nodeValue 701.1Srkujawa if key in match_table: 711.1Srkujawa match = match_table[key] 721.1Srkujawa elif key == 'info.capabilities': 731.1Srkujawa booltype = True 741.1Srkujawa 751.1Srkujawa # bail out now if it's unrecognized 761.1Srkujawa if not match and not booltype: 771.1Srkujawa return (match, value) 781.1Srkujawa 791.1Srkujawa if node.attributes.has_key('string'): 801.1Srkujawa value = node.attributes['string'].nodeValue 811.1Srkujawa elif node.attributes.has_key('contains'): 821.1Srkujawa value = node.attributes['contains'].nodeValue 831.1Srkujawa if match == 'MatchDevicePath': 841.1Srkujawa value = device_glob(value) 851.1Srkujawa elif booltype and value in cap_match_table: 861.1Srkujawa match = cap_match_table[value] 871.1Srkujawa value = 'yes' 881.1Srkujawa elif node.attributes.has_key('string_outof'): 891.1Srkujawa value = node.attributes['string_outof'].nodeValue.replace(';','|') 901.1Srkujawa elif node.attributes.has_key('contains_outof'): 911.1Srkujawa all_values = node.attributes['contains_outof'].nodeValue.split(';') 921.1Srkujawa for v in all_values: 931.2Srkujawa if match == 'MatchDevicePath': 941.1Srkujawa v = device_glob(v) 951.1Srkujawa elif match == 'MatchPnPID' and len(v) < 7: 961.1Srkujawa v += '*' 971.1Srkujawa if value: 981.1Srkujawa value += '|' + v 991.1Srkujawa else: 1001.1Srkujawa value = v 1011.1Srkujawa 1021.1Srkujawa return (match, value) 1031.1Srkujawa 1041.1Srkujawadef parse_options(node): 1051.1Srkujawa '''Parse the x11_* options and return InputClass entries''' 106 driver = '' 107 ignore = False 108 options = [] 109 for n in node.childNodes: 110 if n.nodeType != xml.dom.minidom.Node.ELEMENT_NODE: 111 continue 112 113 tag = n.tagName 114 key = n.attributes['key'].nodeValue 115 value = '' 116 117 if n.hasChildNodes(): 118 content_node = n.childNodes[0] 119 assert content_node.nodeType == xml.dom.Node.TEXT_NODE 120 value = content_node.nodeValue 121 122 if tag == 'match': 123 continue 124 assert tag in ('addset', 'merge', 'append', 'remove') 125 126 if tag == 'remove' and key == 'input.x11_driver': 127 ignore = True 128 elif key == 'input.x11_driver': 129 driver = value 130 elif key.startswith('input.x11_options.'): 131 option = key.split('.', 2)[2] 132 options.append((option, value)) 133 134 return (driver, ignore, options) 135 136def is_match_node(node): 137 '''Check if a node is a <match> element''' 138 return node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and \ 139 node.tagName == 'match' 140 141def parse_all_matches(node): 142 '''Parse a x11 match tag and any parents that don't supply their 143 own options''' 144 matches = [] 145 146 while True: 147 (key, value) = parse_match(node) 148 if key and value: 149 matches.append((key, value)) 150 151 # walk up to a parent match node 152 node = node.parentNode 153 if node == None or not is_match_node(node): 154 break 155 156 # leave if there other options at this level 157 children = set([n.tagName for n in node.childNodes 158 if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 159 if children & set(['addset', 'merge', 'append']): 160 break 161 162 return matches 163 164# stupid counter to give "unique" rule names 165num_sections = 1 166def print_section(matches, driver, ignore, options): 167 '''Print a valid InputClass section to stdout''' 168 global num_sections 169 print 'Section "InputClass"' 170 print '\tIdentifier "Converted Class %d"' % num_sections 171 num_sections += 1 172 for m, v in matches: 173 print '\t%s "%s"' % (m, v) 174 if driver: 175 print '\tDriver "%s"' % driver 176 if ignore: 177 print '\tOption "Ignore" "yes"' 178 for o, v in options: 179 print '\tOption "%s" "%s"' % (o, v) 180 print 'EndSection' 181 182def parse_fdi(fdi): 183 '''Parse x11 matches from fdi''' 184 # find all <match> leaf nodes 185 num = 0 186 for match_node in fdi.getElementsByTagName('match'): 187 children = set([n.tagName for n in match_node.childNodes 188 if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) 189 190 # see if there are any options at this level 191 (driver, ignore, options) = parse_options(match_node) 192 if not driver and not ignore and not options: 193 continue 194 195 matches = parse_all_matches(match_node) 196 if num > 0: 197 print 198 print_section(matches, driver, ignore, options) 199 num += 1 200 201for f in sys.argv[1:]: 202 parse_fdi(xml.dom.minidom.parse(f)) 203