xbintsc 0.5.36 → 0.5.38
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.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/html.js +89 -13
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +13 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +161 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +22 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +38 -9
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +51 -4
- package/doc/gui/webgl.md +20 -10
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +10 -8
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +33 -4
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +45 -3
- package/doc/zh-CN/gui/webgl.md +3 -3
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +7 -6
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +34 -49
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +55 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +10 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/gui.cpp +59 -5
- package/runtime/ext_gui/gui_engine.h +49 -0
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_shader.cpp +12 -6
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/rt.h +29 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +91 -15
- package/src/extensions/gui/index.ts +13 -0
- package/src/extensions/gui/three.ts +4 -3
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| 正则按字节匹配 | 引擎按 UTF-8 字节匹配:多字节字面量仍可匹配,但 `.` 与字符类每次只消费一个字节(`\d`/`\w` 仍是 ASCII)。`exec` / `search` / 替换回调返回的偏移会转换为 UTF-16 码元 |
|
|
10
10
|
| 全局正则 `lastIndex` | `/g`、`/y` 正则的 `test` / `exec` 不推进也不读取调用方设置的 `lastIndex` |
|
|
11
|
+
| `URL` 解析器 | 只读 WHATWG 子集:未实现路径百分号编码、IDNA、setter 与 `searchParams` 实时联动;无协议引用按工作目录解析为 `file:` URL |
|
|
11
12
|
| `String.normalize` | 未实现 |
|
|
12
13
|
| 内置方法一等公民 | 以未绑定方法值实现(见[函数](./functions.md));`fn.toString()` 返回占位字符串而非源码文本 |
|
|
13
14
|
| `for...in` | 枚举对象 / 数组 / 字符串的自身键;不含原型链属性 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xbintsc",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.38",
|
|
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.
|
|
76
|
-
"@xbintsc/linux-arm64": "0.5.
|
|
77
|
-
"@xbintsc/darwin-x64": "0.5.
|
|
78
|
-
"@xbintsc/darwin-arm64": "0.5.
|
|
79
|
-
"@xbintsc/win32-x64": "0.5.
|
|
80
|
-
"@xbintsc/win32-arm64": "0.5.
|
|
75
|
+
"@xbintsc/linux-x64": "0.5.38",
|
|
76
|
+
"@xbintsc/linux-arm64": "0.5.38",
|
|
77
|
+
"@xbintsc/darwin-x64": "0.5.38",
|
|
78
|
+
"@xbintsc/darwin-arm64": "0.5.38",
|
|
79
|
+
"@xbintsc/win32-x64": "0.5.38",
|
|
80
|
+
"@xbintsc/win32-arm64": "0.5.38"
|
|
81
81
|
},
|
|
82
82
|
"engines": {
|
|
83
83
|
"node": ">=22"
|
|
@@ -29,7 +29,6 @@ namespace {
|
|
|
29
29
|
xt_value g_navigator = XT_UNDEFINED;
|
|
30
30
|
xt_value g_local_storage = XT_UNDEFINED;
|
|
31
31
|
xt_value g_location = XT_UNDEFINED;
|
|
32
|
-
xt_value g_media_list = XT_UNDEFINED;
|
|
33
32
|
xt_value g_document_global = XT_UNDEFINED;
|
|
34
33
|
/** Type name -> the constructor value handed out for it (built on first use). */
|
|
35
34
|
std::unordered_map<std::string, xt_value> g_types;
|
|
@@ -156,35 +155,28 @@ xt_value win_location_get(xt_value self, xt_value env, int32_t argc, xt_value *a
|
|
|
156
155
|
return g_location;
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* current window's viewport, using the same evaluator as `@media` rules. The
|
|
163
|
-
* list is a snapshot (there is no live change event), which matches the
|
|
164
|
-
* engine's restyle-on-resize model. */
|
|
158
|
+
/** `matchMedia(query)` -> a live `MediaQueryList` evaluated against the current
|
|
159
|
+
* viewport with the same evaluator as `@media` rules. The list is cached per
|
|
160
|
+
* window and query, and `change` listeners fire when a resize flips it. */
|
|
165
161
|
xt_value win_match_media(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
166
162
|
(void)env;
|
|
167
163
|
std::string query;
|
|
168
164
|
if (argc >= 1) query = cstr(xt_arg(argc, argv, 0));
|
|
169
165
|
XtGuiWindow *win = xt_gui_window_from_this(self);
|
|
170
|
-
if (win == NULL)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
float width = 0.0f;
|
|
175
|
-
float height = 0.0f;
|
|
176
|
-
if (win != NULL) xt_gui_window_viewport(win, &width, &height);
|
|
177
|
-
return media_list_build(query, xtgui::xt_css_media_matches(query, width, height));
|
|
166
|
+
if (win == NULL) win = xt_gui_window_from_this(xt_gui_ensure_window());
|
|
167
|
+
if (win == NULL) return xt_object_new();
|
|
168
|
+
return xt_browser_media_list(win->object, query);
|
|
178
169
|
}
|
|
179
170
|
|
|
180
|
-
/** `getComputedStyle(el)`:
|
|
181
|
-
*
|
|
171
|
+
/** `getComputedStyle(el, pseudoElt)`: a live view of the *cascaded* style, so
|
|
172
|
+
* stylesheet rules (not only the inline `style` attribute) are visible. The
|
|
173
|
+
* optional pseudo-element selector ("::before"/"::after") resolves against the
|
|
174
|
+
* generated-content box; any other pseudo-element yields empty values, like a
|
|
175
|
+
* browser does for one the engine did not generate. */
|
|
182
176
|
xt_value win_get_computed_style(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
183
177
|
(void)self;
|
|
184
178
|
(void)env;
|
|
185
|
-
|
|
186
|
-
xt_value style = xt_get(element, xt_string_from_cstr("style"));
|
|
187
|
-
return XT_IS_OBJECT(style) ? style : xt_object_new();
|
|
179
|
+
return domapi::computed_style_handle(xt_arg(argc, argv, 0), xt_arg(argc, argv, 1));
|
|
188
180
|
}
|
|
189
181
|
|
|
190
182
|
xt_value win_alert(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
@@ -196,19 +188,13 @@ xt_value win_alert(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
|
196
188
|
}
|
|
197
189
|
|
|
198
190
|
xt_value win_confirm(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
199
|
-
(void)self;
|
|
200
191
|
(void)env;
|
|
201
|
-
(
|
|
202
|
-
(void)argv;
|
|
203
|
-
return XT_FALSE;
|
|
192
|
+
return xt_browser_confirm(xt_gui_window_from_this(self), argc, argv);
|
|
204
193
|
}
|
|
205
194
|
|
|
206
195
|
xt_value win_prompt(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
207
|
-
(void)self;
|
|
208
196
|
(void)env;
|
|
209
|
-
(
|
|
210
|
-
(void)argv;
|
|
211
|
-
return XT_UNDEFINED;
|
|
197
|
+
return xt_browser_prompt(xt_gui_window_from_this(self), argc, argv);
|
|
212
198
|
}
|
|
213
199
|
|
|
214
200
|
/* -- host objects --------------------------------------------------------- */
|
|
@@ -253,18 +239,6 @@ xt_value location_build(void) {
|
|
|
253
239
|
return location;
|
|
254
240
|
}
|
|
255
241
|
|
|
256
|
-
xt_value media_list_build(const std::string &query, bool matches) {
|
|
257
|
-
xt_value list = xt_object_new();
|
|
258
|
-
set_prop(list, "media", xt_string_from_cstr(query.c_str()));
|
|
259
|
-
set_prop(list, "matches", matches ? XT_TRUE : XT_FALSE);
|
|
260
|
-
set_method(list, "addListener", (void *)noop);
|
|
261
|
-
set_method(list, "removeListener", (void *)noop);
|
|
262
|
-
set_method(list, "addEventListener", (void *)noop);
|
|
263
|
-
set_method(list, "removeEventListener", (void *)noop);
|
|
264
|
-
set_method(list, "dispatchEvent", (void *)noop);
|
|
265
|
-
return list;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
242
|
/* -- localStorage --------------------------------------------------------- */
|
|
269
243
|
|
|
270
244
|
xt_value storage_get_item(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
@@ -338,7 +312,12 @@ xt_value host_type_construct(xt_value self, xt_value env, int32_t argc, xt_value
|
|
|
338
312
|
/** The prototype a DOM type name should inherit, so `el instanceof Element`
|
|
339
313
|
* holds for every element handle the engine hands out. */
|
|
340
314
|
xt_value type_proto(const std::string &name) {
|
|
341
|
-
if (name == "
|
|
315
|
+
if (name == "AbortController") return domapi::abort_controller_proto();
|
|
316
|
+
if (name == "AbortSignal") return domapi::abort_signal_proto();
|
|
317
|
+
if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent" ||
|
|
318
|
+
name == "UIEvent" || name == "MouseEvent" || name == "PointerEvent" ||
|
|
319
|
+
name == "WheelEvent" || name == "KeyboardEvent" || name == "FocusEvent" ||
|
|
320
|
+
name == "InputEvent" || name == "SubmitEvent") {
|
|
342
321
|
return domapi::event_proto();
|
|
343
322
|
}
|
|
344
323
|
if (name == "AudioContext" || name == "webkitAudioContext") return xt_gui_audio_proto();
|
|
@@ -357,6 +336,7 @@ xt_value type_proto(const std::string &name) {
|
|
|
357
336
|
if (name == "WebGLQuery") return domapi::webgl_object_proto("query");
|
|
358
337
|
if (name == "WebGLSync") return domapi::webgl_object_proto("sync");
|
|
359
338
|
if (name == "WebGLTransformFeedback") return domapi::webgl_object_proto("transformfeedback");
|
|
339
|
+
if (name == "Path2D") return domapi::path2d_proto();
|
|
360
340
|
if (name == "ImageBitmap") {
|
|
361
341
|
/* A bitmap is a plain immutable object, not a DOM node: a node proto would
|
|
362
342
|
* expose the element width/height accessors and answer 0. */
|
|
@@ -394,9 +374,15 @@ xt_value host_type_construct(xt_value self, xt_value env, int32_t argc, xt_value
|
|
|
394
374
|
if (name == "AudioContext" || name == "webkitAudioContext") {
|
|
395
375
|
return xt_gui_audio_create(argc, argv);
|
|
396
376
|
}
|
|
397
|
-
if (name == "
|
|
377
|
+
if (name == "AbortController") return domapi::abort_controller_new();
|
|
378
|
+
if (name == "AbortSignal") return domapi::abort_signal_new(false);
|
|
379
|
+
if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent" ||
|
|
380
|
+
name == "UIEvent" || name == "MouseEvent" || name == "PointerEvent" ||
|
|
381
|
+
name == "WheelEvent" || name == "KeyboardEvent" || name == "FocusEvent" ||
|
|
382
|
+
name == "InputEvent" || name == "SubmitEvent") {
|
|
398
383
|
return domapi::event_construct(xt_arg(argc, argv, 0), xt_arg(argc, argv, 1));
|
|
399
384
|
}
|
|
385
|
+
if (name == "Path2D") return domapi::path2d_create(xt_arg(argc, argv, 0));
|
|
400
386
|
xt_value target = xt_object_new_with_proto(type_proto(name));
|
|
401
387
|
set_prop(target, "constructor", type_value(name));
|
|
402
388
|
return target;
|
|
@@ -438,10 +424,6 @@ void ensure_singletons(void) {
|
|
|
438
424
|
g_location = location_build();
|
|
439
425
|
xt_gc_add_root(&g_location);
|
|
440
426
|
}
|
|
441
|
-
if (!XT_IS_OBJECT(g_media_list)) {
|
|
442
|
-
g_media_list = media_list_build("", false);
|
|
443
|
-
xt_gc_add_root(&g_media_list);
|
|
444
|
-
}
|
|
445
427
|
}
|
|
446
428
|
|
|
447
429
|
} // namespace
|
|
@@ -474,9 +456,8 @@ void xt_browser_install_window(xt_value proto) {
|
|
|
474
456
|
set_method(proto, "confirm", (void *)win_confirm);
|
|
475
457
|
set_method(proto, "prompt", (void *)win_prompt);
|
|
476
458
|
set_method(proto, "escape", (void *)xt_browser_escape);
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
set_method(proto, "scrollTo", (void *)noop);
|
|
459
|
+
/* Page scroll and window focus/blur live in `browser_navigation.cpp`. */
|
|
460
|
+
xt_browser_install_window_navigation(proto);
|
|
480
461
|
}
|
|
481
462
|
|
|
482
463
|
/** `window` / `self`. */
|
|
@@ -581,3 +562,7 @@ xt_value xt_browser_alert(int32_t argc, xt_value *argv) {
|
|
|
581
562
|
xt_value xt_browser_prompt(int32_t argc, xt_value *argv) {
|
|
582
563
|
return win_prompt(XT_UNDEFINED, XT_UNDEFINED, argc, argv);
|
|
583
564
|
}
|
|
565
|
+
|
|
566
|
+
xt_value xt_browser_confirm_global(int32_t argc, xt_value *argv) {
|
|
567
|
+
return win_confirm(XT_UNDEFINED, XT_UNDEFINED, argc, argv);
|
|
568
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `AbortController` / `AbortSignal`.
|
|
3
|
+
*
|
|
4
|
+
* The pair exists so `addEventListener(..., { signal })` can actually remove a
|
|
5
|
+
* listener, as the DOM specifies. The engine has no Web-IDL machinery, so both
|
|
6
|
+
* objects are plain runtime objects with a shared hidden state:
|
|
7
|
+
*
|
|
8
|
+
* controller.__xt_gui_abort_state the state object
|
|
9
|
+
* controller.__xt_gui_abort_signal the signal handed out by `.signal`
|
|
10
|
+
* signal.__xt_gui_abort_state the same state object
|
|
11
|
+
*
|
|
12
|
+
* The state holds `aborted`, `reason` and the 'abort' listener list. Every value
|
|
13
|
+
* lives in a runtime object reachable from the handle the page kept, so the
|
|
14
|
+
* mark-sweep collector sees the whole graph.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "dom_api_internal.h"
|
|
18
|
+
#include "listener_options.h"
|
|
19
|
+
|
|
20
|
+
namespace domapi {
|
|
21
|
+
|
|
22
|
+
xt_value abort_signal_proto();
|
|
23
|
+
xt_value abort_controller_proto();
|
|
24
|
+
|
|
25
|
+
namespace {
|
|
26
|
+
|
|
27
|
+
xt_value new_state() {
|
|
28
|
+
xt_value state = xt_object_new();
|
|
29
|
+
xt_object_set(state, xt_string_from_cstr("aborted"), XT_FALSE);
|
|
30
|
+
xt_object_set(state, xt_string_from_cstr("reason"), XT_UNDEFINED);
|
|
31
|
+
xt_object_set(state, xt_string_from_cstr("listeners"), xt_array_new(0, nullptr));
|
|
32
|
+
return state;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
xt_value state_of(xt_value self) { return xt_object_get_cstr(self, "__xt_gui_abort_state"); }
|
|
36
|
+
|
|
37
|
+
xt_value signal_of(xt_value self) { return xt_object_get_cstr(self, "__xt_gui_abort_signal"); }
|
|
38
|
+
|
|
39
|
+
bool state_aborted(xt_value state) {
|
|
40
|
+
return XT_IS_OBJECT(state) && value_truthy(xt_object_get_cstr(state, "aborted"));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
xt_value abort_signal_from_state(xt_value state, xt_value controller) {
|
|
44
|
+
xt_value signal = xt_object_new_with_proto(abort_signal_proto());
|
|
45
|
+
xt_object_set(signal, xt_string_from_cstr("__xt_gui_abort_state"), state);
|
|
46
|
+
if (!XT_IS_UNDEFINED(controller)) {
|
|
47
|
+
xt_object_set(signal, xt_string_from_cstr("__xt_gui_abort_controller"), controller);
|
|
48
|
+
}
|
|
49
|
+
return signal;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Call `fn` (or `{ handleEvent }`) with an `{ type, target }` event. */
|
|
53
|
+
void call_abort_listener(xt_value fn, xt_value signal) {
|
|
54
|
+
xt_value event = xt_object_new_with_proto(event_proto());
|
|
55
|
+
xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr("abort"));
|
|
56
|
+
xt_object_set(event, xt_string_from_cstr("target"), signal);
|
|
57
|
+
xt_object_set(event, xt_string_from_cstr("currentTarget"), signal);
|
|
58
|
+
xt_object_set(event, xt_string_from_cstr("defaultPrevented"), XT_FALSE);
|
|
59
|
+
if (XT_IS_FUNCTION(fn)) {
|
|
60
|
+
xt_value args[1] = {event};
|
|
61
|
+
xt_call_with_this(fn, signal, 1, args);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (XT_IS_OBJECT(fn)) {
|
|
65
|
+
xt_value handler = xt_object_get_cstr(fn, "handleEvent");
|
|
66
|
+
if (XT_IS_FUNCTION(handler)) {
|
|
67
|
+
xt_value args[1] = {event};
|
|
68
|
+
xt_call_with_this(handler, fn, 1, args);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
|
|
74
|
+
|
|
75
|
+
METHOD(signal_aborted_get) {
|
|
76
|
+
(void)env;
|
|
77
|
+
(void)argc;
|
|
78
|
+
(void)argv;
|
|
79
|
+
return state_aborted(state_of(self)) ? XT_TRUE : XT_FALSE;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
METHOD(signal_reason_get) {
|
|
83
|
+
(void)env;
|
|
84
|
+
(void)argc;
|
|
85
|
+
(void)argv;
|
|
86
|
+
xt_value state = state_of(self);
|
|
87
|
+
if (!XT_IS_OBJECT(state)) return XT_UNDEFINED;
|
|
88
|
+
return xt_object_get_cstr(state, "reason");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
METHOD(signal_onabort_get) {
|
|
92
|
+
(void)env;
|
|
93
|
+
(void)argc;
|
|
94
|
+
(void)argv;
|
|
95
|
+
return xt_object_get_cstr(self, "__xt_gui_onabort");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
METHOD(signal_onabort_set) {
|
|
99
|
+
(void)env;
|
|
100
|
+
if (argc >= 1) xt_object_set(self, xt_string_from_cstr("__xt_gui_onabort"), xt_arg(argc, argv, 0));
|
|
101
|
+
return XT_UNDEFINED;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** `signal.addEventListener("abort", fn, options?)` — only 'abort' exists. */
|
|
105
|
+
METHOD(signal_add_listener) {
|
|
106
|
+
(void)env;
|
|
107
|
+
xt_value state = state_of(self);
|
|
108
|
+
if (!XT_IS_OBJECT(state) || argc < 2) return XT_UNDEFINED;
|
|
109
|
+
std::string type = to_string_value(xt_arg(argc, argv, 0));
|
|
110
|
+
if (type != "abort") return XT_UNDEFINED;
|
|
111
|
+
xt_value fn = xt_arg(argc, argv, 1);
|
|
112
|
+
if (!XT_IS_FUNCTION(fn) && !XT_IS_OBJECT(fn)) return XT_UNDEFINED;
|
|
113
|
+
XtGuiListenerOptions options = xt_gui_listener_options(xt_arg(argc, argv, 2));
|
|
114
|
+
xt_value entry = xt_object_new();
|
|
115
|
+
xt_object_set(entry, xt_string_from_cstr("fn"), fn);
|
|
116
|
+
xt_object_set(entry, xt_string_from_cstr("once"), options.once ? XT_TRUE : XT_FALSE);
|
|
117
|
+
xt_array_push(xt_object_get_cstr(state, "listeners"), entry);
|
|
118
|
+
return XT_UNDEFINED;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
METHOD(signal_remove_listener) {
|
|
122
|
+
(void)env;
|
|
123
|
+
xt_value state = state_of(self);
|
|
124
|
+
if (!XT_IS_OBJECT(state) || argc < 2) return XT_UNDEFINED;
|
|
125
|
+
xt_value listeners = xt_object_get_cstr(state, "listeners");
|
|
126
|
+
if (!XT_IS_ARRAY(listeners)) return XT_UNDEFINED;
|
|
127
|
+
xt_value fn = xt_arg(argc, argv, 1);
|
|
128
|
+
xt_value kept = xt_array_new(0, nullptr);
|
|
129
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(listeners));
|
|
130
|
+
for (int32_t i = 0; i < count; i++) {
|
|
131
|
+
xt_value entry = xt_array_get(listeners, xt_number((double)i));
|
|
132
|
+
if (!xt_truthy(xt_seq(xt_object_get_cstr(entry, "fn"), fn))) xt_array_push(kept, entry);
|
|
133
|
+
}
|
|
134
|
+
xt_object_set(state, xt_string_from_cstr("listeners"), kept);
|
|
135
|
+
return XT_UNDEFINED;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** `signal.throwIfAborted()`: raise the abort reason (an `Error` by default). */
|
|
139
|
+
METHOD(signal_throw_if_aborted) {
|
|
140
|
+
(void)env;
|
|
141
|
+
(void)argc;
|
|
142
|
+
(void)argv;
|
|
143
|
+
xt_value state = state_of(self);
|
|
144
|
+
if (!state_aborted(state)) return XT_UNDEFINED;
|
|
145
|
+
xt_value reason = xt_object_get_cstr(state, "reason");
|
|
146
|
+
if (XT_IS_UNDEFINED(reason) || reason == XT_NULL) {
|
|
147
|
+
reason = xt_error_ctor_by_name(xt_string_from_cstr("Error"), 0, nullptr);
|
|
148
|
+
}
|
|
149
|
+
xt_throw(reason);
|
|
150
|
+
return XT_UNDEFINED;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
METHOD(controller_signal_get) {
|
|
154
|
+
(void)env;
|
|
155
|
+
(void)argc;
|
|
156
|
+
(void)argv;
|
|
157
|
+
xt_value signal = signal_of(self);
|
|
158
|
+
if (XT_IS_OBJECT(signal)) return signal;
|
|
159
|
+
xt_value state = state_of(self);
|
|
160
|
+
if (!XT_IS_OBJECT(state)) {
|
|
161
|
+
state = new_state();
|
|
162
|
+
xt_object_set(self, xt_string_from_cstr("__xt_gui_abort_state"), state);
|
|
163
|
+
}
|
|
164
|
+
signal = abort_signal_from_state(state, self);
|
|
165
|
+
xt_object_set(self, xt_string_from_cstr("__xt_gui_abort_signal"), signal);
|
|
166
|
+
return signal;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** `controller.abort(reason)`: flip the state and dispatch 'abort' once. */
|
|
170
|
+
METHOD(controller_abort) {
|
|
171
|
+
(void)env;
|
|
172
|
+
xt_value state = state_of(self);
|
|
173
|
+
if (!XT_IS_OBJECT(state) || state_aborted(state)) return XT_UNDEFINED;
|
|
174
|
+
xt_value reason = argc >= 1 ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
|
|
175
|
+
if (XT_IS_UNDEFINED(reason)) {
|
|
176
|
+
xt_value message = xt_string_from_cstr("This operation was aborted");
|
|
177
|
+
reason = xt_error_ctor_by_name(xt_string_from_cstr("Error"), 1, &message);
|
|
178
|
+
}
|
|
179
|
+
xt_object_set(state, xt_string_from_cstr("aborted"), XT_TRUE);
|
|
180
|
+
xt_object_set(state, xt_string_from_cstr("reason"), reason);
|
|
181
|
+
xt_value signal = signal_of(self);
|
|
182
|
+
if (!XT_IS_OBJECT(signal)) signal = controller_signal_get(self, XT_UNDEFINED, 0, nullptr);
|
|
183
|
+
xt_value onabort = xt_object_get_cstr(signal, "__xt_gui_onabort");
|
|
184
|
+
if (XT_IS_FUNCTION(onabort)) call_abort_listener(onabort, signal);
|
|
185
|
+
xt_value listeners = xt_object_get_cstr(state, "listeners");
|
|
186
|
+
int32_t count = XT_IS_ARRAY(listeners) ? (int32_t)xt_to_number(xt_array_length(listeners)) : 0;
|
|
187
|
+
xt_value snapshot = xt_array_new(0, nullptr);
|
|
188
|
+
for (int32_t i = 0; i < count; i++) {
|
|
189
|
+
xt_array_push(snapshot, xt_array_get(listeners, xt_number((double)i)));
|
|
190
|
+
}
|
|
191
|
+
xt_object_set(state, xt_string_from_cstr("listeners"), xt_array_new(0, nullptr));
|
|
192
|
+
for (int32_t i = 0; i < count; i++) {
|
|
193
|
+
xt_value entry = xt_array_get(snapshot, xt_number((double)i));
|
|
194
|
+
call_abort_listener(xt_object_get_cstr(entry, "fn"), signal);
|
|
195
|
+
}
|
|
196
|
+
return XT_UNDEFINED;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#undef METHOD
|
|
200
|
+
|
|
201
|
+
} // namespace
|
|
202
|
+
|
|
203
|
+
xt_value abort_signal_proto() {
|
|
204
|
+
static xt_value proto = XT_UNDEFINED;
|
|
205
|
+
if (XT_IS_OBJECT(proto)) return proto;
|
|
206
|
+
proto = xt_object_new();
|
|
207
|
+
xt_gc_add_root(&proto);
|
|
208
|
+
xt_object_define_getter(proto, xt_string_from_cstr("aborted"),
|
|
209
|
+
xt_closure_new((void *)signal_aborted_get, 0, nullptr));
|
|
210
|
+
xt_object_define_getter(proto, xt_string_from_cstr("reason"),
|
|
211
|
+
xt_closure_new((void *)signal_reason_get, 0, nullptr));
|
|
212
|
+
xt_object_define_getter(proto, xt_string_from_cstr("onabort"),
|
|
213
|
+
xt_closure_new((void *)signal_onabort_get, 0, nullptr));
|
|
214
|
+
xt_object_define_setter(proto, xt_string_from_cstr("onabort"),
|
|
215
|
+
xt_closure_new((void *)signal_onabort_set, 0, nullptr));
|
|
216
|
+
xt_object_set(proto, xt_string_from_cstr("addEventListener"),
|
|
217
|
+
xt_closure_new((void *)signal_add_listener, 0, nullptr));
|
|
218
|
+
xt_object_set(proto, xt_string_from_cstr("removeEventListener"),
|
|
219
|
+
xt_closure_new((void *)signal_remove_listener, 0, nullptr));
|
|
220
|
+
xt_object_set(proto, xt_string_from_cstr("throwIfAborted"),
|
|
221
|
+
xt_closure_new((void *)signal_throw_if_aborted, 0, nullptr));
|
|
222
|
+
return proto;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
xt_value abort_controller_proto() {
|
|
226
|
+
static xt_value proto = XT_UNDEFINED;
|
|
227
|
+
if (XT_IS_OBJECT(proto)) return proto;
|
|
228
|
+
proto = xt_object_new();
|
|
229
|
+
xt_gc_add_root(&proto);
|
|
230
|
+
xt_object_define_getter(proto, xt_string_from_cstr("signal"),
|
|
231
|
+
xt_closure_new((void *)controller_signal_get, 0, nullptr));
|
|
232
|
+
xt_object_set(proto, xt_string_from_cstr("abort"),
|
|
233
|
+
xt_closure_new((void *)controller_abort, 0, nullptr));
|
|
234
|
+
return proto;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
xt_value abort_controller_new() {
|
|
238
|
+
xt_value controller = xt_object_new_with_proto(abort_controller_proto());
|
|
239
|
+
xt_object_set(controller, xt_string_from_cstr("__xt_gui_abort_state"), new_state());
|
|
240
|
+
return controller;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
xt_value abort_signal_new(bool aborted) {
|
|
244
|
+
xt_value state = new_state();
|
|
245
|
+
if (aborted) xt_object_set(state, xt_string_from_cstr("aborted"), XT_TRUE);
|
|
246
|
+
return abort_signal_from_state(state, XT_UNDEFINED);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
} // namespace domapi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `window.confirm` / `window.prompt`.
|
|
3
|
+
*
|
|
4
|
+
* A compiled page has no browser chrome, so a blocking dialog is opt-in: with
|
|
5
|
+
* `XT_GUI_DIALOGS=1` and a live window the engine shows a native SDL message
|
|
6
|
+
* box. Otherwise (and always for `prompt`, which would need a text input) the
|
|
7
|
+
* message is written to stderr and the documented default is returned — the
|
|
8
|
+
* arguments are never silently dropped. `confirm` answers false and `prompt`
|
|
9
|
+
* answers `defaultValue` (or "") when no dialog is shown.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "gui_engine.h"
|
|
13
|
+
|
|
14
|
+
#include <stdio.h>
|
|
15
|
+
#include <string>
|
|
16
|
+
|
|
17
|
+
namespace {
|
|
18
|
+
|
|
19
|
+
std::string message_of(int32_t argc, xt_value *argv) {
|
|
20
|
+
if (argc < 1) return std::string();
|
|
21
|
+
const char *data = xt_string_data(xt_to_string(xt_arg(argc, argv, 0)));
|
|
22
|
+
return data != nullptr ? data : "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
bool dialogs_enabled() {
|
|
26
|
+
const char *value = SDL_getenv("XT_GUI_DIALOGS");
|
|
27
|
+
return value != nullptr && value[0] == '1';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
bool show_message(XtGuiWindow *win, const std::string &message, bool with_cancel) {
|
|
31
|
+
if (win == nullptr || win->window == nullptr) return false;
|
|
32
|
+
SDL_MessageBoxButtonData buttons[2];
|
|
33
|
+
SDL_zero(buttons);
|
|
34
|
+
int count = 1;
|
|
35
|
+
buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
|
|
36
|
+
buttons[0].buttonID = 1;
|
|
37
|
+
buttons[0].text = "OK";
|
|
38
|
+
if (with_cancel) {
|
|
39
|
+
buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
|
|
40
|
+
buttons[1].buttonID = 0;
|
|
41
|
+
buttons[1].text = "Cancel";
|
|
42
|
+
count = 2;
|
|
43
|
+
}
|
|
44
|
+
SDL_MessageBoxData data;
|
|
45
|
+
SDL_zero(data);
|
|
46
|
+
data.flags = SDL_MESSAGEBOX_INFORMATION;
|
|
47
|
+
data.window = win->window;
|
|
48
|
+
data.title = "xbintsc";
|
|
49
|
+
data.message = message.c_str();
|
|
50
|
+
data.numbuttons = count;
|
|
51
|
+
data.buttons = buttons;
|
|
52
|
+
int pressed = -1;
|
|
53
|
+
if (!SDL_ShowMessageBox(&data, &pressed)) return false;
|
|
54
|
+
return pressed == 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
} // namespace
|
|
58
|
+
|
|
59
|
+
xt_value xt_browser_confirm(XtGuiWindow *win, int32_t argc, xt_value *argv) {
|
|
60
|
+
std::string message = message_of(argc, argv);
|
|
61
|
+
if (dialogs_enabled() && show_message(win, message, true)) return XT_TRUE;
|
|
62
|
+
fprintf(stderr, "confirm: %s\n", message.c_str());
|
|
63
|
+
return XT_FALSE;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
xt_value xt_browser_prompt(XtGuiWindow *win, int32_t argc, xt_value *argv) {
|
|
67
|
+
std::string message = message_of(argc, argv);
|
|
68
|
+
std::string fallback;
|
|
69
|
+
if (argc >= 2) {
|
|
70
|
+
const char *data = xt_string_data(xt_to_string(xt_arg(argc, argv, 1)));
|
|
71
|
+
if (data != nullptr) fallback = data;
|
|
72
|
+
}
|
|
73
|
+
if (dialogs_enabled()) show_message(win, message, false);
|
|
74
|
+
fprintf(stderr, "prompt: %s\n", message.c_str());
|
|
75
|
+
return xt_string_from_cstr(fallback.c_str());
|
|
76
|
+
}
|