summaryrefslogtreecommitdiffstats
path: root/keymap
diff options
context:
space:
mode:
Diffstat (limited to 'keymap')
-rwxr-xr-xkeymap/fdi2rules.py179
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"'