diff options
Diffstat (limited to 'keymap/fdi2rules.py')
-rwxr-xr-x | keymap/fdi2rules.py | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/keymap/fdi2rules.py b/keymap/fdi2rules.py new file mode 100755 index 0000000..719877e --- /dev/null +++ b/keymap/fdi2rules.py @@ -0,0 +1,179 @@ +#!/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"' |