| #!/usr/bin/env python3 |
| # -*- python -*- |
| # |
| # Keycode Map Generator |
| # |
| # Copyright (C) 2009-2017 Red Hat, Inc. |
| # |
| # This file is dual license under the terms of the GPLv2 or later |
| # and 3-clause BSD licenses. |
| # |
| |
| # Requires >= 2.6 |
| from __future__ import print_function |
| |
| import csv |
| import argparse |
| import hashlib |
| import time |
| import sys |
| |
| class Database: |
| |
| # Linux: linux/input.h |
| MAP_LINUX = "linux" |
| |
| # OS-X: Carbon/HIToolbox/Events.h |
| MAP_OSX = "osx" |
| |
| # AT Set 1: linux/drivers/input/keyboard/atkbd.c |
| # (atkbd_set2_keycode + atkbd_unxlate_table) |
| MAP_ATSET1 = "atset1" |
| |
| # AT Set 2: linux/drivers/input/keyboard/atkbd.c |
| # (atkbd_set2_keycode) |
| MAP_ATSET2 = "atset2" |
| |
| # AT Set 3: linux/drivers/input/keyboard/atkbd.c |
| # (atkbd_set3_keycode) |
| MAP_ATSET3 = "atset3" |
| |
| # Linux RAW: linux/drivers/char/keyboard.c (x86_keycodes) |
| MAP_XTKBD = "xtkbd" |
| |
| # USB HID: linux/drivers/hid/usbhid/usbkbd.c (usb_kbd_keycode) |
| MAP_USB = "usb" |
| |
| # Win32: mingw32/winuser.h |
| MAP_WIN32 = "win32" |
| |
| # XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h} |
| # (xt + manually transcribed) |
| MAP_XWINXT = "xwinxt" |
| |
| # X11: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h |
| MAP_X11 = "x11" |
| |
| # XKBD XT: xf86-input-keyboard/src/at_scancode.c |
| # (xt + manually transcribed) |
| MAP_XKBDXT = "xkbdxt" |
| |
| # Xorg with evdev: linux + an offset |
| MAP_XORGEVDEV = "xorgevdev" |
| |
| # Xorg with kbd: xkbdxt + an offset |
| MAP_XORGKBD = "xorgkbd" |
| |
| # Xorg with OS-X: osx + an offset |
| MAP_XORGXQUARTZ = "xorgxquartz" |
| |
| # Xorg + Cygwin: xwinxt + an offset |
| MAP_XORGXWIN = "xorgxwin" |
| |
| # QEMU key numbers: xtkbd + special re-encoding of high bit |
| MAP_QNUM = "qnum" |
| |
| # HTML codes |
| MAP_HTML = "html" |
| |
| # XKB key names |
| MAP_XKB = "xkb" |
| |
| # QEMU keycodes |
| MAP_QCODE = "qcode" |
| |
| # Sun / Sparc scan codes |
| # Reference: "SPARC International Keyboard Spec 1", page 7 "US scan set" |
| MAP_SUN = "sun" |
| |
| # Apple Desktop Bus |
| # Reference: http://www.archive.org/stream/apple-guide-macintosh-family-hardware/Apple_Guide_to_the_Macintosh_Family_Hardware_2e#page/n345/mode/2up |
| MAP_ADB = "adb" |
| |
| # NeXT Cube |
| # Reference: https://github.com/spenczar/usb-next/blob/main/keymap.h |
| MAP_NEXT = "next" |
| |
| MAP_LIST = ( |
| MAP_LINUX, |
| MAP_OSX, |
| MAP_ATSET1, |
| MAP_ATSET2, |
| MAP_ATSET3, |
| MAP_USB, |
| MAP_WIN32, |
| MAP_XWINXT, |
| MAP_XKBDXT, |
| MAP_X11, |
| MAP_HTML, |
| MAP_XKB, |
| MAP_QCODE, |
| MAP_SUN, |
| MAP_ADB, |
| MAP_NEXT, |
| |
| # These are derived from maps above |
| MAP_XTKBD, |
| MAP_XORGEVDEV, |
| MAP_XORGKBD, |
| MAP_XORGXQUARTZ, |
| MAP_XORGXWIN, |
| MAP_QNUM, |
| ) |
| |
| CODE_COLUMNS = { |
| MAP_LINUX: 1, |
| MAP_OSX: 3, |
| MAP_ATSET1: 4, |
| MAP_ATSET2: 5, |
| MAP_ATSET3: 6, |
| MAP_USB: 7, |
| MAP_WIN32: 9, |
| MAP_XWINXT: 10, |
| MAP_XKBDXT: 11, |
| MAP_X11: 13, |
| MAP_HTML: 14, |
| MAP_XKB: 15, |
| MAP_SUN: 17, |
| MAP_ADB: 18, |
| MAP_NEXT: 19, |
| } |
| |
| ENUM_COLUMNS = { |
| MAP_QCODE: 14, |
| } |
| |
| NAME_COLUMNS = { |
| MAP_LINUX: 0, |
| MAP_OSX: 2, |
| MAP_WIN32: 8, |
| MAP_X11: 12, |
| MAP_HTML: 14, |
| MAP_XKB: 15, |
| MAP_QCODE: 16, |
| } |
| |
| ENUM_BOUND = { |
| MAP_QCODE: "Q_KEY_CODE__MAX", |
| } |
| |
| # These keys need special handling since the key press scan code overlaps |
| # the 0x80 bit commonly used to indicate key release. |
| # It also needs handling to ensure that the QNum matches its historical value. |
| HANJA_HANGEUL_KEYS = (0xf1, 0xf2) |
| |
| def __init__(self): |
| |
| self.mapto = {} |
| self.mapfrom = {} |
| self.mapname = {} |
| self.mapchecksum = None |
| |
| for name in self.MAP_LIST: |
| # Key is a MAP_LINUX, value is a MAP_XXX |
| self.mapto[name] = {} |
| # key is a MAP_XXX, value is a MAP_LINUX |
| self.mapfrom[name] = {} |
| |
| for name in self.NAME_COLUMNS.keys(): |
| # key is a MAP_LINUX, value is a string |
| self.mapname[name] = {} |
| |
| def _generate_checksum(self, filename): |
| hash = hashlib.sha256() |
| with open(filename, "rb") as f: |
| for chunk in iter(lambda: f.read(4096), b""): |
| hash.update(chunk) |
| self.mapchecksum = hash.hexdigest() |
| |
| def load(self, filename): |
| self._generate_checksum(filename) |
| |
| with open(filename, 'r') as f: |
| reader = csv.reader(f) |
| |
| first = True |
| |
| for row in reader: |
| # Discard column headings |
| if first: |
| first = False |
| continue |
| |
| # We special case MAP_LINUX since that is out |
| # master via which all other mappings are done |
| linux = self.load_linux(row) |
| |
| # Now load all the remaining master data values |
| self.load_data(row, linux) |
| |
| # Then load all the keycode names |
| self.load_names(row, linux) |
| |
| # Finally calculate derived key maps |
| self.derive_data(row, linux) |
| |
| def load_linux(self, row): |
| col = self.CODE_COLUMNS[self.MAP_LINUX] |
| linux = row[col] |
| |
| if linux.startswith("0x"): |
| linux = int(linux, 16) |
| else: |
| linux = int(linux, 10) |
| |
| self.mapto[self.MAP_LINUX][linux] = linux |
| self.mapfrom[self.MAP_LINUX][linux] = linux |
| |
| return linux |
| |
| |
| def load_data(self, row, linux): |
| for mapname in self.CODE_COLUMNS: |
| if mapname == self.MAP_LINUX: |
| continue |
| |
| col = self.CODE_COLUMNS[mapname] |
| val = row[col] |
| |
| if val == "": |
| continue |
| |
| if val.startswith("0x"): |
| val = int(val, 16) |
| elif val.isdigit(): |
| val = int(val, 10) |
| |
| self.mapto[mapname][linux] = val |
| self.mapfrom[mapname][val] = linux |
| |
| def load_names(self, row, linux): |
| for mapname in self.NAME_COLUMNS: |
| col = self.NAME_COLUMNS[mapname] |
| val = row[col] |
| |
| if val == "": |
| continue |
| |
| self.mapname[mapname][linux] = val |
| |
| |
| def derive_data(self, row, linux): |
| # Linux RAW is XT scan codes with special encoding of the |
| # 0xe0 scan codes |
| if linux in self.mapto[self.MAP_ATSET1]: |
| at1 = self.mapto[self.MAP_ATSET1][linux] |
| if at1 > 0x7f and at1 not in self.HANJA_HANGEUL_KEYS: |
| assert((at1 & ~0x7f) == 0xe000) |
| xtkbd = 0x100 | (at1 & 0x7f) |
| else: |
| xtkbd = at1 |
| self.mapto[self.MAP_XTKBD][linux] = xtkbd |
| self.mapfrom[self.MAP_XTKBD][xtkbd] = linux |
| |
| # Xorg KBD is XKBD XT offset by 8 |
| if linux in self.mapto[self.MAP_XKBDXT]: |
| xorgkbd = self.mapto[self.MAP_XKBDXT][linux] + 8 |
| self.mapto[self.MAP_XORGKBD][linux] = xorgkbd |
| self.mapfrom[self.MAP_XORGKBD][xorgkbd] = linux |
| |
| # Xorg evdev is Linux offset by 8 |
| self.mapto[self.MAP_XORGEVDEV][linux] = linux + 8 |
| self.mapfrom[self.MAP_XORGEVDEV][linux + 8] = linux |
| |
| # Xorg XQuartx is OS-X offset by 8 |
| if linux in self.mapto[self.MAP_OSX]: |
| xorgxquartz = self.mapto[self.MAP_OSX][linux] + 8 |
| self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgxquartz |
| self.mapfrom[self.MAP_XORGXQUARTZ][xorgxquartz] = linux |
| |
| # Xorg Xwin (aka Cygwin) is XWin XT offset by 8 |
| if linux in self.mapto[self.MAP_XWINXT]: |
| xorgxwin = self.mapto[self.MAP_XWINXT][linux] + 8 |
| self.mapto[self.MAP_XORGXWIN][linux] = xorgxwin |
| self.mapfrom[self.MAP_XORGXWIN][xorgxwin] = linux |
| |
| # QNUM keycodes are XT scan codes with a slightly |
| # different encoding of 0xe0 scan codes |
| if linux in self.mapto[self.MAP_ATSET1]: |
| at1 = self.mapto[self.MAP_ATSET1][linux] |
| if at1 in self.HANJA_HANGEUL_KEYS: |
| qnum = at1 & 0x7f |
| elif at1 > 0x7f: |
| assert((at1 & ~0x7f) == 0xe000) |
| qnum = 0x80 | (at1 & 0x7f) |
| else: |
| qnum = at1 |
| self.mapto[self.MAP_QNUM][linux] = qnum |
| self.mapfrom[self.MAP_QNUM][qnum] = linux |
| |
| # Hack for compatibility with previous mistakes in handling |
| # Print/SysRq. The preferred qnum for Print/SysRq is 0x54, |
| # but QEMU previously allowed 0xb7 too |
| if qnum == 0x54: |
| self.mapfrom[self.MAP_QNUM][0xb7] = self.mapfrom[self.MAP_QNUM][0x54] |
| |
| if linux in self.mapname[self.MAP_QCODE]: |
| qcodeenum = self.mapname[self.MAP_QCODE][linux] |
| qcodeenum = "Q_KEY_CODE_" + qcodeenum.upper() |
| self.mapto[self.MAP_QCODE][linux] = qcodeenum |
| self.mapfrom[self.MAP_QCODE][qcodeenum] = linux |
| |
| class LanguageGenerator(object): |
| |
| def _boilerplate(self, lines): |
| raise NotImplementedError() |
| |
| def generate_header(self, database, args): |
| self._boilerplate([ |
| "This file is auto-generated from keymaps.csv", |
| "Database checksum sha256(%s)" % database.mapchecksum, |
| "To re-generate, run:", |
| " %s" % args, |
| ]) |
| |
| class LanguageSrcGenerator(LanguageGenerator): |
| |
| TYPE_INT = "integer" |
| TYPE_STRING = "string" |
| TYPE_ENUM = "enum" |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| raise NotImplementedError() |
| |
| def _array_end(self, fromtype, totype): |
| raise NotImplementedError() |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| raise NotImplementedError() |
| |
| def generate_code_map(self, varname, database, frommapname, tomapname): |
| if frommapname not in database.mapfrom: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| frommapname, ", ".join(database.mapfrom.keys()))) |
| if tomapname not in database.mapto: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| tomapname, ", ".join(database.mapto.keys()))) |
| |
| tolinux = database.mapfrom[frommapname] |
| fromlinux = database.mapto[tomapname] |
| |
| if varname is None: |
| varname = "code_map_%s_to_%s" % (frommapname, tomapname) |
| |
| if frommapname in database.ENUM_COLUMNS: |
| fromtype = self.TYPE_ENUM |
| elif type(list(tolinux.keys())[0]) == str: |
| fromtype = self.TYPE_STRING |
| else: |
| fromtype = self.TYPE_INT |
| |
| if tomapname in database.ENUM_COLUMNS: |
| totype = self.TYPE_ENUM |
| elif type(list(fromlinux.values())[0]) == str: |
| totype = self.TYPE_STRING |
| else: |
| totype = self.TYPE_INT |
| |
| keys = list(tolinux.keys()) |
| keys.sort() |
| if fromtype == self.TYPE_INT: |
| keys = range(keys[-1] + 1) |
| |
| if fromtype == self.TYPE_ENUM: |
| keymax = database.ENUM_BOUND[frommapname] |
| else: |
| keymax = len(keys) |
| |
| defvalue = fromlinux.get(0, None) |
| if fromtype == self.TYPE_ENUM: |
| self._array_start(varname, keymax, defvalue, fromtype, totype) |
| else: |
| self._array_start(varname, keymax, None, fromtype, totype) |
| |
| for src in keys: |
| linux = tolinux.get(src, None) |
| if linux is None: |
| dst = None |
| else: |
| dst = fromlinux.get(linux, defvalue) |
| |
| comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), |
| self._label(database, Database.MAP_LINUX, linux, linux), |
| self._label(database, tomapname, dst, linux)) |
| self._array_entry(src, dst, comment, fromtype, totype) |
| self._array_end(fromtype, totype) |
| |
| def generate_code_table(self, varname, database, mapname): |
| if mapname not in database.mapto: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| mapname, ", ".join(database.mapto.keys()))) |
| |
| keys = list(database.mapto[Database.MAP_LINUX].keys()) |
| keys.sort() |
| names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] |
| |
| if varname is None: |
| varname = "code_table_%s" % mapname |
| |
| if mapname in database.ENUM_COLUMNS: |
| totype = self.TYPE_ENUM |
| elif type(list(database.mapto[mapname].values())[0]) == str: |
| totype = self.TYPE_STRING |
| else: |
| totype = self.TYPE_INT |
| |
| self._array_start(varname, len(keys), None, self.TYPE_INT, totype) |
| |
| defvalue = database.mapto[mapname].get(0, None) |
| for i in range(len(keys)): |
| key = keys[i] |
| dst = database.mapto[mapname].get(key, defvalue) |
| self._array_entry(i, dst, names[i], self.TYPE_INT, totype) |
| |
| self._array_end(self.TYPE_INT, totype) |
| |
| def generate_name_map(self, varname, database, frommapname, tomapname): |
| if frommapname not in database.mapfrom: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| frommapname, ", ".join(database.mapfrom.keys()))) |
| if tomapname not in database.mapname: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| tomapname, ", ".join(database.mapname.keys()))) |
| |
| tolinux = database.mapfrom[frommapname] |
| fromlinux = database.mapname[tomapname] |
| |
| if varname is None: |
| varname = "name_map_%s_to_%s" % (frommapname, tomapname) |
| |
| keys = list(tolinux.keys()) |
| keys.sort() |
| if type(keys[0]) == int: |
| keys = range(keys[-1] + 1) |
| |
| if type(keys[0]) == int: |
| fromtype = self.TYPE_INT |
| else: |
| fromtype = self.TYPE_STRING |
| |
| self._array_start(varname, len(keys), None, fromtype, self.TYPE_STRING) |
| |
| for src in keys: |
| linux = tolinux.get(src, None) |
| if linux is None: |
| dst = None |
| else: |
| dst = fromlinux.get(linux, None) |
| |
| comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), |
| self._label(database, Database.MAP_LINUX, linux, linux), |
| self._label(database, tomapname, dst, linux)) |
| self._array_entry(src, dst, comment, fromtype, self.TYPE_STRING) |
| self._array_end(fromtype, self.TYPE_STRING) |
| |
| def generate_name_table(self, varname, database, mapname): |
| if mapname not in database.mapname: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| mapname, ", ".join(database.mapname.keys()))) |
| |
| keys = list(database.mapto[Database.MAP_LINUX].keys()) |
| keys.sort() |
| names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] |
| |
| if varname is None: |
| varname = "name_table_%s" % mapname |
| |
| self._array_start(varname, len(keys), None, self.TYPE_INT, self.TYPE_STRING) |
| |
| for i in range(len(keys)): |
| key = keys[i] |
| dst = database.mapname[mapname].get(key, None) |
| self._array_entry(i, dst, names[i], self.TYPE_INT, self.TYPE_STRING) |
| |
| self._array_end(self.TYPE_INT, self.TYPE_STRING) |
| |
| def _label(self, database, mapname, val, linux): |
| if mapname in database.mapname: |
| return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(linux, "unnamed")) |
| else: |
| return "%s:%s" % (mapname, val) |
| |
| class LanguageDocGenerator(LanguageGenerator): |
| |
| def _array_start_name_doc(self, varname, namemap): |
| raise NotImplementedError() |
| |
| def _array_start_code_doc(self, varname, namemap, codemap): |
| raise NotImplementedError() |
| |
| def _array_end(self): |
| raise NotImplementedError() |
| |
| def _array_name_entry(self, value, name): |
| raise NotImplementedError() |
| |
| def _array_code_entry(self, value, name): |
| raise NotImplementedError() |
| |
| def generate_name_docs(self, title, subtitle, database, mapname): |
| if mapname not in database.mapname: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| mapname, ", ".join(database.mapname.keys()))) |
| |
| keys = list(database.mapto[Database.MAP_LINUX].keys()) |
| keys.sort() |
| names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] |
| |
| if title is None: |
| title = mapname |
| if subtitle is None: |
| subtitle = "Docs for %s" % mapname |
| |
| self._array_start_name_doc(title, subtitle, mapname) |
| |
| for i in range(len(keys)): |
| key = keys[i] |
| dst = database.mapname[mapname].get(key, None) |
| self._array_name_entry(key, dst) |
| |
| self._array_end() |
| |
| |
| def generate_code_docs(self, title, subtitle, database, mapname): |
| if mapname not in database.mapfrom: |
| raise Exception("Unknown map %s, expected one of %s" % ( |
| mapname, ", ".join(database.mapfrom.keys()))) |
| |
| tolinux = database.mapfrom[mapname] |
| keys = list(tolinux.keys()) |
| keys.sort() |
| if mapname in database.mapname: |
| names = database.mapname[mapname] |
| namemap = mapname |
| else: |
| names = database.mapname[Database.MAP_LINUX] |
| namemap = Database.MAP_LINUX |
| |
| if title is None: |
| title = mapname |
| if subtitle is None: |
| subtitle = "Docs for %s" % mapname |
| |
| self._array_start_code_doc(title, subtitle, mapname, namemap) |
| |
| for i in range(len(keys)): |
| key = keys[i] |
| self._array_code_entry(key, names.get(tolinux[key], "unnamed")) |
| |
| self._array_end() |
| |
| class CLanguageGenerator(LanguageSrcGenerator): |
| |
| def __init__(self, inttypename, strtypename, lentypename): |
| self.inttypename = inttypename |
| self.strtypename = strtypename |
| self.lentypename = lentypename |
| |
| def _boilerplate(self, lines): |
| print("/*") |
| for line in lines: |
| print(" * %s" % line) |
| print("*/") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| self._varname = varname; |
| totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename |
| if fromtype in (self.TYPE_INT, self.TYPE_ENUM): |
| if type(length) == str: |
| print("const %s %s[%s] = {" % (totypename, varname, length)) |
| else: |
| print("const %s %s[%d] = {" % (totypename, varname, length)) |
| else: |
| print("const struct _%s {" % varname) |
| print(" const %s from;" % self.strtypename) |
| print(" const %s to;" % totypename) |
| print("} %s[] = {" % varname) |
| |
| if defvalue != None: |
| if totype == self.TYPE_ENUM: |
| if type(length) == str: |
| print(" [0 ... %s-1] = %s," % (length, defvalue)) |
| else: |
| print(" [0 ... 0x%x-1] = %s," % (length, defvalue)) |
| else: |
| if type(length) == str: |
| print(" [0 ... %s-1] = 0x%x," % (length, defvalue)) |
| else: |
| print(" [0 ... 0x%x-1] = 0x%x," % (length, defvalue)) |
| |
| def _array_end(self, fromtype, totype): |
| print("};") |
| print("const %s %s_len = sizeof(%s)/sizeof(%s[0]);" % |
| (self.lentypename, self._varname, self._varname, self._varname)) |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if value is None: |
| return |
| if fromtype == self.TYPE_INT: |
| indexfmt = "0x%x" |
| elif fromtype == self.TYPE_ENUM: |
| indexfmt = "%s" |
| else: |
| indexfmt = "\"%s\"" |
| |
| if totype == self.TYPE_INT: |
| valuefmt = "0x%x" |
| elif totype == self.TYPE_ENUM: |
| valuefmt = "%s" |
| else: |
| valuefmt = "\"%s\"" |
| |
| if fromtype != self.TYPE_STRING: |
| print((" [" + indexfmt + "] = " + valuefmt + ", /* %s */") % (index, value, comment)) |
| else: |
| print((" {" + indexfmt + ", " + valuefmt + "}, /* %s */") % (index, value, comment)) |
| |
| class StdCLanguageGenerator(CLanguageGenerator): |
| |
| def __init__(self): |
| super(StdCLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") |
| |
| class GLib2LanguageGenerator(CLanguageGenerator): |
| |
| def __init__(self): |
| super(GLib2LanguageGenerator, self).__init__("guint16", "gchar *", "guint") |
| |
| class CHeaderLanguageGenerator(LanguageSrcGenerator): |
| |
| def __init__(self, inttypename, strtypename, lentypename): |
| self.inttypename = inttypename |
| self.strtypename = strtypename |
| self.lentypename = lentypename |
| |
| def _boilerplate(self, lines): |
| print("/*") |
| for line in lines: |
| print(" * %s" % line) |
| print("*/") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| self._varname = varname |
| if fromtype == self.TYPE_STRING: |
| self._length = 0 |
| else: |
| self._length = length |
| |
| def _array_end(self, fromtype, totype): |
| totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename |
| if fromtype == self.TYPE_STRING: |
| vartypename = "struct _%s" % self._varname |
| print("%s {" % vartypename) |
| print(" const %s from;" % self.strtypename) |
| print(" const %s to;" % totypename) |
| print("};") |
| else: |
| vartypename = totypename |
| if type(self._length) == str: |
| print("extern const %s %s[%s];" % (vartypename, self._varname, self._length)) |
| else: |
| print("extern const %s %s[%d];" % (vartypename, self._varname, self._length)) |
| print("extern const %s %s_len;" % (self.lentypename, self._varname)) |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if value is None: |
| return |
| if fromtype == self.TYPE_STRING: |
| self._length += 1 |
| |
| class StdCHeaderLanguageGenerator(CHeaderLanguageGenerator): |
| |
| def __init__(self): |
| super(StdCHeaderLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") |
| |
| class GLib2HeaderLanguageGenerator(CHeaderLanguageGenerator): |
| |
| def __init__(self): |
| super(GLib2HeaderLanguageGenerator, self).__init__("guint16", "gchar *", "guint") |
| |
| class CppLanguageGenerator(CLanguageGenerator): |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| if fromtype == self.TYPE_ENUM: |
| raise NotImplementedError("Enums not supported as source in C++ generator") |
| totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename |
| if fromtype == self.TYPE_INT: |
| print("#include <vector>") |
| print("extern const std::vector<%s> %s;" % (totypename, varname)); |
| print("const std::vector<%s> %s = {" % (totypename, varname)) |
| else: |
| print("#include <map>") |
| print("#include <string>") |
| print("extern const std::map<const std::string, %s> %s;" % (totypename, varname)) |
| print("const std::map<const std::string, %s> %s = {" % (totypename, varname)) |
| |
| def _array_end(self, fromtype, totype): |
| print("};") |
| |
| # designated initializers not available in C++ |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if fromtype == self.TYPE_STRING: |
| return super(CppLanguageGenerator, self)._array_entry(index, value, comment, fromtype, totype) |
| |
| if value is None: |
| print(" 0, /* %s */" % comment) |
| elif totype == self.TYPE_INT: |
| print(" 0x%x, /* %s */" % (value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" %s, /* %s */" % (value, comment)) |
| else: |
| print(" \"%s\", /* %s */" % (value, comment)) |
| |
| class StdCppLanguageGenerator(CppLanguageGenerator): |
| |
| def __init__(self): |
| super(StdCppLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") |
| |
| class CppHeaderLanguageGenerator(CHeaderLanguageGenerator): |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| if fromtype == self.TYPE_ENUM: |
| raise NotImplementedError("Enums not supported as source in C++ generator") |
| totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename |
| if fromtype == self.TYPE_INT: |
| print("#include <vector>") |
| print("extern const std::vector<%s> %s;" % (totypename, varname)); |
| else: |
| print("#include <map>") |
| print("#include <string>") |
| print("extern const std::map<const std::string, %s> %s;" % (totypename, varname)) |
| |
| def _array_end(self, fromtype, totype): |
| pass |
| |
| # designated initializers not available in C++ |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| pass |
| |
| class StdCppHeaderLanguageGenerator(CppHeaderLanguageGenerator): |
| |
| def __init__(self): |
| super(StdCppHeaderLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") |
| |
| |
| class RustLanguageGenerator(LanguageSrcGenerator): |
| |
| def _boilerplate(self, lines): |
| print("//") |
| for line in lines: |
| print("// %s" % line) |
| print("//") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| if fromtype == self.TYPE_ENUM: |
| raise NotImplementedError("Enums not supported as source in Rust generator") |
| |
| totypename = "&str" if totype == self.TYPE_STRING else "u16" |
| if fromtype != self.TYPE_STRING: |
| print("pub static %s: &[%s] = &[" % (varname.upper(), totypename)) |
| else: |
| print("pub static %s: phf::Map<&str, %s> = phf::phf_map! {" % |
| (varname.upper(), totypename)) |
| |
| def _array_end(self, fromtype, totype): |
| if fromtype != self.TYPE_STRING: |
| print("];") |
| else: |
| print("};") |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| none = "\"\"" if totype == self.TYPE_STRING else "0" |
| if fromtype == self.TYPE_INT: |
| if value is None: |
| print(" %s, // %s" % (none, comment)) |
| elif totype == self.TYPE_INT: |
| print(" 0x%x, // %s" % (value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" %s, // %s" % (value, comment)) |
| else: |
| print(" \"%s\", // %s" % (value, comment)) |
| else: |
| if value is None: |
| print(" \"%s\" => %s, // %s" % (index, none, comment)) |
| elif totype == self.TYPE_INT: |
| print(" \"%s\" => 0x%x, // %s" % (index, value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" \"%s\" => %s, // %s" % (index, value, comment)) |
| else: |
| print(" \"%s\" => \"%s\", // %s" % (index, value, comment)) |
| |
| |
| class PythonLanguageGenerator(LanguageSrcGenerator): |
| |
| def _boilerplate(self, lines): |
| print("#") |
| for line in lines: |
| print("# %s" % line) |
| print("#") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| if fromtype == self.TYPE_ENUM: |
| raise NotImplementedError("Enums not supported as source in Python generator") |
| |
| if fromtype != self.TYPE_STRING: |
| print("%s = [" % varname) |
| else: |
| print("%s = {" % varname) |
| |
| def _array_end(self, fromtype, totype): |
| if fromtype != self.TYPE_STRING: |
| print("]") |
| else: |
| print("}") |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if fromtype == self.TYPE_INT: |
| if value is None: |
| print(" None, # %s" % (comment)) |
| elif totype == self.TYPE_INT: |
| print(" 0x%x, # %s" % (value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" %s, # %s" % (value, comment)) |
| else: |
| print(" \"%s\", # %s" % (value, comment)) |
| else: |
| if value is None: |
| print(" \"%s\": None, # %s" % (index, comment)) |
| elif totype == self.TYPE_INT: |
| print(" \"%s\": 0x%x, # %s" % (index, value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" \"%s\": %s, # %s" % (index, value, comment)) |
| else: |
| print(" \"%s\": \"%s\", # %s" % (index, value, comment)) |
| |
| class PerlLanguageGenerator(LanguageSrcGenerator): |
| |
| def _boilerplate(self, lines): |
| print("#") |
| for line in lines: |
| print("# %s" % line) |
| print("#") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| if fromtype == self.TYPE_ENUN: |
| raise NotImplementedError("Enums not supported as source in Python generator") |
| if fromtype == self.TYPE_INT: |
| print("my @%s = (" % varname) |
| else: |
| print("my %%%s = (" % varname) |
| |
| def _array_end(self, fromtype, totype): |
| print(");") |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if fromtype == self.TYPE_INT: |
| if value is None: |
| print(" undef, # %s" % (comment)) |
| elif totype == self.TYPE_INT: |
| print(" 0x%x, # %s" % (value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" %s, # %s" % (value, comment)) |
| else: |
| print(" \"%s\", # %s" % (value, comment)) |
| else: |
| if value is None: |
| print(" \"%s\", undef, # %s" % (index, comment)) |
| elif totype == self.TYPE_INT: |
| print(" \"%s\", 0x%x, # %s" % (index, value, comment)) |
| elif totype == self.TYPE_ENUM: |
| print(" \"%s\", 0x%x, # %s" % (index, value, comment)) |
| else: |
| print(" \"%s\", \"%s\", # %s" % (index, value, comment)) |
| |
| class JavaScriptLanguageGenerator(LanguageSrcGenerator): |
| |
| def _boilerplate(self, lines): |
| print("/*") |
| for line in lines: |
| print(" * %s" % line) |
| print("*/") |
| |
| def _array_start(self, varname, length, defvalue, fromtype, totype): |
| print("export default {") |
| |
| def _array_end(self, fromtype, totype): |
| print("};") |
| |
| def _array_entry(self, index, value, comment, fromtype, totype): |
| if value is None: |
| return |
| |
| if fromtype == self.TYPE_INT: |
| fromfmt = "0x%x" |
| elif fromtype == self.TYPE_ENUM: |
| fromfmt = "%s" |
| else: |
| fromfmt = "\"%s\"" |
| |
| if totype == self.TYPE_INT: |
| tofmt = "0x%x" |
| elif totype == self.TYPE_ENUM: |
| tofmt = "%s" |
| else: |
| tofmt = "\"%s\"" |
| |
| print((" " + fromfmt + ": " + tofmt + ", /* %s */") % (index, value, comment)) |
| |
| class PodLanguageGenerator(LanguageDocGenerator): |
| |
| def _boilerplate(self, lines): |
| print("#") |
| for line in lines: |
| print("# %s" % line) |
| print("#") |
| |
| def _array_start_name_doc(self, title, subtitle, namemap): |
| print("=head1 NAME") |
| print("") |
| print("%s - %s" % (title, subtitle)) |
| print("") |
| print("=head1 DESCRIPTION") |
| print("") |
| print("List of %s key code names, with corresponding key code values" % namemap) |
| print("") |
| print("=over 4") |
| print("") |
| |
| def _array_start_code_doc(self, title, subtitle, codemap, namemap): |
| print("=head1 NAME") |
| print("") |
| print("%s - %s" % (title, subtitle)) |
| print("") |
| print("=head1 DESCRIPTION") |
| print("") |
| print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap)) |
| print("") |
| print("=over 4") |
| print("") |
| |
| def _array_end(self): |
| print("=back") |
| print("") |
| |
| def _array_name_entry(self, value, name): |
| print("=item %s" % name) |
| print("") |
| print("Key value %d (0x%x)" % (value, value)) |
| print("") |
| |
| def _array_code_entry(self, value, name): |
| print("=item %d (0x%x)" % (value, value)) |
| print("") |
| print("Key name %s" % name) |
| print("") |
| |
| class RSTLanguageGenerator(LanguageDocGenerator): |
| |
| def _boilerplate(self, lines): |
| print("..") |
| for line in lines: |
| print(" %s" % line) |
| print("") |
| |
| def _array_start_name_doc(self, title, subtitle, namemap): |
| print("=" * len(title)) |
| print(title) |
| print("=" * len(title)) |
| print("") |
| print("-" * len(subtitle)) |
| print(subtitle) |
| print("-" * len(subtitle)) |
| print("") |
| print(":Manual section: 7") |
| print(":Manual group: Virtualization Support") |
| print("") |
| print("DESCRIPTION") |
| print("===========") |
| print("List of %s key code names, with corresponding key code values" % namemap) |
| print("") |
| |
| def _array_start_code_doc(self, title, subtitle, codemap, namemap): |
| print("=" * len(title)) |
| print(title) |
| print("=" * len(title)) |
| print("") |
| print("-" * len(subtitle)) |
| print(subtitle) |
| print("-" * len(subtitle)) |
| print("") |
| print(":Manual section: 7") |
| print(":Manual group: Virtualization Support") |
| print("") |
| print("DESCRIPTION") |
| print("===========") |
| print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap)) |
| print("") |
| |
| def _array_end(self): |
| print("") |
| |
| def _array_name_entry(self, value, name): |
| print("* %s" % name) |
| print("") |
| print(" Key value %d (0x%x)" % (value, value)) |
| print("") |
| |
| def _array_code_entry(self, value, name): |
| print("* %d (0x%x)" % (value, value)) |
| print("") |
| print(" Key name %s" % name) |
| print("") |
| |
| SRC_GENERATORS = { |
| "stdc": StdCLanguageGenerator(), |
| "stdc-header": StdCHeaderLanguageGenerator(), |
| "stdc++": StdCppLanguageGenerator(), |
| "stdc++-header": StdCppHeaderLanguageGenerator(), |
| "glib2": GLib2LanguageGenerator(), |
| "glib2-header": GLib2HeaderLanguageGenerator(), |
| "python2": PythonLanguageGenerator(), |
| "python3": PythonLanguageGenerator(), |
| "perl": PerlLanguageGenerator(), |
| "js": JavaScriptLanguageGenerator(), |
| "rust": RustLanguageGenerator(), |
| } |
| DOC_GENERATORS = { |
| "pod": PodLanguageGenerator(), |
| "rst": RSTLanguageGenerator(), |
| } |
| |
| def code_map(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| cliargs = ["keymap-gen", "code-map", "--lang=%s" % args.lang] |
| if args.varname is not None: |
| cliargs.append("--varname=%s" % args.varname) |
| cliargs.extend(["keymaps.csv", args.frommapname, args.tomapname]) |
| SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| SRC_GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname) |
| |
| def code_table(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| cliargs = ["keymap-gen", "code-table", "--lang=%s" % args.lang] |
| if args.varname is not None: |
| cliargs.append("--varname=%s" % args.varname) |
| cliargs.extend(["keymaps.csv", args.mapname]) |
| SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| SRC_GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname) |
| |
| def name_map(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| cliargs = ["keymap-gen", "name-map", "--lang=%s" % args.lang] |
| if args.varname is not None: |
| cliargs.append("--varname=%s" % args.varname) |
| cliargs.extend(["keymaps.csv", args.frommapname, args.tomapname]) |
| SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| SRC_GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname) |
| |
| def name_table(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| |
| cliargs = ["keymap-gen", "name-table", "--lang=%s" % args.lang] |
| if args.varname is not None: |
| cliargs.append("--varname=%s" % args.varname) |
| cliargs.extend(["keymaps.csv", args.mapname]) |
| SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| SRC_GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname) |
| |
| def code_docs(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| |
| cliargs = ["keymap-gen", "code-docs", "--lang=%s" % args.lang] |
| if args.title is not None: |
| cliargs.append("--title=%s" % args.title) |
| if args.subtitle is not None: |
| cliargs.append("--subtitle=%s" % args.subtitle) |
| cliargs.extend(["keymaps.csv", args.mapname]) |
| DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| DOC_GENERATORS[args.lang].generate_code_docs(args.title, args.subtitle, database, args.mapname) |
| |
| def name_docs(args): |
| database = Database() |
| database.load(args.keymaps) |
| |
| |
| cliargs = ["keymap-gen", "name-docs", "--lang=%s" % args.lang] |
| if args.title is not None: |
| cliargs.append("--title=%s" % args.title) |
| if args.subtitle is not None: |
| cliargs.append("--subtitle=%s" % args.subtitle) |
| cliargs.extend(["keymaps.csv", args.mapname]) |
| DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) |
| |
| DOC_GENERATORS[args.lang].generate_name_docs(args.title, args.subtitle, database, args.mapname) |
| |
| def usage(): |
| print ("Please select a command:") |
| print (" 'code-map', 'code-table', 'name-map', 'name-table', 'docs'") |
| sys.exit(1) |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| |
| subparsers = parser.add_subparsers(help="sub-command help") |
| |
| codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables") |
| codemapparser.add_argument("--varname", default=None, help="Data variable name") |
| codemapparser.add_argument("--lang", default="stdc", |
| help="Output language (%s)" % ( |
| ",".join(SRC_GENERATORS.keys()))) |
| codemapparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| codemapparser.add_argument("frommapname", help="Source code table name") |
| codemapparser.add_argument("tomapname", help="Target code table name") |
| codemapparser.set_defaults(func=code_map) |
| |
| codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table") |
| codetableparser.add_argument("--lang", default="stdc", |
| help="Output language (%s)" % ( |
| ",".join(SRC_GENERATORS.keys()))) |
| codetableparser.add_argument("--varname", default=None, help="Data variable name") |
| codetableparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| codetableparser.add_argument("mapname", help="Code table name") |
| codetableparser.set_defaults(func=code_table) |
| |
| namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names") |
| namemapparser.add_argument("--lang", default="stdc", |
| help="Output language (%s)" % ( |
| ",".join(SRC_GENERATORS.keys()))) |
| namemapparser.add_argument("--varname", default=None, help="Data variable name") |
| namemapparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| namemapparser.add_argument("frommapname", help="Source code table name") |
| namemapparser.add_argument("tomapname", help="Target name table name") |
| namemapparser.set_defaults(func=name_map) |
| |
| nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table") |
| nametableparser.add_argument("--lang", default="stdc", |
| help="Output language, (%s)" % ( |
| ",".join(SRC_GENERATORS.keys()))) |
| nametableparser.add_argument("--varname", default=None, help="Data variable name") |
| nametableparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| nametableparser.add_argument("mapname", help="Name table name") |
| nametableparser.set_defaults(func=name_table) |
| |
| codedocsparser = subparsers.add_parser("code-docs", help="Generate code documentation") |
| codedocsparser.add_argument("--lang", default="pod", |
| help="Output language (%s)" % ( |
| ",".join(DOC_GENERATORS.keys()))) |
| codedocsparser.add_argument("--title", default=None, help="Document title") |
| codedocsparser.add_argument("--subtitle", default=None, help="Document subtitle") |
| codedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| codedocsparser.add_argument("mapname", help="Code table name") |
| codedocsparser.set_defaults(func=code_docs) |
| |
| namedocsparser = subparsers.add_parser("name-docs", help="Generate name documentation") |
| namedocsparser.add_argument("--lang", default="pod", |
| help="Output language (%s)" % ( |
| ",".join(DOC_GENERATORS.keys()))) |
| namedocsparser.add_argument("--title", default=None, help="Document title") |
| namedocsparser.add_argument("--subtitle", default=None, help="Document subtitle") |
| namedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") |
| namedocsparser.add_argument("mapname", help="Name table name") |
| namedocsparser.set_defaults(func=name_docs) |
| |
| args = parser.parse_args() |
| if hasattr(args, "func"): |
| args.func(args) |
| else: |
| usage() |
| |
| |
| main() |