diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2009-05-09 12:09:20 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2009-05-09 12:09:20 +0200 |
commit | b09932ec598a6f9366c819855a2dbc6f34a7209c (patch) | |
tree | 4da358002628fc919591f449323cef6b4f856526 | |
parent | 489d381c50a2c639ea27bc8fac7f58c145ac8679 (diff) |
keymap: add fdi2rules.py
Tool to 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). After running this, the result should be cleaned up,
and errors from unknown operators etc. corrected manually.
-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"' |