diff options
Diffstat (limited to '.gitlab-ci')
-rwxr-xr-x | .gitlab-ci/generate-evdev-keysyms.py | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/.gitlab-ci/generate-evdev-keysyms.py b/.gitlab-ci/generate-evdev-keysyms.py new file mode 100755 index 0000000..9c8611b --- /dev/null +++ b/.gitlab-ci/generate-evdev-keysyms.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# +# Usage: generate-evdev-keysyms.py symbols/inet.in > symbols/inet +# +# Generate the symbols/inet file from the names defined in +# linux/input-event-codes.h +# +# Note that this script relies on libevdev to provide the key names and +# those are compiled in. Ensure you have a recent-enough libevdev to +# generate this list. +# + +import contextlib +import argparse +import re +import sys + + +# The marker to search for in the template file, replaced with our generated +# codes. +replacement_marker = "@evdevsyms@" + +# These markers are put into the result file and are used to detect +# the section that we added when parsing an existing file. +section_header = "Key symbol mappings below are autogenerated" +section_footer = "End of autogenerated key symbol mappings" + + +def existing_keysyms(template): + """ + Return an iterator of type (keycode, [keysym, keysym]) with the strings + representing the internal keycode (e.g. I123) and the assigned keysyms. + + Only the "evdev" xkb_symbols section is parsed. + """ + start_pattern = re.compile(r"xkb_symbols \"evdev\" {") + end_pattern = re.compile(r"^};") + + # This does not handle multiline keysyms + keysym_pattern = re.compile( + r"\s+key \<(?P<keycode>\w+)\>\s+{\s+\[(?P<keysyms>[^]]+)\s*\]\s*};" + ) + + in_evdev = False + + template.seek(0) + for line in template.readlines(): + if re.match(start_pattern, line): + in_evdev = True + continue + if in_evdev: + if re.match(end_pattern, line): + break + match = re.match(keysym_pattern, line) + if match: + keycode = match.group("keycode") + keysyms = [k.strip() for k in match.group("keysyms").split(",")] + yield keycode, keysyms + + +def xorgproto_evdev_keysyms(header): + """ + Return an iterator of type ('XF86Foo', xkeycode, kernelname) for the keysyms in the + given header file (usually XF86keysym.h). + + The returned xkeycode is the X keycode value (i.e. kernel value + 8) + """ + + # The two allowed formats in the proto header are: + # #define XF86XK_FooBar _EVDEVK(0x123) ... KEY_FOOBAR + # /* Use: XF86XK_FooBar _EVDEVK(0x123) ... KEY_FOOBAR + # The keysym may not start with XF86XK_ (e.g. "dollar" or "euro") + pattern = re.compile( + r"(#define|/\* Use:)\s(?P<keyname>\w+)\s+_EVDEVK\(0x(?P<keycode>[0-9A-Fa-f]+)\).*(?P<kernelname>KEY_\w+)" + ) + for line in header: + match = re.match(pattern, line) + if match: + kernelname = match.group("kernelname") + keyname = match.group("keyname").replace("XK_", "") + keycode = int(match.group("keycode"), 16) + xkeycode = keycode + 8 + yield keyname, xkeycode, kernelname + + +def generate_symbols_file(template, header): + """ + Generate a new symbols/inet file with line containing @evdevsyms@ + replaced by the full list of evdev keysym mappings, including our + section_header/footer. Expected output: :: + + // $section_header + key <I$keycode> { [ XF86Foo ] }; // KEY_FOO + // key <I$keycode> { if assignment already exists }; // KEY_FOO + ... + // $section_footer + + Assignments that already exist in the file are commented out. + """ + + # We only care about the I123-style keysyms here + keysyms = { + int(kc[1:]) + for kc, _ in existing_keysyms(template) + if re.match(r"I[0-9]{3}", kc) + } + + output = [] + template.seek(0) + for line in template.readlines(): + if replacement_marker not in line: + output.append(line) + continue + + output.append(f" // {section_header}\n") + for name, xkeycode, kernelname in xorgproto_evdev_keysyms(header): + if xkeycode in keysyms: + prefix = "//" + else: + prefix = " " + output.append( + f"{prefix} key <I{xkeycode}> {{ [ {name:30s} ] }}; // {kernelname}\n" + ) + output.append(f" // {section_footer}\n") + + return output + + +def main(): + parser = argparse.ArgumentParser(description="Generate the evdev symbol lists.") + parser.add_argument( + "--template", + type=argparse.FileType("r"), + default="symbols/inet.in", + help="The template file, usually symbols/inet.in", + ) + parser.add_argument( + "--header", + type=argparse.FileType("r"), + help="Path to the XF86keysym.h header file", + default="/usr/include/X11/XF86keysym.h", + ) + parser.add_argument( + "--output", + type=str, + help="The file to be written to, usually symbols/inet", + default="symbols/inet", + ) + ns = parser.parse_args() + + with contextlib.ExitStack() as stack: + if ns.output == "-": + fd = sys.stdout + else: + fd = stack.enter_context(open(ns.output, "w")) + output = generate_symbols_file(ns.template, ns.header) + fd.write("".join(output)) + + +if __name__ == "__main__": + main() |