xbintsc 0.5.31 → 0.5.36
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/README.md +25 -16
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/binder/references.js +14 -1
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -0
- package/dist/src/codegen/generator/module.js +54 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +6 -4
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.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/css.d.ts +26 -0
- package/dist/src/extensions/gui/css.js +100 -0
- package/dist/src/extensions/gui/css.js.map +1 -0
- package/dist/src/extensions/gui/html.js +9 -2
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +22 -7
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +16 -0
- package/dist/src/extensions/gui/three.js +22 -0
- package/dist/src/extensions/gui/three.js.map +1 -0
- package/dist/src/extensions/media/index.d.ts +6 -5
- package/dist/src/extensions/media/index.js +15 -16
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/compile-builtins.test.js +24 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +23 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-browser.test.js +1 -1
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-effects.test.d.ts +6 -0
- package/dist/tests/e2e/gui-effects.test.js +262 -0
- package/dist/tests/e2e/gui-effects.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-webgl.test.js +75 -0
- package/dist/tests/e2e/gui-example-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl2.test.d.ts +8 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js +72 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +48 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +83 -4
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.d.ts +11 -0
- package/dist/tests/e2e/gui-webgl.test.js +437 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2.test.js +282 -0
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -0
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/gui.test.js +41 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +8 -3
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +35 -12
- package/doc/ai/troubleshooting.md +17 -11
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +30 -10
- package/doc/ai/zh-CN/troubleshooting.md +14 -8
- package/doc/gui/api.md +63 -14
- package/doc/gui/build.md +79 -16
- package/doc/gui/canvas.md +11 -3
- package/doc/gui/dom.md +35 -17
- package/doc/gui/known-issues.md +57 -24
- package/doc/gui/layout.md +18 -0
- package/doc/gui/media.md +21 -13
- package/doc/gui/rendering.md +50 -3
- package/doc/gui/webgl.md +315 -0
- package/doc/gui.md +113 -18
- package/doc/npm-package.md +59 -29
- package/doc/requirements.md +24 -11
- package/doc/zh-CN/gui/api.md +53 -11
- package/doc/zh-CN/gui/build.md +70 -16
- package/doc/zh-CN/gui/canvas.md +9 -2
- package/doc/zh-CN/gui/dom.md +22 -7
- package/doc/zh-CN/gui/known-issues.md +45 -19
- package/doc/zh-CN/gui/layout.md +14 -0
- package/doc/zh-CN/gui/media.md +18 -11
- package/doc/zh-CN/gui/rendering.md +41 -4
- package/doc/zh-CN/gui/webgl.md +121 -0
- package/doc/zh-CN/gui.md +91 -15
- package/doc/zh-CN/npm-package.md +48 -24
- package/doc/zh-CN/requirements.md +16 -7
- package/llms.txt +2 -0
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +50 -14
- package/runtime/ext_gui/canvas.cpp +3 -18
- package/runtime/ext_gui/canvas_internal.h +10 -0
- package/runtime/ext_gui/canvas_raster.cpp +60 -0
- package/runtime/ext_gui/clip_values.cpp +246 -0
- package/runtime/ext_gui/css.cpp +94 -0
- package/runtime/ext_gui/css.h +22 -1
- package/runtime/ext_gui/css_match.cpp +3 -0
- package/runtime/ext_gui/css_selector.cpp +30 -3
- package/runtime/ext_gui/css_sheet.cpp +89 -45
- package/runtime/ext_gui/document.cpp +4 -1
- package/runtime/ext_gui/document.h +2 -0
- package/runtime/ext_gui/dom.h +16 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +17 -80
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +130 -1
- package/runtime/ext_gui/dom_api_canvas_pending.cpp +41 -0
- package/runtime/ext_gui/dom_api_internal.h +275 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +383 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +549 -0
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +269 -0
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +216 -0
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_rt.h +30 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +443 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +590 -0
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +485 -0
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/effect_values.cpp +312 -0
- package/runtime/ext_gui/gradient_values.cpp +476 -0
- package/runtime/ext_gui/grid_values.cpp +230 -0
- package/runtime/ext_gui/gui.cpp +44 -9
- package/runtime/ext_gui/gui_canvas.cpp +50 -2
- package/runtime/ext_gui/gui_engine.h +36 -4
- package/runtime/ext_gui/gui_events.cpp +4 -1
- package/runtime/ext_gui/gui_media.cpp +1 -1
- package/runtime/ext_gui/gui_webgl.cpp +83 -0
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/layout.cpp +21 -0
- package/runtime/ext_gui/layout_flow.cpp +81 -4
- package/runtime/ext_gui/layout_flow_internal.h +31 -0
- package/runtime/ext_gui/layout_grid.cpp +374 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +14 -1
- package/runtime/ext_gui/media_api.h +25 -2
- package/runtime/ext_gui/media_query.cpp +211 -0
- package/runtime/ext_gui/paint.cpp +111 -77
- package/runtime/ext_gui/paint.h +33 -3
- package/runtime/ext_gui/paint_background.cpp +229 -0
- package/runtime/ext_gui/paint_clip.cpp +268 -0
- package/runtime/ext_gui/paint_effects.cpp +324 -0
- package/runtime/ext_gui/paint_internal.h +85 -0
- package/runtime/ext_gui/renderer.cpp +8 -31
- package/runtime/ext_gui/renderer_clip.cpp +196 -0
- package/runtime/ext_gui/renderer_image.cpp +13 -8
- package/runtime/ext_gui/renderer_internal.h +15 -8
- package/runtime/ext_gui/renderer_text.cpp +6 -31
- package/runtime/ext_gui/style.cpp +343 -20
- package/runtime/ext_gui/style.h +144 -3
- package/runtime/ext_gui/style_print.cpp +134 -1
- package/runtime/ext_gui/style_values.cpp +152 -126
- package/runtime/ext_gui/style_values_internal.h +35 -0
- package/runtime/ext_gui/webgl.h +600 -0
- package/runtime/ext_gui/webgl2_constants.h +274 -0
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +480 -0
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_constants.h +317 -0
- package/runtime/ext_gui/webgl_context.cpp +551 -0
- package/runtime/ext_gui/webgl_draw.cpp +560 -0
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +165 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +437 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +367 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +590 -0
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_msl.cpp +62 -0
- package/runtime/ext_gui/webgl_msl.h +33 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +326 -0
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +24 -0
- package/runtime/ext_gui/webgl_shader.cpp +594 -0
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +420 -0
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform.cpp +167 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +277 -0
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media.cpp +1 -1
- package/runtime/ext_media/media_bridge.c +11 -3
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +1 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_containers/access.inc +8 -0
- package/runtime/xt_regex/api.inc +2 -0
- package/runtime/xt_regex/matcher.inc +16 -3
- package/runtime/xt_regex/parser.inc +8 -2
- package/runtime/xt_regex/types.inc +3 -0
- package/runtime/xt_stdlib2/regexp-match.inc +60 -21
- package/runtime/xt_stdlib2/regexp.inc +1 -0
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/runtime/xt_typed_array.c +1 -1
- package/scripts/build-gui.ts +90 -3
- package/scripts/build-media.ts +335 -62
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/setup-vs-toolchain.ps1 +152 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/binder/references.ts +13 -1
- package/src/cli/main.ts +5 -1
- package/src/codegen/generator/module.ts +50 -0
- package/src/codegen/generator/statements/variables.ts +6 -3
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/css.ts +113 -0
- package/src/extensions/gui/html.ts +9 -2
- package/src/extensions/gui/index.ts +22 -7
- package/src/extensions/gui/three.ts +25 -0
- package/src/extensions/media/index.ts +16 -18
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
package/runtime/ext_gui/gui.cpp
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
#include "paint.h"
|
|
20
20
|
|
|
21
21
|
#include <stdio.h>
|
|
22
|
+
#include <stdlib.h>
|
|
22
23
|
#include <string.h>
|
|
23
24
|
|
|
24
25
|
namespace guidev {
|
|
@@ -98,11 +99,32 @@ static void xt_gui_gc_scan_roots(void) {
|
|
|
98
99
|
|
|
99
100
|
/* -- initialisation ------------------------------------------------------- */
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Exit status for "this host cannot run a GUI program": no video driver, or no
|
|
104
|
+
* GPU device the SDL_GPU backends accept. It is deliberately distinct from a
|
|
105
|
+
* failed program so a CI script (or a user) can skip the run instead of
|
|
106
|
+
* reporting a crash. 77 is the autotools "skip" convention; `ci.yml` treats it
|
|
107
|
+
* that way for the provisional macOS job.
|
|
108
|
+
*/
|
|
109
|
+
const int kGuiUnavailableExit = 77;
|
|
110
|
+
|
|
111
|
+
/** Report a window that could not be created as a JavaScript `Error`. Returning
|
|
112
|
+
* `undefined` used to surface one line later as `Cannot read properties of
|
|
113
|
+
* undefined (reading 'on')`, which hid the real diagnostic. */
|
|
114
|
+
static void throw_gui_error(const char *message) {
|
|
115
|
+
xt_value text = xt_string_from_cstr(message);
|
|
116
|
+
xt_throw(xt_error_ctor_by_name(xt_string_from_cstr("Error"), 1, &text));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static void ensure_init(void) {
|
|
120
|
+
if (g_sdl_initialized) return;
|
|
103
121
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
104
122
|
fprintf(stderr, "xt_gui: SDL_Init failed: %s\n", SDL_GetError());
|
|
105
|
-
|
|
123
|
+
fprintf(stderr,
|
|
124
|
+
"xt_gui: the GUI extension needs a display (or a virtual one such as Xvfb); "
|
|
125
|
+
"exiting with status %d.\n",
|
|
126
|
+
kGuiUnavailableExit);
|
|
127
|
+
exit(kGuiUnavailableExit);
|
|
106
128
|
}
|
|
107
129
|
/* Accept every shader format so the same code runs on Metal, Vulkan and
|
|
108
130
|
* D3D12; the backend only needs the one it supports. The engine ships MSL,
|
|
@@ -113,11 +135,17 @@ static int ensure_init(void) { if (g_sdl_initialized) return 1;
|
|
|
113
135
|
g_device = SDL_CreateGPUDevice(formats, false, NULL);
|
|
114
136
|
if (g_device == NULL) {
|
|
115
137
|
fprintf(stderr, "xt_gui: SDL_CreateGPUDevice failed: %s\n", SDL_GetError());
|
|
138
|
+
fprintf(stderr,
|
|
139
|
+
"xt_gui: the GUI extension needs a Metal (macOS), Vulkan or D3D12 device; "
|
|
140
|
+
"this host does not provide a usable one. Exiting with status %d.\n",
|
|
141
|
+
kGuiUnavailableExit);
|
|
116
142
|
SDL_Quit();
|
|
117
|
-
|
|
143
|
+
exit(kGuiUnavailableExit);
|
|
118
144
|
}
|
|
145
|
+
/* Registered only once a window can exist: a failed init has nothing to
|
|
146
|
+
* mark, and a caller that cannot retry never needs a second provider. */
|
|
147
|
+
xt_gc_register_root_provider(xt_gui_gc_scan_roots);
|
|
119
148
|
g_sdl_initialized = 1;
|
|
120
|
-
return 1;
|
|
121
149
|
}
|
|
122
150
|
|
|
123
151
|
/* -- window lookup -------------------------------------------------------- */
|
|
@@ -254,15 +282,18 @@ xt_value xt_gui_ensure_window(void) {
|
|
|
254
282
|
for (int i = g_window_count - 1; i >= 0; i--) {
|
|
255
283
|
if (g_windows[i].open) return g_windows[i].object;
|
|
256
284
|
}
|
|
257
|
-
|
|
285
|
+
ensure_init();
|
|
258
286
|
XtGuiWindow *record = create_window_record("xbintsc gui", 800, 600, true);
|
|
259
|
-
if (record == NULL)
|
|
287
|
+
if (record == NULL) {
|
|
288
|
+
throw_gui_error("xbintsc gui: could not open the implicit window (see the diagnostic above)");
|
|
289
|
+
return XT_UNDEFINED;
|
|
290
|
+
}
|
|
260
291
|
g_implicit_window = 1;
|
|
261
292
|
return record->object;
|
|
262
293
|
}
|
|
263
294
|
|
|
264
295
|
extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
|
|
265
|
-
|
|
296
|
+
ensure_init();
|
|
266
297
|
|
|
267
298
|
const char *title = "xbintsc gui";
|
|
268
299
|
int width = 800;
|
|
@@ -304,7 +335,11 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
|
|
|
304
335
|
}
|
|
305
336
|
|
|
306
337
|
XtGuiWindow *record = create_window_record(title, width, height, high_density);
|
|
307
|
-
|
|
338
|
+
if (record == NULL) {
|
|
339
|
+
throw_gui_error("xbintsc gui: could not open a window (see the diagnostic above)");
|
|
340
|
+
return XT_UNDEFINED;
|
|
341
|
+
}
|
|
342
|
+
return record->object;
|
|
308
343
|
}
|
|
309
344
|
|
|
310
345
|
/* -- run() / quit() ------------------------------------------------------- */
|
|
@@ -65,11 +65,17 @@ xtgui::Canvas2D *xt_gui_canvas_get(XtGuiWindow *win, const xtgui::Node *node) {
|
|
|
65
65
|
auto canvas = std::make_unique<xtgui::Canvas2D>(width, height);
|
|
66
66
|
xtgui::Canvas2D *raw = canvas.get();
|
|
67
67
|
win->canvases[node] = std::move(canvas);
|
|
68
|
+
/* A context obtained after the first frame must reach the display list
|
|
69
|
+
* immediately, so `paintList`/the next paint sees its size and texture id. */
|
|
70
|
+
if (win->document != nullptr) {
|
|
71
|
+
win->document->setCanvasInfo(node, (float)width, (float)height, raw->id());
|
|
72
|
+
}
|
|
68
73
|
return raw;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
void xt_gui_canvas_forget(XtGuiWindow *win, const xtgui::Node *node) {
|
|
72
77
|
if (win == nullptr || node == nullptr) return;
|
|
78
|
+
xt_gui_webgl_forget(win, node);
|
|
73
79
|
auto found = win->canvases.find(node);
|
|
74
80
|
if (found == win->canvases.end()) return;
|
|
75
81
|
releaseCanvasTexture(found->second.get());
|
|
@@ -81,8 +87,46 @@ void xt_gui_canvas_sync(XtGuiWindow *win) {
|
|
|
81
87
|
std::vector<const xtgui::Node *> nodes;
|
|
82
88
|
collectCanvases(win->document->root(), &nodes);
|
|
83
89
|
for (const xtgui::Node *node : nodes) {
|
|
84
|
-
xtgui::
|
|
85
|
-
|
|
90
|
+
if (xtgui::WebGLContext *webgl = xt_gui_webgl_find(win, node)) {
|
|
91
|
+
win->document->setCanvasInfo(node, (float)webgl->width(), (float)webgl->height(),
|
|
92
|
+
webgl->id());
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
xtgui::Canvas2D *canvas = xt_gui_canvas_find(win, node);
|
|
96
|
+
if (canvas != nullptr) {
|
|
97
|
+
win->document->setCanvasInfo(node, (float)canvas->width(), (float)canvas->height(),
|
|
98
|
+
canvas->id());
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
/* No context yet: the attributes still define the intrinsic box, but there
|
|
102
|
+
* is nothing to paint (a canvas with no context is transparent). */
|
|
103
|
+
int width = canvasAttributeSize(node, "width", 300);
|
|
104
|
+
int height = canvasAttributeSize(node, "height", 150);
|
|
105
|
+
win->document->setCanvasInfo(node, (float)width, (float)height, 0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void xt_gui_canvas_bind_pending(XtGuiWindow *win, const xtgui::Node *node, xt_value handle) {
|
|
110
|
+
if (win == nullptr || node == nullptr) return;
|
|
111
|
+
xt_value pending = xt_object_get_cstr(handle, "__xt_gui_pending_canvas");
|
|
112
|
+
if (!XT_IS_NUMBER(pending)) return;
|
|
113
|
+
uint64_t id = (uint64_t)xt_to_number(pending);
|
|
114
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_pending_canvas"), XT_UNDEFINED);
|
|
115
|
+
auto found = win->pending_canvases.find(id);
|
|
116
|
+
if (found == win->pending_canvases.end()) return;
|
|
117
|
+
if (!isCanvasElement(node)) {
|
|
118
|
+
win->pending_canvases.erase(found);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
xtgui::Canvas2D *canvas = found->second.get();
|
|
122
|
+
/* The parsed element's width/height attributes win over the default 300x150
|
|
123
|
+
* a pre-load context started with. */
|
|
124
|
+
int width = canvasAttributeSize(node, "width", canvas->width());
|
|
125
|
+
int height = canvasAttributeSize(node, "height", canvas->height());
|
|
126
|
+
if (width != canvas->width() || height != canvas->height()) canvas->resize(width, height);
|
|
127
|
+
win->canvases[node] = std::move(found->second);
|
|
128
|
+
win->pending_canvases.erase(found);
|
|
129
|
+
if (win->document != nullptr) {
|
|
86
130
|
win->document->setCanvasInfo(node, (float)canvas->width(), (float)canvas->height(),
|
|
87
131
|
canvas->id());
|
|
88
132
|
}
|
|
@@ -90,6 +134,10 @@ void xt_gui_canvas_sync(XtGuiWindow *win) {
|
|
|
90
134
|
|
|
91
135
|
void xt_gui_canvas_shutdown(XtGuiWindow *win) {
|
|
92
136
|
if (win == nullptr) return;
|
|
137
|
+
xt_gui_webgl_shutdown(win);
|
|
93
138
|
for (auto &entry : win->canvases) releaseCanvasTexture(entry.second.get());
|
|
94
139
|
win->canvases.clear();
|
|
140
|
+
/* `pending_canvases` survives `loadHTML`: a module that asked for a context
|
|
141
|
+
* before the page existed still needs its bitmap when the parsed element
|
|
142
|
+
* binds. They are released with the window record. */
|
|
95
143
|
}
|
|
@@ -45,6 +45,9 @@ xt_value xt_browser_local_storage(int32_t argc, xt_value *argv);
|
|
|
45
45
|
xt_value xt_browser_missing(int32_t argc, xt_value *argv);
|
|
46
46
|
/** A DOM type name (`Image`, `HTMLCanvasElement`, …) as a constructor value. */
|
|
47
47
|
xt_value xt_browser_type(int32_t argc, xt_value *argv);
|
|
48
|
+
/** `createImageBitmap(source)`, exposed as a global *value*: returns the
|
|
49
|
+
* closure the compiler stores for the bare identifier. */
|
|
50
|
+
xt_value xt_browser_create_image_bitmap(int32_t argc, xt_value *argv);
|
|
48
51
|
xt_value xt_browser_request_animation_frame(int32_t argc, xt_value *argv);
|
|
49
52
|
xt_value xt_browser_cancel_animation_frame(int32_t argc, xt_value *argv);
|
|
50
53
|
xt_value xt_browser_match_media(int32_t argc, xt_value *argv);
|
|
@@ -63,11 +66,13 @@ void xt_browser_schedule_frame(void);
|
|
|
63
66
|
/** Whether `run()` currently owns the frame loop (the pump then stays idle). */
|
|
64
67
|
int xt_gui_run_active(void);
|
|
65
68
|
|
|
66
|
-
/* Web Audio objects (`audio.cpp`).
|
|
67
|
-
|
|
69
|
+
/* Web Audio objects (`audio.cpp`).
|
|
70
|
+
* `AudioContext.prototype` — also the `instanceof AudioContext` target. */
|
|
68
71
|
xt_value xt_gui_audio_proto(void);
|
|
69
|
-
/** `new AudioContext(...)`:
|
|
70
|
-
*
|
|
72
|
+
/** `new AudioContext(...)`: builds a real graph (mixer in `audio_mixer.cpp`)
|
|
73
|
+
* and decodes through `audio_decode.cpp` + the optional media backend. The
|
|
74
|
+
* device opens on the first `start()`/`resume()`; a headless host stays silent
|
|
75
|
+
* while the graph still renders. */
|
|
71
76
|
xt_value xt_gui_audio_create(int32_t argc, xt_value *argv);
|
|
72
77
|
|
|
73
78
|
#ifdef __cplusplus
|
|
@@ -84,6 +89,7 @@ xt_value xt_gui_audio_create(int32_t argc, xt_value *argv);
|
|
|
84
89
|
#include "document.h"
|
|
85
90
|
#include "media.h"
|
|
86
91
|
#include "renderer.h"
|
|
92
|
+
#include "webgl.h"
|
|
87
93
|
|
|
88
94
|
/** One element-level event listener registered through `addEventListener`. */
|
|
89
95
|
struct XtGuiNodeListener {
|
|
@@ -162,6 +168,14 @@ struct XtGuiWindow {
|
|
|
162
168
|
* lazily by `getContext("2d")` (or by `xt_gui_canvas_sync` for a live canvas),
|
|
163
169
|
* released when the node is forgotten or the document reloads. */
|
|
164
170
|
std::unordered_map<const xtgui::Node *, std::unique_ptr<xtgui::Canvas2D>> canvases;
|
|
171
|
+
/** One WebGL context per `<canvas>` that asked for `getContext("webgl")`.
|
|
172
|
+
* A canvas uses either this map or `canvases`, never both. */
|
|
173
|
+
std::unordered_map<const xtgui::Node *, std::unique_ptr<xtgui::WebGLContext>> webgl_canvases;
|
|
174
|
+
/** Canvases whose element handle was created before `loadHTML` (a module
|
|
175
|
+
* that calls `getElementById("scene").getContext("2d")` while it
|
|
176
|
+
* initialises). Keyed by canvas id; moved into `canvases` when the pending
|
|
177
|
+
* handle binds to the parsed element. */
|
|
178
|
+
std::unordered_map<uint64_t, std::unique_ptr<xtgui::Canvas2D>> pending_canvases;
|
|
165
179
|
/** The batch currently running: its callbacks have been swapped out of
|
|
166
180
|
* `animation_frames`, and a collection triggered by one of them must still
|
|
167
181
|
* see the rest. Kept on the record (not a local) so the GC root provider can
|
|
@@ -243,12 +257,30 @@ xtgui::Canvas2D *xt_gui_canvas_get(XtGuiWindow *win, const xtgui::Node *node);
|
|
|
243
257
|
xtgui::Canvas2D *xt_gui_canvas_find(XtGuiWindow *win, const xtgui::Node *node);
|
|
244
258
|
/** Drop a node's canvas (called when its handle is forgotten). */
|
|
245
259
|
void xt_gui_canvas_forget(XtGuiWindow *win, const xtgui::Node *node);
|
|
260
|
+
/** Move a pre-load canvas into the live table when its pending element handle
|
|
261
|
+
* binds. `handle` is the DOM handle that was used for `getContext("2d")`. */
|
|
262
|
+
void xt_gui_canvas_bind_pending(XtGuiWindow *win, const xtgui::Node *node, xt_value handle);
|
|
263
|
+
|
|
246
264
|
/** Publish live canvases to the document (sizes + render ids) so layout and
|
|
247
265
|
* paint see them; creates a context for a canvas that has none yet. */
|
|
248
266
|
void xt_gui_canvas_sync(XtGuiWindow *win);
|
|
249
267
|
/** Release every canvas of `win` (window close / `loadHTML`). */
|
|
250
268
|
void xt_gui_canvas_shutdown(XtGuiWindow *win);
|
|
251
269
|
|
|
270
|
+
/** The WebGL context of a `<canvas>`, creating it on first use. `webgl2`
|
|
271
|
+
* selects the WebGL 2 API/prototype; a canvas keeps the first context kind it
|
|
272
|
+
* asked for, so a later request for the other one answers null. */
|
|
273
|
+
xtgui::WebGLContext *xt_gui_webgl_get(XtGuiWindow *win, const xtgui::Node *node, bool webgl2);
|
|
274
|
+
/** The existing WebGL context of `node`, or null (no side effects). */
|
|
275
|
+
xtgui::WebGLContext *xt_gui_webgl_find(XtGuiWindow *win, const xtgui::Node *node);
|
|
276
|
+
/** Drop a node's WebGL context (called when its handle is forgotten). */
|
|
277
|
+
void xt_gui_webgl_forget(XtGuiWindow *win, const xtgui::Node *node);
|
|
278
|
+
/** Release every WebGL context of `win`. */
|
|
279
|
+
void xt_gui_webgl_shutdown(XtGuiWindow *win);
|
|
280
|
+
/** Submit every live WebGL context's queued commands. Called once per frame
|
|
281
|
+
* before the page's own render pass samples a canvas texture. */
|
|
282
|
+
void xt_gui_webgl_submit_all();
|
|
283
|
+
|
|
252
284
|
#endif /* __cplusplus */
|
|
253
285
|
|
|
254
286
|
#endif /* XT_GUI_ENGINE_H */
|
|
@@ -272,7 +272,7 @@ void xt_gui_render_window(XtGuiWindow *win) {
|
|
|
272
272
|
bool can_paint = xt_gui_renderer_ensure(g_device, win->window);
|
|
273
273
|
if (can_paint && win->geometry.dirty && win->document != nullptr) {
|
|
274
274
|
xtgui::DisplayList list;
|
|
275
|
-
xtgui::xt_paint_build(win->document->layout().root(), list);
|
|
275
|
+
xtgui::xt_paint_build(win->document->layout().root(), list, viewport_width, viewport_height);
|
|
276
276
|
xt_gui_geometry_upload(g_device, &win->geometry, list);
|
|
277
277
|
float density = SDL_GetWindowPixelDensity(win->window);
|
|
278
278
|
float text_scale = density > 0.0f ? density : 1.0f;
|
|
@@ -287,6 +287,9 @@ void xt_gui_render_window(XtGuiWindow *win) {
|
|
|
287
287
|
xt_gui_media_upload_frames(g_device);
|
|
288
288
|
/* Canvases are CPU-drawn: push any new bitmap without a relayout. */
|
|
289
289
|
xt_gui_canvas_upload_frames(g_device);
|
|
290
|
+
/* WebGL contexts record into their own command buffers; submit them first so
|
|
291
|
+
* the page's render pass samples this frame's drawing-buffer contents. */
|
|
292
|
+
xt_gui_webgl_submit_all();
|
|
290
293
|
SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(g_device);
|
|
291
294
|
if (cmd == NULL) return;
|
|
292
295
|
SDL_GPUTexture *swapchain = NULL;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - the renderer (`renderer_image.cpp`), which owns the GPU textures.
|
|
9
9
|
*
|
|
10
10
|
* It also provides the SDL audio sink the decoder writes PCM to, which keeps
|
|
11
|
-
* SDL out of `media.a`.
|
|
11
|
+
* SDL out of `media.a`/`media.lib`.
|
|
12
12
|
*
|
|
13
13
|
* The frame loop calls `xt_gui_media_sync` (open/close decoders so the next
|
|
14
14
|
* layout sees the current sources and intrinsic sizes) and `xt_gui_media_tick`
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `<canvas>` WebGL lifetime.
|
|
3
|
+
*
|
|
4
|
+
* A WebGL context is owned by the window, keyed by its DOM element (the same
|
|
5
|
+
* shape as the 2D canvas and video tables). Its colour texture is what the
|
|
6
|
+
* page's image pipeline samples, so no pixels pass through the CPU.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include <memory>
|
|
10
|
+
|
|
11
|
+
#include "gui_engine.h"
|
|
12
|
+
#include "gui_internal.h"
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
bool isCanvasElement(const xtgui::Node *node) {
|
|
17
|
+
return node != nullptr && node->isElement() && node->isTag("canvas");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
uint64_t next_webgl_id() {
|
|
21
|
+
static uint64_t counter = 0;
|
|
22
|
+
counter++;
|
|
23
|
+
/* Keep WebGL ids out of the Canvas2D id space so the renderer can tell the
|
|
24
|
+
* two kinds of canvas apart from the id alone. */
|
|
25
|
+
return (1ULL << 32) | counter;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
} // namespace
|
|
29
|
+
|
|
30
|
+
xtgui::WebGLContext *xt_gui_webgl_find(XtGuiWindow *win, const xtgui::Node *node) {
|
|
31
|
+
if (win == nullptr || node == nullptr) return nullptr;
|
|
32
|
+
auto found = win->webgl_canvases.find(node);
|
|
33
|
+
return found == win->webgl_canvases.end() ? nullptr : found->second.get();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
xtgui::WebGLContext *xt_gui_webgl_get(XtGuiWindow *win, const xtgui::Node *node, bool webgl2) {
|
|
37
|
+
if (win == nullptr || !isCanvasElement(node)) return nullptr;
|
|
38
|
+
xtgui::WebGLContext *existing = xt_gui_webgl_find(win, node);
|
|
39
|
+
if (existing != nullptr) return existing->is_webgl2() == webgl2 ? existing : nullptr;
|
|
40
|
+
if (win->canvases.find(node) != win->canvases.end()) return nullptr; // already a 2D canvas
|
|
41
|
+
/* WebGL shaders reach SDL_GPU as SPIR-V (Vulkan, translated by glslang) or as
|
|
42
|
+
* MSL (Metal, the same module cross-compiled by SPIRV-Cross). A device whose
|
|
43
|
+
* backend consumes neither format — Direct3D 12 needs DXIL — has no WebGL
|
|
44
|
+
* context in this build, so `getContext("webgl")` answers null instead of
|
|
45
|
+
* handing out a context whose draws could never create a pipeline. */
|
|
46
|
+
if (guidev::g_device == nullptr ||
|
|
47
|
+
(SDL_GetGPUShaderFormats(guidev::g_device) &
|
|
48
|
+
(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL)) == 0) {
|
|
49
|
+
return nullptr;
|
|
50
|
+
}
|
|
51
|
+
const std::string *width_attr = node->attr("width");
|
|
52
|
+
const std::string *height_attr = node->attr("height");
|
|
53
|
+
float width = 300.0f;
|
|
54
|
+
float height = 150.0f;
|
|
55
|
+
if (width_attr != nullptr) xtgui::xt_css_parse_number(*width_attr, &width);
|
|
56
|
+
if (height_attr != nullptr) xtgui::xt_css_parse_number(*height_attr, &height);
|
|
57
|
+
auto context = std::make_unique<xtgui::WebGLContext>(
|
|
58
|
+
next_webgl_id(), (int)(width + 0.5f), (int)(height + 0.5f), webgl2);
|
|
59
|
+
xtgui::WebGLContext *raw = context.get();
|
|
60
|
+
win->webgl_canvases[node] = std::move(context);
|
|
61
|
+
if (win->document != nullptr) {
|
|
62
|
+
win->document->setCanvasInfo(node, (float)raw->width(), (float)raw->height(), raw->id());
|
|
63
|
+
}
|
|
64
|
+
return raw;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void xt_gui_webgl_forget(XtGuiWindow *win, const xtgui::Node *node) {
|
|
68
|
+
if (win == nullptr || node == nullptr) return;
|
|
69
|
+
win->webgl_canvases.erase(node);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void xt_gui_webgl_shutdown(XtGuiWindow *win) {
|
|
73
|
+
if (win == nullptr) return;
|
|
74
|
+
win->webgl_canvases.clear();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
void xt_gui_webgl_submit_all() {
|
|
78
|
+
for (int i = 0; i < XT_GUI_MAX_WINDOWS; i++) {
|
|
79
|
+
XtGuiWindow &win = guidev::g_windows[i];
|
|
80
|
+
if (!win.open) continue;
|
|
81
|
+
for (auto &entry : win.webgl_canvases) entry.second->submit();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `ImageBitmap` pixel storage.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include "image_bitmap.h"
|
|
6
|
+
|
|
7
|
+
#include <unordered_map>
|
|
8
|
+
|
|
9
|
+
namespace xtgui {
|
|
10
|
+
namespace {
|
|
11
|
+
|
|
12
|
+
std::unordered_map<uint64_t, Image> g_bitmaps;
|
|
13
|
+
uint64_t g_next_bitmap = 1;
|
|
14
|
+
|
|
15
|
+
} // namespace
|
|
16
|
+
|
|
17
|
+
uint64_t xt_image_bitmap_add(const Image &image) {
|
|
18
|
+
const uint64_t id = g_next_bitmap++;
|
|
19
|
+
g_bitmaps[id] = image;
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const Image *xt_image_bitmap_get(uint64_t id) {
|
|
24
|
+
auto found = g_bitmaps.find(id);
|
|
25
|
+
return found == g_bitmaps.end() ? nullptr : &found->second;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
} // namespace xtgui
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `ImageBitmap` storage.
|
|
3
|
+
*
|
|
4
|
+
* `createImageBitmap(source)` returns a settled promise of an immutable
|
|
5
|
+
* bitmap. The pixels stay in C++ so `texImage2D` can consume them without
|
|
6
|
+
* materialising a JavaScript array; the handle object carries the numeric id
|
|
7
|
+
* under `__xt_bitmap`.
|
|
8
|
+
*/
|
|
9
|
+
#ifndef XT_GUI_IMAGE_BITMAP_H
|
|
10
|
+
#define XT_GUI_IMAGE_BITMAP_H
|
|
11
|
+
|
|
12
|
+
#include <cstdint>
|
|
13
|
+
|
|
14
|
+
#include "image.h"
|
|
15
|
+
|
|
16
|
+
namespace xtgui {
|
|
17
|
+
|
|
18
|
+
/** Store a decoded bitmap and return its process-wide id. */
|
|
19
|
+
uint64_t xt_image_bitmap_add(const Image &image);
|
|
20
|
+
|
|
21
|
+
/** Look up a bitmap; null when the id is unknown. */
|
|
22
|
+
const Image *xt_image_bitmap_get(uint64_t id);
|
|
23
|
+
|
|
24
|
+
} // namespace xtgui
|
|
25
|
+
|
|
26
|
+
#endif /* XT_GUI_IMAGE_BITMAP_H */
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `createImageBitmap`.
|
|
3
|
+
*
|
|
4
|
+
* The browser entry point returns a promise (as it does on the web), so code
|
|
5
|
+
* that writes `const bitmap = await createImageBitmap(canvas)` works unchanged.
|
|
6
|
+
* The pixels are kept in `image_bitmap.cpp`; `texImage2D` reads the same
|
|
7
|
+
* registry through the bitmap's `__xt_bitmap` id.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "gui_engine.h"
|
|
11
|
+
#include "dom_api_internal.h"
|
|
12
|
+
|
|
13
|
+
#include <cstring>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <vector>
|
|
16
|
+
|
|
17
|
+
#include "image_bitmap.h"
|
|
18
|
+
#include "webgl.h"
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
void flip_rows(std::vector<unsigned char> *pixels, int width, int height) {
|
|
23
|
+
const size_t stride = (size_t)width * 4;
|
|
24
|
+
std::vector<unsigned char> row(stride);
|
|
25
|
+
for (int y = 0; y < height / 2; y++) {
|
|
26
|
+
unsigned char *top = pixels->data() + (size_t)y * stride;
|
|
27
|
+
unsigned char *bottom = pixels->data() + (size_t)(height - 1 - y) * stride;
|
|
28
|
+
memcpy(row.data(), top, stride);
|
|
29
|
+
memcpy(top, bottom, stride);
|
|
30
|
+
memcpy(bottom, row.data(), stride);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
bool source_pixels(xt_value source, xtgui::Image *out) {
|
|
35
|
+
/* ImageData / another bitmap-like object: width, height, data. */
|
|
36
|
+
xt_value width_value = xt_object_get_cstr(source, "width");
|
|
37
|
+
xt_value height_value = xt_object_get_cstr(source, "height");
|
|
38
|
+
xt_value data = xt_object_get_cstr(source, "data");
|
|
39
|
+
if (XT_IS_OBJECT(data) && XT_IS_NUMBER(width_value) && XT_IS_NUMBER(height_value)) {
|
|
40
|
+
const int width = (int)xt_to_number(width_value);
|
|
41
|
+
const int height = (int)xt_to_number(height_value);
|
|
42
|
+
const std::vector<uint8_t> bytes = domapi::webgl_bytes(data);
|
|
43
|
+
if (width <= 0 || height <= 0 || bytes.size() < (size_t)width * height * 4) return false;
|
|
44
|
+
out->width = width;
|
|
45
|
+
out->height = height;
|
|
46
|
+
out->pixels.assign(bytes.begin(), bytes.begin() + (size_t)width * height * 4);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
XtGuiWindow *win = nullptr;
|
|
51
|
+
xtgui::Node *node = domapi::resolve_node(source, &win);
|
|
52
|
+
if (node == nullptr || !node->isElement()) return false;
|
|
53
|
+
if (node->isTag("img")) {
|
|
54
|
+
const std::string *src = node->attr("src");
|
|
55
|
+
const xtgui::Image *image = src != nullptr ? xtgui::xt_image_load(*src) : nullptr;
|
|
56
|
+
if (image == nullptr) return false;
|
|
57
|
+
*out = *image;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (node->isTag("canvas")) {
|
|
61
|
+
if (xtgui::Canvas2D *canvas = xt_gui_canvas_find(win, node)) {
|
|
62
|
+
*out = canvas->image();
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (xtgui::WebGLContext *context = xt_gui_webgl_find(win, node)) {
|
|
66
|
+
out->width = context->width();
|
|
67
|
+
out->height = context->height();
|
|
68
|
+
out->pixels.resize((size_t)out->width * out->height * 4);
|
|
69
|
+
if (!context->read_pixels(0, 0, out->width, out->height, out->pixels.data())) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/* readPixels is GL-ordered (bottom-up); bitmaps are stored top-down. */
|
|
73
|
+
flip_rows(&out->pixels, out->width, out->height);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (node->isTag("video")) {
|
|
79
|
+
xtgui::MediaPlayer *player = xt_gui_media_player(win, node);
|
|
80
|
+
const xtgui::Image *frame = player != nullptr ? player->frame() : nullptr;
|
|
81
|
+
if (frame == nullptr || frame->width <= 0 || frame->height <= 0) return false;
|
|
82
|
+
*out = *frame;
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
xt_value bitmap_object(const xtgui::Image &image) {
|
|
89
|
+
const uint64_t id = xtgui::xt_image_bitmap_add(image);
|
|
90
|
+
xt_value name = xt_string_from_cstr("ImageBitmap");
|
|
91
|
+
xt_value constructor = xt_browser_type(1, &name);
|
|
92
|
+
xt_value prototype = xt_object_get_cstr(constructor, "prototype");
|
|
93
|
+
xt_value bitmap = XT_IS_OBJECT(prototype) ? xt_object_new_with_proto(prototype)
|
|
94
|
+
: xt_object_new();
|
|
95
|
+
xt_object_set(bitmap, xt_string_from_cstr("width"), xt_number(image.width));
|
|
96
|
+
xt_object_set(bitmap, xt_string_from_cstr("height"), xt_number(image.height));
|
|
97
|
+
xt_object_set(bitmap, xt_string_from_cstr("__xt_bitmap"), xt_number((double)id));
|
|
98
|
+
return bitmap;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
} // namespace
|
|
102
|
+
|
|
103
|
+
namespace {
|
|
104
|
+
|
|
105
|
+
xt_value create_bitmap(int32_t argc, xt_value *argv) {
|
|
106
|
+
if (argc < 1) return xt_promise_reject(xt_string_from_cstr("TypeError"));
|
|
107
|
+
xtgui::Image image;
|
|
108
|
+
if (!source_pixels(xt_arg(argc, argv, 0), &image)) {
|
|
109
|
+
return xt_promise_reject(xt_string_from_cstr("InvalidStateError"));
|
|
110
|
+
}
|
|
111
|
+
return xt_promise_resolve(bitmap_object(image));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The closure ABI the runtime calls for a first-class function value. */
|
|
115
|
+
xt_value create_bitmap_call(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
116
|
+
(void)self;
|
|
117
|
+
(void)env;
|
|
118
|
+
return create_bitmap(argc, argv);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
xt_value g_create_bitmap = XT_UNDEFINED;
|
|
122
|
+
|
|
123
|
+
} // namespace
|
|
124
|
+
|
|
125
|
+
/* `createImageBitmap` is a *global value*: the compiler calls this getter once
|
|
126
|
+
* and stores the returned closure, which is then called like any function. */
|
|
127
|
+
xt_value xt_browser_create_image_bitmap(int32_t argc, xt_value *argv) {
|
|
128
|
+
(void)argc;
|
|
129
|
+
(void)argv;
|
|
130
|
+
if (!XT_IS_OBJECT(g_create_bitmap)) {
|
|
131
|
+
g_create_bitmap = xt_closure_new((void *)create_bitmap_call, 0, nullptr);
|
|
132
|
+
xt_gc_add_root(&g_create_bitmap);
|
|
133
|
+
}
|
|
134
|
+
return g_create_bitmap;
|
|
135
|
+
}
|
|
136
|
+
|
|
@@ -123,6 +123,21 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
123
123
|
if (box->intrinsic_height <= 0.0f) box->intrinsic_height = 150.0f;
|
|
124
124
|
}
|
|
125
125
|
if (box->is_inline_svg) return box;
|
|
126
|
+
|
|
127
|
+
/* `::before` / `::after` boxes are emitted around the DOM children. Only a
|
|
128
|
+
* pseudo-element with `content` generates a box (CSS: a `::before` rule with
|
|
129
|
+
* no content has no effect). Replaced elements never generate them. */
|
|
130
|
+
auto appendPseudo = [&](Node *pseudo) {
|
|
131
|
+
if (pseudo == nullptr || box->is_image || box->is_canvas || box->is_media) return;
|
|
132
|
+
auto pseudoStyle = styles.find(pseudo);
|
|
133
|
+
if (pseudoStyle == styles.end() || !pseudoStyle->second.has_content) return;
|
|
134
|
+
std::unique_ptr<LayoutBox> pseudoBox = buildBoxTree(pseudo, styles, style, media, canvases);
|
|
135
|
+
if (pseudoBox != nullptr) {
|
|
136
|
+
pseudoBox->parent = box.get();
|
|
137
|
+
box->children.push_back(std::move(pseudoBox));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
if (node->isElement() && !node->is_pseudo) appendPseudo(node->pseudo_before);
|
|
126
141
|
for (const std::unique_ptr<Node> &child : node->children) {
|
|
127
142
|
std::unique_ptr<LayoutBox> childBox =
|
|
128
143
|
buildBoxTree(child.get(), styles, style, media, canvases);
|
|
@@ -131,6 +146,7 @@ std::unique_ptr<LayoutBox> buildBoxTree(const Node *node,
|
|
|
131
146
|
box->children.push_back(std::move(childBox));
|
|
132
147
|
}
|
|
133
148
|
}
|
|
149
|
+
if (node->isElement() && !node->is_pseudo) appendPseudo(node->pseudo_after);
|
|
134
150
|
return box;
|
|
135
151
|
}
|
|
136
152
|
|
|
@@ -211,15 +227,20 @@ namespace {
|
|
|
211
227
|
|
|
212
228
|
const LayoutBox *hitTestBox(const LayoutBox *box, float x, float y) {
|
|
213
229
|
if (box == nullptr || box->display == Display::None) return nullptr;
|
|
230
|
+
if (box->style != nullptr && !box->style->visible) return nullptr;
|
|
214
231
|
/* Later siblings paint on top, so probe them first. */
|
|
215
232
|
for (auto it = box->children.rbegin(); it != box->children.rend(); ++it) {
|
|
216
233
|
if (const LayoutBox *hit = hitTestBox(it->get(), x, y)) return hit;
|
|
217
234
|
}
|
|
235
|
+
if (box->style != nullptr && !box->style->pointer_events) return nullptr;
|
|
218
236
|
float local_x = x;
|
|
219
237
|
float local_y = y;
|
|
220
238
|
if (box->has_transform) box->inverse.apply(x, y, &local_x, &local_y);
|
|
221
239
|
if (local_x >= box->x && local_x < box->x + box->width && local_y >= box->y &&
|
|
222
240
|
local_y < box->y + box->height) {
|
|
241
|
+
/* A generated box is part of its originating element: events must target
|
|
242
|
+
* the real element handle, not the synthetic node. */
|
|
243
|
+
if (box->node != nullptr && box->node->is_pseudo && box->parent != nullptr) return box->parent;
|
|
223
244
|
return box;
|
|
224
245
|
}
|
|
225
246
|
return nullptr;
|