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
@@ -51,17 +51,18 @@ static void write_declarations(xtgui::Node *node, const std::vector<xtgui::Decla
51
51
  }
52
52
 
53
53
  static void set_style_property(XtGuiWindow *win, xtgui::Node *node, const std::string &name,
54
- const std::string &value) {
54
+ const std::string &value, bool important) {
55
55
  std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
56
56
  bool found = false;
57
57
  for (xtgui::Declaration &decl : decls) {
58
58
  if (decl.property == name) {
59
59
  decl.value = value;
60
- decl.important = false;
60
+ decl.important = important;
61
61
  found = true;
62
+ break;
62
63
  }
63
64
  }
64
- if (!found) decls.push_back({name, value, false});
65
+ if (!found) decls.push_back({name, value, important});
65
66
  write_declarations(node, decls);
66
67
  mark_dirty(win);
67
68
  }
@@ -75,6 +76,26 @@ static std::string get_style_property(const xtgui::Node *node, const std::string
75
76
  return value;
76
77
  }
77
78
 
79
+ static std::string get_style_priority(const xtgui::Node *node, const std::string &name) {
80
+ std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
81
+ for (const xtgui::Declaration &decl : decls) {
82
+ if (decl.property == name) return decl.important ? "important" : "";
83
+ }
84
+ return "";
85
+ }
86
+
87
+ static std::string serialize_declarations(const xtgui::Node *node) {
88
+ std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
89
+ std::string out;
90
+ for (const xtgui::Declaration &decl : decls) {
91
+ if (!out.empty()) out += " ";
92
+ out += decl.property + ": " + decl.value;
93
+ if (decl.important) out += " !important";
94
+ out += ";";
95
+ }
96
+ return out;
97
+ }
98
+
78
99
  static void remove_style_property(XtGuiWindow *win, xtgui::Node *node, const std::string &name) {
79
100
  std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
80
101
  std::vector<xtgui::Declaration> kept;
@@ -133,17 +154,36 @@ METHOD(class_list_toggle) {
133
154
  xtgui::Node *node = class_list_node(self, &win);
134
155
  if (node == nullptr || argc < 1) return XT_FALSE;
135
156
  std::string name = to_string_value(xt_arg(argc, argv, 0));
157
+ if (name.empty()) return XT_FALSE;
136
158
  std::vector<std::string> names = split_classes(class_name_of(node));
137
159
  auto it = std::find(names.begin(), names.end(), name);
138
- if (it != names.end()) {
139
- names.erase(it);
140
- set_class_name(win, node, join_classes(names));
160
+ bool present = it != names.end();
161
+ /* `toggle(name, force)`: with `force` the call is idempotent — it adds when
162
+ * the second argument is truthy and removes when it is falsy, instead of
163
+ * flipping. The argument is optional, so `undefined` still means "flip". */
164
+ bool forceProvided = argc >= 2 && !XT_IS_UNDEFINED(xt_arg(argc, argv, 1));
165
+ bool force = forceProvided && value_truthy(xt_arg(argc, argv, 1));
166
+ if (forceProvided) {
167
+ if (force) {
168
+ if (!present) {
169
+ names.push_back(name);
170
+ set_class_name(win, node, join_classes(names));
171
+ }
172
+ return XT_TRUE;
173
+ }
174
+ if (present) {
175
+ names.erase(it);
176
+ set_class_name(win, node, join_classes(names));
177
+ }
141
178
  return XT_FALSE;
142
179
  }
143
- if (!name.empty()) {
144
- names.push_back(name);
180
+ if (present) {
181
+ names.erase(it);
145
182
  set_class_name(win, node, join_classes(names));
183
+ return XT_FALSE;
146
184
  }
185
+ names.push_back(name);
186
+ set_class_name(win, node, join_classes(names));
147
187
  return XT_TRUE;
148
188
  }
149
189
 
@@ -165,8 +205,19 @@ METHOD(style_set_property) {
165
205
  XtGuiWindow *win = nullptr;
166
206
  xtgui::Node *node = style_node(self, &win);
167
207
  if (node == nullptr || argc < 2) return XT_UNDEFINED;
168
- set_style_property(win, node, to_string_value(xt_arg(argc, argv, 0)),
169
- to_string_value(xt_arg(argc, argv, 1)));
208
+ std::string name = xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(xt_arg(argc, argv, 0))));
209
+ if (name.empty()) return XT_UNDEFINED;
210
+ /* `priority` is "" or "important"; any other value makes the whole call a
211
+ * no-op, exactly as in the CSSOM. */
212
+ std::string priority;
213
+ if (argc >= 3) priority = xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(xt_arg(argc, argv, 2))));
214
+ if (!priority.empty() && priority != "important") return XT_UNDEFINED;
215
+ std::string value = to_string_value(xt_arg(argc, argv, 1));
216
+ if (xtgui::xt_css_trim(value).empty()) {
217
+ remove_style_property(win, node, name);
218
+ return XT_UNDEFINED;
219
+ }
220
+ set_style_property(win, node, name, value, priority == "important");
170
221
  return XT_UNDEFINED;
171
222
  }
172
223
 
@@ -175,18 +226,177 @@ METHOD(style_get_property_value) {
175
226
  xtgui::Node *node = style_node(self, nullptr);
176
227
  if (node == nullptr || argc < 1) return xt_string_from_cstr("");
177
228
  return xt_string_from_cstr(
178
- get_style_property(node, to_string_value(xt_arg(argc, argv, 0))).c_str());
229
+ get_style_property(node, xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(xt_arg(argc, argv, 0)))))
230
+ .c_str());
231
+ }
232
+
233
+ METHOD(style_get_property_priority) {
234
+ (void)env;
235
+ xtgui::Node *node = style_node(self, nullptr);
236
+ if (node == nullptr || argc < 1) return xt_string_from_cstr("");
237
+ return xt_string_from_cstr(
238
+ get_style_priority(node, xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(xt_arg(argc, argv, 0)))))
239
+ .c_str());
179
240
  }
180
241
 
181
242
  METHOD(style_remove_property) {
182
243
  (void)env;
183
244
  XtGuiWindow *win = nullptr;
184
245
  xtgui::Node *node = style_node(self, &win);
185
- if (node == nullptr || argc < 1) return XT_UNDEFINED;
186
- remove_style_property(win, node, to_string_value(xt_arg(argc, argv, 0)));
246
+ if (node == nullptr || argc < 1) return xt_string_from_cstr("");
247
+ std::string name = xtgui::xt_css_lower(xtgui::xt_css_trim(to_string_value(xt_arg(argc, argv, 0))));
248
+ std::string previous = get_style_property(node, name);
249
+ remove_style_property(win, node, name);
250
+ return xt_string_from_cstr(previous.c_str());
251
+ }
252
+
253
+ METHOD(style_css_text_get) {
254
+ (void)env;
255
+ (void)argc;
256
+ (void)argv;
257
+ xtgui::Node *node = style_node(self, nullptr);
258
+ if (node == nullptr) return xt_string_from_cstr("");
259
+ return xt_string_from_cstr(serialize_declarations(node).c_str());
260
+ }
261
+
262
+ METHOD(style_css_text_set) {
263
+ (void)env;
264
+ XtGuiWindow *win = nullptr;
265
+ xtgui::Node *node = style_node(self, &win);
266
+ if (node == nullptr) return XT_UNDEFINED;
267
+ std::vector<xtgui::Declaration> decls =
268
+ xtgui::xt_css_parse_declarations(to_string_value(xt_arg(argc, argv, 0)));
269
+ write_declarations(node, decls);
270
+ mark_dirty(win);
187
271
  return XT_UNDEFINED;
188
272
  }
189
273
 
274
+ /* -- named properties (`style.width = "50%"`) -----------------------------
275
+ *
276
+ * A CSSStyleDeclaration exposes every supported CSS property as a camel-cased
277
+ * property (`style.width`, `style.backgroundColor`) as well as through
278
+ * `setProperty`. There are hundreds of them, so instead of enumerating
279
+ * accessors the style object installs the runtime's dynamic-property hooks:
280
+ * a miss on the prototype is answered from the inline declaration block, and
281
+ * an assignment writes it back there. That is what makes the common
282
+ * `el.style.display = "none"` idiom affect layout instead of silently parking
283
+ * a field on the JS object.
284
+ */
285
+
286
+ namespace {
287
+
288
+ bool property_key(xt_value key, std::string *out) {
289
+ if (!XT_IS_STRING(key)) return false;
290
+ /* Named style properties are camel-cased (`style.backgroundColor`); the
291
+ * declaration block stores the CSS spelling (`background-color`). Custom
292
+ * properties keep their leading `--`, and internal bookkeeping keys stay
293
+ * out of the view (looking one up must not re-enter it). */
294
+ std::string raw = to_string_value(key);
295
+ if (raw.compare(0, 6, "__xt_") == 0) return false;
296
+ std::string mapped;
297
+ for (char c : raw) {
298
+ if (c >= 'A' && c <= 'Z') {
299
+ mapped.push_back('-');
300
+ mapped.push_back((char)std::tolower((unsigned char)c));
301
+ } else {
302
+ mapped.push_back(c);
303
+ }
304
+ }
305
+ *out = xtgui::xt_css_lower(xtgui::xt_css_trim(mapped));
306
+ return !out->empty();
307
+ }
308
+
309
+ xt_value style_view_get(xt_value self, xt_value key, void *data) {
310
+ (void)data;
311
+ std::string name;
312
+ if (!property_key(key, &name)) return XT_UNDEFINED;
313
+ xtgui::Node *node = style_node(self, nullptr);
314
+ if (node == nullptr) return xt_string_from_cstr("");
315
+ return xt_string_from_cstr(get_style_property(node, name).c_str());
316
+ }
317
+
318
+ int style_view_set(xt_value self, xt_value key, xt_value value, void *data) {
319
+ (void)data;
320
+ std::string name;
321
+ if (!property_key(key, &name)) return 0;
322
+ XtGuiWindow *win = nullptr;
323
+ xtgui::Node *node = style_node(self, &win);
324
+ if (node == nullptr) return 1;
325
+ std::string text = to_string_value(value);
326
+ if (text.empty()) {
327
+ remove_style_property(win, node, name);
328
+ } else {
329
+ set_style_property(win, node, name, text, false);
330
+ }
331
+ return 1;
332
+ }
333
+
334
+ int style_view_has(xt_value self, xt_value key, void *data) {
335
+ (void)data;
336
+ std::string name;
337
+ if (!property_key(key, &name)) return 0;
338
+ xtgui::Node *node = style_node(self, nullptr);
339
+ if (node == nullptr) return 0;
340
+ std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
341
+ for (const xtgui::Declaration &decl : decls) {
342
+ if (decl.property == name) return 1;
343
+ }
344
+ return 0;
345
+ }
346
+
347
+ int style_view_remove(xt_value self, xt_value key, void *data) {
348
+ (void)data;
349
+ std::string name;
350
+ if (!property_key(key, &name)) return 0;
351
+ XtGuiWindow *win = nullptr;
352
+ xtgui::Node *node = style_node(self, &win);
353
+ if (node == nullptr) return 1;
354
+ remove_style_property(win, node, name);
355
+ return 1;
356
+ }
357
+
358
+ xt_value style_view_keys(xt_value self, void *data) {
359
+ (void)data;
360
+ xtgui::Node *node = style_node(self, nullptr);
361
+ xt_value result = xt_array_new(0, nullptr);
362
+ if (node == nullptr) return result;
363
+ std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
364
+ for (const xtgui::Declaration &decl : decls) {
365
+ xt_array_push(result, xt_string_from_cstr(decl.property.c_str()));
366
+ }
367
+ return result;
368
+ }
369
+
370
+ const xt_object_hooks kStyleViewHooks = {style_view_get, style_view_set, style_view_has,
371
+ style_view_remove, style_view_keys};
372
+
373
+ } // namespace
374
+
375
+ void install_style_hooks(xt_value style) {
376
+ xt_object_set_hooks(style, &kStyleViewHooks, nullptr);
377
+ }
378
+
379
+ METHOD(style_length_get) {
380
+ (void)env;
381
+ (void)argc;
382
+ (void)argv;
383
+ xtgui::Node *node = style_node(self, nullptr);
384
+ if (node == nullptr) return xt_number(0);
385
+ return xt_number(
386
+ (double)xtgui::xt_css_parse_declarations(inline_style_of(node)).size());
387
+ }
388
+
389
+ /** `style.item(index)`: the property name at `index`, or "" past the end. */
390
+ METHOD(style_item) {
391
+ (void)env;
392
+ xtgui::Node *node = style_node(self, nullptr);
393
+ if (node == nullptr || argc < 1) return xt_string_from_cstr("");
394
+ std::vector<xtgui::Declaration> decls = xtgui::xt_css_parse_declarations(inline_style_of(node));
395
+ double index = xt_to_number(xt_arg(argc, argv, 0));
396
+ if (index < 0 || index >= (double)decls.size()) return xt_string_from_cstr("");
397
+ return xt_string_from_cstr(decls[(size_t)index].property.c_str());
398
+ }
399
+
190
400
  #undef METHOD
191
401
 
192
402
  } // namespace domapi
@@ -159,6 +159,8 @@ const MethodEntry kMethods[] = {
159
159
  {"getTexParameter", (void *)webgl_get_tex_parameter},
160
160
  {"texImage2D", (void *)webgl_tex_image_2d},
161
161
  {"texSubImage2D", (void *)webgl_tex_sub_image_2d},
162
+ {"compressedTexImage2D", (void *)webgl_compressed_tex_image_2d},
163
+ {"compressedTexSubImage2D", (void *)webgl_compressed_tex_sub_image_2d},
162
164
  {"generateMipmap", (void *)webgl_generate_mipmap},
163
165
  {"createFramebuffer", (void *)webgl_create_framebuffer},
164
166
  {"deleteFramebuffer", (void *)webgl_delete_framebuffer},
@@ -238,6 +240,11 @@ xtgui::WebGLContext *webgl_from_context(xt_value self) {
238
240
  return id != 0 ? xtgui::xt_webgl_by_id(id) : nullptr;
239
241
  }
240
242
 
243
+ xtgui::WebGLContext *webgl_live_context(xt_value self) {
244
+ xtgui::WebGLContext *context = webgl_from_context(self);
245
+ return context != nullptr && !context->lost() ? context : nullptr;
246
+ }
247
+
241
248
  xt_value webgl_context_handle(xt_value element, xtgui::WebGLContext *context) {
242
249
  xt_value cached = xt_object_get_cstr(element, "__xt_gui_gl");
243
250
  if (XT_IS_OBJECT(cached)) return cached;
@@ -81,12 +81,13 @@ const MethodEntry kMethods[] = {
81
81
  {"getActiveUniformBlockParameter", (void *)webgl2_get_active_uniform_block_parameter},
82
82
  {"getActiveUniformBlockName", (void *)webgl2_get_active_uniform_block_name},
83
83
  {"uniformBlockBinding", (void *)webgl2_uniform_block_binding},
84
+ {"drawingBufferStorage", (void *)webgl2_drawing_buffer_storage},
84
85
  {"texStorage2D", (void *)webgl2_tex_storage_2d},
85
86
  {"texStorage3D", (void *)webgl2_tex_storage_3d},
86
87
  {"texImage3D", (void *)webgl2_tex_image_3d},
87
88
  {"texSubImage3D", (void *)webgl2_tex_sub_image_3d},
88
- {"compressedTexImage2D", (void *)webgl2_compressed_tex_image2d},
89
- {"compressedTexSubImage2D", (void *)webgl2_compressed_tex_sub_image2d},
89
+ {"compressedTexImage2D", (void *)webgl_compressed_tex_image_2d},
90
+ {"compressedTexSubImage2D", (void *)webgl_compressed_tex_sub_image_2d},
90
91
  {"compressedTexImage3D", (void *)webgl2_compressed_tex_image3d},
91
92
  {"compressedTexSubImage3D", (void *)webgl2_compressed_tex_sub_image3d},
92
93
  {"clearBufferfi", (void *)webgl2_clear_buffer_fi},
@@ -107,7 +108,7 @@ void define_method(xt_value proto, const char *name, void *fn) {
107
108
  xt_object_set(proto, xt_string_from_cstr(name), xt_closure_new(fn, 0, NULL));
108
109
  }
109
110
 
110
- xtgui::WebGLContext *context_of(xt_value self) { return webgl_from_context(self); }
111
+ xtgui::WebGLContext *context_of(xt_value self) { return webgl_live_context(self); }
111
112
 
112
113
  xt_value uint_array(const unsigned int *values, int count) {
113
114
  xt_value array = xt_array_new(0, nullptr);
@@ -202,14 +203,66 @@ WEBGL2_NOOP(webgl2_end_transform_feedback)
202
203
  WEBGL2_NOOP(webgl2_pause_transform_feedback)
203
204
  WEBGL2_NOOP(webgl2_resume_transform_feedback)
204
205
  WEBGL2_NOOP(webgl2_transform_feedback_varyings)
205
- WEBGL2_NOOP(webgl2_bind_buffer_base)
206
- WEBGL2_NOOP(webgl2_bind_buffer_range)
207
- WEBGL2_NOOP(webgl2_uniform_block_binding)
208
- WEBGL2_NOOP(webgl2_blit_framebuffer)
209
- WEBGL2_NOOP(webgl2_framebuffer_texture_layer)
210
206
  WEBGL2_NOOP(webgl2_invalidate_framebuffer)
211
207
  WEBGL2_NOOP(webgl2_invalidate_sub_framebuffer)
212
- WEBGL2_NOOP(webgl2_read_buffer)
208
+
209
+ METHOD(webgl2_blit_framebuffer) {
210
+ (void)env;
211
+ xtgui::WebGLContext *context = context_of(self);
212
+ if (context == nullptr) return XT_UNDEFINED;
213
+ const int source[4] = {webgl_int(argc, argv, 0, 0), webgl_int(argc, argv, 1, 0),
214
+ webgl_int(argc, argv, 2, 0), webgl_int(argc, argv, 3, 0)};
215
+ const int destination[4] = {webgl_int(argc, argv, 4, 0), webgl_int(argc, argv, 5, 0),
216
+ webgl_int(argc, argv, 6, 0), webgl_int(argc, argv, 7, 0)};
217
+ context->blit_framebuffer(source, destination, webgl_int(argc, argv, 8, GL_COLOR_BUFFER_BIT),
218
+ webgl_int(argc, argv, 9, GL_NEAREST));
219
+ return XT_UNDEFINED;
220
+ }
221
+
222
+ METHOD(webgl2_tex_sub_image_3d) {
223
+ (void)env;
224
+ xtgui::WebGLContext *context = context_of(self);
225
+ if (context == nullptr) return XT_UNDEFINED;
226
+ const uint32_t texture = context->bound_texture(0);
227
+ if (texture == 0 || webgl_int(argc, argv, 1, 0) != 0) return XT_UNDEFINED;
228
+ const int x = webgl_int(argc, argv, 2, 0);
229
+ const int y = webgl_int(argc, argv, 3, 0);
230
+ const int z = webgl_int(argc, argv, 4, 0);
231
+ const int width = webgl_int(argc, argv, 5, 0);
232
+ const int height = webgl_int(argc, argv, 6, 0);
233
+ const int depth = webgl_int(argc, argv, 7, 0);
234
+ const int format = webgl_int(argc, argv, 8, GL_RGBA);
235
+ const int type = webgl_int(argc, argv, 9, GL_UNSIGNED_BYTE);
236
+ if (type != GL_UNSIGNED_BYTE) {
237
+ context->set_error(GL_INVALID_ENUM);
238
+ return XT_UNDEFINED;
239
+ }
240
+ int channels = 1;
241
+ if (format == GL_RGBA) channels = 4;
242
+ else if (format == GL_RGB) channels = 3;
243
+ else if (format == GL_LUMINANCE_ALPHA) channels = 2;
244
+ const std::vector<uint8_t> bytes = webgl_bytes(xt_arg(argc, argv, 10));
245
+ const size_t count = (size_t)width * height * depth;
246
+ if (bytes.size() < count * (size_t)channels) {
247
+ context->set_error(GL_INVALID_VALUE);
248
+ return XT_UNDEFINED;
249
+ }
250
+ std::vector<uint8_t> rgba;
251
+ webgl_convert_pixels(bytes.data(), (int)count, format, false, &rgba);
252
+ context->tex_sub_image_3d(texture, x, y, z, width, height, depth, rgba.data());
253
+ return XT_UNDEFINED;
254
+ }
255
+
256
+ METHOD(webgl2_framebuffer_texture_layer) {
257
+ (void)env;
258
+ xtgui::WebGLContext *context = context_of(self);
259
+ if (context == nullptr) return XT_UNDEFINED;
260
+ context->framebuffer_texture_layer(
261
+ context->bound_framebuffer_id(),
262
+ webgl_object_id(xt_arg(argc, argv, 2), "__xt_gl_texture"),
263
+ webgl_int(argc, argv, 1, GL_COLOR_ATTACHMENT0), webgl_int(argc, argv, 4, 0));
264
+ return XT_UNDEFINED;
265
+ }
213
266
 
214
267
  METHOD(webgl2_get_transform_feedback_varying) {
215
268
  (void)env;
@@ -259,12 +312,6 @@ METHOD(webgl2_get_sync_parameter) {
259
312
 
260
313
  /* -- uniform blocks and indexed queries ---------------------------------- */
261
314
 
262
- METHOD(webgl2_get_indexed_parameter) {
263
- (void)env;
264
- (void)argc;
265
- (void)argv;
266
- return XT_NULL;
267
- }
268
315
 
269
316
  METHOD(webgl2_get_uniform_indices) {
270
317
  (void)env;
@@ -290,19 +337,7 @@ METHOD(webgl2_get_uniform_block_index) {
290
337
  return xt_number((double)index);
291
338
  }
292
339
 
293
- METHOD(webgl2_get_active_uniform_block_parameter) {
294
- (void)env;
295
- (void)argc;
296
- (void)argv;
297
- return xt_number(0);
298
- }
299
340
 
300
- METHOD(webgl2_get_active_uniform_block_name) {
301
- (void)env;
302
- (void)argc;
303
- (void)argv;
304
- return XT_NULL;
305
- }
306
341
 
307
342
  /* -- buffers ------------------------------------------------------------- */
308
343
 
@@ -340,22 +375,80 @@ METHOD(webgl2_get_buffer_sub_data) {
340
375
 
341
376
  /* -- textures ------------------------------------------------------------ */
342
377
 
378
+ METHOD(webgl2_drawing_buffer_storage) {
379
+ (void)env;
380
+ xtgui::WebGLContext *context = context_of(self);
381
+ if (context != nullptr) {
382
+ context->drawing_buffer_storage(webgl_int(argc, argv, 0, 0),
383
+ webgl_int(argc, argv, 1, 0));
384
+ }
385
+ return XT_UNDEFINED;
386
+ }
387
+
343
388
  METHOD(webgl2_tex_storage_2d) {
344
389
  (void)env;
345
390
  xtgui::WebGLContext *context = context_of(self);
346
391
  if (context != nullptr) {
347
392
  const uint32_t texture = context->bound_texture(0);
348
- context->tex_storage_2d(texture, webgl_int(argc, argv, 1, 1),
349
- webgl_int(argc, argv, 3, 0), webgl_int(argc, argv, 4, 0));
393
+ const int internalformat = webgl_int(argc, argv, 2, 0x8058); // GL_RGBA8
394
+ context->tex_storage_format(texture, webgl_int(argc, argv, 1, 1),
395
+ webgl_int(argc, argv, 3, 0), webgl_int(argc, argv, 4, 0),
396
+ xtgui::webgl_storage_format(internalformat));
397
+ }
398
+ return XT_UNDEFINED;
399
+ }
400
+
401
+ METHOD(webgl2_tex_storage_3d) {
402
+ (void)env;
403
+ xtgui::WebGLContext *context = context_of(self);
404
+ if (context == nullptr) return XT_UNDEFINED;
405
+ const int internalformat = webgl_int(argc, argv, 2, 0x8058); // GL_RGBA8
406
+ context->tex_storage_3d(context->bound_texture(0), webgl_int(argc, argv, 1, 1),
407
+ webgl_int(argc, argv, 3, 0), webgl_int(argc, argv, 4, 0),
408
+ webgl_int(argc, argv, 5, 0),
409
+ xtgui::webgl_storage_format(internalformat),
410
+ webgl_int(argc, argv, 0, GL_TEXTURE_2D_ARRAY));
411
+ return XT_UNDEFINED;
412
+ }
413
+
414
+ METHOD(webgl2_tex_image_3d) {
415
+ (void)env;
416
+ xtgui::WebGLContext *context = context_of(self);
417
+ if (context == nullptr) return XT_UNDEFINED;
418
+ const uint32_t texture = context->bound_texture(0);
419
+ if (texture == 0) return XT_UNDEFINED;
420
+ if (webgl_int(argc, argv, 1, 0) != 0) return XT_UNDEFINED;
421
+ const int internalformat = webgl_int(argc, argv, 2, GL_RGBA);
422
+ const int width = webgl_int(argc, argv, 3, 0);
423
+ const int height = webgl_int(argc, argv, 4, 0);
424
+ const int depth = webgl_int(argc, argv, 5, 0);
425
+ const int format = webgl_int(argc, argv, 7, GL_RGBA);
426
+ const int type = webgl_int(argc, argv, 8, GL_UNSIGNED_BYTE);
427
+ if (type != GL_UNSIGNED_BYTE || width <= 0 || height <= 0 || depth <= 0) {
428
+ context->set_error(GL_INVALID_VALUE);
429
+ return XT_UNDEFINED;
430
+ }
431
+ int channels = 1;
432
+ if (format == GL_RGBA) channels = 4;
433
+ else if (format == GL_RGB) channels = 3;
434
+ else if (format == GL_LUMINANCE_ALPHA) channels = 2;
435
+ const std::vector<uint8_t> bytes = webgl_bytes(xt_arg(argc, argv, 9));
436
+ const size_t count = (size_t)width * height * depth;
437
+ std::vector<uint8_t> rgba;
438
+ if (bytes.empty()) {
439
+ rgba.assign(count * 4, 0);
440
+ } else if (bytes.size() < count * (size_t)channels) {
441
+ context->set_error(GL_INVALID_VALUE);
442
+ return XT_UNDEFINED;
443
+ } else {
444
+ webgl_convert_pixels(bytes.data(), (int)count, format, false, &rgba);
350
445
  }
446
+ context->tex_image_3d(texture, width, height, depth,
447
+ xtgui::webgl_storage_format(internalformat), rgba.data(),
448
+ webgl_int(argc, argv, 0, GL_TEXTURE_2D_ARRAY));
351
449
  return XT_UNDEFINED;
352
450
  }
353
451
 
354
- WEBGL2_NOOP(webgl2_tex_storage_3d)
355
- WEBGL2_NOOP(webgl2_tex_image_3d)
356
- WEBGL2_NOOP(webgl2_tex_sub_image_3d)
357
- WEBGL2_NOOP(webgl2_compressed_tex_image2d)
358
- WEBGL2_NOOP(webgl2_compressed_tex_sub_image2d)
359
452
  WEBGL2_NOOP(webgl2_compressed_tex_image3d)
360
453
  WEBGL2_NOOP(webgl2_compressed_tex_sub_image3d)
361
454
 
@@ -410,8 +503,30 @@ METHOD(webgl2_clear_buffer_fi) {
410
503
 
411
504
  METHOD(webgl2_draw_buffers) {
412
505
  (void)env;
413
- (void)argc;
414
- (void)argv;
506
+ xtgui::WebGLContext *context = context_of(self);
507
+ if (context == nullptr) return XT_UNDEFINED;
508
+ const xt_value list = xt_arg(argc, argv, 0);
509
+ std::vector<double> values = webgl_numbers(list);
510
+ if (values.empty()) {
511
+ /* `webgl_numbers` only walks typed arrays; plain WebGL argument arrays
512
+ * (`drawBuffers([gl.COLOR_ATTACHMENT0, ...])`) need the element accessor. */
513
+ const double length = xt_to_number(xt_get(list, xt_string_from_cstr("length")));
514
+ if (length > 0.0 && length <= 16.0) {
515
+ for (int i = 0; i < (int)length; i++) {
516
+ values.push_back(xt_to_number(xt_get(list, xt_number((double)i))));
517
+ }
518
+ }
519
+ }
520
+ std::vector<int> buffers(values.size());
521
+ for (size_t i = 0; i < values.size(); i++) buffers[i] = (int)values[i];
522
+ context->draw_buffers((int)buffers.size(), buffers.data());
523
+ return XT_UNDEFINED;
524
+ }
525
+
526
+ METHOD(webgl2_read_buffer) {
527
+ (void)env;
528
+ xtgui::WebGLContext *context = context_of(self);
529
+ if (context != nullptr) context->read_buffer(webgl_int(argc, argv, 0, GL_BACK));
415
530
  return XT_UNDEFINED;
416
531
  }
417
532
 
@@ -18,7 +18,7 @@
18
18
  namespace domapi {
19
19
  namespace {
20
20
 
21
- xtgui::WebGLContext *context_of(xt_value self) { return webgl_from_context(self); }
21
+ xtgui::WebGLContext *context_of(xt_value self) { return webgl_live_context(self); }
22
22
 
23
23
  uint32_t vertex_array_argument(xt_value value) {
24
24
  return webgl_object_id(value, "__xt_gl_vertex_array");
@@ -81,7 +81,7 @@ METHOD(webgl2_vertex_attrib_ipointer) {
81
81
  namespace {
82
82
 
83
83
  void attrib_integer(xt_value self, int32_t argc, xt_value *argv, bool is_unsigned) {
84
- xtgui::WebGLContext *context = webgl_from_context(self);
84
+ xtgui::WebGLContext *context = webgl_live_context(self);
85
85
  if (context == nullptr) return;
86
86
  float values[4] = {0.0f, 0.0f, 0.0f, 1.0f};
87
87
  const std::vector<double> numbers = webgl_numbers(xt_arg(argc, argv, 1));
@@ -162,7 +162,7 @@ METHOD(webgl2_draw_range_elements) {
162
162
  namespace {
163
163
 
164
164
  void uniform_unsigned(xt_value self, int32_t argc, xt_value *argv, int components, bool vector) {
165
- xtgui::WebGLContext *context = webgl_from_context(self);
165
+ xtgui::WebGLContext *context = webgl_live_context(self);
166
166
  if (context == nullptr) return;
167
167
  xt_value handle = xt_arg(argc, argv, 0);
168
168
  if (!XT_IS_OBJECT(handle)) return;
@@ -14,7 +14,7 @@
14
14
  namespace domapi {
15
15
  namespace {
16
16
 
17
- xtgui::WebGLContext *context_of(xt_value self) { return webgl_from_context(self); }
17
+ xtgui::WebGLContext *context_of(xt_value self) { return webgl_live_context(self); }
18
18
 
19
19
  uint32_t buffer_argument(xt_value value) { return webgl_object_id(value, "__xt_gl_buffer"); }
20
20
 
@@ -85,6 +85,10 @@ METHOD(webgl_get_buffer_parameter) {
85
85
  const int pname = webgl_int(argc, argv, 1, 0);
86
86
  const uint32_t buffer = context->bound_buffer(target);
87
87
  if (pname == GL_BUFFER_SIZE) return xt_number((double)context->buffer_size(buffer));
88
+ if (pname == 0x8765) { // GL_BUFFER_USAGE
89
+ xtgui::WebGLBuffer *record = context->buffer(buffer);
90
+ return xt_number(record != nullptr ? (double)record->usage : 0.0);
91
+ }
88
92
  return xt_number(0);
89
93
  }
90
94
 
@@ -116,7 +120,7 @@ METHOD(webgl_vertex_attrib_pointer) {
116
120
  namespace {
117
121
 
118
122
  void attrib_value(xt_value self, int32_t argc, xt_value *argv, int components) {
119
- xtgui::WebGLContext *context = webgl_from_context(self);
123
+ xtgui::WebGLContext *context = webgl_live_context(self);
120
124
  if (context == nullptr) return;
121
125
  float values[4] = {0.0f, 0.0f, 0.0f, 1.0f};
122
126
  for (int i = 0; i < components; i++) values[i] = (float)webgl_number(argc, argv, i + 1, 0.0);