summaryrefslogtreecommitdiff
path: root/.gitlab-ci
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2021-01-18 14:05:41 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2021-03-24 07:10:22 +1000
commit2a0c538c518becc49aedd66dc21460418af1dcd8 (patch)
treeb909fe8ac42d912c12244ff45718a1fdd8240b15 /.gitlab-ci
parent5ca9f8aea2876fe6926fc27f564d36eaf5ca5c8d (diff)
downloadxkeyboard-config-2a0c538c518becc49aedd66dc21460418af1dcd8.tar.gz
gitlab CI: check for new XF86 keysyms in the xorgproto repo
xorgproto 2021.2 and later has a recognizable pattern for adding new keysyms to XF86keysym.h based on the Linux kernel input-event-codes.h. Use this to detect any keysyms that are present in the header file but not yet in symbols/inet. This is merely a gitlab CI job as we only have to do an actual update once every few months or so. A git diff is sufficient here too, it contains all the information we will need to understand what is missing from the updated file. We check xorgproto master because that gives us some heads-up on what will come. There is a minor chance that we are mapping keycodes that change before the release but it's minor and fixable anyway. Requires: https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/merge_requests/23 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to '.gitlab-ci')
-rwxr-xr-x.gitlab-ci/generate-evdev-keysyms.py161
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()