File: update-keysyms-definitions.py

package info (click to toggle)
libxkbcommon 1.13.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,344 kB
  • sloc: ansic: 57,807; xml: 8,905; python: 7,451; yacc: 913; sh: 253; makefile: 23
file content (191 lines) | stat: -rwxr-xr-x 6,552 bytes parent folder | download
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")