From 11e03ab28302821c582f417f572cd8f9f6628b85 Mon Sep 17 00:00:00 2001 From: Andrew Neitsch Date: Sun, 23 Nov 2008 00:20:48 +0100 Subject: [PATCH 1/1] Initial import from neitsch's repository --- klc2xkb | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100755 klc2xkb diff --git a/klc2xkb b/klc2xkb new file mode 100755 index 0000000..426f728 --- /dev/null +++ b/klc2xkb @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# coding: utf-8 + +"""%(prog) [options] KLCFILE + +Read a .KLC file saved by Microsoft Keyboard Layout Creator, and print +an equivalent xkb_symbol description. + +The resulting output can be included in the xkb_symbol section of a keymap +description. This may work for you: + + $ %(prog) foo.klc > foo.xkbsym + $ setxkbmap -print \ + > | sed '/xkb_symbols/s/};/\n\t\t\tinclude "foo.xkbsym" };/' \ + > | xkbcomp - "${DISPLAY}" + $ setxkbmap -option lv3:ralt_switch # For AltGr + +To use the optional dead-key compose file, written out to say +~/.XCompose.dead_keys, create a file called ~/.XCompose containing + + include "%L" + include "%H/.XCompose.dead_keys" + +See §4.19 “Xlib Compose file support and extensions” of the X11R7.1 release +notes (http://xorg.freedesktop.org/releases/X11R7.1/doc/RELNOTES4.html#30) for +more information. + +X applications read the compose file on startup, so you will need to restart +applications to pick up changes in dead keys, whereas other changes will take +effect immediately on running setxkbmap. + +There are fundamental difference in the way that X and Windows handle dead +keys. For the key sequence 〈dead_key〉〈other_key〉, where 〈other_key〉 has no +mapping for this particular dead key, windows treats both keypresses normally, +while X ignores both. +""" + +from __future__ import with_statement + +import codecs +import re +import sys +import unicodedata +from optparse import OptionParser + +class KeyMapping(object): + def __init__(self, scancode, normal, shifted, altgr, shiftedaltgr): + self.scancode = scancode + self.normal = self._cleanup(normal) + self.shifted = self._cleanup(shifted) + self.altgr = self._cleanup(altgr) + self.shiftedaltgr = self._cleanup(shiftedaltgr) + + @classmethod + def _cleanup(cls, charstr): + if charstr == '-1': + return None + match = re.match('[0-9A-Fa-f]{4}', charstr) + if match: + return unichr(int(match.group(), 16)) + assert len(charstr) == 1 + return charstr + + @classmethod + def scancode_to_xkbname(cls, scancode): + special = {0x29: "TLDE", 0x2b: "BKSL", 0x39: "SPCE", 0x53: "KPDL"} + if scancode in special: + return special[scancode] + + elif scancode <= 0x0d: + return "AE%02d" % (scancode - 1) + elif scancode <= 0x1b: + return "AD%02d" % (scancode - 0xf) + elif scancode <= 0x28: + return "AC%02d" % (scancode - 0x1d) + elif scancode <= 0x35: + return "AB%02d" % (scancode - 0x2b) + return None + + def to_xkb_def_str(self): + xkbname = self.scancode_to_xkbname(self.scancode) + if not xkbname: + return "" + def format_symbol(sym): + if sym is None: + return "NoSymbol" + return "U%04x" % ord(sym) + + return ("key <%s> { [ %s, %s, %s, %s ] };" + % tuple([xkbname] + + [format_symbol(sym) + for sym in [self.normal, self.shifted, + self.altgr, self.shiftedaltgr]])) + +def DDDD_to_name(DDDD): + "'0046' -> 'FULL STOP'" + return unicodedata.name(unichr(int(DDDD, 16))) + +class DeadKey(object): + def __init__(self, deadkey): + self.deadkey = deadkey + self.maps = {} + + def add_entry(self, key, value): + self.maps[key] = value + + def to_xcompose_str(self): + return '# %s' % DDDD_to_name(self.deadkey) + "\n" \ + + "\n".join(" : U%s # '%s' -> %s" % (self.deadkey, + k, v, + unichr(int(k, 16)), + DDDD_to_name(v)) + for k, v in self.maps.items()) + "\n" + +class MSKLCFile(object): + def __init__(self, filename): + self.filename = filename + self.mappings = [] + self.deadkeys = [] + self.parse() + + def parse(self): + with codecs.open(self.filename, 'r', encoding='utf-16') as file: + in_deadkey_def = False + + for line in file: + line = line.strip() + + if in_deadkey_def: + match = re.match(r"""^([0-9a-fA-F]{4})\s+ + ([0-9a-fA-F]{4})\s+ + // + |\s?$""", + line, + re.VERBOSE) + if not match: + in_deadkey_def = False + else: + if match.group(1): + self.deadkeys[-1].add_entry(match.group(1), + match.group(2)) + continue + + match = re.match(r'^DEADKEY\s+([0-9a-fA-F]{4})$', line) + if match: + in_deadkey_def = True + self.deadkeys.append(DeadKey(match.group(1))) + + match = re.match('(.*?)//', line) + if match: + pieces = match.group().split() + if len(pieces) == 9: + self.parse_layout_line(pieces) + + def parse_layout_line(self, pieces): + scancode, vk, cap, normal, shift, ctrl, altgr, shiftaltgr, junk = \ + pieces + scancode = int(scancode, 16) + mapping = KeyMapping(scancode, normal, shift, altgr, shiftaltgr) + self.mappings.append(mapping) + +def main(args=None): + if args is None: + args = sys.argv[1:] + + optp = OptionParser(usage=__doc__) + optp.add_option('--compose', dest='compose_file', metavar="FILE", + help="Write dead key definitions to compose file FILE") + options, args = optp.parse_args(args) + + if len(args) != 1: + optp.print_help() + return 1 + + filename = args[0] + + layout = MSKLCFile(filename) + + print "xkb_symbols {" + for mapping in layout.mappings: + print mapping.to_xkb_def_str() + print "};" + + if options.compose_file is not None: + with open(options.compose_file, 'w') as compose_file: + for key in layout.deadkeys: + print >> compose_file, key.to_xcompose_str() + +if __name__ == '__main__': + sys.exit(main()) -- 2.32.0.93.g670b81a890