Logo Search packages:      
Sourcecode: udev-extras version File versions

fdi2rules.py

#!/usr/bin/python
# Convert hal keymap FDIs into udev rules and key map files
# Please note that this is far from perfect, since the mapping between fdi and
# udev rules is not straightforward (and impossible in some cases).
#
# (C) 2009 Canonical Ltd.
# Author: Martin Pitt <martin.pitt@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, os.path, xml.dom.minidom

def string_op2glob(node):
    '''Convert FDI string match operator to a glob.
    
    Return pair (glob, identifier) with the actual glob, and a string
    containing no glob characters or spaces, which is suitable as a file
    name.
    '''
    if node.attributes.has_key('string'):
        v = node.attributes['string'].nodeValue
        return (v, v.replace(' ', '_').replace('/', '_').lower())

    if node.attributes.has_key('prefix'):
        v = node.attributes['prefix'].nodeValue
        return (v + '*', v.replace(' ', '_').replace('/', '_').lower())

    if node.attributes.has_key('suffix'):
        v = node.attributes['suffix'].nodeValue
        return ('*' + v, v.replace(' ', '_').replace('/', '_').lower())

    if node.attributes.has_key('contains'):
        v = node.attributes['contains'].nodeValue
        return ('*' + v + '*', v.replace(' ', '_').replace('/', '_').lower())

    if node.attributes.has_key('contains_outof'):
        alternatives = node.attributes['contains_outof'].nodeValue.split(';')
        gl = '|'.join(['*%s*' % v for v in alternatives])
        id = '_'.join([v.replace(' ', '_').replace('/', '_').lower() for v in alternatives])
        return (gl, id)

    if node.attributes.has_key('string_outof'):
        alternatives = node.attributes['string_outof'].nodeValue.split(';')
        gl = '|'.join(alternatives)
        id = '_'.join([v.replace(' ', '_').lower() for v in alternatives])
        return (gl, id)

    if node.attributes.has_key('contains_ncase'):
        v = node.attributes['contains_ncase'].nodeValue 
        nocase_glob = ''.join(['[%s%s]' % (c.lower(), c.upper()) for c in v])
        return ('*' + nocase_glob + '*', v.replace(' ', '_').lower())

    if node.attributes.has_key('prefix_ncase'):
        v = node.attributes['prefix_ncase'].nodeValue 
        nocase_glob = ''.join(['[%s%s]' % (c.lower(), c.upper()) for c in v])
        return (nocase_glob + '*', v.replace(' ', '_').lower())

    raise NotImplementedError, 'unknown string operator ' + str(node.attributes.keys())

def get_node_comment(node):
    '''Find the next comment node after node'''

    while node:
        node = node.nextSibling
        if node and node.nodeType == xml.dom.Node.COMMENT_NODE:
            return node.nodeValue.strip()
    return None

def normalize_code(code):
    code = int(code, 16)
    if code >= 0xE000:
        return code - 0xE000 + 128
    return code

def create_keylist(node, filename):
    '''Parse key code assignmends from <append> notes and create map file.'''

    if not os.path.isdir('keymaps'):
        os.mkdir('keymaps')
    f = open(os.path.join('keymaps', filename), 'w')
    #f = sys.stdout
    #print '-------------- %s -------------' % filename
    for c in node.childNodes:
        if c.nodeName == 'append' and c.attributes.get('key').nodeValue == 'input.keymap.data':
            content_node = c.childNodes[0]
            assert content_node.nodeType == xml.dom.Node.TEXT_NODE
            (code, name) = content_node.nodeValue.split(':')
            comment = get_node_comment(c)
            if comment:
                print >> f, '0x%X %s # %s' % (normalize_code(code), name, comment)
            else:
                print >> f, '0x%X %s' % (normalize_code(code), name)
    #print '---------------------------'
    f.close()

def get_vendor_node(node):
    '''Find the node's parent which matches the system vendor.'''

    while True:
        node = node.parentNode
        if not node:
            raise SystemError, 'no vendor parent node found'
        if node.nodeName == 'match' and node.attributes['key'].nodeValue == \
                '/org/freedesktop/Hal/devices/computer:system.hardware.vendor':
            return node

def parse_fdi_vendor(node):
    (vendor_glob, fname) = string_op2glob(node)
    print 'ATTR{[dmi/id]sys_vendor}=="%s", RUN+="keymap $name %s"' % (vendor_glob, fname)
    create_keylist(node, fname)

def parse_fdi_product(node):
    (vendor_glob, vendor_fname) = string_op2glob(get_vendor_node(node))
    (product_glob, product_fname) = string_op2glob(node)
    fname = '%s-%s' % (vendor_fname, product_fname)
    print 'ATTR{[dmi/id]sys_vendor}=="%s", ATTR{[dmi/id]product_name}=="%s", RUN+="keymap $name %s"' % (vendor_glob, product_glob, fname)
    create_keylist(node, fname)

def parse_fdi_version(node):
    (vendor_glob, vendor_fname) = string_op2glob(get_vendor_node(node))
    (product_glob, product_fname) = string_op2glob(node)
    fname = '%s-%s' % (vendor_fname, product_fname)
    print 'ATTR{[dmi/id]sys_vendor}=="%s", ATTR{[dmi/id]product_version}=="%s", RUN+="keymap $name %s"' % (vendor_glob, product_glob, fname)
    create_keylist(node, fname)

def parse_fdi(fdi):
    '''Parse keymaps from a fdi node.'''

    for match_node in fdi.getElementsByTagName('match'):
        key = match_node.attributes['key'].nodeValue
        if key == '/org/freedesktop/Hal/devices/computer:system.hardware.vendor':
            # vendor list without model specific quirks
            parse_fdi_vendor(match_node)
        elif key == '/org/freedesktop/Hal/devices/computer:system.hardware.product':
            # product specific list
            parse_fdi_product(match_node)
        elif key == '/org/freedesktop/Hal/devices/computer:system.hardware.version':
            # product version specific list
            parse_fdi_version(match_node)
        elif key == '/org/freedesktop/Hal/devices/computer:system.formfactor':
            # this assumes that a formfactor does not have product submatches
            try:
                (vendor_glob, fname) = string_op2glob(get_vendor_node(match_node))
                create_keylist(match_node, fname)
            except SystemError:
                # formfactor match is at toplevel
                pass
        elif key == '@input.originating_device:info.linux.driver':
            # already covered in udev rules header
            pass
        else:
            print >> sys.stderr, 'WARNING: I do not understand key type', key

# udev rules header
print '''ACTION!="add", GOTO="keyboard_end"
SUBSYSTEM!="input", GOTO="keyboard_end"
DRIVERS=="atkbd", GOTO="keyboard_vendorcheck"
GOTO="keyboard_end"

LABEL="keyboard_vendorcheck"'''

# parse FDI files
for f in sys.argv[1:]:
    parse_fdi(xml.dom.minidom.parse(f))

# udev rules footer
print '\nLABEL="keyboard_end"'

Generated by  Doxygen 1.6.0   Back to index