Keysyms: Fix missing hpYdiaeresis
[platform/upstream/libxkbcommon.git] / scripts / makeheader
1 #!/usr/bin/env python3
2 from __future__ import print_function
3 import re
4 import os
5 from pathlib import Path
6
7 # Expected format:
8 #     #define XF86XK_FooBar 0x1234         /* some optional comment */
9 # or:
10 #     #define XF86XK_FooBar _EVDEVK(0x123) /* some optional comment */
11 # We also need to match commented evdev entries:
12 #     /* Use: XF86XK_FooBar _EVDEVK(0x123)    some optional comment */
13 keysym_entry_pattern = re.compile(
14     r"""^
15     (?P<define>\#define|/\*\s+Use:)\s+
16     (?P<prefix>\w*)XK_(?P<name>\w+)(?P<spacing>\s+)
17     (?P<evdev>_EVDEVK\()?(?P<value>0x[0-9A-Fa-f]+)(?(evdev)\))
18     """,
19     re.VERBOSE,
20 )
21
22 # Match keysym guarded by #ifndef
23 keysym_ifndef_pattern = re.compile(r"^#ifndef\s+(?P<prefix>\w*)XK_(?P<name>\w+)\s*$")
24
25 # Match remaining XK_ references in the comments, e.g we will replace:
26 #       XF86XK_CamelCaseKernelName      _EVDEVK(kernel value)
27 #       #define XKB_KEY_SunCompose              0x0000FF20      /* Same as XK_Multi_key */
28 # with:
29 #       XKB_KEY_XF86CamelCaseKernelName _EVDEVK(kernel value)
30 #       #define XKB_KEY_SunCompose              0x0000FF20      /* Same as XKB_KEY_Multi_key */
31 xorgproto_keysym_prefix_pattern = re.compile(r"\b(?P<prefix>\w*)XK_(?!KOREAN\b)")
32
33
34 def make_keysym_name(m: re.Match[str]) -> str:
35     return m.group("prefix") + m.group("name")
36
37
38 def make_keysym_entry(m: re.Match[str]) -> str:
39     """
40     Perform the substitutions
41     """
42     if m.group("evdev"):
43         if m.group("define").startswith("#"):
44             # Replace the xorgproto _EVDEVK macro with the actual value:
45             # 0x10081000 is the base, the evdev hex code is added to that.
46             # We replace to make parsing of the keys later easier.
47             value = 0x10081000 + int(m.group("value"), 16)
48             value_str = f"{value:#x}    "
49         else:
50             value_str = f"""_EVDEVK({m.group('value')})"""
51     else:
52         value_str = m.group("value")
53     define = m.group("define")
54     prefix = m.group("prefix") or ""
55     name = m.group("name")
56     spacing = m.group("spacing")
57     return f"""{define} XKB_KEY_{prefix}{name}{spacing}{value_str}"""
58
59
60 prefix = Path(os.environ.get("X11_HEADERS_PREFIX", "/usr"))
61 HEADERS = (
62     prefix / "include/X11/keysymdef.h",
63     prefix / "include/X11/XF86keysym.h",
64     prefix / "include/X11/Sunkeysym.h",
65     prefix / "include/X11/DECkeysym.h",
66     prefix / "include/X11/HPkeysym.h",
67 )
68
69 print(
70     """#ifndef _XKBCOMMON_KEYSYMS_H
71 #define _XKBCOMMON_KEYSYMS_H
72
73 /* This file is autogenerated; please do not commit directly. */
74
75 /**
76  * @file
77  * Key symbols (keysyms) definitions.
78  */
79
80 #define XKB_KEY_NoSymbol                    0x000000  /* Special KeySym */
81 """
82 )
83
84 keysyms: set[str] = set()
85 for path in HEADERS:
86     pending_guarded_keysym: str | None = None
87     with path.open("rt", encoding="utf-8") as header:
88         for line in header:
89             # Duplicate keysym name guard
90             if m := keysym_ifndef_pattern.match(line):
91                 if pending_guarded_keysym:
92                     raise ValueError(f"Nested #ifndef {pending_guarded_keysym}")
93                 pending_guarded_keysym = make_keysym_name(m)
94                 continue
95
96             # Ignore C macro #ifdef/#ifndef
97             elif line.startswith("#ifdef") or line.startswith("#ifndef"):
98                 if pending_guarded_keysym:
99                     raise ValueError(f"Nested C macro {pending_guarded_keysym}")
100                 continue
101
102             # Ignore C macro #endif and check end of keysym name guard
103             elif line.startswith("#endif"):
104                 if pending_guarded_keysym:
105                     pending_guarded_keysym = None
106                 continue
107
108             # Remove #define _OSF_Keysyms and such.
109             elif line.startswith("#define _"):
110                 continue
111
112             # Keysym entry: proceed various tests
113             if line.startswith("#") and (m := keysym_entry_pattern.match(line)):
114                 name = make_keysym_name(m)
115                 # Check expected guarded keysym, if relevant
116                 if pending_guarded_keysym and name != pending_guarded_keysym:
117                     raise ValueError(f"{path}: Malformed keysym name guard: {line}")
118                 # Check if name already defined
119                 elif name in keysyms:
120                     if pending_guarded_keysym:
121                         # Ignore guarded keysym
122                         continue
123                     else:
124                         raise ValueError(f"{path}: Unguarded redefinition: {line}")
125                 else:
126                     keysyms.add(name)
127
128             # Perform _EVDEV and XK_ substitutions
129             line = keysym_entry_pattern.sub(make_keysym_entry, line)
130             line = xorgproto_keysym_prefix_pattern.sub(r"XKB_KEY_\1", line)
131
132             print(line, end="")
133 print("\n\n#endif")