blob: 5e30ce19e11a2ac2fa9d506c585f4a5a8e758afb [file] [log] [blame]
#!/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()