Hash :
584e0690
Author :
Date :
2025-10-08T11:55:22
keysyms: Update using latest xorgproto xorgproto commit: 81931cc0fd4761b42603f7da7d4f50fc282cecc6 Relevant MR: https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/merge_requests/103
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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
#!/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}|(?=NoSymbol))(?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")
todo_commentʹ = xorgproto_keysym_prefix_pattern.sub(
rf"{KEY_PREFIX}\1", todo_comment
)
if todo_commentʹ != todo_comment:
diff = len(todo_commentʹ) - len(todo_comment)
if diff != len(EXTRA_SPACES):
raise ValueError()
todo_comment = todo_commentʹ
else:
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")
if m.group("use") and name == "NoSymbol":
spacing += EXTRA_SPACES
key_prefix = ""
else:
key_prefix = KEY_PREFIX
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
# Ignore C macro #undef
elif line.startswith("#undef"):
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()
print("#endif")