xbintsc 0.5.43 → 0.5.45

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.
@@ -4,4 +4,4 @@
4
4
  * It lives apart from the command dispatch so the executable entry point stays
5
5
  * under the repository's per-file line budget; the text is data, not logic.
6
6
  */
7
- export declare const HELP = "xbintsc 0.5.43 - TypeScript binary compiler\n\nUsage:\n xbintsc build <file.ts> [options] Compile to a native binary\n xbintsc run <file.ts> [-- args] Compile and execute\n xbintsc check <file.ts> Parse, bind and check without linking\n xbintsc watch <file.ts> [options] Rebuild whenever the source tree changes\n xbintsc emit <file.ts> Print LLVM IR\n xbintsc doctor Report the resolved toolchain\n xbintsc version Print the version\n xbintsc help Show this message\n\nOptions:\n -o, --output <path> Output path\n --out <dir> Output directory (default: build/)\n --emit <kind> exe | obj | ir (default: exe)\n -O0..-O3 Optimization level (default: -O2)\n --ext <names> Comma separated extensions (e.g. node,gui,media)\n --ext-native <m> Register a C++/Rust extension from a JSON manifest\n (comma separated for several)\n --config <path> Use a project config (default: auto-detect\n xbintsc.config.json)\n --no-config Do not read any project config\n --icon <path> Embed an application icon (PNG/ICO/ICNS)\n --bundle macOS: also produce a .app bundle\n --app-name <name> Bundle / display name\n --app-id <id> macOS bundle identifier (e.g. com.example.demo)\n --force Ignore the incremental cache\n --verbose Print progress information\n --time Print per-phase build timings\n --debug Emit debug info (DWARF/CodeView) and keep frame pointers\n --check Run the semantic checker (default)\n --no-check Skip the semantic checker; only erase type syntax\n --lto Compile program and runtime to LTO bitcode; link with lld\n --pgo-generate [dir] Build an instrumented binary writing profiles to dir\n --pgo-use [dir] Build with the profiles collected from --pgo-generate\n --json Print diagnostics as a JSON array on stdout\n --no-hints Do not append the \"hint: \u2026 see <doc>\" line to failures\n\nxbintsc compiles a subset of TypeScript to a native binary. Types are erased;\nonly inference-free facts (names, redeclarations, call arity) are checked.\nNode built-ins need --ext node, packages in node_modules are bundled at build\ntime, and clang 16+ is required to link.\n\nInstalled as a project dependency (npm):\n npx xbintsc doctor\n npx xbintsc build <entry.ts> --out build\n npx xbintsc run <entry.ts>\nThe first build compiles the C runtime; later builds reuse the cache.\n\nDocs: doc/ai/README.md (task guide) - doc/ai/language-support.md (subset) -\ndoc/ai/troubleshooting.md (failures) - llms.txt (index of every document).\nWhen installed from npm, these live under node_modules/xbintsc/doc/ and\nnode_modules/xbintsc/doc/npm-package.md is the project guide.\n";
7
+ export declare const HELP = "xbintsc 0.5.45 - TypeScript binary compiler\n\nUsage:\n xbintsc build <file.ts> [options] Compile to a native binary\n xbintsc run <file.ts> [-- args] Compile and execute\n xbintsc check <file.ts> Parse, bind and check without linking\n xbintsc watch <file.ts> [options] Rebuild whenever the source tree changes\n xbintsc emit <file.ts> Print LLVM IR\n xbintsc doctor Report the resolved toolchain\n xbintsc version Print the version\n xbintsc help Show this message\n\nOptions:\n -o, --output <path> Output path\n --out <dir> Output directory (default: build/)\n --emit <kind> exe | obj | ir (default: exe)\n -O0..-O3 Optimization level (default: -O2)\n --ext <names> Comma separated extensions (e.g. node,gui,media)\n --ext-native <m> Register a C++/Rust extension from a JSON manifest\n (comma separated for several)\n --config <path> Use a project config (default: auto-detect\n xbintsc.config.json)\n --no-config Do not read any project config\n --icon <path> Embed an application icon (PNG/ICO/ICNS)\n --bundle macOS: also produce a .app bundle\n --app-name <name> Bundle / display name\n --app-id <id> macOS bundle identifier (e.g. com.example.demo)\n --force Ignore the incremental cache\n --verbose Print progress information\n --time Print per-phase build timings\n --debug Emit debug info (DWARF/CodeView) and keep frame pointers\n --check Run the semantic checker (default)\n --no-check Skip the semantic checker; only erase type syntax\n --lto Compile program and runtime to LTO bitcode; link with lld\n --pgo-generate [dir] Build an instrumented binary writing profiles to dir\n --pgo-use [dir] Build with the profiles collected from --pgo-generate\n --json Print diagnostics as a JSON array on stdout\n --no-hints Do not append the \"hint: \u2026 see <doc>\" line to failures\n\nxbintsc compiles a subset of TypeScript to a native binary. Types are erased;\nonly inference-free facts (names, redeclarations, call arity) are checked.\nNode built-ins need --ext node, packages in node_modules are bundled at build\ntime, and clang 16+ is required to link.\n\nInstalled as a project dependency (npm):\n npx xbintsc doctor\n npx xbintsc build <entry.ts> --out build\n npx xbintsc run <entry.ts>\nThe first build compiles the C runtime; later builds reuse the cache.\n\nDocs: doc/ai/README.md (task guide) - doc/ai/language-support.md (subset) -\ndoc/ai/troubleshooting.md (failures) - llms.txt (index of every document).\nWhen installed from npm, these live under node_modules/xbintsc/doc/ and\nnode_modules/xbintsc/doc/npm-package.md is the project guide.\n";
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export declare const COMPILER_VERSION = "0.5.43";
12
+ export declare const COMPILER_VERSION = "0.5.45";
@@ -9,5 +9,5 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.43";
12
+ export const COMPILER_VERSION = "0.5.45";
13
13
  //# sourceMappingURL=version.js.map
@@ -59,6 +59,28 @@ const WAV_HELPERS = `
59
59
  describe.skipIf(!guiAvailable)("gui extension — Web Audio", () => {
60
60
  beforeAll(() => harness.setup());
61
61
  afterAll(() => harness.cleanup());
62
+ it("shuts an open context down before SDL quits", () => {
63
+ const { value } = harness.compileAndRun("audio-exit", `
64
+ import { createWindow, run } from "gui";
65
+
66
+ const win = createWindow({ title: "audio-exit", width: 200, height: 150 });
67
+ win.on("ready", () => {
68
+ const ctx = new AudioContext();
69
+ const osc = ctx.createOscillator();
70
+ osc.connect(ctx.destination);
71
+ osc.start();
72
+ window.setTimeout(() => {
73
+ console.log("state=" + ctx.state);
74
+ win.close();
75
+ }, 100);
76
+ });
77
+ win.loadHTML("<div>audio</div>");
78
+ run();
79
+ `, { autocloseMs: 2000 });
80
+ // The window closed with the graph still live: the engine must destroy the
81
+ // SDL audio stream before SDL_Quit, or the process crashes on exit.
82
+ expect(value("state")).toBe("running");
83
+ });
62
84
  it("exposes the standard AudioContext surface", () => {
63
85
  const { value } = harness.compileAndRun("audio-surface", `
64
86
  async function main(): Promise<void> {
@@ -1 +1 @@
1
- {"version":3,"file":"gui-audio.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-audio.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-audio.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-audio.test.ts"],"names":[],"mappings":"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"}
@@ -40,6 +40,8 @@ describe.skipIf(!guiAvailable)("gui extension — browser globals", () => {
40
40
  console.log("buffer=" + buffer.length + ":" + samples[3] + ":" + buffer.numberOfChannels);
41
41
  // A renderer-only type answers \`undefined\`, so feature probes fall back.
42
42
  console.log("webgl=" + (typeof WebGLRenderingContext));
43
+ // fetch is a core runtime global, not a gui extension export.
44
+ console.log("fetch=" + typeof fetch);
43
45
 
44
46
  const win = createWindow({ title: "page", width: 320, height: 240 });
45
47
  console.log("adopt=" + (window === win) + ":" + (document.defaultView === win));
@@ -75,6 +77,7 @@ describe.skipIf(!guiAvailable)("gui extension — browser globals", () => {
75
77
  expect(value("audio")).toBe("48000:running:0.5");
76
78
  expect(value("buffer")).toBe("8:0.25:1");
77
79
  expect(value("webgl")).toBe("function");
80
+ expect(value("fetch")).toBe("function");
78
81
  expect(value("adopt")).toBe("true:true");
79
82
  expect(value("bound")).toBe("hi:hi");
80
83
  expect(value("title")).toBe("ropes page:complete:false");
@@ -1 +1 @@
1
- {"version":3,"file":"gui-browser.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-browser.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-browser.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-browser.test.ts"],"names":[],"mappings":"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"}
@@ -200,5 +200,72 @@ describe.skipIf(!guiAvailable)("gui extension — events", () => {
200
200
  // dispatched object keeps its identity and gains its target.
201
201
  expect(value("path")).toBe("capture1:true|target2:true:identity=true:target=true");
202
202
  });
203
+ it("installs the browser host globals on window", () => {
204
+ const { value } = harness.compileAndRun("window-globals", `
205
+ import { createWindow, run } from "gui";
206
+
207
+ const win = createWindow({ title: "globals", width: 300, height: 200 });
208
+ win.on("ready", () => {
209
+ const w: any = window;
210
+ console.log("audio=" + typeof w.AudioContext);
211
+ console.log("image=" + typeof w.Image);
212
+ console.log("event=" + typeof w.Event);
213
+ console.log("secure=" + w.isSecureContext);
214
+ // The game-facing spelling: a page feature-detects through window.
215
+ const Ctor = w.AudioContext;
216
+ const ctx = new Ctor({ __offline: true });
217
+ console.log("ctx=" + (ctx != null) + ":" + (ctx instanceof AudioContext));
218
+ ctx.close();
219
+ win.close();
220
+ });
221
+ win.loadHTML("<div>globals</div>");
222
+ run();
223
+ `);
224
+ expect(value("audio")).toBe("function");
225
+ expect(value("image")).toBe("function");
226
+ expect(value("event")).toBe("function");
227
+ expect(value("secure")).toBe("true");
228
+ expect(value("ctx")).toBe("true:true");
229
+ });
230
+ it("delivers physical keys to document and window with browser names", () => {
231
+ const { value } = harness.compileAndRun("keyboard-dom", `
232
+ import { createWindow, run } from "gui";
233
+
234
+ const win = createWindow({ title: "keys", width: 300, height: 200 });
235
+ win.on("ready", () => {
236
+ // No element is focused: a browser targets <body>, and the event still
237
+ // bubbles to the document listener a game binds to.
238
+ const seen: string[] = [];
239
+ document.addEventListener("keydown", (e: any) => {
240
+ seen.push(e.code + ":" + e.key + ":" + e.shiftKey);
241
+ });
242
+ // A listener on the event target (body when nothing is focused) runs
243
+ // too: the engine must pick the real body, not the document root.
244
+ let bodySeen = 0;
245
+ document.body.addEventListener("keydown", () => { bodySeen += 1; });
246
+
247
+ // SDL scancodes: A = 4, Up = 82, Space = 44, P = 19.
248
+ win.sendEvent("keydown", { scancode: 4 });
249
+ win.sendEvent("keydown", { scancode: 82 });
250
+ win.sendEvent("keydown", { scancode: 4, shift: true });
251
+ win.sendEvent("keydown", { scancode: 44 });
252
+ win.sendEvent("keyup", { scancode: 4 });
253
+ console.log("dom=" + seen.join(","));
254
+ console.log("body=" + bodySeen);
255
+
256
+ let windowKey = "";
257
+ win.on("keydown", (e: any) => { windowKey = e.code + ":" + e.key; });
258
+ win.sendEvent("keydown", { scancode: 19 });
259
+ console.log("window=" + windowKey);
260
+ win.close();
261
+ });
262
+ win.loadHTML("<html><body><div id='box'>x</div></body></html>");
263
+ run();
264
+ `);
265
+ // `code` is the physical key ("KeyA"), `key` the produced value ("a"/"A").
266
+ expect(value("dom")).toBe("KeyA:a:false,ArrowUp:ArrowUp:false,KeyA:A:true,Space: :false");
267
+ expect(value("body")).toBe("4");
268
+ expect(value("window")).toBe("KeyP:p");
269
+ });
203
270
  });
204
271
  //# sourceMappingURL=gui-events.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gui-events.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-events.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-events.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-events.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,wBAAwB,EAAE,GAAG,EAAE;IAC5D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCC,CACF,CAAC;QAEF,2DAA2D;QAC3D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,2EAA2E;QAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,YAAY,EACZ;YACE,0CAA0C;YAC1C,EAAE;YACF,sEAAsE;YACtE,iBAAiB;YACjB,uDAAuD;YACvD,gDAAgD;YAChD,yCAAyC;YACzC,yCAAyC;YACzC,4CAA4C;YAC5C,EAAE;YACF,sFAAsF;YACtF,uCAAuC;YACvC,+CAA+C;YAC/C,sDAAsD;YACtD,yDAAyD;YACzD,6DAA6D;YAC7D,gBAAgB;YAChB,KAAK;YACL,0CAA0C;YAC1C,QAAQ;YACR,4CAA4C;SAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,8CAA8C;QAC9C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QACnD,6EAA6E;QAC7E,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,eAAe,EACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgGC,CACF,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,yEAAyE;QACzE,6DAA6D;QAC7D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,gBAAgB,EAChB;;;;;;;;;;;;;;;;;;;OAmBC,CACF,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,cAAc,EACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCC,CACF,CAAC;QAEF,2EAA2E;QAC3E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CACvB,8DAA8D,CAC/D,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -110,9 +110,11 @@ SPIRV-Cross on Metal — and drawn through SDL_GPU; the page composites the colo
110
110
  texture without a CPU readback, and WebGL 2 adds VAOs, instancing, integer
111
111
  attributes, sampler objects, `texStorage2D` and buffer copies (see
112
112
  [../gui/webgl.md](../gui/webgl.md)). WebXR, `<video>`/`<audio>` without
113
- `media`, `fetch` and streaming are **not** implemented:
114
- `typeof` answers `"undefined"` so a page takes its own fallback branch. See
115
- [../gui/api.md](../gui/api.md#browser-globals).
113
+ `media`, and streaming are **not** implemented:
114
+ `typeof` answers `"undefined"` so a page takes its own fallback branch. `fetch`
115
+ is **not** a GUI extension feature: the core runtime provides it (`http://`,
116
+ plus local `file://` and relative paths; no HTTPS or streaming), so GUI
117
+ programs can use it too. See [../implemented/fetch.md](../implemented/fetch.md).
116
118
 
117
119
  ### `media` — video/audio decoding
118
120
 
@@ -99,9 +99,10 @@ AudioParam 自动化,`decodeAudioData` 引擎内解码 WAV,压缩格式则
99
99
  并通过 SDL_GPU 绘制;页面直接合成颜色纹理而无须 CPU 读回,WebGL 2 还增加
100
100
  VAO、实例化、整数属性、采样器对象、`texStorage2D` 与缓冲拷贝(见
101
101
  [../../zh-CN/gui/webgl.md](../../zh-CN/gui/webgl.md))。WebXR、未启用
102
- `media` 时的 `<video>`/`<audio>`、`fetch` 与流式接口**尚未实现**:`typeof` 会回答
103
- `"undefined"`,让页面走自己的降级分支。
104
- 见 [../../zh-CN/gui/api.md](../../zh-CN/gui/api.md)。
102
+ `media` 时的 `<video>`/`<audio>` 与流式接口**尚未实现**:`typeof` 会回答
103
+ `"undefined"`,让页面走自己的降级分支。`fetch` 不是 GUI 扩展的功能——它由核心
104
+ 运行时提供(`http://`,以及本地 `file://` 与相对路径;不支持 HTTPS 与流式),
105
+ 因此 GUI 程序同样可用。见 [../../implemented/fetch.md](../../implemented/fetch.md)。
105
106
 
106
107
  ### `media` —— 视频/音频解码
107
108
 
package/doc/gui/api.md CHANGED
@@ -91,12 +91,13 @@ requestAnimationFrame(frame);
91
91
  | `navigator` | `userAgent` (a desktop Chrome string ending in `xbintsc`), `platform`, `vendor`, `language(s)`, `hardwareConcurrency`, `maxTouchPoints`, `onLine`, `vibrate()` |
92
92
  | `localStorage` | an in-memory `getItem` / `setItem` / `removeItem` / `clear` / `key` / `length` store |
93
93
  | `AudioContext`, `webkitAudioContext` | a Web Audio graph with a real mixer and decoder (see below) |
94
+ | `fetch(input, init)` | the core runtime's HTTP/1.1 client, available to GUI programs too: `http://`, plus local `file://` and relative paths; `https://` and streaming are not supported. See [Implemented fetch](../implemented/fetch.md). |
94
95
  | `Event`, `CustomEvent`, `ProgressEvent`, `MouseEvent`, `KeyboardEvent`, `PointerEvent`, `WheelEvent`, `FocusEvent`, `InputEvent`, `SubmitEvent` | event constructors; the `init` argument is kept as given (`detail`, `composed`, mouse/keyboard fields, …) |
95
96
  | `AbortController`, `AbortSignal` | a real controller/signal pair for `{ signal }` listener removal, with `abort()`, `aborted`, `reason`, `onabort` and `throwIfAborted()` |
96
97
  | `HTMLCanvasElement`, `HTMLImageElement`, `WebGLRenderingContext`, `WebGL2RenderingContext`, `WebGLBuffer`/`WebGLTexture`/`WebGLShader`/`WebGLProgram`/`WebGLFramebuffer`/`WebGLRenderbuffer`/`WebGLUniformLocation`/`WebGLActiveInfo`/`WebGLVertexArrayObject`/`WebGLSampler`/`WebGLQuery`/`WebGLSync`/`WebGLTransformFeedback` | real constructor values. `canvas.getContext("webgl")` and `getContext("webgl2")` return GPU contexts (see [WebGL](./webgl.md)). |
97
98
  | `ImageBitmap`, `createImageBitmap(source)` | immutable bitmaps backed by decoded pixels (`<img>`, `<canvas>`, `<video>` frames, `OffscreenCanvas` and `ImageData` shapes); the promise resolves immediately and `texImage2D` accepts the result |
98
99
  | `OffscreenCanvas` | a real 2D offscreen bitmap with `getContext("2d")`, `width`/`height`, `transferToImageBitmap()` and `convertToBlob()`; `canvas.transferControlToOffscreen()` hands an element's bitmap over (see [Canvas](./canvas.md)). There is no offscreen WebGL context. |
99
- | `ImageData`, `XRWebGL*`, `Request`, `Headers`, `Response`, `ReadableStream`, `DOMParser` | `typeof` probes answer `"undefined"` (no XR/fetch/streaming backend), which lets a page take its "unsupported" branch instead of calling a no-op stub. Three.js pages use its real renderer on this backend (see [Known issues](./known-issues.md)). |
100
+ | `ImageData`, `XRWebGL*`, `Request`, `Headers`, `Response`, `ReadableStream`, `DOMParser` | `typeof` probes answer `"undefined"` (no XR, DOM parser or streaming backend), which lets a page take its "unsupported" branch instead of calling a no-op stub. `fetch` still works and resolves with a response-shaped object, but there is no `Response` constructor and `response.arrayBuffer()` currently returns a byte array rather than an `ArrayBuffer`. Three.js pages use its real renderer on this backend (see [Known issues](./known-issues.md)). |
100
101
  | `requestAnimationFrame` / `cancelAnimationFrame` | queued on the window's frame list; see [Runtime integration](./runtime.md) |
101
102
  | `alert` / `confirm` / `prompt`, `matchMedia`, `getComputedStyle`, `escape` | host implementations. `matchMedia` returns a **live** list (cached per query): `matches` is re-evaluated on resize and `change` listeners fire when it flips. `confirm`/`prompt` use a native dialog only with `XT_GUI_DIALOGS=1`; otherwise they report the message on stderr and return the documented default (Annex B `escape` encodes per UTF-16 code unit, matching `decodeURIComponent(escape(bytes))`) |
102
103
 
@@ -163,17 +164,24 @@ Input handlers receive a single payload object (lifecycle handlers receive none)
163
164
  ```ts
164
165
  win.on("click", (e) => console.log(e.x, e.y, e.button, e.target));
165
166
  win.on("wheel", (e) => console.log(e.deltaX, e.deltaY));
166
- win.on("keydown", (e) => console.log(e.key, e.code, e.ctrl, e.shift, e.alt, e.meta));
167
+ win.on("keydown", (e) => console.log(e.key, e.code, e.ctrlKey, e.shiftKey));
167
168
  ```
168
169
 
170
+ `document.addEventListener("keydown", handler)` receives the same event after it
171
+ bubbles from the focused element or `<body>`, before the window listeners run.
172
+ `code` is the physical key and uses the browser spelling (`"KeyW"`,
173
+ `"ArrowUp"`, `"Space"`); `key` is the produced value (`"w"`/`"W"`, `"ArrowUp"`,
174
+ `" "`). See [input.md](./input.md).
175
+
169
176
  | Field | Events | Meaning |
170
177
  | --- | --- | --- |
171
178
  | `x`, `y` / `clientX`, `clientY` | pointer, wheel | viewport-relative logical pixels |
172
179
  | `button` | pointer | `0` left, `1` middle, `2` right (`-1` for move) |
173
180
  | `clicks` | pointer | click count reported by the OS |
174
181
  | `deltaX`, `deltaY` | wheel | scroll amount (`deltaY` positive = down) |
175
- | `key`, `code` | keyboard | key name and physical scancode name |
176
- | `repeat`, `ctrl`, `shift`, `alt`, `meta` | keyboard | modifiers |
182
+ | `key`, `code` | keyboard | browser `KeyboardEvent` spellings: produced value and physical key |
183
+ | `repeat`, `ctrl`, `shift`, `alt`, `meta` | keyboard | modifiers (the legacy DOM spellings) |
184
+ | `ctrlKey`, `shiftKey`, `altKey`, `metaKey` | keyboard | modifiers (the current DOM spellings) |
177
185
  | `target` | pointer, wheel | deepest element under the point as a CSS-like descriptor (`div#main.card`), or absent |
178
186
 
179
187
  ## Diagnostics
package/doc/gui/input.md CHANGED
@@ -13,6 +13,13 @@ follows the artwork, not the box flow left behind.
13
13
 
14
14
  - SDL pointer/wheel/key events are routed to the owning window, hit tested, and
15
15
  delivered to the handlers registered with `on`. `mousedown` also moves focus.
16
+ - Keyboard events also follow the DOM path: the target is the focused element (or
17
+ `<body>` when nothing is focused), the event bubbles through its ancestors to
18
+ `document`, and finally reaches `window` listeners. This is what lets a page
19
+ written for a browser use `document.addEventListener("keydown", …)`. The
20
+ payload carries browser `KeyboardEvent` spellings — `code` is the physical key
21
+ (`"KeyW"`, `"ArrowUp"`, `"Space"`) and `key` the produced value (`"w"`, `"W"`,
22
+ `" "`) — not SDL's scancode/key names.
16
23
  - `:hover` matches the hovered element **and its ancestors** (so hovering a child
17
24
  lights up its parents); `:focus` matches the focused element. When either
18
25
  changes, `XtDocument::setHover`/`setFocus` recompute styles and layout and mark
@@ -334,7 +334,8 @@ legacy `target` field over the handle.
334
334
  - **Top-level `await`** inside a deferred `<script src>` module is not
335
335
  supported: the module body is wrapped in a synchronous init function.
336
336
  - **Assets.** `<script src>` is resolved relative to the HTML asset; URL `src`
337
- values are ignored. There is no fetch/network loading.
337
+ values are ignored. The loader does not fetch network URLs; the runtime's
338
+ global `fetch` is a separate API.
338
339
 
339
340
  ## Testing
340
341
 
@@ -88,12 +88,13 @@ requestAnimationFrame(frame);
88
88
  | `navigator` | `userAgent`(以 `xbintsc` 结尾的桌面 Chrome 字符串)、`platform`、`vendor`、`language(s)`、`hardwareConcurrency`、`maxTouchPoints`、`onLine`、`vibrate()` |
89
89
  | `localStorage` | 内存中的 `getItem` / `setItem` / `removeItem` / `clear` / `key` / `length` |
90
90
  | `AudioContext`、`webkitAudioContext` | 带真实混音器与解码器的 Web Audio 图(见下) |
91
+ | `fetch(input, init)` | 核心运行时的 HTTP/1.1 客户端,GUI 程序同样可用:支持 `http://`、本地 `file://` 与相对路径;不支持 `https://` 与流式响应。见[已实现的 fetch](../../implemented/fetch.md)。 |
91
92
  | `Event`、`CustomEvent`、`ProgressEvent`、`MouseEvent`、`KeyboardEvent`、`PointerEvent`、`WheelEvent`、`FocusEvent`、`InputEvent`、`SubmitEvent` | 事件构造器;`init` 参数按原样保留(`detail`、`composed`、鼠标/键盘字段等) |
92
93
  | `AbortController`、`AbortSignal` | 真实的 controller/signal 对,用于 `{ signal }` 移除监听器,支持 `abort()`、`aborted`、`reason`、`onabort` 与 `throwIfAborted()` |
93
94
  | `HTMLCanvasElement`、`HTMLImageElement`、`WebGLRenderingContext`、`WebGL2RenderingContext`、`WebGLBuffer`/`WebGLTexture`/`WebGLShader`/`WebGLProgram`/`WebGLFramebuffer`/`WebGLRenderbuffer`/`WebGLUniformLocation`/`WebGLActiveInfo`/`WebGLVertexArrayObject`/`WebGLSampler`/`WebGLQuery`/`WebGLSync`/`WebGLTransformFeedback` | 真实的构造函数值。`canvas.getContext("webgl")` 与 `getContext("webgl2")` 返回 GPU 上下文(见 [WebGL](./webgl.md))。 |
94
95
  | `ImageBitmap`、`createImageBitmap(source)` | 由已解码像素支撑的不可变位图(`<img>`、`<canvas>`、`<video>` 帧、`OffscreenCanvas` 与 `ImageData` 形状);promise 立即 settle,`texImage2D` 可直接接受该对象 |
95
96
  | `OffscreenCanvas` | 真正的 2D 离屏位图,带 `getContext("2d")`、`width`/`height`、`transferToImageBitmap()` 与 `convertToBlob()`;`canvas.transferControlToOffscreen()` 会把元素位图交给它(见 [Canvas](./canvas.md))。离屏 WebGL 上下文不提供。 |
96
- | `ImageData`、`XRWebGL*`、`Request`、`Headers`、`Response`、`ReadableStream`、`DOMParser` | `typeof` 探测返回 `"undefined"`(没有 XR/fetch/流式后端),让页面走自己的“不支持”分支,而不是调用空壳。Three.js 页面在本后端上使用真实渲染器(见[已知问题](./known-issues.md))。 |
97
+ | `ImageData`、`XRWebGL*`、`Request`、`Headers`、`Response`、`ReadableStream`、`DOMParser` | `typeof` 探测返回 `"undefined"`(没有 XR、DOM 解析器或流式后端),让页面走自己的“不支持”分支,而不是调用空壳。`fetch` 仍然可用,并返回 response 形状的对象,但没有 `Response` 构造函数,且 `response.arrayBuffer()` 目前返回字节数组而不是 `ArrayBuffer`。Three.js 页面在本后端上使用真实渲染器(见[已知问题](./known-issues.md))。 |
97
98
  | `requestAnimationFrame` / `cancelAnimationFrame` | 排入窗口的动画帧列表,见[运行时集成](./runtime.md) |
98
99
  | `alert` / `confirm` / `prompt`、`matchMedia`、`getComputedStyle`、`escape` | 宿主实现。`matchMedia` 返回**实时**列表(按查询缓存):`matches` 在窗口尺寸变化时重新求值,翻转时触发 `change` 监听器。`confirm`/`prompt` 仅在 `XT_GUI_DIALOGS=1` 时使用原生对话框,否则把消息写到 stderr 并返回文档化的默认值(Annex B `escape` 按 UTF-16 码元编码,与 `decodeURIComponent(escape(bytes))` 一致) |
99
100
 
@@ -152,17 +153,23 @@ const samples = ctx.__render(480); // 交错立体声 Float32Array
152
153
  ```ts
153
154
  win.on("click", (e) => console.log(e.x, e.y, e.button, e.target));
154
155
  win.on("wheel", (e) => console.log(e.deltaX, e.deltaY));
155
- win.on("keydown", (e) => console.log(e.key, e.code, e.ctrl, e.shift, e.alt, e.meta));
156
+ win.on("keydown", (e) => console.log(e.key, e.code, e.ctrlKey, e.shiftKey));
156
157
  ```
157
158
 
159
+ `document.addEventListener("keydown", handler)` 会在事件从聚焦元素或 `<body>`
160
+ 冒泡后收到同一个事件,早于 window 监听器。`code` 是物理键并采用浏览器写法
161
+ (`"KeyW"`、`"ArrowUp"`、`"Space"`);`key` 是产生的值(`"w"`/`"W"`、
162
+ `"ArrowUp"`、`" "`)。见 [input.md](./input.md)。
163
+
158
164
  | 字段 | 事件 | 含义 |
159
165
  | --- | --- | --- |
160
166
  | `x`、`y` / `clientX`、`clientY` | pointer、wheel | 相对于视口的逻辑像素 |
161
167
  | `button` | pointer | `0` 左键、`1` 中键、`2` 右键(移动时为 `-1`) |
162
168
  | `clicks` | pointer | 操作系统报告的点击次数 |
163
169
  | `deltaX`、`deltaY` | wheel | 滚动量(`deltaY` 为正表示向下) |
164
- | `key`、`code` | keyboard | 键名与物理扫描码名 |
165
- | `repeat`、`ctrl`、`shift`、`alt`、`meta` | keyboard | 修饰键 |
170
+ | `key`、`code` | keyboard | 浏览器 `KeyboardEvent` 写法:产生的值与物理键 |
171
+ | `repeat`、`ctrl`、`shift`、`alt`、`meta` | keyboard | 修饰键(旧版 DOM 写法) |
172
+ | `ctrlKey`、`shiftKey`、`altKey`、`metaKey` | keyboard | 修饰键(现行 DOM 写法) |
166
173
  | `target` | pointer、wheel | 该点下最深的元素,表示为类 CSS 描述符(`div#main.card`),可能缺省 |
167
174
 
168
175
  ## 诊断
@@ -12,6 +12,11 @@
12
12
 
13
13
  - SDL 指针/滚轮/键盘事件会被路由到所属窗口,进行命中测试,并投递给用 `on`
14
14
  注册的处理器。`mousedown` 还会移动焦点。
15
+ - 键盘事件同时走 DOM 路径:目标为聚焦元素(没有焦点时为 `<body>`),事件沿祖先
16
+ 冒泡到 `document`,最后到达 `window` 监听器。因此按浏览器写的
17
+ `document.addEventListener("keydown", …)` 可以直接工作。负载使用浏览器
18
+ `KeyboardEvent` 的写法:`code` 是物理键(`"KeyW"`、`"ArrowUp"`、`"Space"`),
19
+ `key` 是产生的值(`"w"`、`"W"`、`" "`),而不是 SDL 的扫描码/键名。
15
20
  - `:hover` 匹配悬停元素**及其祖先**(因此悬停子元素会点亮其父元素);
16
21
  `:focus` 匹配聚焦元素。当任一改变时,`XtDocument::setHover`/`setFocus`
17
22
  重新计算样式与布局,并把窗口几何标记为脏,因此改动会在下一帧绘制。
@@ -314,8 +314,8 @@ Event 的 `target` 是句柄,其描述符(`toString`)与同一 `div#id.cla
314
314
  导入的资产会)。如有需要,M9d 会处理。
315
315
  - **外部 `<script src>` 文件之间的 import 绑定冲突。** 会被检测并报告
316
316
  (给 import 起别名);之后可以做到带引用重写的按脚本重命名。
317
- - **资产。** `<script src>` 相对 HTML 资产解析;URL `src` 值被忽略。没有
318
- fetch/网络加载。
317
+ - **资产。** `<script src>` 相对 HTML 资产解析;URL `src` 值被忽略。
318
+ 加载器不会请求网络 URL;运行时全局 `fetch` 是另一套 API。
319
319
 
320
320
  ## 测试
321
321
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xbintsc",
3
- "version": "0.5.43",
3
+ "version": "0.5.45",
4
4
  "description": "xbintsc - a TypeScript binary compiler (TypeScript -> LLVM IR -> native binary)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -72,12 +72,12 @@
72
72
  "vitest": "^2.1.8"
73
73
  },
74
74
  "optionalDependencies": {
75
- "@xbintsc/linux-x64": "0.5.43",
76
- "@xbintsc/linux-arm64": "0.5.43",
77
- "@xbintsc/darwin-x64": "0.5.43",
78
- "@xbintsc/darwin-arm64": "0.5.43",
79
- "@xbintsc/win32-x64": "0.5.43",
80
- "@xbintsc/win32-arm64": "0.5.43"
75
+ "@xbintsc/linux-x64": "0.5.45",
76
+ "@xbintsc/linux-arm64": "0.5.45",
77
+ "@xbintsc/darwin-x64": "0.5.45",
78
+ "@xbintsc/darwin-arm64": "0.5.45",
79
+ "@xbintsc/win32-x64": "0.5.45",
80
+ "@xbintsc/win32-arm64": "0.5.45"
81
81
  },
82
82
  "engines": {
83
83
  "node": ">=22"
@@ -500,6 +500,24 @@ uint64_t audio_context_id(xt_value object) {
500
500
 
501
501
  xt_value xt_gui_audio_proto(void) { return audio_proto(); }
502
502
 
503
+ /** Close every live context before `SDL_Quit`. The mixers hold SDL audio
504
+ * streams whose destructors must not run after the audio subsystem (and its
505
+ * device objects) were torn down; keeping the registry alive until static
506
+ * destruction was a crash on exit for every program that played a sound. */
507
+ void xt_gui_audio_shutdown(void) {
508
+ std::vector<std::shared_ptr<AudioMixer>> mixers;
509
+ {
510
+ std::lock_guard<std::mutex> lock(g_registry_mutex);
511
+ mixers.reserve(g_mixers.size());
512
+ for (const auto &entry : g_mixers) mixers.push_back(entry.second);
513
+ g_mixers.clear();
514
+ }
515
+ for (const std::shared_ptr<AudioMixer> &mixer : mixers) {
516
+ mixer->close_device();
517
+ mixer->shutdown();
518
+ }
519
+ }
520
+
503
521
  xt_value xt_gui_audio_create(int32_t argc, xt_value *argv) {
504
522
  bool offline = false;
505
523
  xt_value options = xt_arg(argc, argv, 0);
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include <SDL3/SDL.h>
15
15
 
16
+ #include <cstdio>
16
17
  #include <vector>
17
18
 
18
19
  namespace xtgui {
@@ -39,14 +40,23 @@ bool AudioMixer::start_device() {
39
40
  if (stream_ != nullptr) return true;
40
41
  if (device_tried_) return false;
41
42
  device_tried_ = true;
42
- if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) return false;
43
+ if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
44
+ /* A missing device is not fatal (the page keeps its graph and clock), but
45
+ * silently staying quiet is indistinguishable from a broken mixer. */
46
+ std::fprintf(stderr, "xt_gui: audio subsystem unavailable: %s\n", SDL_GetError());
47
+ return false;
48
+ }
43
49
  SDL_AudioSpec spec;
44
50
  spec.format = SDL_AUDIO_F32;
45
51
  spec.channels = kAudioMixChannels;
46
52
  spec.freq = kAudioMixRate;
47
53
  stream_ = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, stream_callback,
48
54
  this);
49
- if (stream_ == nullptr) return false;
55
+ if (stream_ == nullptr) {
56
+ std::fprintf(stderr, "xt_gui: could not open the default audio device: %s\n",
57
+ SDL_GetError());
58
+ return false;
59
+ }
50
60
  SDL_ResumeAudioStreamDevice(stream_);
51
61
  return true;
52
62
  }
@@ -393,7 +393,7 @@ xt_value host_type_construct(xt_value self, xt_value env, int32_t argc, xt_value
393
393
  /** Host constructors are window members too — a page writes
394
394
  * `new window.AudioContext()` as often as `new AudioContext()`. */
395
395
  void install_window_globals(xt_value win) {
396
- if (xt_seq(win, g_installed_window)) return;
396
+ if (xt_truthy(xt_seq(win, g_installed_window))) return;
397
397
  g_installed_window = win;
398
398
  xt_gc_add_root(&g_installed_window);
399
399
  if (XT_IS_OBJECT(g_navigator)) set_prop(win, "navigator", g_navigator);
@@ -479,7 +479,7 @@ xt_value xt_browser_document(int32_t argc, xt_value *argv) {
479
479
  xt_value win = xt_browser_window(0, NULL);
480
480
  if (XT_IS_UNDEFINED(win)) return XT_UNDEFINED;
481
481
  xt_value doc = xt_gui_document_handle(xt_gui_window_from_this(win));
482
- if (!xt_seq(doc, g_document_global)) {
482
+ if (!xt_truthy(xt_seq(doc, g_document_global))) {
483
483
  g_document_global = doc;
484
484
  xt_gc_add_root(&g_document_global);
485
485
  }
@@ -28,7 +28,8 @@ static xt_value make_dom_event(XtGuiWindow *win, const char *type, const xtgui::
28
28
  static const char *const fields[] = {
29
29
  "x", "y", "clientX", "clientY", "button", "clicks",
30
30
  "deltaX", "deltaY", "key", "code", "repeat", "ctrl",
31
- "shift", "alt", "meta",
31
+ "shift", "alt", "meta", "ctrlKey", "shiftKey", "altKey",
32
+ "metaKey",
32
33
  };
33
34
  for (const char *field : fields) {
34
35
  xt_value value = xt_object_get_cstr(legacy, field);
@@ -460,6 +460,10 @@ extern "C" xt_value xt_gui_run(int32_t argc, xt_value *argv) {
460
460
  SDL_DestroyGPUDevice(device);
461
461
  g_device = NULL;
462
462
  }
463
+ /* Audio streams belong to SDL, so close them before quitting the audio
464
+ * subsystem; otherwise a still-live AudioMixer's destructor runs after
465
+ * `SDL_Quit` and touches freed SDL state. */
466
+ xt_gui_audio_shutdown();
463
467
  if (g_sdl_initialized) {
464
468
  SDL_Quit();
465
469
  g_sdl_initialized = 0;
@@ -478,5 +482,8 @@ extern "C" xt_value xt_gui_quit(int32_t argc, xt_value *argv) {
478
482
  for (int i = 0; i < g_window_count; i++) {
479
483
  if (g_windows[i].open) xt_gui_quit_window(&g_windows[i]);
480
484
  }
485
+ /* A browser-style page never enters `xt_gui_run`, so stop its audio here
486
+ * too instead of leaving the device open until process exit. */
487
+ xt_gui_audio_shutdown();
481
488
  return XT_UNDEFINED;
482
489
  }
@@ -314,6 +314,15 @@ void xt_gui_dispatch_pointer(XtGuiWindow *win, const char *type, float x, float
314
314
  void xt_gui_dispatch_wheel(XtGuiWindow *win, float x, float y, float delta_x, float delta_y);
315
315
  /** Synthesise a key event (`keydown`/`keyup`). */
316
316
  void xt_gui_dispatch_key(XtGuiWindow *win, const char *type, const char *key, const char *code);
317
+ /** Translate one physical key (SDL scancode/keycode + modifiers) to the browser
318
+ * `key`/`code` names and dispatch it to the DOM and window listeners. */
319
+ void xt_gui_dispatch_physical_key(XtGuiWindow *win, const char *type, int scancode, int keycode,
320
+ bool repeat, bool ctrl, bool shift, bool alt, bool meta,
321
+ bool caps_lock);
322
+ /** Close every AudioContext's device and drop its graph. Called before
323
+ * `SDL_Quit` so an AudioMixer's destructor never touches an SDL stream after
324
+ * the audio subsystem was shut down. */
325
+ void xt_gui_audio_shutdown(void);
317
326
 
318
327
  /** The decoder for one media element, opening it (and its `src`) on first use.
319
328
  * Returns `nullptr` for a non-media element or an unreadable source. */
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  #include "gui_internal.h"
7
+ #include "gui_keymap.h"
7
8
  #include "listener_options.h"
8
9
 
9
10
  #include "paint.h"
@@ -216,21 +217,64 @@ static xt_value xt_gui_key_payload(const char *type, const char *key, const char
216
217
  xt_object_set(payload, xt_string_from_cstr("key"), xt_string_from_cstr(key));
217
218
  xt_object_set(payload, xt_string_from_cstr("code"), xt_string_from_cstr(code));
218
219
  xt_object_set(payload, xt_string_from_cstr("repeat"), repeat ? XT_TRUE : XT_FALSE);
220
+ /* Legacy DOM fields and their current `*Key` spellings: a page may read
221
+ * either, and WheelEvent/MouseEvent already expose both where they apply. */
219
222
  xt_object_set(payload, xt_string_from_cstr("ctrl"), ctrl ? XT_TRUE : XT_FALSE);
220
223
  xt_object_set(payload, xt_string_from_cstr("shift"), shift ? XT_TRUE : XT_FALSE);
221
224
  xt_object_set(payload, xt_string_from_cstr("alt"), alt ? XT_TRUE : XT_FALSE);
222
225
  xt_object_set(payload, xt_string_from_cstr("meta"), meta ? XT_TRUE : XT_FALSE);
226
+ xt_object_set(payload, xt_string_from_cstr("ctrlKey"), ctrl ? XT_TRUE : XT_FALSE);
227
+ xt_object_set(payload, xt_string_from_cstr("shiftKey"), shift ? XT_TRUE : XT_FALSE);
228
+ xt_object_set(payload, xt_string_from_cstr("altKey"), alt ? XT_TRUE : XT_FALSE);
229
+ xt_object_set(payload, xt_string_from_cstr("metaKey"), meta ? XT_TRUE : XT_FALSE);
223
230
  return payload;
224
231
  }
225
232
 
226
- void xt_gui_dispatch_key(XtGuiWindow *win, const char *type, const char *key, const char *code) {
233
+ static const xtgui::Node *xt_gui_find_body(const xtgui::Node *node) {
234
+ if (node == nullptr) return nullptr;
235
+ if (node->isElement() && node->tag == "body") return node;
236
+ for (const std::unique_ptr<xtgui::Node> &child : node->children) {
237
+ if (const xtgui::Node *found = xt_gui_find_body(child.get())) return found;
238
+ }
239
+ return nullptr;
240
+ }
241
+
242
+ /** The element a keyboard event targets when nothing is focused: `<body>`,
243
+ * like a browser, or the document root for a page without one. */
244
+ static const xtgui::Node *xt_gui_key_target(XtGuiWindow *win) {
245
+ if (win->document == nullptr) return nullptr;
246
+ if (const xtgui::Node *focused = win->document->focus()) return focused;
247
+ const xtgui::Node *root = win->document->root();
248
+ if (const xtgui::Node *body = xt_gui_find_body(root)) return body;
249
+ return root;
250
+ }
251
+
252
+ /** Deliver one fully built keyboard payload to the DOM (bubbling from the
253
+ * focused element / body to `document`) and then to `window` listeners. */
254
+ static void xt_gui_dispatch_key_payload(XtGuiWindow *win, const char *type, xt_value payload) {
227
255
  if (win == NULL || !win->open) return;
228
- xt_value payload = xt_gui_key_payload(type, key, code, false, false, false, false, false);
229
- const xtgui::Node *node = win->document != nullptr ? win->document->focus() : nullptr;
256
+ const xtgui::Node *node = xt_gui_key_target(win);
230
257
  if (node != nullptr) xt_gui_emit_dom_event(win, type, node, payload);
231
258
  xt_gui_emit_payload(win, type, payload);
232
259
  }
233
260
 
261
+ void xt_gui_dispatch_key(XtGuiWindow *win, const char *type, const char *key, const char *code) {
262
+ xt_value payload = xt_gui_key_payload(type, key, code, false, false, false, false, false);
263
+ xt_gui_dispatch_key_payload(win, type, payload);
264
+ }
265
+
266
+ void xt_gui_dispatch_physical_key(XtGuiWindow *win, const char *type, int scancode, int keycode,
267
+ bool repeat, bool ctrl, bool shift, bool alt, bool meta,
268
+ bool caps_lock) {
269
+ std::string key;
270
+ std::string code;
271
+ xt_gui_key_names(static_cast<SDL_Scancode>(scancode), static_cast<SDL_Keycode>(keycode), shift,
272
+ caps_lock, &key, &code);
273
+ xt_value payload = xt_gui_key_payload(type, key.c_str(), code.c_str(), repeat, ctrl, shift, alt,
274
+ meta);
275
+ xt_gui_dispatch_key_payload(win, type, payload);
276
+ }
277
+
234
278
  namespace guidev {
235
279
 
236
280
  void handle_event(const SDL_Event *event) {
@@ -306,13 +350,11 @@ void handle_event(const SDL_Event *event) {
306
350
  XtGuiWindow *win = xt_gui_find_by_window_id(event->key.windowID);
307
351
  if (win == NULL || !win->open) return;
308
352
  const char *type = event->type == SDL_EVENT_KEY_DOWN ? "keydown" : "keyup";
309
- xt_gui_emit_payload(win, type,
310
- xt_gui_key_payload(type, SDL_GetKeyName(event->key.key),
311
- SDL_GetScancodeName(event->key.scancode),
312
- event->key.repeat, (event->key.mod & SDL_KMOD_CTRL) != 0,
313
- (event->key.mod & SDL_KMOD_SHIFT) != 0,
314
- (event->key.mod & SDL_KMOD_ALT) != 0,
315
- (event->key.mod & SDL_KMOD_GUI) != 0));
353
+ xt_gui_dispatch_physical_key(
354
+ win, type, static_cast<int>(event->key.scancode), static_cast<int>(event->key.key),
355
+ event->key.repeat, (event->key.mod & SDL_KMOD_CTRL) != 0,
356
+ (event->key.mod & SDL_KMOD_SHIFT) != 0, (event->key.mod & SDL_KMOD_ALT) != 0,
357
+ (event->key.mod & SDL_KMOD_GUI) != 0, (event->key.mod & SDL_KMOD_CAPS) != 0);
316
358
  return;
317
359
  }
318
360
  }
@@ -0,0 +1,254 @@
1
+ /*
2
+ * xbintsc GUI engine — browser KeyboardEvent names from SDL key events.
3
+ *
4
+ * `code` is the *physical* key, which is what the DOM's `KeyboardEvent.code`
5
+ * reports and what games normally bind to. `key` is the produced value, so it
6
+ * follows Shift/Caps Lock for letters and Shift for the US punctuation row.
7
+ * The mapping is intentionally the US layout: SDL's key names are US-based,
8
+ * and a page that needs another layout should use `code` anyway.
9
+ */
10
+
11
+ #include "gui_keymap.h"
12
+
13
+ #include <cstdio>
14
+
15
+ namespace guidev {
16
+
17
+ namespace {
18
+
19
+ void set_names(std::string *key, std::string *code, const char *key_name, const char *code_name) {
20
+ *key = key_name;
21
+ *code = code_name;
22
+ }
23
+
24
+ /** Digits and their shifted symbols, indexed 0..9 for keys 1..0. */
25
+ const char *const kDigitKeys = "1234567890";
26
+ const char *const kDigitShifted = "!@#$%^&*()";
27
+
28
+ /** Keypad digits, indexed 0..9 for KP_1..KP_0. */
29
+ const char *const kKeypadDigits = "1234567890";
30
+
31
+ } // namespace
32
+
33
+ void xt_gui_key_names(SDL_Scancode scancode, SDL_Keycode keycode, bool shift, bool caps_lock,
34
+ std::string *key, std::string *code) {
35
+ key->clear();
36
+ code->clear();
37
+
38
+ /* Letters: SDL numbers A..Z consecutively. Caps Lock and Shift cancel out. */
39
+ if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
40
+ char letter = static_cast<char>('A' + (scancode - SDL_SCANCODE_A));
41
+ *code = "Key";
42
+ code->push_back(letter);
43
+ bool upper = shift != caps_lock;
44
+ key->push_back(upper ? letter : static_cast<char>(letter + ('a' - 'A')));
45
+ return;
46
+ }
47
+
48
+ /* Number row. */
49
+ if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) {
50
+ int index = static_cast<int>(scancode - SDL_SCANCODE_1);
51
+ *code = "Digit";
52
+ code->push_back(kDigitKeys[index]);
53
+ key->push_back(shift ? kDigitShifted[index] : kDigitKeys[index]);
54
+ return;
55
+ }
56
+
57
+ /* Function keys: F1..F12 and F13..F24 are two contiguous runs; the gap
58
+ * between F12 and F13 contains PrintScreen, arrows and the keypad. */
59
+ if ((scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) ||
60
+ (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24)) {
61
+ int number;
62
+ if (scancode <= SDL_SCANCODE_F12) {
63
+ number = static_cast<int>(scancode - SDL_SCANCODE_F1) + 1;
64
+ } else {
65
+ number = static_cast<int>(scancode - SDL_SCANCODE_F13) + 13;
66
+ }
67
+ char name[8];
68
+ std::snprintf(name, sizeof(name), "F%d", number);
69
+ *key = name;
70
+ *code = name;
71
+ return;
72
+ }
73
+
74
+ /* Keypad digits, same 1..9,0 order. */
75
+ if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_0) {
76
+ int index = static_cast<int>(scancode - SDL_SCANCODE_KP_1);
77
+ *code = "Numpad";
78
+ code->push_back(kKeypadDigits[index]);
79
+ key->push_back(kKeypadDigits[index]);
80
+ return;
81
+ }
82
+
83
+ switch (scancode) {
84
+ case SDL_SCANCODE_RETURN:
85
+ case SDL_SCANCODE_RETURN2:
86
+ set_names(key, code, "Enter", "Enter");
87
+ return;
88
+ case SDL_SCANCODE_ESCAPE:
89
+ set_names(key, code, "Escape", "Escape");
90
+ return;
91
+ case SDL_SCANCODE_BACKSPACE:
92
+ set_names(key, code, "Backspace", "Backspace");
93
+ return;
94
+ case SDL_SCANCODE_TAB:
95
+ set_names(key, code, "Tab", "Tab");
96
+ return;
97
+ case SDL_SCANCODE_SPACE:
98
+ set_names(key, code, " ", "Space");
99
+ return;
100
+ case SDL_SCANCODE_CAPSLOCK:
101
+ set_names(key, code, "CapsLock", "CapsLock");
102
+ return;
103
+ case SDL_SCANCODE_PRINTSCREEN:
104
+ set_names(key, code, "PrintScreen", "PrintScreen");
105
+ return;
106
+ case SDL_SCANCODE_SCROLLLOCK:
107
+ set_names(key, code, "ScrollLock", "ScrollLock");
108
+ return;
109
+ case SDL_SCANCODE_PAUSE:
110
+ set_names(key, code, "Pause", "Pause");
111
+ return;
112
+ case SDL_SCANCODE_INSERT:
113
+ set_names(key, code, "Insert", "Insert");
114
+ return;
115
+ case SDL_SCANCODE_HOME:
116
+ set_names(key, code, "Home", "Home");
117
+ return;
118
+ case SDL_SCANCODE_PAGEUP:
119
+ set_names(key, code, "PageUp", "PageUp");
120
+ return;
121
+ case SDL_SCANCODE_DELETE:
122
+ set_names(key, code, "Delete", "Delete");
123
+ return;
124
+ case SDL_SCANCODE_END:
125
+ set_names(key, code, "End", "End");
126
+ return;
127
+ case SDL_SCANCODE_PAGEDOWN:
128
+ set_names(key, code, "PageDown", "PageDown");
129
+ return;
130
+ case SDL_SCANCODE_RIGHT:
131
+ set_names(key, code, "ArrowRight", "ArrowRight");
132
+ return;
133
+ case SDL_SCANCODE_LEFT:
134
+ set_names(key, code, "ArrowLeft", "ArrowLeft");
135
+ return;
136
+ case SDL_SCANCODE_DOWN:
137
+ set_names(key, code, "ArrowDown", "ArrowDown");
138
+ return;
139
+ case SDL_SCANCODE_UP:
140
+ set_names(key, code, "ArrowUp", "ArrowUp");
141
+ return;
142
+ case SDL_SCANCODE_NUMLOCKCLEAR:
143
+ set_names(key, code, "NumLock", "NumLock");
144
+ return;
145
+ case SDL_SCANCODE_KP_ENTER:
146
+ set_names(key, code, "Enter", "NumpadEnter");
147
+ return;
148
+ case SDL_SCANCODE_KP_DIVIDE:
149
+ set_names(key, code, "/", "NumpadDivide");
150
+ return;
151
+ case SDL_SCANCODE_KP_MULTIPLY:
152
+ set_names(key, code, "*", "NumpadMultiply");
153
+ return;
154
+ case SDL_SCANCODE_KP_MINUS:
155
+ set_names(key, code, "-", "NumpadSubtract");
156
+ return;
157
+ case SDL_SCANCODE_KP_PLUS:
158
+ set_names(key, code, "+", "NumpadAdd");
159
+ return;
160
+ case SDL_SCANCODE_KP_PERIOD:
161
+ case SDL_SCANCODE_KP_DECIMAL:
162
+ set_names(key, code, ".", "NumpadDecimal");
163
+ return;
164
+ case SDL_SCANCODE_KP_EQUALS:
165
+ set_names(key, code, "=", "NumpadEqual");
166
+ return;
167
+ case SDL_SCANCODE_KP_COMMA:
168
+ set_names(key, code, ",", "NumpadComma");
169
+ return;
170
+ case SDL_SCANCODE_APPLICATION:
171
+ case SDL_SCANCODE_MENU:
172
+ set_names(key, code, "ContextMenu", "ContextMenu");
173
+ return;
174
+ case SDL_SCANCODE_LCTRL:
175
+ set_names(key, code, "Control", "ControlLeft");
176
+ return;
177
+ case SDL_SCANCODE_RCTRL:
178
+ set_names(key, code, "Control", "ControlRight");
179
+ return;
180
+ case SDL_SCANCODE_LSHIFT:
181
+ set_names(key, code, "Shift", "ShiftLeft");
182
+ return;
183
+ case SDL_SCANCODE_RSHIFT:
184
+ set_names(key, code, "Shift", "ShiftRight");
185
+ return;
186
+ case SDL_SCANCODE_LALT:
187
+ set_names(key, code, "Alt", "AltLeft");
188
+ return;
189
+ case SDL_SCANCODE_RALT:
190
+ set_names(key, code, "Alt", "AltRight");
191
+ return;
192
+ case SDL_SCANCODE_LGUI:
193
+ set_names(key, code, "Meta", "MetaLeft");
194
+ return;
195
+ case SDL_SCANCODE_RGUI:
196
+ set_names(key, code, "Meta", "MetaRight");
197
+ return;
198
+ default:
199
+ break;
200
+ }
201
+
202
+ /* Remaining US punctuation, keyed by scancode. */
203
+ switch (scancode) {
204
+ case SDL_SCANCODE_MINUS:
205
+ set_names(key, code, shift ? "_" : "-", "Minus");
206
+ return;
207
+ case SDL_SCANCODE_EQUALS:
208
+ set_names(key, code, shift ? "+" : "=", "Equal");
209
+ return;
210
+ case SDL_SCANCODE_LEFTBRACKET:
211
+ set_names(key, code, shift ? "{" : "[", "BracketLeft");
212
+ return;
213
+ case SDL_SCANCODE_RIGHTBRACKET:
214
+ set_names(key, code, shift ? "}" : "]", "BracketRight");
215
+ return;
216
+ case SDL_SCANCODE_BACKSLASH:
217
+ case SDL_SCANCODE_NONUSHASH:
218
+ set_names(key, code, shift ? "|" : "\\", "Backslash");
219
+ return;
220
+ case SDL_SCANCODE_SEMICOLON:
221
+ set_names(key, code, shift ? ":" : ";", "Semicolon");
222
+ return;
223
+ case SDL_SCANCODE_APOSTROPHE:
224
+ set_names(key, code, shift ? "\"" : "'", "Quote");
225
+ return;
226
+ case SDL_SCANCODE_GRAVE:
227
+ set_names(key, code, shift ? "~" : "`", "Backquote");
228
+ return;
229
+ case SDL_SCANCODE_COMMA:
230
+ set_names(key, code, shift ? "<" : ",", "Comma");
231
+ return;
232
+ case SDL_SCANCODE_PERIOD:
233
+ set_names(key, code, shift ? ">" : ".", "Period");
234
+ return;
235
+ case SDL_SCANCODE_SLASH:
236
+ set_names(key, code, shift ? "?" : "/", "Slash");
237
+ return;
238
+ case SDL_SCANCODE_NONUSBACKSLASH:
239
+ set_names(key, code, "\\", "IntlBackslash");
240
+ return;
241
+ default:
242
+ break;
243
+ }
244
+
245
+ /* Unknown key: keep SDL's human-readable names. `key` comes from the
246
+ * keycode so a consumer matching "Up" still sees it on a layout whose
247
+ * scancode name differs. */
248
+ const char *key_name = SDL_GetKeyName(keycode);
249
+ const char *code_name = SDL_GetScancodeName(scancode);
250
+ set_names(key, code, key_name != nullptr ? key_name : "",
251
+ code_name != nullptr ? code_name : "");
252
+ }
253
+
254
+ } // namespace guidev
@@ -0,0 +1,31 @@
1
+ /*
2
+ * xbintsc GUI engine — SDL key event to browser KeyboardEvent names.
3
+ *
4
+ * The DOM exposes the browser spellings `KeyboardEvent.key` (the produced
5
+ * character / action name) and `code` (the physical key), while SDL reports a
6
+ * scancode and keycode. A page written for a browser matches on `code`
7
+ * ("KeyW", "ArrowUp", …), so translating `SDL_GetScancodeName` directly
8
+ * ("W", "Up") is not enough.
9
+ */
10
+ #ifndef XT_GUI_KEYMAP_H
11
+ #define XT_GUI_KEYMAP_H
12
+
13
+ #include <SDL3/SDL.h>
14
+
15
+ #include <string>
16
+
17
+ namespace guidev {
18
+
19
+ /** Fill `key` and `code` with the browser names for one physical key press.
20
+ *
21
+ * `scancode` selects the physical `code`; `keycode` and the modifier state
22
+ * select the produced `key` (letters honour Shift and Caps Lock, digits and
23
+ * punctuation use the US layout, which matches SDL's default key names).
24
+ * Unrecognised keys fall back to SDL's own names rather than an empty string,
25
+ * so a page that only logs events still sees something useful. */
26
+ void xt_gui_key_names(SDL_Scancode scancode, SDL_Keycode keycode, bool shift, bool caps_lock,
27
+ std::string *key, std::string *code);
28
+
29
+ } // namespace guidev
30
+
31
+ #endif /* XT_GUI_KEYMAP_H */
@@ -413,7 +413,8 @@ static xt_value win_hit_test(xt_value self, xt_value env, int32_t argc, xt_value
413
413
  /** Test/debug hook: synthesise an input event and deliver it to handlers.
414
414
  * `win.sendEvent("click", { x, y, button, clicks })`,
415
415
  * `win.sendEvent("wheel", { x, y, deltaX, deltaY })` or
416
- * `win.sendEvent("keydown", { key, code })`. */
416
+ * `win.sendEvent("keydown", { key, code })`. A key event may instead name the
417
+ * SDL `scancode`/`keycode` plus modifiers to exercise a real-keyboard path. */
417
418
  static xt_value win_send_event(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
418
419
  (void)env;
419
420
  XtGuiWindow *win = xt_gui_window_from_this(self);
@@ -439,6 +440,14 @@ static xt_value win_send_event(xt_value self, xt_value env, int32_t argc, xt_val
439
440
  return XT_TRUE;
440
441
  }
441
442
  if (name == "keydown" || name == "keyup") {
443
+ if (XT_IS_OBJECT(options) && XT_IS_NUMBER(xt_object_get_cstr(options, "scancode"))) {
444
+ int scancode = (int)xt_gui_number_or(xt_object_get_cstr(options, "scancode"), 0);
445
+ int keycode = (int)xt_gui_number_or(xt_object_get_cstr(options, "keycode"), 0);
446
+ auto flag = [options](const char *key) { return xt_truthy(xt_object_get_cstr(options, key)); };
447
+ xt_gui_dispatch_physical_key(win, type, scancode, keycode, flag("repeat"), flag("ctrl"),
448
+ flag("shift"), flag("alt"), flag("meta"), flag("caps"));
449
+ return XT_TRUE;
450
+ }
442
451
  const char *key = "";
443
452
  const char *code = "";
444
453
  if (XT_IS_OBJECT(options)) {
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.43";
12
+ export const COMPILER_VERSION = "0.5.45";