xbintsc 0.5.32 → 0.5.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +10 -7
  2. package/README.zh-CN.md +10 -8
  3. package/dist/scripts/check-version.d.ts +27 -0
  4. package/dist/scripts/check-version.js +71 -0
  5. package/dist/scripts/check-version.js.map +1 -0
  6. package/dist/scripts/package-platform.d.ts +58 -0
  7. package/dist/scripts/package-platform.js +142 -0
  8. package/dist/scripts/package-platform.js.map +1 -0
  9. package/dist/scripts/verify-platform-package.d.ts +34 -0
  10. package/dist/scripts/verify-platform-package.js +161 -0
  11. package/dist/scripts/verify-platform-package.js.map +1 -0
  12. package/dist/src/cli/help.d.ts +1 -1
  13. package/dist/src/cli/main.js +3 -1
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/runtime-lib.d.ts +29 -4
  16. package/dist/src/driver/runtime-lib.js +68 -10
  17. package/dist/src/driver/runtime-lib.js.map +1 -1
  18. package/dist/src/driver/version.d.ts +1 -1
  19. package/dist/src/driver/version.js +1 -1
  20. package/dist/src/extensions/gui/index.js +18 -5
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/media/index.js +4 -3
  23. package/dist/src/extensions/media/index.js.map +1 -1
  24. package/dist/src/parser/context.d.ts +41 -0
  25. package/dist/src/parser/context.js +89 -4
  26. package/dist/src/parser/context.js.map +1 -1
  27. package/dist/src/parser/literals.js +6 -12
  28. package/dist/src/parser/literals.js.map +1 -1
  29. package/dist/tests/driver/platform-package.test.d.ts +1 -0
  30. package/dist/tests/driver/platform-package.test.js +49 -0
  31. package/dist/tests/driver/platform-package.test.js.map +1 -0
  32. package/dist/tests/driver/runtime-lib.test.js +51 -4
  33. package/dist/tests/driver/runtime-lib.test.js.map +1 -1
  34. package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
  35. package/dist/tests/e2e/gui-audio.test.js +287 -0
  36. package/dist/tests/e2e/gui-audio.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-browser.test.js +65 -0
  38. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  39. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  40. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  41. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  42. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-dom.test.js +215 -0
  44. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  45. package/dist/tests/e2e/gui-events.test.js +112 -0
  46. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  47. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  48. package/dist/tests/e2e/gui-helpers.js +1 -0
  49. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  50. package/dist/tests/e2e/gui-media.test.js +78 -0
  51. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  52. package/dist/tests/e2e/gui-style.test.js +55 -0
  53. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  54. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  55. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  56. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  57. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  58. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  59. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  60. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  61. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  62. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  63. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  64. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  66. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  67. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  69. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  70. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  71. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  72. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  73. package/dist/tests/extensions/gui-index.test.js +1 -0
  74. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  75. package/dist/tests/extensions/media.test.js +1 -0
  76. package/dist/tests/extensions/media.test.js.map +1 -1
  77. package/dist/tests/parser/expressions.test.js +30 -0
  78. package/dist/tests/parser/expressions.test.js.map +1 -1
  79. package/dist/tests/platform-tarball.test.d.ts +1 -0
  80. package/dist/tests/platform-tarball.test.js +111 -0
  81. package/dist/tests/platform-tarball.test.js.map +1 -0
  82. package/dist/tests/version.test.js +6 -0
  83. package/dist/tests/version.test.js.map +1 -1
  84. package/doc/ai/contributing.md +11 -0
  85. package/doc/ai/extensions.md +7 -4
  86. package/doc/ai/troubleshooting.md +7 -5
  87. package/doc/ai/zh-CN/contributing.md +9 -0
  88. package/doc/ai/zh-CN/extensions.md +4 -2
  89. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  90. package/doc/gui/api.md +99 -22
  91. package/doc/gui/build.md +11 -4
  92. package/doc/gui/canvas.md +49 -18
  93. package/doc/gui/dom.md +57 -7
  94. package/doc/gui/media.md +4 -0
  95. package/doc/gui/webgl.md +105 -46
  96. package/doc/gui.md +7 -3
  97. package/doc/npm-package.md +55 -27
  98. package/doc/requirements.md +6 -2
  99. package/doc/zh-CN/gui/api.md +84 -14
  100. package/doc/zh-CN/gui/build.md +7 -2
  101. package/doc/zh-CN/gui/canvas.md +44 -19
  102. package/doc/zh-CN/gui/dom.md +48 -4
  103. package/doc/zh-CN/gui/media.md +3 -0
  104. package/doc/zh-CN/gui/webgl.md +22 -16
  105. package/doc/zh-CN/gui.md +3 -2
  106. package/doc/zh-CN/npm-package.md +44 -23
  107. package/doc/zh-CN/requirements.md +4 -1
  108. package/package.json +17 -1
  109. package/runtime/ext_gui/audio.cpp +365 -153
  110. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  111. package/runtime/ext_gui/audio_buffer.h +37 -0
  112. package/runtime/ext_gui/audio_decode.cpp +219 -0
  113. package/runtime/ext_gui/audio_decode.h +39 -0
  114. package/runtime/ext_gui/audio_device.cpp +87 -0
  115. package/runtime/ext_gui/audio_effects.cpp +163 -0
  116. package/runtime/ext_gui/audio_effects.h +25 -0
  117. package/runtime/ext_gui/audio_internal.h +30 -0
  118. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  119. package/runtime/ext_gui/audio_mixer.h +238 -0
  120. package/runtime/ext_gui/audio_param.cpp +104 -0
  121. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  122. package/runtime/ext_gui/browser.cpp +45 -50
  123. package/runtime/ext_gui/browser_abort.cpp +249 -0
  124. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  125. package/runtime/ext_gui/browser_media.cpp +176 -0
  126. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  127. package/runtime/ext_gui/canvas.cpp +88 -30
  128. package/runtime/ext_gui/canvas.h +74 -9
  129. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  130. package/runtime/ext_gui/canvas_internal.h +8 -5
  131. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  132. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  133. package/runtime/ext_gui/canvas_style.cpp +4 -2
  134. package/runtime/ext_gui/css_selector.cpp +21 -3
  135. package/runtime/ext_gui/dom.h +4 -0
  136. package/runtime/ext_gui/dom_api.cpp +1 -0
  137. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  138. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  139. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  140. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  141. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  142. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  143. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  144. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  145. package/runtime/ext_gui/dom_api_internal.h +71 -2
  146. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  147. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  148. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  149. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  150. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  151. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  152. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  153. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  154. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  155. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  156. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  157. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  158. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  159. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  160. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  161. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  162. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  163. package/runtime/ext_gui/gui.cpp +103 -14
  164. package/runtime/ext_gui/gui_engine.h +56 -4
  165. package/runtime/ext_gui/gui_events.cpp +80 -9
  166. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  167. package/runtime/ext_gui/image_bitmap.h +26 -0
  168. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  169. package/runtime/ext_gui/listener_options.h +43 -0
  170. package/runtime/ext_gui/media.cpp +32 -0
  171. package/runtime/ext_gui/media.h +13 -0
  172. package/runtime/ext_gui/media_api.h +23 -0
  173. package/runtime/ext_gui/paint.cpp +28 -0
  174. package/runtime/ext_gui/paint.h +4 -0
  175. package/runtime/ext_gui/webgl.h +152 -18
  176. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  177. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  178. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  179. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  180. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  181. package/runtime/ext_gui/webgl_context.cpp +241 -156
  182. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  183. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  184. package/runtime/ext_gui/webgl_glsl.h +40 -0
  185. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  186. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  187. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  188. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  189. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  190. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  191. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  192. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  193. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  194. package/runtime/ext_gui/webgl_state.cpp +228 -0
  195. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  196. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  197. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  198. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  199. package/runtime/ext_gui/window.cpp +14 -65
  200. package/runtime/ext_gui/window_events.cpp +125 -0
  201. package/runtime/ext_media/decode_audio.cpp +308 -0
  202. package/runtime/ext_media/media_bridge.c +2 -0
  203. package/runtime/rt.h +43 -0
  204. package/runtime/rt_internal.h +4 -0
  205. package/runtime/xt_array_buffer.c +30 -0
  206. package/runtime/xt_builtins.c +0 -21
  207. package/runtime/xt_containers/access.inc +94 -2
  208. package/runtime/xt_containers/objects.inc +29 -31
  209. package/runtime/xt_typed_array/elements.inc +13 -0
  210. package/scripts/build-media.ts +14 -6
  211. package/scripts/build-runtime.ts +21 -16
  212. package/scripts/check-version.ts +39 -9
  213. package/scripts/package-platform.ts +183 -0
  214. package/scripts/set-platform-version.ts +62 -0
  215. package/scripts/verify-platform-package.ts +182 -0
  216. package/src/cli/main.ts +5 -1
  217. package/src/driver/runtime-lib.ts +73 -9
  218. package/src/driver/version.ts +1 -1
  219. package/src/extensions/gui/index.ts +18 -5
  220. package/src/extensions/media/index.ts +4 -3
  221. package/src/parser/context.ts +99 -4
  222. package/src/parser/literals.ts +6 -12
@@ -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 {
@@ -89,20 +90,49 @@ static void xt_gui_gc_scan_roots(void) {
89
90
  xt_gc_mark_value(win.document_object);
90
91
  for (const auto &entry : win.node_handles) xt_gc_mark_value(entry.second);
91
92
  for (const auto &entry : win.node_listeners) {
92
- for (const XtGuiNodeListener &listener : entry.second) xt_gc_mark_value(listener.fn);
93
+ for (const XtGuiNodeListener &listener : entry.second) {
94
+ xt_gc_mark_value(listener.fn);
95
+ xt_gc_mark_value(listener.signal);
96
+ }
97
+ }
98
+ for (const XtGuiWindowListener &listener : win.window_listeners) {
99
+ xt_gc_mark_value(listener.fn);
100
+ xt_gc_mark_value(listener.signal);
93
101
  }
94
102
  for (const XtGuiAnimationFrame &frame : win.animation_frames) xt_gc_mark_value(frame.fn);
95
103
  for (const XtGuiAnimationFrame &frame : win.running_frames) xt_gc_mark_value(frame.fn);
104
+ for (const xt_value list : win.media_lists) xt_gc_mark_value(list);
96
105
  }
97
106
  }
98
107
 
99
108
  /* -- initialisation ------------------------------------------------------- */
100
109
 
101
- static int ensure_init(void) { if (g_sdl_initialized) return 1;
102
- xt_gc_register_root_provider(xt_gui_gc_scan_roots);
110
+ /**
111
+ * Exit status for "this host cannot run a GUI program": no video driver, or no
112
+ * GPU device the SDL_GPU backends accept. It is deliberately distinct from a
113
+ * failed program so a CI script (or a user) can skip the run instead of
114
+ * reporting a crash. 77 is the autotools "skip" convention; `ci.yml` treats it
115
+ * that way for the provisional macOS job.
116
+ */
117
+ const int kGuiUnavailableExit = 77;
118
+
119
+ /** Report a window that could not be created as a JavaScript `Error`. Returning
120
+ * `undefined` used to surface one line later as `Cannot read properties of
121
+ * undefined (reading 'on')`, which hid the real diagnostic. */
122
+ static void throw_gui_error(const char *message) {
123
+ xt_value text = xt_string_from_cstr(message);
124
+ xt_throw(xt_error_ctor_by_name(xt_string_from_cstr("Error"), 1, &text));
125
+ }
126
+
127
+ static void ensure_init(void) {
128
+ if (g_sdl_initialized) return;
103
129
  if (!SDL_Init(SDL_INIT_VIDEO)) {
104
130
  fprintf(stderr, "xt_gui: SDL_Init failed: %s\n", SDL_GetError());
105
- return 0;
131
+ fprintf(stderr,
132
+ "xt_gui: the GUI extension needs a display (or a virtual one such as Xvfb); "
133
+ "exiting with status %d.\n",
134
+ kGuiUnavailableExit);
135
+ exit(kGuiUnavailableExit);
106
136
  }
107
137
  /* Accept every shader format so the same code runs on Metal, Vulkan and
108
138
  * D3D12; the backend only needs the one it supports. The engine ships MSL,
@@ -113,11 +143,17 @@ static int ensure_init(void) { if (g_sdl_initialized) return 1;
113
143
  g_device = SDL_CreateGPUDevice(formats, false, NULL);
114
144
  if (g_device == NULL) {
115
145
  fprintf(stderr, "xt_gui: SDL_CreateGPUDevice failed: %s\n", SDL_GetError());
146
+ fprintf(stderr,
147
+ "xt_gui: the GUI extension needs a Metal (macOS), Vulkan or D3D12 device; "
148
+ "this host does not provide a usable one. Exiting with status %d.\n",
149
+ kGuiUnavailableExit);
116
150
  SDL_Quit();
117
- return 0;
151
+ exit(kGuiUnavailableExit);
118
152
  }
153
+ /* Registered only once a window can exist: a failed init has nothing to
154
+ * mark, and a caller that cannot retry never needs a second provider. */
155
+ xt_gc_register_root_provider(xt_gui_gc_scan_roots);
119
156
  g_sdl_initialized = 1;
120
- return 1;
121
157
  }
122
158
 
123
159
  /* -- window lookup -------------------------------------------------------- */
@@ -149,6 +185,9 @@ void xt_gui_quit_window(XtGuiWindow *win) {
149
185
  win->open = 0;
150
186
  xt_gui_emit(win, "close");
151
187
  win->animation_frames.clear();
188
+ win->media_lists.clear();
189
+ win->media_lists_by_query.clear();
190
+ win->window_listeners.clear();
152
191
  xt_gui_media_shutdown(win);
153
192
  xt_gui_canvas_shutdown(win);
154
193
  xt_gui_handles_reset(win);
@@ -190,12 +229,12 @@ static int xt_gui_int_or(xt_value value, int fallback) {
190
229
  /* Build (and record) one window. Shared by `createWindow` and the implicit
191
230
  * window a page written against `window`/`document` gets on first access. */
192
231
  static XtGuiWindow *create_window_record(const char *title, int width, int height,
193
- bool high_density) {
232
+ bool high_density, SDL_WindowFlags extra_flags) {
194
233
  if (g_window_count >= XT_GUI_MAX_WINDOWS) {
195
234
  fprintf(stderr, "xt_gui: too many windows (max %d)\n", XT_GUI_MAX_WINDOWS);
196
235
  return NULL;
197
236
  }
198
- SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE;
237
+ SDL_WindowFlags flags = extra_flags;
199
238
  if (high_density) flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
200
239
  SDL_Window *window = SDL_CreateWindow(title, width, height, flags);
201
240
  if (window == NULL) {
@@ -225,6 +264,11 @@ static XtGuiWindow *create_window_record(const char *title, int width, int heigh
225
264
  record->animation_frames.clear();
226
265
  record->media_players.clear();
227
266
  record->next_animation_frame_id = 1;
267
+ record->scroll_x = 0.0f;
268
+ record->scroll_y = 0.0f;
269
+ record->focused = 0;
270
+ record->media_lists.clear();
271
+ record->media_lists_by_query.clear();
228
272
  record->background[0] = 0.08f;
229
273
  record->background[1] = 0.09f;
230
274
  record->background[2] = 0.11f;
@@ -254,20 +298,43 @@ xt_value xt_gui_ensure_window(void) {
254
298
  for (int i = g_window_count - 1; i >= 0; i--) {
255
299
  if (g_windows[i].open) return g_windows[i].object;
256
300
  }
257
- if (!ensure_init()) return XT_UNDEFINED;
258
- XtGuiWindow *record = create_window_record("xbintsc gui", 800, 600, true);
259
- if (record == NULL) return XT_UNDEFINED;
301
+ ensure_init();
302
+ XtGuiWindow *record =
303
+ create_window_record("xbintsc gui", 800, 600, true, SDL_WINDOW_RESIZABLE);
304
+ if (record == NULL) {
305
+ throw_gui_error("xbintsc gui: could not open the implicit window (see the diagnostic above)");
306
+ return XT_UNDEFINED;
307
+ }
260
308
  g_implicit_window = 1;
261
309
  return record->object;
262
310
  }
263
311
 
264
312
  extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
265
- if (!ensure_init()) return XT_UNDEFINED;
313
+ ensure_init();
266
314
 
267
315
  const char *title = "xbintsc gui";
268
316
  int width = 800;
269
317
  int height = 600;
270
318
  bool high_density = true;
319
+ SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE;
320
+ bool has_x = false;
321
+ bool has_y = false;
322
+ bool has_opacity = false;
323
+ int x = 0;
324
+ int y = 0;
325
+ float opacity = 1.0f;
326
+ bool maximized = false;
327
+ bool minimized = false;
328
+ auto option_bool = [](xt_value options, const char *name, bool fallback) {
329
+ xt_value value = xt_object_get_cstr(options, name);
330
+ return XT_IS_BOOL(value) ? (xt_truthy(value) != 0) : fallback;
331
+ };
332
+ auto option_number = [](xt_value options, const char *name, bool *present, double fallback) {
333
+ xt_value value = xt_object_get_cstr(options, name);
334
+ if (!XT_IS_NUMBER(value)) return fallback;
335
+ *present = true;
336
+ return xt_to_number(value);
337
+ };
271
338
  xt_value options = xt_arg(argc, argv, 0);
272
339
  if (XT_IS_OBJECT(options)) {
273
340
  title = xt_gui_string_or(xt_object_get_cstr(options, "title"), title);
@@ -275,6 +342,19 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
275
342
  height = xt_gui_int_or(xt_object_get_cstr(options, "height"), height);
276
343
  xt_value density = xt_object_get_cstr(options, "highDensity");
277
344
  if (!XT_IS_UNDEFINED(density)) high_density = xt_truthy(density);
345
+ /* Window flags and placement: the rest of the options object used to be
346
+ * ignored, which made `{ resizable: false }` or `{ borderless: true }`
347
+ * silently open a plain window. */
348
+ if (!option_bool(options, "resizable", true)) flags &= ~SDL_WINDOW_RESIZABLE;
349
+ if (option_bool(options, "borderless", false)) flags |= SDL_WINDOW_BORDERLESS;
350
+ if (option_bool(options, "alwaysOnTop", false)) flags |= SDL_WINDOW_ALWAYS_ON_TOP;
351
+ if (option_bool(options, "hidden", false)) flags |= SDL_WINDOW_HIDDEN;
352
+ if (option_bool(options, "fullscreen", false)) flags |= SDL_WINDOW_FULLSCREEN;
353
+ x = (int)option_number(options, "x", &has_x, 0);
354
+ y = (int)option_number(options, "y", &has_y, 0);
355
+ opacity = (float)option_number(options, "opacity", &has_opacity, 1.0);
356
+ maximized = option_bool(options, "maximized", false);
357
+ minimized = option_bool(options, "minimized", false);
278
358
  } else if (XT_IS_STRING(options)) {
279
359
  title = xt_string_data(options);
280
360
  width = xt_gui_int_or(xt_arg(argc, argv, 1), width);
@@ -303,8 +383,17 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
303
383
  return record->object;
304
384
  }
305
385
 
306
- XtGuiWindow *record = create_window_record(title, width, height, high_density);
307
- return record != NULL ? record->object : XT_UNDEFINED;
386
+ XtGuiWindow *record = create_window_record(title, width, height, high_density, flags);
387
+ if (record == NULL) {
388
+ throw_gui_error("xbintsc gui: could not open a window (see the diagnostic above)");
389
+ return XT_UNDEFINED;
390
+ }
391
+ if (has_x || has_y) SDL_SetWindowPosition(record->window, has_x ? x : SDL_WINDOWPOS_CENTERED,
392
+ has_y ? y : SDL_WINDOWPOS_CENTERED);
393
+ if (has_opacity) SDL_SetWindowOpacity(record->window, opacity);
394
+ if (maximized) SDL_MaximizeWindow(record->window);
395
+ if (minimized) SDL_MinimizeWindow(record->window);
396
+ return record->object;
308
397
  }
309
398
 
310
399
  /* -- run() / quit() ------------------------------------------------------- */
@@ -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);
@@ -52,6 +55,8 @@ xt_value xt_browser_computed_style(int32_t argc, xt_value *argv);
52
55
  xt_value xt_browser_escape(int32_t argc, xt_value *argv);
53
56
  xt_value xt_browser_alert(int32_t argc, xt_value *argv);
54
57
  xt_value xt_browser_prompt(int32_t argc, xt_value *argv);
58
+ /** The bare `confirm(message)` global (the window method shares the engine). */
59
+ xt_value xt_browser_confirm_global(int32_t argc, xt_value *argv);
55
60
  /** Install `innerWidth`, `setTimeout`, `navigator`, … on the window prototype. */
56
61
  void xt_browser_install_window(xt_value proto);
57
62
  /** Poll and dispatch pending SDL input events once (the browser frame pump in
@@ -63,11 +68,13 @@ void xt_browser_schedule_frame(void);
63
68
  /** Whether `run()` currently owns the frame loop (the pump then stays idle). */
64
69
  int xt_gui_run_active(void);
65
70
 
66
- /* Web Audio objects (`audio.cpp`). */
67
- /** `AudioContext.prototype` — also the `instanceof AudioContext` target. */
71
+ /* Web Audio objects (`audio.cpp`).
72
+ * `AudioContext.prototype` — also the `instanceof AudioContext` target. */
68
73
  xt_value xt_gui_audio_proto(void);
69
- /** `new AudioContext(...)`: it has no mixer backend yet, so the graph records
70
- * its shape and parameters but nothing is rendered to the sound device. */
74
+ /** `new AudioContext(...)`: builds a real graph (mixer in `audio_mixer.cpp`)
75
+ * and decodes through `audio_decode.cpp` + the optional media backend. The
76
+ * device opens on the first `start()`/`resume()`; a headless host stays silent
77
+ * while the graph still renders. */
71
78
  xt_value xt_gui_audio_create(int32_t argc, xt_value *argv);
72
79
 
73
80
  #ifdef __cplusplus
@@ -92,6 +99,22 @@ struct XtGuiNodeListener {
92
99
  xt_value fn;
93
100
  bool once;
94
101
  bool capture;
102
+ /** `{ passive: true }`: `preventDefault()` is ignored while it runs. */
103
+ bool passive;
104
+ /** `{ signal }`: the listener is skipped once the signal is aborted. */
105
+ xt_value signal;
106
+ };
107
+
108
+ /** One window-level listener (`window.addEventListener`). Window delivery has
109
+ * no capture phase, but `capture` is still part of listener identity, as in the
110
+ * DOM; `once`, `passive` and `signal` behave like the element ones. */
111
+ struct XtGuiWindowListener {
112
+ std::string type;
113
+ xt_value fn;
114
+ bool once;
115
+ bool capture;
116
+ bool passive;
117
+ xt_value signal;
95
118
  };
96
119
 
97
120
  /** One pending `requestAnimationFrame` callback. */
@@ -137,6 +160,14 @@ struct XtGuiWindow {
137
160
  float background[4];
138
161
  int width;
139
162
  int height;
163
+ /** Page scroll offset (CSS pixels); painting and hit testing subtract it. */
164
+ float scroll_x = 0;
165
+ float scroll_y = 0;
166
+ /** `document.hasFocus()` state, toggled by `window.focus()` / `blur()`. */
167
+ int focused = 0;
168
+ /** Live `matchMedia` lists for this window (one per query string). */
169
+ std::vector<xt_value> media_lists;
170
+ std::unordered_map<std::string, xt_value> media_lists_by_query;
140
171
 
141
172
  /* -- DOM object model (element handles + events) ----------------------- */
142
173
  /** `document` handle for this window (lazily created). */
@@ -151,6 +182,8 @@ struct XtGuiWindow {
151
182
  std::unordered_map<const xtgui::Node *, xt_value> node_handles;
152
183
  /** Element-level listeners, keyed by node. */
153
184
  std::unordered_map<const xtgui::Node *, std::vector<XtGuiNodeListener>> node_listeners;
185
+ /** Window-level listeners registered through `window.addEventListener`. */
186
+ std::vector<XtGuiWindowListener> window_listeners;
154
187
  /** Set by a DOM mutation; consumed by the frame loop before repainting. */
155
188
  int struct_dirty = 0;
156
189
  /** Callbacks queued for the next frame, plus the id counter. */
@@ -208,6 +241,9 @@ void xt_gui_run_scripts(XtGuiWindow *win);
208
241
  void xt_gui_script_gc_scan(void);
209
242
  /** Shared prototype carrying the window methods. */
210
243
  xt_value xt_gui_window_proto(void);
244
+ /** Install `on` / `off` / `addEventListener` / `removeEventListener` (the
245
+ * listener options bag is parsed in `window_events.cpp`). */
246
+ void xt_gui_install_window_events(xt_value proto);
211
247
  /** Release GPU claim + destroy the window and mark the record closed. */
212
248
  void xt_gui_quit_window(XtGuiWindow *win);
213
249
  /** Deliver `event` (no arguments) to handlers registered with `on`. */
@@ -221,6 +257,22 @@ void xt_gui_render_window(XtGuiWindow *win);
221
257
  void xt_gui_run_animation_frames(XtGuiWindow *win, double timestamp_ms);
222
258
  /** Current logical window size (CSS viewport), in pixels. */
223
259
  void xt_gui_window_viewport(XtGuiWindow *win, float *width, float *height);
260
+ /** Scroll the page to `(x, y)` (clamped to the content bounds), repaint and
261
+ * notify live `matchMedia` lists. */
262
+ void xt_gui_scroll_to(XtGuiWindow *win, float x, float y);
263
+ /** The largest valid scroll offset for the current layout. */
264
+ void xt_gui_scroll_bounds(XtGuiWindow *win, float *max_x, float *max_y);
265
+ /** Re-evaluate every live `matchMedia` list (window resize / scroll). */
266
+ void xt_gui_media_lists_notify(XtGuiWindow *win);
267
+ /** `window.matchMedia(query)`: the cached live list for this window. */
268
+ xt_value xt_browser_media_list(xt_value window, const std::string &query);
269
+ /** `window.confirm` / `window.prompt` (native dialog only when
270
+ * `XT_GUI_DIALOGS=1`; otherwise the message goes to stderr and the documented
271
+ * default is returned). */
272
+ xt_value xt_browser_confirm(XtGuiWindow *win, int32_t argc, xt_value *argv);
273
+ xt_value xt_browser_prompt(XtGuiWindow *win, int32_t argc, xt_value *argv);
274
+ /** Install `scrollTo`/`scrollBy`/`scrollX`/`scrollY`/`focus`/`blur`. */
275
+ void xt_browser_install_window_navigation(xt_value proto);
224
276
  /** CSS-like descriptor of the deepest element at (x, y), or `""` (test/debug). */
225
277
  std::string xt_gui_hit_test(XtGuiWindow *win, float x, float y);
226
278
  /** Synthesise a pointer event (`mousemove`/`mousedown`/`mouseup`/`click`). */
@@ -4,9 +4,11 @@
4
4
  */
5
5
 
6
6
  #include "gui_internal.h"
7
+ #include "listener_options.h"
7
8
 
8
9
  #include "paint.h"
9
10
 
11
+ #include <algorithm>
10
12
  #include <stdio.h>
11
13
  #include <string.h>
12
14
 
@@ -20,22 +22,39 @@ void xt_gui_emit(XtGuiWindow *win, const char *event) {
20
22
  xt_gui_emit_payload(win, event, XT_UNDEFINED);
21
23
  }
22
24
 
25
+ /** Drop one window listener (by identity: type + fn + capture). */
26
+ static void remove_window_listener(XtGuiWindow *win, const XtGuiWindowListener &listener) {
27
+ std::vector<XtGuiWindowListener> &listeners = win->window_listeners;
28
+ for (auto it = listeners.begin(); it != listeners.end(); ++it) {
29
+ if (it->type == listener.type && it->capture == listener.capture &&
30
+ xt_truthy(xt_seq(it->fn, listener.fn))) {
31
+ listeners.erase(it);
32
+ return;
33
+ }
34
+ }
35
+ }
36
+
23
37
  void xt_gui_emit_payload(XtGuiWindow *win, const char *event, xt_value payload) {
24
38
  if (win == NULL || !XT_IS_OBJECT(win->object)) return;
25
- xt_value listeners = xt_object_get_cstr(win->object, "__xt_gui_events");
26
- if (!XT_IS_OBJECT(listeners)) return;
27
- xt_value array = xt_object_get(listeners, xt_string_from_cstr(event));
28
- if (!XT_IS_ARRAY(array)) return;
29
39
  xt_value args[1];
30
40
  int32_t count = 0;
31
41
  if (!XT_IS_UNDEFINED(payload)) {
32
42
  args[0] = payload;
33
43
  count = 1;
34
44
  }
35
- int32_t listener_count = (int32_t)xt_to_number(xt_array_length(array));
36
- for (int32_t i = 0; i < listener_count; i++) {
37
- xt_value fn = xt_array_get(array, xt_number((double)i));
38
- if (XT_IS_FUNCTION(fn)) xt_call_with_this(fn, win->object, count, args);
45
+ /* Snapshot the list: a handler may register or remove listeners while it
46
+ * runs, and the DOM runs the batch as it stood when dispatch started. */
47
+ std::vector<XtGuiWindowListener> snapshot = win->window_listeners;
48
+ for (const XtGuiWindowListener &listener : snapshot) {
49
+ if (listener.type != event) continue;
50
+ /* `{ signal }` removes the listener at abort time (checked here, so no
51
+ * callback plumbing is needed inside the signal). */
52
+ if (xt_gui_signal_aborted(listener.signal)) {
53
+ remove_window_listener(win, listener);
54
+ continue;
55
+ }
56
+ if (listener.once) remove_window_listener(win, listener);
57
+ if (XT_IS_FUNCTION(listener.fn)) xt_call_with_this(listener.fn, win->object, count, args);
39
58
  }
40
59
  }
41
60
 
@@ -52,6 +71,53 @@ void xt_gui_flush_dom(XtGuiWindow *win) {
52
71
  win->geometry.dirty = 1;
53
72
  }
54
73
 
74
+ /* -- page scroll ---------------------------------------------------------- */
75
+
76
+ namespace {
77
+
78
+ void content_bounds(const xtgui::LayoutBox *box, float *max_x, float *max_y) {
79
+ if (box == nullptr) return;
80
+ float right = box->x + box->width;
81
+ float bottom = box->y + box->height;
82
+ if (right > *max_x) *max_x = right;
83
+ if (bottom > *max_y) *max_y = bottom;
84
+ for (const std::unique_ptr<xtgui::LayoutBox> &child : box->children) {
85
+ content_bounds(child.get(), max_x, max_y);
86
+ }
87
+ }
88
+
89
+ } // namespace
90
+
91
+ void xt_gui_scroll_bounds(XtGuiWindow *win, float *max_x, float *max_y) {
92
+ float content_x = 0;
93
+ float content_y = 0;
94
+ if (win != nullptr && win->document != nullptr) {
95
+ xt_gui_flush_dom(win);
96
+ content_bounds(win->document->layout().root(), &content_x, &content_y);
97
+ }
98
+ float viewport_width = 0;
99
+ float viewport_height = 0;
100
+ if (win != nullptr) xt_gui_window_viewport(win, &viewport_width, &viewport_height);
101
+ *max_x = content_x > viewport_width ? content_x - viewport_width : 0.0f;
102
+ *max_y = content_y > viewport_height ? content_y - viewport_height : 0.0f;
103
+ }
104
+
105
+ void xt_gui_scroll_to(XtGuiWindow *win, float x, float y) {
106
+ if (win == nullptr) return;
107
+ float max_x = 0;
108
+ float max_y = 0;
109
+ xt_gui_scroll_bounds(win, &max_x, &max_y);
110
+ if (x < 0.0f) x = 0.0f;
111
+ if (y < 0.0f) y = 0.0f;
112
+ if (x > max_x) x = max_x;
113
+ if (y > max_y) y = max_y;
114
+ if (x == win->scroll_x && y == win->scroll_y) return;
115
+ win->scroll_x = x;
116
+ win->scroll_y = y;
117
+ win->geometry.dirty = 1;
118
+ xt_gui_emit(win, "scroll");
119
+ }
120
+
55
121
  static XtGuiWindow *xt_gui_find_by_window_id(SDL_WindowID id) {
56
122
  SDL_Window *window = SDL_GetWindowFromID(id);
57
123
  if (window == NULL) return NULL;
@@ -66,7 +132,8 @@ static XtGuiWindow *xt_gui_find_by_window_id(SDL_WindowID id) {
66
132
  /** Deepest element under (x, y), or NULL. */
67
133
  static const xtgui::Node *xt_gui_hit_node(XtGuiWindow *win, float x, float y) {
68
134
  if (win->document == nullptr) return nullptr;
69
- const xtgui::LayoutBox *box = win->document->layout().hitTest(x, y);
135
+ const xtgui::LayoutBox *box =
136
+ win->document->layout().hitTest(x + win->scroll_x, y + win->scroll_y);
70
137
  const xtgui::Node *node = box != nullptr ? box->node : nullptr;
71
138
  while (node != nullptr && !node->isElement()) node = node->parent;
72
139
  return node;
@@ -122,6 +189,8 @@ void xt_gui_dispatch_wheel(XtGuiWindow *win, float x, float y, float delta_x, fl
122
189
  const xtgui::Node *node = xt_gui_hit_node(win, x, y);
123
190
  if (node != nullptr) xt_gui_emit_dom_event(win, "wheel", node, payload);
124
191
  xt_gui_emit_payload(win, "wheel", payload);
192
+ /* Default action: scroll the page (content that fits does not move). */
193
+ xt_gui_scroll_to(win, win->scroll_x + delta_x, win->scroll_y + delta_y);
125
194
  }
126
195
 
127
196
  static xt_value xt_gui_key_payload(const char *type, const char *key, const char *code, bool repeat,
@@ -168,6 +237,7 @@ void handle_event(const SDL_Event *event) {
168
237
  /* Web 'resize', so a document can re-measure on a window change in every
169
238
  * host (a browser already fires one). */
170
239
  xt_gui_emit(win, "resize");
240
+ xt_gui_media_lists_notify(win);
171
241
  return;
172
242
  }
173
243
 
@@ -273,6 +343,7 @@ void xt_gui_render_window(XtGuiWindow *win) {
273
343
  if (can_paint && win->geometry.dirty && win->document != nullptr) {
274
344
  xtgui::DisplayList list;
275
345
  xtgui::xt_paint_build(win->document->layout().root(), list, viewport_width, viewport_height);
346
+ xt_paint_translate(list, -win->scroll_x, -win->scroll_y);
276
347
  xt_gui_geometry_upload(g_device, &win->geometry, list);
277
348
  float density = SDL_GetWindowPixelDensity(win->window);
278
349
  float text_scale = density > 0.0f ? density : 1.0f;
@@ -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
+