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