@driftengine/native-host 4.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 (134) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +29 -0
  3. package/README.md +58 -0
  4. package/dist/activation.d.ts +26 -0
  5. package/dist/activation.js +38 -0
  6. package/dist/alphaCopy.d.ts +36 -0
  7. package/dist/alphaCopy.js +178 -0
  8. package/dist/audio.d.ts +55 -0
  9. package/dist/audio.js +193 -0
  10. package/dist/audioParams.d.ts +25 -0
  11. package/dist/audioParams.js +128 -0
  12. package/dist/bridge.d.ts +70 -0
  13. package/dist/bridge.js +170 -0
  14. package/dist/canvas.d.ts +97 -0
  15. package/dist/canvas.js +197 -0
  16. package/dist/canvasStyle.d.ts +23 -0
  17. package/dist/canvasStyle.js +79 -0
  18. package/dist/device.d.ts +34 -0
  19. package/dist/device.js +144 -0
  20. package/dist/dialogs.d.ts +23 -0
  21. package/dist/dialogs.js +85 -0
  22. package/dist/domEvents.d.ts +37 -0
  23. package/dist/domEvents.js +145 -0
  24. package/dist/domTree.d.ts +34 -0
  25. package/dist/domTree.js +194 -0
  26. package/dist/dualRumble.d.ts +19 -0
  27. package/dist/dualRumble.js +47 -0
  28. package/dist/eventClasses.d.ts +12 -0
  29. package/dist/eventClasses.js +35 -0
  30. package/dist/files.d.ts +16 -0
  31. package/dist/files.js +59 -0
  32. package/dist/fullscreen.d.ts +33 -0
  33. package/dist/fullscreen.js +65 -0
  34. package/dist/game.d.ts +25 -0
  35. package/dist/game.js +38 -0
  36. package/dist/gamepads.d.ts +65 -0
  37. package/dist/gamepads.js +178 -0
  38. package/dist/globals.d.ts +24 -0
  39. package/dist/globals.js +37 -0
  40. package/dist/gpu.d.ts +26 -0
  41. package/dist/gpu.js +47 -0
  42. package/dist/images.d.ts +50 -0
  43. package/dist/images.js +111 -0
  44. package/dist/index.d.ts +23 -0
  45. package/dist/index.js +21 -0
  46. package/dist/keyCodes.d.ts +30 -0
  47. package/dist/keyCodes.js +205 -0
  48. package/dist/keyEvents.d.ts +54 -0
  49. package/dist/keyEvents.js +117 -0
  50. package/dist/mediaQueries.d.ts +62 -0
  51. package/dist/mediaQueries.js +152 -0
  52. package/dist/mouseEvents.d.ts +96 -0
  53. package/dist/mouseEvents.js +287 -0
  54. package/dist/page.d.ts +118 -0
  55. package/dist/page.js +302 -0
  56. package/dist/pointerCapture.d.ts +32 -0
  57. package/dist/pointerCapture.js +69 -0
  58. package/dist/present.d.ts +27 -0
  59. package/dist/present.js +83 -0
  60. package/dist/rawPad.d.ts +44 -0
  61. package/dist/rawPad.js +65 -0
  62. package/dist/readback.d.ts +18 -0
  63. package/dist/readback.js +46 -0
  64. package/dist/runtime.d.ts +69 -0
  65. package/dist/runtime.js +103 -0
  66. package/dist/standardPad.d.ts +49 -0
  67. package/dist/standardPad.js +80 -0
  68. package/dist/textHost.d.ts +37 -0
  69. package/dist/textHost.js +43 -0
  70. package/dist/threads.d.ts +27 -0
  71. package/dist/threads.js +59 -0
  72. package/dist/tileFiles.d.ts +16 -0
  73. package/dist/tileFiles.js +36 -0
  74. package/dist/touchEvents.d.ts +67 -0
  75. package/dist/touchEvents.js +210 -0
  76. package/dist/uiEvents.d.ts +157 -0
  77. package/dist/uiEvents.js +206 -0
  78. package/dist/uiTouch.d.ts +59 -0
  79. package/dist/uiTouch.js +75 -0
  80. package/dist/window.d.ts +71 -0
  81. package/dist/window.js +164 -0
  82. package/dist/windowMode.d.ts +19 -0
  83. package/dist/windowMode.js +15 -0
  84. package/dist/windowTitle.d.ts +18 -0
  85. package/dist/windowTitle.js +33 -0
  86. package/dist/workerScope.mjs +48 -0
  87. package/licenses/GPL-3.0.txt +674 -0
  88. package/package.json +83 -0
  89. package/src/activation.ts +42 -0
  90. package/src/alphaCopy.ts +201 -0
  91. package/src/audio.ts +246 -0
  92. package/src/audioParams.ts +150 -0
  93. package/src/bridge.ts +230 -0
  94. package/src/canvas.ts +227 -0
  95. package/src/canvasStyle.ts +89 -0
  96. package/src/device.ts +167 -0
  97. package/src/dialogs.ts +98 -0
  98. package/src/domEvents.ts +170 -0
  99. package/src/domTree.ts +215 -0
  100. package/src/drs.d.ts +9 -0
  101. package/src/dualRumble.ts +58 -0
  102. package/src/eventClasses.ts +46 -0
  103. package/src/files.ts +61 -0
  104. package/src/fixtures/grey.jpg +0 -0
  105. package/src/fixtures/rgb.png +0 -0
  106. package/src/fixtures/tone.opus +0 -0
  107. package/src/fullscreen.ts +71 -0
  108. package/src/game.ts +53 -0
  109. package/src/gamepads.ts +195 -0
  110. package/src/globals.ts +39 -0
  111. package/src/gpu.ts +59 -0
  112. package/src/images.ts +148 -0
  113. package/src/index.ts +24 -0
  114. package/src/keyCodes.ts +203 -0
  115. package/src/keyEvents.ts +135 -0
  116. package/src/mediaQueries.ts +173 -0
  117. package/src/mouseEvents.ts +328 -0
  118. package/src/page.ts +337 -0
  119. package/src/pointerCapture.ts +73 -0
  120. package/src/present.ts +92 -0
  121. package/src/rawPad.ts +85 -0
  122. package/src/readback.ts +61 -0
  123. package/src/runtime.ts +162 -0
  124. package/src/standardPad.ts +126 -0
  125. package/src/textHost.ts +66 -0
  126. package/src/threads.ts +66 -0
  127. package/src/tileFiles.ts +39 -0
  128. package/src/touchEvents.ts +238 -0
  129. package/src/uiEvents.ts +268 -0
  130. package/src/uiTouch.ts +97 -0
  131. package/src/window.ts +192 -0
  132. package/src/windowMode.ts +24 -0
  133. package/src/windowTitle.ts +36 -0
  134. package/src/workerScope.mjs +48 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Fingers, as a page hears them from Chrome: pointer events of type `touch`, touch events, and the
3
+ * mouse events and click Chrome makes of a tap.
4
+ *
5
+ * **What Chrome sends, measured 2026-09-19 in Chrome 151 over the DevTools protocol** on a page
6
+ * whose canvas filled it, is what this sends:
7
+ * - A finger down is `pointerover`, `pointerenter` at the document and the canvas, `pointerdown`,
8
+ * then `touchstart`. Fingers are pointers 2, 3 and on, never reused, the first down primary, and
9
+ * a touch's `identifier` is the lowest one free.
10
+ * - A finger is captured by the canvas when it lands, so `gotpointercapture` comes before its next
11
+ * event and `lostpointercapture` right after its `pointerup`, then `pointerout`, `pointerleave`
12
+ * at the canvas and the document, and `touchend`.
13
+ * - A `touchmove` within 15 px of where the finger landed is not sent, unless the `touchstart` was
14
+ * prevented; 13 px was held back and 17 px sent. A pointer move is always sent.
15
+ * - A tap — one finger, no further than 15 px, neither its `touchstart` nor its `touchend`
16
+ * prevented — is then a mouse: `mouseover` and `mouseenter` the first time, `mousemove`,
17
+ * `mousedown`, `mouseup`, and a `click` that is the finger's own pointer event. A second tap
18
+ * landing within 400 ms of the first's lift and 20 px of it counts two, with a `dblclick`: 340 ms
19
+ * counted and 380 did not, 20 px counted and 30 did not.
20
+ *
21
+ * **SDL makes a mouse of every finger**, and a finger of the mouse where asked; both are dropped,
22
+ * each already said by the other.
23
+ *
24
+ * What it gives up: SDL reports no contact size, so a finger is a pointer one pixel across; no
25
+ * `touchcancel`, which SDL never reports; and a `touchend` after a move is cancelable here, which
26
+ * Chrome's was not.
27
+ */
28
+ import { MouseEvent, PointerEvent } from './uiEvents.js';
29
+ import { Touch, TouchEvent } from './uiTouch.js';
30
+ const SLOP_PX = 15;
31
+ const DOUBLE_TAP_MS = 400;
32
+ const DOUBLE_TAP_PX = 20;
33
+ export class TouchModel {
34
+ window;
35
+ page;
36
+ canvas;
37
+ modifiers;
38
+ now;
39
+ fingers = new Map();
40
+ nextPointer = 2;
41
+ /** Whether this run of touches may still be a tap: one finger, nothing prevented. */
42
+ tappable = true;
43
+ lastTap = { liftedAt: Number.NEGATIVE_INFINITY, x: 0, y: 0, count: 0 };
44
+ /** Whether the mouse Chrome makes of taps is over the canvas yet. */
45
+ mouseOver = false;
46
+ constructor(window, page, canvas, modifiers, now) {
47
+ this.window = window;
48
+ this.page = page;
49
+ this.canvas = canvas;
50
+ this.modifiers = modifiers;
51
+ this.now = now;
52
+ }
53
+ down(event) {
54
+ if (event.mouse === true || this.fingers.has(event.fingerId))
55
+ return;
56
+ this.page.noteTouch();
57
+ const taken = new Set([...this.fingers.values()].map((f) => f.identifier));
58
+ let identifier = 0;
59
+ while (taken.has(identifier))
60
+ identifier += 1;
61
+ const primary = this.fingers.size === 0;
62
+ /* A second finger down makes the run a gesture of two, which is no tap. */
63
+ this.tappable = primary;
64
+ const x = event.x * this.window.width;
65
+ const y = event.y * this.window.height;
66
+ const finger = {
67
+ identifier,
68
+ pointerId: this.nextPointer,
69
+ primary,
70
+ landedAt: this.now(),
71
+ startX: x,
72
+ startY: y,
73
+ x,
74
+ y,
75
+ pressure: event.pressure,
76
+ travelled: false,
77
+ };
78
+ this.nextPointer += 1;
79
+ this.fingers.set(event.fingerId, finger);
80
+ const { capture, document } = this.page;
81
+ capture.add(finger.pointerId, () => this.fingers.get(event.fingerId) === finger);
82
+ const init = this.init(finger, 0, 1);
83
+ this.pointer('pointerover', init);
84
+ this.boundary('pointerenter', init, [document, this.canvas]);
85
+ this.pointer('pointerdown', init);
86
+ /* Held by the canvas it landed on, as a browser holds a finger, unless a listener chose. */
87
+ if (!capture.has(this.canvas, finger.pointerId))
88
+ capture.set(this.canvas, finger.pointerId);
89
+ if (!this.touch('touchstart', finger))
90
+ this.tappable = false;
91
+ }
92
+ move(event) {
93
+ const finger = this.fingers.get(event.fingerId);
94
+ if (event.mouse === true || finger === undefined)
95
+ return;
96
+ finger.x = event.x * this.window.width;
97
+ finger.y = event.y * this.window.height;
98
+ finger.pressure = event.pressure;
99
+ if (Math.hypot(finger.x - finger.startX, finger.y - finger.startY) > SLOP_PX) {
100
+ finger.travelled = true;
101
+ }
102
+ this.pointer('pointermove', this.init(finger, -1, 1));
103
+ /* A move still inside the slop is held back, unless the page took the touch at its start. */
104
+ if (finger.travelled || !this.tappable)
105
+ this.touch('touchmove', finger);
106
+ }
107
+ up(event) {
108
+ const finger = this.fingers.get(event.fingerId);
109
+ if (event.mouse === true || finger === undefined)
110
+ return;
111
+ finger.x = event.x * this.window.width;
112
+ finger.y = event.y * this.window.height;
113
+ /* A finger lifted is a person's gesture, noted before its events so their listeners may use it. */
114
+ this.page.userActivation.notify();
115
+ /* The pointer's pressure is gone; the touch keeps the force it last had, as Chrome's did. */
116
+ const init = { ...this.init(finger, 0, 0), pressure: 0 };
117
+ this.pointer('pointerup', init);
118
+ this.page.capture.releaseImplicitly(finger.pointerId, init);
119
+ this.pointer('pointerout', init);
120
+ this.boundary('pointerleave', init, [this.canvas, this.page.document]);
121
+ this.fingers.delete(event.fingerId);
122
+ this.page.capture.remove(finger.pointerId);
123
+ const ended = this.touch('touchend', finger);
124
+ if (ended && this.tappable && !finger.travelled && this.fingers.size === 0)
125
+ this.tap(finger);
126
+ }
127
+ /** The mouse Chrome makes of a tap, and its click. */
128
+ tap(finger) {
129
+ const last = this.lastTap;
130
+ const again = finger.landedAt - last.liftedAt <= DOUBLE_TAP_MS &&
131
+ Math.hypot(finger.startX - last.x, finger.startY - last.y) <= DOUBLE_TAP_PX;
132
+ const count = again ? last.count + 1 : 1;
133
+ this.lastTap = { liftedAt: this.now(), x: finger.startX, y: finger.startY, count };
134
+ const at = { ...this.init(finger, -1, 0), clientX: finger.startX, clientY: finger.startY };
135
+ if (!this.mouseOver) {
136
+ this.mouseOver = true;
137
+ this.mouse('mouseover', at);
138
+ this.boundary('mouseenter', at, [this.page.document, this.canvas]);
139
+ }
140
+ this.mouse('mousemove', at);
141
+ const down = this.mouse('mousedown', { ...at, button: 0, buttons: 1, detail: count });
142
+ if (!down.defaultPrevented)
143
+ this.canvas.focus();
144
+ this.mouse('mouseup', { ...at, button: 0, buttons: 0, detail: count });
145
+ this.canvas.dispatchEvent(new PointerEvent('click', {
146
+ ...at,
147
+ button: 0,
148
+ buttons: 0,
149
+ detail: count,
150
+ isPrimary: false,
151
+ bubbles: true,
152
+ cancelable: true,
153
+ }));
154
+ if (count === 2)
155
+ this.mouse('dblclick', { ...at, button: 0, buttons: 0, detail: count });
156
+ }
157
+ init(finger, button, buttons) {
158
+ return {
159
+ clientX: finger.x,
160
+ clientY: finger.y,
161
+ screenX: this.window.x + finger.x,
162
+ screenY: this.window.y + finger.y,
163
+ button,
164
+ buttons,
165
+ pressure: finger.pressure,
166
+ pointerId: finger.pointerId,
167
+ pointerType: 'touch',
168
+ isPrimary: finger.primary,
169
+ ...this.modifiers(),
170
+ };
171
+ }
172
+ pointer(type, init) {
173
+ this.page.capture.process(init.pointerId ?? 0, init);
174
+ this.canvas.dispatchEvent(new PointerEvent(type, { ...init, bubbles: true, cancelable: true }));
175
+ }
176
+ mouse(type, init) {
177
+ const event = new MouseEvent(type, { ...init, bubbles: true, cancelable: true });
178
+ this.canvas.dispatchEvent(event);
179
+ return event;
180
+ }
181
+ /** An enter or a leave, which does not bubble: sent to each node it crosses, in turn. */
182
+ boundary(type, init, nodes) {
183
+ const Kind = type.startsWith('pointer') ? PointerEvent : MouseEvent;
184
+ for (const node of nodes)
185
+ node.dispatchEvent(new Kind(type, { ...init, bubbles: false }));
186
+ }
187
+ /** A touch event for `finger`, answering whether it went uncancelled. */
188
+ touch(type, finger) {
189
+ const touchOf = (f) => new Touch({
190
+ identifier: f.identifier,
191
+ target: this.canvas,
192
+ clientX: f.x,
193
+ clientY: f.y,
194
+ screenX: this.window.x + f.x,
195
+ screenY: this.window.y + f.y,
196
+ force: f.pressure,
197
+ });
198
+ const all = [...this.fingers.values()].map(touchOf);
199
+ const event = new TouchEvent(type, {
200
+ bubbles: true,
201
+ cancelable: true,
202
+ touches: all,
203
+ targetTouches: all,
204
+ changedTouches: [touchOf(finger)],
205
+ ...this.modifiers(),
206
+ });
207
+ this.canvas.dispatchEvent(event);
208
+ return !event.defaultPrevented;
209
+ }
210
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The browser's input events, for a runtime that has only `Event`: keys, the mouse, pointers, the
3
+ * wheel, touches, focus and pads, as classes with the members a browser's carry.
4
+ *
5
+ * **A page reads more of an event than the engine does.** The engine's input reads a key's `code`
6
+ * and a pointer's movement; a consumer's own code reads `getModifierState`, `detail` for a double
7
+ * click, the legacy `keyCode`, `pressure`, `offsetX`, or asks `instanceof PointerEvent`. An event
8
+ * assembled from a bare `Event` answers none of those, and the failure is a `TypeError` inside the
9
+ * consumer's handler or an `undefined` in its arithmetic. So each is its class, installed as the
10
+ * global a browser has, defaulting every member as the UI Events and Pointer Events specifications
11
+ * do.
12
+ *
13
+ * **Two of Chrome's own answers are kept where they differ from a first reading of those
14
+ * specifications**, both measured in Chrome 151 on 2026-09-19: a `MouseEvent` whose button did not
15
+ * change reports `button` 0 where a `PointerEvent` reports −1, and `which` is that raw button plus
16
+ * one, so 0 for a move either way.
17
+ *
18
+ * What it gives up: coordinates are the window's, which is the canvas's here, so `pageX` is
19
+ * `clientX` (nothing scrolls) and `offsetX` is measured from the target's box when it has one.
20
+ */
21
+ export interface ModifierInit {
22
+ readonly ctrlKey?: boolean;
23
+ readonly shiftKey?: boolean;
24
+ readonly altKey?: boolean;
25
+ readonly metaKey?: boolean;
26
+ readonly modifierAltGraph?: boolean;
27
+ readonly modifierCapsLock?: boolean;
28
+ readonly modifierNumLock?: boolean;
29
+ }
30
+ interface UiInit extends EventInit {
31
+ readonly detail?: number;
32
+ readonly view?: unknown;
33
+ }
34
+ export declare class UIEvent extends Event {
35
+ readonly detail: number;
36
+ readonly view: unknown;
37
+ constructor(type: string, init?: UiInit);
38
+ }
39
+ export interface MouseInit extends UiInit, ModifierInit {
40
+ readonly screenX?: number;
41
+ readonly screenY?: number;
42
+ readonly clientX?: number;
43
+ readonly clientY?: number;
44
+ readonly movementX?: number;
45
+ readonly movementY?: number;
46
+ /** −1 where no button changed: a move, an enter, a leave. */
47
+ readonly button?: number;
48
+ readonly buttons?: number;
49
+ readonly relatedTarget?: EventTarget | null;
50
+ }
51
+ export declare class MouseEvent extends UIEvent {
52
+ readonly screenX: number;
53
+ readonly screenY: number;
54
+ readonly clientX: number;
55
+ readonly clientY: number;
56
+ readonly movementX: number;
57
+ readonly movementY: number;
58
+ readonly ctrlKey: boolean;
59
+ readonly shiftKey: boolean;
60
+ readonly altKey: boolean;
61
+ readonly metaKey: boolean;
62
+ readonly buttons: number;
63
+ readonly relatedTarget: EventTarget | null;
64
+ protected readonly rawButton: number;
65
+ private readonly modifiers;
66
+ constructor(type: string, init?: MouseInit);
67
+ get button(): number;
68
+ get which(): number;
69
+ get x(): number;
70
+ get y(): number;
71
+ get pageX(): number;
72
+ get pageY(): number;
73
+ get offsetX(): number;
74
+ get offsetY(): number;
75
+ getModifierState(key: string): boolean;
76
+ }
77
+ export interface WheelInit extends MouseInit {
78
+ readonly deltaX?: number;
79
+ readonly deltaY?: number;
80
+ readonly deltaZ?: number;
81
+ readonly deltaMode?: number;
82
+ }
83
+ export declare class WheelEvent extends MouseEvent {
84
+ static readonly DOM_DELTA_PIXEL = 0;
85
+ static readonly DOM_DELTA_LINE = 1;
86
+ static readonly DOM_DELTA_PAGE = 2;
87
+ readonly deltaX: number;
88
+ readonly deltaY: number;
89
+ readonly deltaZ: number;
90
+ readonly deltaMode: number;
91
+ constructor(type: string, init?: WheelInit);
92
+ }
93
+ export interface PointerInit extends MouseInit {
94
+ readonly pointerId?: number;
95
+ readonly width?: number;
96
+ readonly height?: number;
97
+ readonly pressure?: number;
98
+ readonly pointerType?: string;
99
+ readonly isPrimary?: boolean;
100
+ }
101
+ export declare class PointerEvent extends MouseEvent {
102
+ readonly pointerId: number;
103
+ readonly width: number;
104
+ readonly height: number;
105
+ readonly pressure: number;
106
+ readonly tangentialPressure = 0;
107
+ readonly tiltX = 0;
108
+ readonly tiltY = 0;
109
+ readonly twist = 0;
110
+ readonly altitudeAngle: number;
111
+ readonly azimuthAngle = 0;
112
+ readonly pointerType: string;
113
+ readonly isPrimary: boolean;
114
+ constructor(type: string, init?: PointerInit);
115
+ get button(): number;
116
+ /** A move stands for itself: this host coalesces nothing, so there is one event in it. */
117
+ getCoalescedEvents(): PointerEvent[];
118
+ getPredictedEvents(): PointerEvent[];
119
+ }
120
+ export interface KeyInit extends UiInit, ModifierInit {
121
+ readonly key?: string;
122
+ readonly code?: string;
123
+ readonly location?: number;
124
+ readonly repeat?: boolean;
125
+ readonly isComposing?: boolean;
126
+ readonly keyCode?: number;
127
+ readonly charCode?: number;
128
+ }
129
+ export declare class KeyboardEvent extends UIEvent {
130
+ static readonly DOM_KEY_LOCATION_STANDARD = 0;
131
+ static readonly DOM_KEY_LOCATION_LEFT = 1;
132
+ static readonly DOM_KEY_LOCATION_RIGHT = 2;
133
+ static readonly DOM_KEY_LOCATION_NUMPAD = 3;
134
+ readonly key: string;
135
+ readonly code: string;
136
+ readonly location: number;
137
+ readonly repeat: boolean;
138
+ readonly isComposing: boolean;
139
+ readonly ctrlKey: boolean;
140
+ readonly shiftKey: boolean;
141
+ readonly altKey: boolean;
142
+ readonly metaKey: boolean;
143
+ /** The legacy pair: a key's code on `keydown` and `keyup`, a character's on `keypress`. */
144
+ readonly keyCode: number;
145
+ readonly charCode: number;
146
+ private readonly modifiers;
147
+ constructor(type: string, init?: KeyInit);
148
+ get which(): number;
149
+ getModifierState(key: string): boolean;
150
+ }
151
+ export declare class FocusEvent extends UIEvent {
152
+ readonly relatedTarget: EventTarget | null;
153
+ constructor(type: string, init?: UiInit & {
154
+ relatedTarget?: EventTarget | null;
155
+ });
156
+ }
157
+ export {};
@@ -0,0 +1,206 @@
1
+ /**
2
+ * The browser's input events, for a runtime that has only `Event`: keys, the mouse, pointers, the
3
+ * wheel, touches, focus and pads, as classes with the members a browser's carry.
4
+ *
5
+ * **A page reads more of an event than the engine does.** The engine's input reads a key's `code`
6
+ * and a pointer's movement; a consumer's own code reads `getModifierState`, `detail` for a double
7
+ * click, the legacy `keyCode`, `pressure`, `offsetX`, or asks `instanceof PointerEvent`. An event
8
+ * assembled from a bare `Event` answers none of those, and the failure is a `TypeError` inside the
9
+ * consumer's handler or an `undefined` in its arithmetic. So each is its class, installed as the
10
+ * global a browser has, defaulting every member as the UI Events and Pointer Events specifications
11
+ * do.
12
+ *
13
+ * **Two of Chrome's own answers are kept where they differ from a first reading of those
14
+ * specifications**, both measured in Chrome 151 on 2026-09-19: a `MouseEvent` whose button did not
15
+ * change reports `button` 0 where a `PointerEvent` reports −1, and `which` is that raw button plus
16
+ * one, so 0 for a move either way.
17
+ *
18
+ * What it gives up: coordinates are the window's, which is the canvas's here, so `pageX` is
19
+ * `clientX` (nothing scrolls) and `offsetX` is measured from the target's box when it has one.
20
+ */
21
+ export class UIEvent extends Event {
22
+ detail;
23
+ view;
24
+ constructor(type, init = {}) {
25
+ super(type, init);
26
+ this.detail = init.detail ?? 0;
27
+ this.view = init.view ?? null;
28
+ }
29
+ }
30
+ /** `getModifierState`, which every event carrying modifiers answers the same way. */
31
+ function modifierState(init, key) {
32
+ if (key === 'Shift')
33
+ return init.shiftKey === true;
34
+ if (key === 'Control')
35
+ return init.ctrlKey === true;
36
+ if (key === 'Alt')
37
+ return init.altKey === true;
38
+ if (key === 'Meta')
39
+ return init.metaKey === true;
40
+ const locked = {
41
+ AltGraph: init.modifierAltGraph,
42
+ CapsLock: init.modifierCapsLock,
43
+ NumLock: init.modifierNumLock,
44
+ };
45
+ return locked[key] === true;
46
+ }
47
+ export class MouseEvent extends UIEvent {
48
+ screenX;
49
+ screenY;
50
+ clientX;
51
+ clientY;
52
+ movementX;
53
+ movementY;
54
+ ctrlKey;
55
+ shiftKey;
56
+ altKey;
57
+ metaKey;
58
+ buttons;
59
+ relatedTarget;
60
+ rawButton;
61
+ modifiers;
62
+ constructor(type, init = {}) {
63
+ super(type, init);
64
+ this.screenX = init.screenX ?? 0;
65
+ this.screenY = init.screenY ?? 0;
66
+ this.clientX = init.clientX ?? 0;
67
+ this.clientY = init.clientY ?? 0;
68
+ this.movementX = init.movementX ?? 0;
69
+ this.movementY = init.movementY ?? 0;
70
+ this.ctrlKey = init.ctrlKey === true;
71
+ this.shiftKey = init.shiftKey === true;
72
+ this.altKey = init.altKey === true;
73
+ this.metaKey = init.metaKey === true;
74
+ this.rawButton = init.button ?? 0;
75
+ this.buttons = init.buttons ?? 0;
76
+ this.relatedTarget = init.relatedTarget ?? null;
77
+ this.modifiers = init;
78
+ }
79
+ get button() {
80
+ return Math.max(this.rawButton, 0);
81
+ }
82
+ get which() {
83
+ return this.rawButton + 1;
84
+ }
85
+ get x() {
86
+ return this.clientX;
87
+ }
88
+ get y() {
89
+ return this.clientY;
90
+ }
91
+ get pageX() {
92
+ return this.clientX;
93
+ }
94
+ get pageY() {
95
+ return this.clientY;
96
+ }
97
+ get offsetX() {
98
+ return this.clientX - boxOf(this.target).left;
99
+ }
100
+ get offsetY() {
101
+ return this.clientY - boxOf(this.target).top;
102
+ }
103
+ getModifierState(key) {
104
+ return modifierState(this.modifiers, key);
105
+ }
106
+ }
107
+ function boxOf(target) {
108
+ const box = target?.getBoundingClientRect;
109
+ return typeof box === 'function' ? box.call(target) : { left: 0, top: 0 };
110
+ }
111
+ export class WheelEvent extends MouseEvent {
112
+ static DOM_DELTA_PIXEL = 0;
113
+ static DOM_DELTA_LINE = 1;
114
+ static DOM_DELTA_PAGE = 2;
115
+ deltaX;
116
+ deltaY;
117
+ deltaZ;
118
+ deltaMode;
119
+ constructor(type, init = {}) {
120
+ super(type, init);
121
+ this.deltaX = init.deltaX ?? 0;
122
+ this.deltaY = init.deltaY ?? 0;
123
+ this.deltaZ = init.deltaZ ?? 0;
124
+ this.deltaMode = init.deltaMode ?? 0;
125
+ }
126
+ }
127
+ export class PointerEvent extends MouseEvent {
128
+ pointerId;
129
+ width;
130
+ height;
131
+ pressure;
132
+ tangentialPressure = 0;
133
+ tiltX = 0;
134
+ tiltY = 0;
135
+ twist = 0;
136
+ altitudeAngle = Math.PI / 2;
137
+ azimuthAngle = 0;
138
+ pointerType;
139
+ isPrimary;
140
+ constructor(type, init = {}) {
141
+ super(type, init);
142
+ this.pointerId = init.pointerId ?? 0;
143
+ this.width = init.width ?? 1;
144
+ this.height = init.height ?? 1;
145
+ this.pressure = init.pressure ?? 0;
146
+ this.pointerType = init.pointerType ?? '';
147
+ this.isPrimary = init.isPrimary === true;
148
+ }
149
+ get button() {
150
+ return this.rawButton;
151
+ }
152
+ /** A move stands for itself: this host coalesces nothing, so there is one event in it. */
153
+ getCoalescedEvents() {
154
+ return this.type === 'pointermove' ? [this] : [];
155
+ }
156
+ getPredictedEvents() {
157
+ return [];
158
+ }
159
+ }
160
+ export class KeyboardEvent extends UIEvent {
161
+ static DOM_KEY_LOCATION_STANDARD = 0;
162
+ static DOM_KEY_LOCATION_LEFT = 1;
163
+ static DOM_KEY_LOCATION_RIGHT = 2;
164
+ static DOM_KEY_LOCATION_NUMPAD = 3;
165
+ key;
166
+ code;
167
+ location;
168
+ repeat;
169
+ isComposing;
170
+ ctrlKey;
171
+ shiftKey;
172
+ altKey;
173
+ metaKey;
174
+ /** The legacy pair: a key's code on `keydown` and `keyup`, a character's on `keypress`. */
175
+ keyCode;
176
+ charCode;
177
+ modifiers;
178
+ constructor(type, init = {}) {
179
+ super(type, init);
180
+ this.key = init.key ?? '';
181
+ this.code = init.code ?? '';
182
+ this.location = init.location ?? 0;
183
+ this.repeat = init.repeat === true;
184
+ this.isComposing = init.isComposing === true;
185
+ this.ctrlKey = init.ctrlKey === true;
186
+ this.shiftKey = init.shiftKey === true;
187
+ this.altKey = init.altKey === true;
188
+ this.metaKey = init.metaKey === true;
189
+ this.keyCode = init.keyCode ?? 0;
190
+ this.charCode = init.charCode ?? 0;
191
+ this.modifiers = init;
192
+ }
193
+ get which() {
194
+ return this.keyCode;
195
+ }
196
+ getModifierState(key) {
197
+ return modifierState(this.modifiers, key);
198
+ }
199
+ }
200
+ export class FocusEvent extends UIEvent {
201
+ relatedTarget;
202
+ constructor(type, init = {}) {
203
+ super(type, init);
204
+ this.relatedTarget = init.relatedTarget ?? null;
205
+ }
206
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Touches, as a browser's page reads them: `Touch`, the `TouchList` three of which a `TouchEvent`
3
+ * carries, and the event. The sibling of `uiEvents.ts`, split out only so each stays one screenful.
4
+ *
5
+ * A `TouchList` indexes, iterates and answers `item`, the three ways a page reads one; the engine's
6
+ * input reads `changedTouches[i]`.
7
+ */
8
+ import { type ModifierInit, UIEvent } from './uiEvents.ts';
9
+ export interface TouchInit {
10
+ readonly identifier: number;
11
+ readonly target: EventTarget;
12
+ readonly clientX?: number;
13
+ readonly clientY?: number;
14
+ readonly screenX?: number;
15
+ readonly screenY?: number;
16
+ readonly radiusX?: number;
17
+ readonly radiusY?: number;
18
+ readonly force?: number;
19
+ }
20
+ export declare class Touch {
21
+ readonly identifier: number;
22
+ readonly target: EventTarget;
23
+ readonly clientX: number;
24
+ readonly clientY: number;
25
+ readonly screenX: number;
26
+ readonly screenY: number;
27
+ readonly radiusX: number;
28
+ readonly radiusY: number;
29
+ readonly rotationAngle = 0;
30
+ readonly force: number;
31
+ readonly altitudeAngle: number;
32
+ readonly azimuthAngle = 0;
33
+ readonly touchType = "direct";
34
+ constructor(init: TouchInit);
35
+ get pageX(): number;
36
+ get pageY(): number;
37
+ }
38
+ export declare class TouchList {
39
+ readonly length: number;
40
+ [index: number]: Touch;
41
+ constructor(touches?: readonly Touch[]);
42
+ item(index: number): Touch | null;
43
+ [Symbol.iterator](): IterableIterator<Touch>;
44
+ }
45
+ export interface TouchEventInit extends EventInit, ModifierInit {
46
+ readonly touches?: readonly Touch[];
47
+ readonly targetTouches?: readonly Touch[];
48
+ readonly changedTouches?: readonly Touch[];
49
+ }
50
+ export declare class TouchEvent extends UIEvent {
51
+ readonly touches: TouchList;
52
+ readonly targetTouches: TouchList;
53
+ readonly changedTouches: TouchList;
54
+ readonly ctrlKey: boolean;
55
+ readonly shiftKey: boolean;
56
+ readonly altKey: boolean;
57
+ readonly metaKey: boolean;
58
+ constructor(type: string, init?: TouchEventInit);
59
+ }