Hash :
e9fd95a5
Author :
Date :
2025-07-23T10:01:45
keysyms: Update using latest xorgproto xorgproto commit: 7fc33fe6d9cf0abc9b62ee976e5cb7ddcd050d1f Relevant MR: https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/merge_requests/93
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
#!/usr/bin/env python3
"""
Convert X11 keysyms headers into our keysyms header.
"""
from __future__ import print_function
import re
import os
from pathlib import Path
XORG_KEY_PREFIX = "XK_"
KEY_PREFIX = "XKB_KEY_"
EVDEV_MACRO = "_EVDEVK"
EXTRA_SPACES = " " * (len(KEY_PREFIX) - len(XORG_KEY_PREFIX))
# Expected format:
# #define XF86XK_FooBar 0x1234 /* some optional comment */
# or:
# #define XF86XK_FooBar _EVDEVK(0x123) /* some optional comment */
# We also need to match commented evdev entries:
# /* Use: XF86XK_FooBar _EVDEVK(0x123) some optional comment */
# /* TODO: … _EVDEVK(0x123) some optional comment */
keysym_entry_pattern = re.compile(
rf"""^
(?:(?P<define>\#define)\s+|(?P<use>/\*\s+Use:)\s+|(?P<todo>/\*\s+TODO:))
(?(todo)
(?P<todo_comment>(?:\s+(?!{EVDEV_MACRO}|0x)\S+)+) |
(?P<prefix>\w*){XORG_KEY_PREFIX}(?P<name>\w+)
)
(?P<spacing>\s+)
(?P<evdev>{EVDEV_MACRO}\()?(?P<value>0x[0-9A-Fa-f]+)(?(evdev)\))
""",
re.VERBOSE,
)
# Match keysym guarded by #ifndef
keysym_ifndef_pattern = re.compile(
rf"^#ifndef\s+(?P<prefix>\w*){XORG_KEY_PREFIX}(?P<name>\w+)\s*$"
)
# Match remaining XK_ references in the comments, e.g we will replace:
# XF86XK_CamelCaseKernelName _EVDEVK(kernel value)
# #define XKB_KEY_SunCompose 0x0000FF20 /* Same as XK_Multi_key */
# with:
# XKB_KEY_XF86CamelCaseKernelName _EVDEVK(kernel value)
# #define XKB_KEY_SunCompose 0x0000FF20 /* Same as XKB_KEY_Multi_key */
xorgproto_keysym_prefix_pattern = re.compile(
rf"\b(?P<prefix>\w*){XORG_KEY_PREFIX}(?!KOREAN\b)"
)
alias_pattern = re.compile(
rf"(?P<alias>(?:(?:D|d)eprecated )?(?:a|A)lias for ){KEY_PREFIX}(?P<comment>[^\*]+)"
)
def make_keysym_name(m: re.Match[str]) -> str:
return m.group("prefix") + m.group("name")
def make_keysym_entry(m: re.Match[str]) -> str:
"""
Perform the substitutions
"""
if m.group("evdev"):
if m.group("define"):
# Replace the xorgproto _EVDEVK macro with the actual value:
# 0x10081000 is the base, the evdev hex code is added to that.
# We replace to make parsing of the keys later easier.
value = 0x10081000 + int(m.group("value"), 16)
value_str = f"{value:#x} "
else:
value_str = f"""{EVDEV_MACRO}({m.group("value")})"""
else:
value_str = m.group("value")
spacing = m.group("spacing")
if todo := m.group("todo"):
todo_comment = m.group("todo_comment")
spacing += EXTRA_SPACES
return f"""{todo}{todo_comment}{spacing}{value_str}"""
else:
prefix = m.group("prefix") or ""
name = m.group("name")
define = m.group("define") or m.group("use")
return f"""{define} {KEY_PREFIX}{prefix}{name}{spacing}{value_str}"""
def fix_alias(m: re.Match[str]) -> str:
alias = m.group("alias")
comment = m.group("comment")
if len(comment) - len(comment.rstrip()) > 1:
spaces = " " * len(XORG_KEY_PREFIX)
else:
spaces = ""
return f"{alias}{comment}{spaces}"
prefix = Path(os.environ.get("X11_HEADERS_PREFIX", "/usr"))
HEADERS = (
prefix / "include/X11/keysymdef.h",
prefix / "include/X11/XF86keysym.h",
prefix / "include/X11/Sunkeysym.h",
prefix / "include/X11/DECkeysym.h",
prefix / "include/X11/HPkeysym.h",
)
print(
f"""#ifndef _XKBCOMMON_KEYSYMS_H
#define _XKBCOMMON_KEYSYMS_H
/* This file is autogenerated; please do not commit directly. */
/**
* @file
* Key symbols (keysyms) definitions.
*/
#define {KEY_PREFIX}NoSymbol 0x000000 /* Special KeySym */
"""
)
keysyms: set[str] = set()
for path in HEADERS:
pending_guarded_keysym: str | None = None
with path.open("rt", encoding="utf-8") as header:
for line in header:
# Duplicate keysym name guard
if m := keysym_ifndef_pattern.match(line):
if pending_guarded_keysym:
raise ValueError(f"Nested #ifndef {pending_guarded_keysym}")
pending_guarded_keysym = make_keysym_name(m)
continue
# Ignore C macro #ifdef/#ifndef
elif line.startswith("#ifdef") or line.startswith("#ifndef"):
if pending_guarded_keysym:
raise ValueError(f"Nested C macro {pending_guarded_keysym}")
continue
# Ignore C macro #endif and check end of keysym name guard
elif line.startswith("#endif"):
if pending_guarded_keysym:
pending_guarded_keysym = None
continue
# Remove #define _OSF_Keysyms and such.
elif line.startswith("#define _"):
continue
# Keysym entry: proceed various tests
if line.startswith("#") and (m := keysym_entry_pattern.match(line)):
name = make_keysym_name(m)
# Check expected guarded keysym, if relevant
if pending_guarded_keysym and name != pending_guarded_keysym:
raise ValueError(f"{path}: Malformed keysym name guard: {line}")
# Check if name already defined
elif name in keysyms:
if pending_guarded_keysym:
# Ignore guarded keysym
continue
else:
raise ValueError(f"{path}: Unguarded redefinition: {line}")
else:
keysyms.add(name)
# Perform _EVDEV and XK_ substitutions
line = keysym_entry_pattern.sub(make_keysym_entry, line)
line = xorgproto_keysym_prefix_pattern.sub(rf"{KEY_PREFIX}\1", line)
line = alias_pattern.sub(fix_alias, line)
print(line.rstrip(), end="\n")
print("\n\n#endif")