react-x11 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +330 -81
  2. package/package.json +132 -14
  3. package/src/ClickToComponent.js +9 -2
  4. package/src/DevToolsIntegration.js +440 -32
  5. package/src/Reconciler.js +463 -165
  6. package/src/a11y.js +1299 -0
  7. package/src/acceleratorhooks.js +86 -0
  8. package/src/accelerators.js +128 -0
  9. package/src/activate.js +168 -0
  10. package/src/anchor.js +360 -0
  11. package/src/appcontext.js +161 -0
  12. package/src/appearance.js +748 -0
  13. package/src/appearancehooks.js +96 -0
  14. package/src/apphooks.js +73 -0
  15. package/src/application.js +748 -0
  16. package/src/atspi.js +1953 -0
  17. package/src/bus.js +545 -0
  18. package/src/bushooks.js +136 -0
  19. package/src/clientmessage.js +140 -0
  20. package/src/clipboard.js +121 -0
  21. package/src/components/Button.js +148 -33
  22. package/src/components/Checkbox.js +66 -32
  23. package/src/components/Dialog.js +96 -29
  24. package/src/components/FileDialog.js +491 -0
  25. package/src/components/Icon.js +329 -0
  26. package/src/components/Menu.js +797 -133
  27. package/src/components/PasswordInput.js +434 -0
  28. package/src/components/ProgressBar.js +91 -12
  29. package/src/components/Radio.js +78 -35
  30. package/src/components/Select.js +255 -65
  31. package/src/components/Slider.js +102 -41
  32. package/src/components/SplitPane.js +206 -0
  33. package/src/components/Switch.js +100 -22
  34. package/src/components/Table.js +529 -0
  35. package/src/components/Tabs.js +211 -0
  36. package/src/components/Tooltip.js +528 -54
  37. package/src/components/anchor.js +154 -107
  38. package/src/components/change.js +34 -0
  39. package/src/components/dnd.js +103 -0
  40. package/src/components/index.js +16 -3
  41. package/src/components/keys.js +17 -20
  42. package/src/components/scribble.js +247 -0
  43. package/src/components/theme.js +350 -42
  44. package/src/components/typeahead.js +12 -2
  45. package/src/compose.js +868 -0
  46. package/src/compositing.js +224 -0
  47. package/src/dbusmenu.js +384 -0
  48. package/src/debug.d.ts +63 -0
  49. package/src/debug.js +642 -0
  50. package/src/decorations.js +486 -0
  51. package/src/desktopsettings.js +209 -0
  52. package/src/desktopsettingshooks.js +62 -0
  53. package/src/dnd.js +1725 -0
  54. package/src/editmenu.js +272 -0
  55. package/src/errors.js +98 -0
  56. package/src/events.js +1406 -162
  57. package/src/extensions.js +45 -0
  58. package/src/filedialog.js +375 -0
  59. package/src/filedialoghooks.js +132 -0
  60. package/src/fonthooks.js +64 -0
  61. package/src/fonts.js +301 -0
  62. package/src/foreignnodes.js +519 -0
  63. package/src/frame/child.js +33 -0
  64. package/src/frame/childmain.js +274 -0
  65. package/src/frame/env.js +140 -0
  66. package/src/frame/index.js +444 -0
  67. package/src/frame/lifecycle.js +67 -0
  68. package/src/frame/protocol.js +179 -0
  69. package/src/frames.js +73 -0
  70. package/src/glbackend.js +114 -0
  71. package/src/glnodes.js +125 -67
  72. package/src/globalmenu.js +683 -0
  73. package/src/host.d.ts +88 -0
  74. package/src/host.js +42 -0
  75. package/src/idle.js +490 -0
  76. package/src/idlehooks.js +100 -0
  77. package/src/imagesource.js +349 -0
  78. package/src/index.d.ts +475 -0
  79. package/src/index.js +83 -9
  80. package/src/inputtime.js +206 -0
  81. package/src/jsx-dev-runtime.d.ts +21 -0
  82. package/src/jsx-dev-runtime.js +2 -0
  83. package/src/jsx-runtime.d.ts +39 -0
  84. package/src/jsx-runtime.js +6 -0
  85. package/src/keyboard.js +256 -0
  86. package/src/keyboardstate.js +278 -0
  87. package/src/keyboardstatehooks.js +58 -0
  88. package/src/keysyms.d.ts +149 -0
  89. package/src/keysyms.js +270 -0
  90. package/src/locale.js +170 -0
  91. package/src/localehooks.js +47 -0
  92. package/src/menuitem.js +223 -0
  93. package/src/node.d.ts +597 -0
  94. package/src/node.js +44 -0
  95. package/src/nodes.js +9546 -690
  96. package/src/ntk.d.ts +44 -0
  97. package/src/ntk.js +25 -0
  98. package/src/paintcache.js +366 -0
  99. package/src/palette.js +380 -0
  100. package/src/pastestate.js +66 -0
  101. package/src/portal.js +461 -0
  102. package/src/priority.js +26 -0
  103. package/src/refresh/index.d.ts +40 -0
  104. package/src/refresh/index.js +122 -0
  105. package/src/refresh/loader.d.ts +37 -0
  106. package/src/refresh/loader.js +401 -0
  107. package/src/refresh/register.d.ts +5 -0
  108. package/src/refresh/register.js +13 -0
  109. package/src/registry.js +232 -0
  110. package/src/scale.js +626 -0
  111. package/src/scalehooks.js +27 -0
  112. package/src/screencolor.js +640 -0
  113. package/src/screencolorhooks.js +101 -0
  114. package/src/screens.js +754 -0
  115. package/src/screenshooks.js +137 -0
  116. package/src/startup.js +302 -0
  117. package/src/style.d.ts +126 -0
  118. package/src/style.js +33 -0
  119. package/src/styles.js +1482 -6
  120. package/src/svgnodes.js +306 -0
  121. package/src/testing/a11y.js +484 -0
  122. package/src/testing/components.js +414 -0
  123. package/src/testing/events.js +407 -0
  124. package/src/testing/harness.js +455 -0
  125. package/src/testing/index.d.ts +558 -0
  126. package/src/testing/index.js +85 -0
  127. package/src/testing/mock-app.js +463 -0
  128. package/src/testing/pixels.js +152 -0
  129. package/src/testing/queries.js +224 -0
  130. package/src/textrange.js +83 -0
  131. package/src/textselection.js +439 -0
  132. package/src/trace-registry.js +63 -0
  133. package/src/transfer.js +93 -0
  134. package/src/types/appearance.d.ts +84 -0
  135. package/src/types/application.d.ts +173 -0
  136. package/src/types/components.d.ts +820 -0
  137. package/src/types/dbus.d.ts +177 -0
  138. package/src/types/elements.d.ts +897 -0
  139. package/src/types/events.d.ts +549 -0
  140. package/src/types/filedialog.d.ts +260 -0
  141. package/src/types/fonts.d.ts +124 -0
  142. package/src/types/frame.d.ts +146 -0
  143. package/src/types/globalmenu.d.ts +42 -0
  144. package/src/types/nodes.d.ts +199 -0
  145. package/src/types/screencolor.d.ts +84 -0
  146. package/src/types/style.d.ts +434 -0
  147. package/src/types/system.d.ts +287 -0
  148. package/src/windowid.js +151 -0
  149. package/src/windowstate.js +393 -0
  150. package/src/xsettings.js +336 -0
  151. package/src/yoga.d.ts +55 -0
  152. package/src/yoga.js +135 -0
  153. package/src/components/Canvas3D.js +0 -28
  154. package/src/geometry3d.js +0 -223
  155. package/src/pointer3d.js +0 -158
  156. package/src/raycast3d.js +0 -146
  157. package/src/richnodes.js +0 -436
  158. package/src/scene3d.js +0 -683
@@ -0,0 +1,278 @@
1
+ // Caps Lock, Num Lock, and which keyboard layout is live.
2
+ //
3
+ // The one thing an application cannot work out for itself. A key event
4
+ // carries the modifier state at the moment it was pressed, so an app can see
5
+ // that Caps Lock was on for *a key it received* — which is exactly no help in
6
+ // the case that matters, a password field that wants to warn before the first
7
+ // character is typed and while the field is merely focused.
8
+ //
9
+ // XKB answers both halves directly: `GetState` for the state now,
10
+ // `StateNotify` for every change after that, including the ones that happen
11
+ // while another window has the keyboard. There is no polling and no timer.
12
+ //
13
+ // ## Which bit is which lock
14
+ //
15
+ // Caps Lock is `LockMask`, fixed by the core protocol. **Num Lock is not
16
+ // fixed by anything** — it is wherever the modifier map puts it, and that is
17
+ // `Mod2` on every Linux and BSD desktop, on XQuartz, and in every toolkit
18
+ // that hardcodes it (GTK reads the modmap; Qt hardcodes Mod2). Reading the
19
+ // modifier map to be sure costs a round trip and a keysym search to arrive at
20
+ // Mod2 in every real case, so this takes the convention — the same one
21
+ // `MOD.Mod2` in `keysyms.js` already documents for Alt and Super.
22
+ //
23
+ // ## Where the layout names come from
24
+ //
25
+ // `GetState().group` is an *index*, 0 to 3, and says nothing about what is in
26
+ // the group. The names live in `_XKB_RULES_NAMES` on the root window — the
27
+ // property `setxkbmap` writes and `setxkbmap -query` reads back — as
28
+ // NUL-separated rules, model, layouts, variants and options. So `us,ru` plus
29
+ // group 1 is `'ru'`.
30
+ //
31
+ // XKB's own `GetNames(GroupNames)` is the other route and is not used here:
32
+ // it answers atoms that have to be interned back one round trip each, and
33
+ // what it returns is a description like `English (US)` where an indicator in
34
+ // a status bar wants `us`.
35
+
36
+ import { requireExtension } from './extensions.js';
37
+
38
+ const sessions = new WeakMap();
39
+
40
+ /** `1 << xkbType` for the two events this needs. */
41
+ const NEW_KEYBOARD_NOTIFY = 1 << 0;
42
+ const STATE_NOTIFY = 1 << 2;
43
+
44
+ const XKB_USE_CORE_KBD = 0x100;
45
+ const LOCK_MASK = 2; // MOD.Lock — Caps Lock, per the core protocol
46
+ const MOD2_MASK = 16; // MOD.Mod2 — Num Lock, per universal convention
47
+ const RULES_PROPERTY = '_XKB_RULES_NAMES';
48
+
49
+ /**
50
+ * What is known before XKB has answered, and what stays true on a display
51
+ * without the extension.
52
+ *
53
+ * Every field is the "off" answer rather than a null, because every caller of
54
+ * this is drawing something: a Caps Lock warning that renders `null` as truthy
55
+ * is worse than one that is briefly absent, and it is absent for one frame.
56
+ * `layouts` being empty is what distinguishes "not known" from "no lock on".
57
+ */
58
+ const UNKNOWN = Object.freeze({
59
+ capsLock: false,
60
+ numLock: false,
61
+ group: 0,
62
+ layout: null,
63
+ layouts: Object.freeze([]),
64
+ });
65
+
66
+ class KeyboardSession {
67
+ constructor(app) {
68
+ this.app = app;
69
+ this.snapshot = UNKNOWN;
70
+ this.listeners = new Set();
71
+ this.armed = false;
72
+ this.stopped = false;
73
+ this._handler = null;
74
+ }
75
+
76
+ publish(values) {
77
+ const next = { ...this.snapshot, ...values };
78
+ // `layout` is derived in one place so it cannot disagree with the group
79
+ // and the list it comes from — a group index past the end of a
80
+ // just-reconfigured list is null rather than undefined.
81
+ next.layout = next.layouts[next.group] ?? null;
82
+ const prev = this.snapshot;
83
+ if (
84
+ next.capsLock === prev.capsLock &&
85
+ next.numLock === prev.numLock &&
86
+ next.group === prev.group &&
87
+ next.layout === prev.layout &&
88
+ // by value: `readLayouts` builds a fresh array every time it runs, and
89
+ // it runs again on every keymap replacement, most of which change
90
+ // nothing about the list
91
+ next.layouts.length === prev.layouts.length &&
92
+ next.layouts.every((l, i) => l === prev.layouts[i])
93
+ ) {
94
+ return;
95
+ }
96
+ this.snapshot = Object.freeze(next);
97
+ for (const fn of [...this.listeners]) {
98
+ try {
99
+ fn();
100
+ } catch {
101
+ // one subscriber throwing must not take the others with it, nor the
102
+ // X event loop this runs on
103
+ }
104
+ }
105
+ }
106
+
107
+ stop() {
108
+ this.stopped = true;
109
+ if (this._handler && this.app?.X?.off) {
110
+ try {
111
+ this.app.X.off('event', this._handler);
112
+ } catch {
113
+ // an ntk old enough to hand back a client with no `off`
114
+ }
115
+ }
116
+ this._handler = null;
117
+ this.listeners.clear();
118
+ }
119
+ }
120
+
121
+ /**
122
+ * `_XKB_RULES_NAMES` → the configured layout codes.
123
+ *
124
+ * Five NUL-terminated strings — rules, model, layouts, variants, options —
125
+ * of which the third is `us,ru`. Pure, and exported for the test: the
126
+ * in-process X server has no XKB, so this is the part of the path that can
127
+ * be pinned without one.
128
+ *
129
+ * A trailing NUL leaves an empty final field, and a layout list can
130
+ * legitimately have an empty entry (`us,,ru` from a hand-edited config), so
131
+ * the split keeps position and drops only what is empty at the ends.
132
+ */
133
+ export function layoutsFromRules(data) {
134
+ if (!data?.length) return [];
135
+ const fields = data.toString('latin1').split('\0');
136
+ const layouts = fields[2];
137
+ if (!layouts) return [];
138
+ const list = layouts.split(',').map((s) => s.trim());
139
+ while (list.length && !list[list.length - 1]) list.pop();
140
+ // `empty` is a real xkeyboard-config layout, and what it is defined as is a
141
+ // layout with no symbols in it — which is exactly what XQuartz writes here,
142
+ // because it synthesizes its keymap from macOS and has no XKB layout to
143
+ // name. Reporting it would put "EMPTY" in a status-bar indicator; an empty
144
+ // list is what "this display cannot say" already means everywhere else here.
145
+ return list.every((name) => name === 'empty') ? [] : list;
146
+ }
147
+
148
+ /** Caps and Num out of a locked-modifier mask. */
149
+ export function locksFromMods(lockedMods) {
150
+ return {
151
+ capsLock: Boolean(lockedMods & LOCK_MASK),
152
+ numLock: Boolean(lockedMods & MOD2_MASK),
153
+ };
154
+ }
155
+
156
+ async function arm(session) {
157
+ if (session.armed) return;
158
+ session.armed = true;
159
+ const app = session.app;
160
+ const xkb = await requireExtension(app, 'xkb');
161
+ if (!xkb || session.stopped) return;
162
+
163
+ // Subscribe before reading. A lock toggled between the two would otherwise
164
+ // be lost for the life of the connection — the same ordering rule the
165
+ // appearance ladder and the clipboard watch both follow.
166
+ const handler = (ev) => {
167
+ if (session.stopped || ev.type !== xkb.firstEvent) return;
168
+ if (ev.xkbType === xkb.events.StateNotify) {
169
+ session.publish({ ...locksFromMods(ev.lockedMods), group: ev.group });
170
+ return;
171
+ }
172
+ // NewKeyboardNotify: the keymap itself was replaced, which is what
173
+ // `setxkbmap` does when the *set* of layouts changes rather than which
174
+ // one is active. Rare, and cheap to answer — one property read.
175
+ if (ev.xkbType === 0) readLayouts(session);
176
+ };
177
+ session._handler = handler;
178
+ app.X.on('event', handler);
179
+
180
+ try {
181
+ xkb.SelectEvents(
182
+ XKB_USE_CORE_KBD,
183
+ STATE_NOTIFY | NEW_KEYBOARD_NOTIFY,
184
+ 0,
185
+ STATE_NOTIFY | NEW_KEYBOARD_NOTIFY,
186
+ 0,
187
+ 0,
188
+ );
189
+ } catch {
190
+ return;
191
+ }
192
+
193
+ readLayouts(session);
194
+
195
+ const state = await new Promise((resolve) => {
196
+ try {
197
+ xkb.GetState(XKB_USE_CORE_KBD, (err, value) =>
198
+ resolve(err ? null : value),
199
+ );
200
+ } catch {
201
+ resolve(null);
202
+ }
203
+ });
204
+ if (state && !session.stopped) {
205
+ session.publish({ ...locksFromMods(state.lockedMods), group: state.group });
206
+ }
207
+ }
208
+
209
+ async function readLayouts(session) {
210
+ const X = session.app.X;
211
+ const root = X.display?.screen?.[0]?.root;
212
+ if (root == null) return;
213
+ try {
214
+ const atom = await new Promise((resolve, reject) =>
215
+ X.InternAtom(false, RULES_PROPERTY, (err, value) =>
216
+ err ? reject(err) : resolve(value),
217
+ ),
218
+ );
219
+ const prop = await new Promise((resolve) =>
220
+ X.GetProperty(0, root, atom, 0, 0, 0x1fffffff, (err, value) =>
221
+ resolve(err ? null : value),
222
+ ),
223
+ );
224
+ if (session.stopped) return;
225
+ const layouts = layoutsFromRules(prop?.data);
226
+ if (layouts.length) session.publish({ layouts: Object.freeze(layouts) });
227
+ } catch {
228
+ // no rules property: a server configured by other means, or XQuartz,
229
+ // where the group index is all there is. `layouts` stays empty and
230
+ // `layout` stays null, which is what those say.
231
+ }
232
+ }
233
+
234
+ /** What is known right now. Not public — `useKeyboardState()` is. */
235
+ export function keyboardStateSnapshot(app) {
236
+ return sessions.get(app)?.snapshot ?? UNKNOWN;
237
+ }
238
+
239
+ /** Subscribe to the lock and layout state changing. */
240
+ export function watchKeyboardState(app, onChange) {
241
+ if (!app) return () => {};
242
+ let session = sessions.get(app);
243
+ if (!session) {
244
+ session = new KeyboardSession(app);
245
+ sessions.set(app, session);
246
+ }
247
+ arm(session).catch(() => {
248
+ // an extension that is not there is not an error
249
+ });
250
+ session.listeners.add(onChange);
251
+ return () => session.listeners.delete(onChange);
252
+ }
253
+
254
+ /** Tear down with the root that started it. */
255
+ export function endKeyboardState(app) {
256
+ const session = sessions.get(app);
257
+ if (!session) return;
258
+ session.stop();
259
+ sessions.delete(app);
260
+ }
261
+
262
+ /**
263
+ * Test seam: state the keyboard without a server that has XKB. Marks the
264
+ * session armed, so nothing tries to reach the extension behind the value.
265
+ */
266
+ export function setKeyboardStateForTests(app, values) {
267
+ let session = sessions.get(app);
268
+ if (!session) {
269
+ session = new KeyboardSession(app);
270
+ sessions.set(app, session);
271
+ }
272
+ session.armed = true;
273
+ session.publish({
274
+ ...values,
275
+ layouts: Object.freeze(values.layouts ?? UNKNOWN.layouts),
276
+ });
277
+ return session;
278
+ }
@@ -0,0 +1,58 @@
1
+ // `useKeyboardState()` — the locks and the active layout, as something a
2
+ // component re-renders on.
3
+ //
4
+ // The store lives in `keyboardstate.js`, keyed by connection: there is one
5
+ // keyboard per display, and its state is the same for every window on it.
6
+
7
+ import { useCallback, useSyncExternalStore } from 'react';
8
+
9
+ import { useApp } from './appcontext.js';
10
+ import { keyboardStateSnapshot, watchKeyboardState } from './keyboardstate.js';
11
+
12
+ /**
13
+ * The keyboard's locks and layout, live.
14
+ *
15
+ * ```jsx
16
+ * const { capsLock, layout } = useKeyboardState();
17
+ *
18
+ * <PasswordInput value={password} onChange={setPassword} />
19
+ * {capsLock && <text style={{ color: theme.warning }}>Caps Lock is on</text>}
20
+ * ```
21
+ *
22
+ * | | |
23
+ * | --- | --- |
24
+ * | `capsLock` `numLock` | on or off, **now** — not "was on for the last key" |
25
+ * | `group` | the active XKB group, `0`–`3` |
26
+ * | `layout` | that group's layout code, `'ru'` — or null |
27
+ * | `layouts` | every configured layout, `['us', 'ru']` — `[]` where unknown |
28
+ *
29
+ * The point of the first row is that it is true of the keyboard rather than
30
+ * of an event: a password field can warn about Caps Lock **before** the first
31
+ * character is typed, which is the only time the warning is worth anything.
32
+ * A key event's own modifier state cannot do that, because it needs a key.
33
+ *
34
+ * The second is for a status-bar indicator, and for the case that costs
35
+ * people real time — a password typed in the wrong script, which looks
36
+ * identical to a wrong password. `layout` is a code rather than a
37
+ * description (`'ru'`, not `'Russian'`) because that is what an indicator
38
+ * shows and what a lookup key wants; uppercase it for display.
39
+ *
40
+ * Everything here needs **XKB**, which Xorg and XQuartz both have. Where it
41
+ * is missing the locks read false and `layouts` is empty, and an app that
42
+ * renders a warning only when `capsLock` is true degrades to not warning.
43
+ * `layouts` is the field to check when the difference between "off" and "not
44
+ * known" matters.
45
+ *
46
+ * Nothing is asked of the server until a component calls this, and the change
47
+ * is delivered by `XkbStateNotify` — no polling, and it arrives even while
48
+ * another application has the keyboard.
49
+ */
50
+ export function useKeyboardState() {
51
+ const app = useApp();
52
+ const subscribe = useCallback(
53
+ (onChange) => watchKeyboardState(app, onChange),
54
+ [app],
55
+ );
56
+ const snapshot = useCallback(() => keyboardStateSnapshot(app), [app]);
57
+ return useSyncExternalStore(subscribe, snapshot, snapshot);
58
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * X11 keysyms — `react-x11/keysyms`. One vocabulary for app code, the
3
+ * widgets and `fireEvent.key`.
4
+ */
5
+
6
+ /**
7
+ * The keysym for a character. Latin-1 (U+0020–U+00FF) is identity, so `'a'`
8
+ * is `0x61` and `'é'` is `0xe9`; anything else is `0x01000000 + codePoint`.
9
+ */
10
+ export function keysymOf(char: string): number;
11
+
12
+ /** The character a keysym produces, or `''` for a non-printing key. */
13
+ export function charOf(keysym: number): string;
14
+
15
+ /**
16
+ * The letter of a Ctrl chord, independent of Shift — the keysym for its
17
+ * lowercase form, so `keysymOf('z')` matches both Ctrl+Z and Ctrl+Shift+Z.
18
+ * Null when the event carries neither a keysym nor a codepoint.
19
+ *
20
+ * ntk derives `codepoint` from the *shifted* keysym, so a handler that
21
+ * compared code points would see `Z` for Ctrl+Shift+Z and miss the chord.
22
+ * Here rather than inside `<textinput>` because both layers need it: the
23
+ * built-in editors read Ctrl+C/V/Z, and so does any widget that answers a
24
+ * chord of its own.
25
+ *
26
+ * ```js
27
+ * if (ev.ctrlKey && ctrlChordLetter(ev) === keysymOf('d')) duplicateLine();
28
+ * ```
29
+ */
30
+ export function ctrlChordLetter(ev: {
31
+ keysym?: number | null;
32
+ codepoint?: number | null;
33
+ }): number | null;
34
+
35
+ /**
36
+ * The keysym an X11 key *name* stands for — `'Return'`, `'plus'`, `'F5'`,
37
+ * `'s'` — or `undefined` for a name nothing here knows.
38
+ *
39
+ * This is the vocabulary a menu item's `shortcut` chords are written in
40
+ * (X11's names, which is what `gdk_keyval_name()` emits and what a panel's
41
+ * dbusmenu importer parses), so a chord can be matched, drawn and exported
42
+ * from one spelling. A one-character name is the character itself.
43
+ */
44
+ export function keysymFromName(name: string): number | undefined;
45
+
46
+ export const XK_BACKSPACE: 0xff08;
47
+ export const XK_TAB: 0xff09;
48
+ export const XK_RETURN: 0xff0d;
49
+ export const XK_ESCAPE: 0xff1b;
50
+ export const XK_DELETE: 0xffff;
51
+ export const XK_INSERT: 0xff63;
52
+
53
+ export const XK_HOME: 0xff50;
54
+ export const XK_LEFT: 0xff51;
55
+ export const XK_UP: 0xff52;
56
+ export const XK_RIGHT: 0xff53;
57
+ export const XK_DOWN: 0xff54;
58
+ export const XK_PAGE_UP: 0xff55;
59
+ export const XK_PAGE_DOWN: 0xff56;
60
+ export const XK_END: 0xff57;
61
+
62
+ export const XK_KP_ENTER: 0xff8d;
63
+ export const XK_MENU: 0xff67;
64
+ export const XK_SPACE: 0x0020;
65
+
66
+ /**
67
+ * The Compose key. It types nothing on its own: it opens a sequence that
68
+ * the next keys finish — `Compose o c` is `©`. See
69
+ * [docs/events.md](events.md#composition).
70
+ */
71
+ export const XK_MULTI_KEY: 0xff20;
72
+
73
+ // The dead keys. Each waits for the character it decorates, so `dead_acute`
74
+ // then `e` is `é`; on its own — followed by a space, or pressed twice — it
75
+ // types the accent.
76
+ export const XK_DEAD_GRAVE: 0xfe50;
77
+ export const XK_DEAD_ACUTE: 0xfe51;
78
+ export const XK_DEAD_CIRCUMFLEX: 0xfe52;
79
+ export const XK_DEAD_TILDE: 0xfe53;
80
+ export const XK_DEAD_MACRON: 0xfe54;
81
+ export const XK_DEAD_BREVE: 0xfe55;
82
+ export const XK_DEAD_ABOVEDOT: 0xfe56;
83
+ export const XK_DEAD_DIAERESIS: 0xfe57;
84
+ export const XK_DEAD_ABOVERING: 0xfe58;
85
+ export const XK_DEAD_DOUBLEACUTE: 0xfe59;
86
+ export const XK_DEAD_CARON: 0xfe5a;
87
+ export const XK_DEAD_CEDILLA: 0xfe5b;
88
+ export const XK_DEAD_OGONEK: 0xfe5c;
89
+ export const XK_DEAD_IOTA: 0xfe5d;
90
+ export const XK_DEAD_VOICED_SOUND: 0xfe5e;
91
+ export const XK_DEAD_SEMIVOICED_SOUND: 0xfe5f;
92
+ export const XK_DEAD_BELOWDOT: 0xfe60;
93
+ export const XK_DEAD_HOOK: 0xfe61;
94
+ export const XK_DEAD_HORN: 0xfe62;
95
+ export const XK_DEAD_STROKE: 0xfe63;
96
+ export const XK_DEAD_ABOVECOMMA: 0xfe64;
97
+ export const XK_DEAD_ABOVEREVERSEDCOMMA: 0xfe65;
98
+ export const XK_DEAD_DOUBLEGRAVE: 0xfe66;
99
+ export const XK_DEAD_BELOWRING: 0xfe67;
100
+ export const XK_DEAD_BELOWMACRON: 0xfe68;
101
+ export const XK_DEAD_BELOWCIRCUMFLEX: 0xfe69;
102
+ export const XK_DEAD_BELOWTILDE: 0xfe6a;
103
+ export const XK_DEAD_BELOWBREVE: 0xfe6b;
104
+ export const XK_DEAD_BELOWDIAERESIS: 0xfe6c;
105
+ export const XK_DEAD_INVERTEDBREVE: 0xfe6d;
106
+ export const XK_DEAD_BELOWCOMMA: 0xfe6e;
107
+ export const XK_DEAD_CURRENCY: 0xfe6f;
108
+
109
+ /** Whether a keysym is one of the `XK_dead_*` block. */
110
+ export function isDeadKeysym(keysym: number): boolean;
111
+
112
+ export const XK_SHIFT_L: 0xffe1;
113
+ export const XK_SHIFT_R: 0xffe2;
114
+ export const XK_CONTROL_L: 0xffe3;
115
+ export const XK_CONTROL_R: 0xffe4;
116
+ export const XK_CAPS_LOCK: 0xffe5;
117
+ export const XK_ALT_L: 0xffe9;
118
+ export const XK_ALT_R: 0xffea;
119
+ export const XK_SUPER_L: 0xffeb;
120
+ export const XK_SUPER_R: 0xffec;
121
+
122
+ export const XK_F1: 0xffbe;
123
+ export const XK_F2: 0xffbf;
124
+ export const XK_F3: 0xffc0;
125
+ export const XK_F4: 0xffc1;
126
+ export const XK_F5: 0xffc2;
127
+ export const XK_F6: 0xffc3;
128
+ export const XK_F7: 0xffc4;
129
+ export const XK_F8: 0xffc5;
130
+ export const XK_F9: 0xffc6;
131
+ export const XK_F10: 0xffc7;
132
+ export const XK_F11: 0xffc8;
133
+ export const XK_F12: 0xffc9;
134
+
135
+ /** X11 modifier mask bits, as `ev.nativeEvent.buttons` carries them. */
136
+ export const MOD: {
137
+ Shift: 1;
138
+ Lock: 2;
139
+ Control: 4;
140
+ Mod1: 8;
141
+ Alt: 8;
142
+ Mod2: 16;
143
+ Mod3: 32;
144
+ Mod4: 64;
145
+ Super: 64;
146
+ Mod5: 128;
147
+ };
148
+
149
+ export type ModifierName = keyof typeof MOD;