xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -16,7 +16,9 @@
16
16
  #include "canvas.h"
17
17
  #include "image.h"
18
18
  #include "image_bitmap.h"
19
+ #include "image_api.h"
19
20
  #include "webgl.h"
21
+ #include "webgl_feature.h"
20
22
 
21
23
  namespace domapi {
22
24
  namespace {
@@ -121,10 +123,11 @@ bool webgl_read_source(xt_value source, int *width, int *height, std::vector<uin
121
123
  XtGuiWindow *win = nullptr;
122
124
  xtgui::Node *node = resolve_node(source, &win);
123
125
  const xtgui::Image *image = nullptr;
126
+ const XtImageApi *images = xt_image_api();
124
127
  std::vector<uint8_t> owned;
125
128
  if (node != nullptr && node->isElement() && node->isTag("img")) {
126
129
  const std::string *src = node->attr("src");
127
- if (src != nullptr) image = xtgui::xt_image_load(*src);
130
+ if (src != nullptr && images != nullptr) image = images->load(*src, 0, 0);
128
131
  } else if (node != nullptr && node->isElement() && node->isTag("video")) {
129
132
  xtgui::MediaPlayer *player = xt_gui_media_player(win, node);
130
133
  if (player != nullptr) image = player->frame();
@@ -161,7 +164,7 @@ bool webgl_read_source(xt_value source, int *width, int *height, std::vector<uin
161
164
  xt_value src = xt_object_get_cstr(source, "src");
162
165
  if (XT_IS_STRING(src)) {
163
166
  const char *path = xt_string_data(xt_to_string(src));
164
- if (path != nullptr && path[0] != '\0') image = xtgui::xt_image_load(path);
167
+ if (path != nullptr && path[0] != '\0' && images != nullptr) image = images->load(path, 0, 0);
165
168
  }
166
169
  }
167
170
  if (image == nullptr || image->width <= 0 || image->height <= 0) return false;
@@ -37,18 +37,72 @@ static bool parseTimeSeconds(const std::string &text, float *out) {
37
37
  return true;
38
38
  }
39
39
 
40
+ static std::vector<std::string> splitCommas(const std::string &text);
41
+ static std::vector<std::string> splitTokens(const std::string &text);
42
+
43
+ /** Parse a CSS timing function. Besides the five keywords this accepts
44
+ * `cubic-bezier(x1, y1, x2, y2)` and `steps(n[, start|end|jump-*])`. */
40
45
  static bool parseTimingFunction(const std::string &text, TimingFunction *out) {
41
46
  std::string token = xt_css_lower(xt_css_trim(text));
42
47
  if (token == "linear") {
43
- *out = TimingFunction::Linear;
48
+ *out = TimingFunction();
49
+ out->kind = TimingFunction::Kind::Linear;
44
50
  } else if (token == "ease") {
45
- *out = TimingFunction::Ease;
51
+ *out = TimingFunction();
46
52
  } else if (token == "ease-in") {
47
- *out = TimingFunction::EaseIn;
53
+ *out = TimingFunction();
54
+ out->kind = TimingFunction::Kind::EaseIn;
48
55
  } else if (token == "ease-out") {
49
- *out = TimingFunction::EaseOut;
56
+ *out = TimingFunction();
57
+ out->kind = TimingFunction::Kind::EaseOut;
50
58
  } else if (token == "ease-in-out") {
51
- *out = TimingFunction::EaseInOut;
59
+ *out = TimingFunction();
60
+ out->kind = TimingFunction::Kind::EaseInOut;
61
+ } else if (token == "step-start") {
62
+ *out = TimingFunction();
63
+ out->kind = TimingFunction::Kind::StepsStart;
64
+ out->steps = 1;
65
+ } else if (token == "step-end") {
66
+ *out = TimingFunction();
67
+ out->kind = TimingFunction::Kind::StepsEnd;
68
+ out->steps = 1;
69
+ } else if (token.compare(0, 13, "cubic-bezier(") == 0 && token.back() == ')') {
70
+ std::vector<std::string> args =
71
+ splitCommas(token.substr(13, token.size() - 14));
72
+ if (args.size() != 4) return false;
73
+ float values[4];
74
+ for (int i = 0; i < 4; i++) {
75
+ if (!xt_css_parse_number(args[i], &values[i])) return false;
76
+ }
77
+ TimingFunction fn;
78
+ fn.kind = TimingFunction::Kind::CubicBezier;
79
+ fn.x1 = values[0] < 0.0f ? 0.0f : (values[0] > 1.0f ? 1.0f : values[0]);
80
+ fn.y1 = values[1];
81
+ fn.x2 = values[2] < 0.0f ? 0.0f : (values[2] > 1.0f ? 1.0f : values[2]);
82
+ fn.y2 = values[3];
83
+ *out = fn;
84
+ } else if (token.compare(0, 6, "steps(") == 0 && token.back() == ')') {
85
+ std::string inner = token.substr(6, token.size() - 7);
86
+ for (char &c : inner) {
87
+ if (c == ',') c = ' ';
88
+ }
89
+ std::vector<std::string> args = xt_css_split_whitespace(inner);
90
+ if (args.empty()) return false;
91
+ float count = 0;
92
+ if (!xt_css_parse_number(args[0], &count) || count < 1.0f) return false;
93
+ bool stepStart = false;
94
+ if (args.size() > 1) {
95
+ std::string position = xt_css_lower(args[1]);
96
+ if (position == "start" || position == "jump-start" || position == "jump-both") {
97
+ stepStart = true;
98
+ } else if (position != "end" && position != "jump-end" && position != "jump-none") {
99
+ return false;
100
+ }
101
+ }
102
+ TimingFunction fn;
103
+ fn.kind = stepStart ? TimingFunction::Kind::StepsStart : TimingFunction::Kind::StepsEnd;
104
+ fn.steps = (int)(count + 0.5f);
105
+ *out = fn;
52
106
  } else {
53
107
  return false;
54
108
  }
@@ -56,28 +110,21 @@ static bool parseTimingFunction(const std::string &text, TimingFunction *out) {
56
110
  }
57
111
 
58
112
  static std::vector<std::string> splitCommas(const std::string &text) {
59
- std::vector<std::string> parts;
60
- size_t start = 0;
61
- for (size_t i = 0; i <= text.size(); i++) {
62
- if (i == text.size() || text[i] == ',') {
63
- std::string part = xt_css_trim(text.substr(start, i - start));
64
- if (!part.empty()) parts.push_back(part);
65
- start = i + 1;
66
- }
67
- }
68
- return parts;
113
+ /* Top-level commas only: a timing function or colour function may contain
114
+ * its own comma (`steps(4, end)`, `rgb(1, 2, 3)`). */
115
+ return xt_css_split_commas(text);
69
116
  }
70
117
 
71
118
  static void parseTransitionShorthand(const std::string &value, std::vector<TransitionSpec> *out) {
72
119
  out->clear();
73
120
  for (const std::string &group : splitCommas(value)) {
74
- std::vector<std::string> tokens = xt_css_split_whitespace(group);
121
+ std::vector<std::string> tokens = splitTokens(group);
75
122
  if (tokens.empty()) continue;
76
123
  TransitionSpec spec;
77
124
  int time_seen = 0;
78
125
  for (const std::string &token : tokens) {
79
126
  float seconds = 0;
80
- TimingFunction timing = TimingFunction::Ease;
127
+ TimingFunction timing;
81
128
  if (parseTimeSeconds(token, &seconds)) {
82
129
  if (time_seen == 0) {
83
130
  spec.duration = seconds;
@@ -89,6 +136,8 @@ static void parseTransitionShorthand(const std::string &value, std::vector<Trans
89
136
  spec.timing = timing;
90
137
  } else {
91
138
  spec.property = xt_css_lower(token);
139
+ /* The shorthand animates its colour component. */
140
+ if (spec.property == "background") spec.property = "background-color";
92
141
  }
93
142
  }
94
143
  out->push_back(spec);
@@ -99,7 +148,7 @@ static void parseTransitionShorthand(const std::string &value, std::vector<Trans
99
148
  /* -- filters, shadows and generated content -------------------------------- */
100
149
 
101
150
  /** Split on whitespace outside function parentheses (`rgb(1, 2, 3)` stays one). */
102
- static std::vector<std::string> splitShadowTokens(const std::string &text) {
151
+ static std::vector<std::string> splitTokens(const std::string &text) {
103
152
  std::vector<std::string> parts;
104
153
  std::string current;
105
154
  int depth = 0;
@@ -152,7 +201,7 @@ static bool parseAngleDegrees(const std::string &raw, float *out) {
152
201
 
153
202
  static bool parseShadowLengths(const std::string &value, Length *dx, Length *dy, Length *blur,
154
203
  Color *color, bool *hasColor) {
155
- std::vector<std::string> tokens = splitShadowTokens(value);
204
+ std::vector<std::string> tokens = splitTokens(value);
156
205
  Length lengths[3];
157
206
  size_t count = 0;
158
207
  for (const std::string &token : tokens) {
@@ -255,11 +304,106 @@ static bool parseTextShadow(const std::string &value, std::vector<TextShadow> *o
255
304
  return true;
256
305
  }
257
306
 
307
+ /** Parse a comma-separated `box-shadow` list. Offsets are `<dx> <dy>
308
+ * [<blur> [<spread>]]`, and the optional `inset` keyword plus a colour may
309
+ * appear anywhere in the entry, as in CSS. */
310
+ static bool parseBoxShadow(const std::string &value, std::vector<BoxShadow> *out) {
311
+ std::string text = xt_css_trim(value);
312
+ std::string lower = xt_css_lower(text);
313
+ out->clear();
314
+ if (lower == "none" || text.empty()) return true;
315
+ for (const std::string &part : splitCommas(value)) {
316
+ BoxShadow shadow;
317
+ Length lengths[4];
318
+ size_t count = 0;
319
+ for (const std::string &token : splitTokens(part)) {
320
+ std::string tokenLower = xt_css_lower(token);
321
+ if (tokenLower == "inset") {
322
+ shadow.inset = true;
323
+ continue;
324
+ }
325
+ Length length = xt_css_parse_length(token);
326
+ if (length.unit != Unit::Invalid && length.unit != Unit::Auto &&
327
+ length.unit != Unit::None) {
328
+ if (count < 4) lengths[count++] = length;
329
+ continue;
330
+ }
331
+ Color color = xt_css_parse_color(token);
332
+ if (color.valid) {
333
+ shadow.color = color;
334
+ shadow.has_color = true;
335
+ }
336
+ }
337
+ if (count < 2) return false;
338
+ shadow.dx = lengths[0];
339
+ shadow.dy = lengths[1];
340
+ shadow.blur = count > 2 ? lengths[2] : Length{0.0f, Unit::Px};
341
+ shadow.spread = count > 3 ? lengths[3] : Length{0.0f, Unit::Px};
342
+ out->push_back(shadow);
343
+ }
344
+ return true;
345
+ }
346
+
347
+ static bool parseAnimationShorthand(const std::string &value, std::vector<AnimationSpec> *out) {
348
+ out->clear();
349
+ for (const std::string &group : splitCommas(value)) {
350
+ std::vector<std::string> tokens = splitTokens(group);
351
+ if (tokens.empty()) continue;
352
+ AnimationSpec spec;
353
+ int time_seen = 0;
354
+ for (const std::string &token : tokens) {
355
+ std::string lower = xt_css_lower(token);
356
+ float seconds = 0;
357
+ TimingFunction timing;
358
+ if (parseTimeSeconds(token, &seconds)) {
359
+ if (time_seen == 0) spec.duration = seconds;
360
+ else if (time_seen == 1) spec.delay = seconds;
361
+ time_seen++;
362
+ } else if (parseTimingFunction(token, &timing)) {
363
+ spec.timing = timing;
364
+ } else if (lower == "infinite") {
365
+ spec.iterations = -1.0f;
366
+ } else if (lower == "normal") {
367
+ spec.direction = AnimationDirection::Normal;
368
+ } else if (lower == "reverse") {
369
+ spec.direction = AnimationDirection::Reverse;
370
+ } else if (lower == "alternate") {
371
+ spec.direction = AnimationDirection::Alternate;
372
+ } else if (lower == "alternate-reverse") {
373
+ spec.direction = AnimationDirection::AlternateReverse;
374
+ } else if (lower == "none") {
375
+ spec.fill = AnimationFillMode::None;
376
+ } else if (lower == "forwards") {
377
+ spec.fill = AnimationFillMode::Forwards;
378
+ } else if (lower == "backwards") {
379
+ spec.fill = AnimationFillMode::Backwards;
380
+ } else if (lower == "both") {
381
+ spec.fill = AnimationFillMode::Both;
382
+ } else if (lower == "paused") {
383
+ spec.paused = true;
384
+ } else if (lower == "running") {
385
+ spec.paused = false;
386
+ } else {
387
+ float count = 0;
388
+ if (xt_css_parse_number(token, &count)) spec.iterations = count;
389
+ else spec.name = token;
390
+ }
391
+ }
392
+ out->push_back(spec);
393
+ }
394
+ return true;
395
+ }
396
+
258
397
  bool xt_style_apply_effect(XtStyle *style, const std::string &property,
259
398
  const std::string &value) {
260
399
  if (property == "filter") return parseFilterList(value, &style->filters);
261
400
  if (property == "backdrop-filter") return parseFilterList(value, &style->backdrop_filters);
262
401
  if (property == "text-shadow") return parseTextShadow(value, &style->text_shadow);
402
+ if (property == "box-shadow") return parseBoxShadow(value, &style->box_shadow);
403
+ if (property == "animation") {
404
+ parseAnimationShorthand(value, &style->animations);
405
+ return true;
406
+ }
263
407
  if (property == "content") {
264
408
  std::string text = xt_css_lower(xt_css_trim(value));
265
409
  if (text == "none" || text == "normal") {
@@ -283,7 +427,11 @@ bool xt_style_apply_transition(XtStyle *style, const std::string &property,
283
427
  if (property == "transition-property") {
284
428
  std::vector<std::string> parts = splitCommas(value);
285
429
  style->transitions.resize(parts.size());
286
- for (size_t i = 0; i < parts.size(); i++) style->transitions[i].property = xt_css_lower(parts[i]);
430
+ for (size_t i = 0; i < parts.size(); i++) {
431
+ style->transitions[i].property = xt_css_lower(parts[i]);
432
+ if (style->transitions[i].property == "background")
433
+ style->transitions[i].property = "background-color";
434
+ }
287
435
  return true;
288
436
  }
289
437
  if (property == "transition-duration" || property == "transition-delay") {
@@ -301,7 +449,7 @@ bool xt_style_apply_transition(XtStyle *style, const std::string &property,
301
449
  std::vector<std::string> parts = splitCommas(value);
302
450
  style->transitions.resize(parts.size());
303
451
  for (size_t i = 0; i < parts.size(); i++) {
304
- TimingFunction timing = TimingFunction::Ease;
452
+ TimingFunction timing;
305
453
  if (parseTimingFunction(parts[i], &timing)) style->transitions[i].timing = timing;
306
454
  }
307
455
  return true;
@@ -309,4 +457,80 @@ bool xt_style_apply_transition(XtStyle *style, const std::string &property,
309
457
  return false;
310
458
  }
311
459
 
460
+ bool xt_style_apply_animation(XtStyle *style, const std::string &property,
461
+ const std::string &value) {
462
+ if (property == "animation-name") {
463
+ std::vector<std::string> parts = splitCommas(value);
464
+ style->animations.resize(parts.size());
465
+ for (size_t i = 0; i < parts.size(); i++) {
466
+ style->animations[i].name = parts[i];
467
+ }
468
+ return true;
469
+ }
470
+ if (property == "animation-duration" || property == "animation-delay") {
471
+ std::vector<std::string> parts = splitCommas(value);
472
+ style->animations.resize(parts.size());
473
+ for (size_t i = 0; i < parts.size(); i++) {
474
+ float seconds = 0;
475
+ if (!parseTimeSeconds(parts[i], &seconds)) continue;
476
+ if (property == "animation-duration") style->animations[i].duration = seconds;
477
+ else style->animations[i].delay = seconds;
478
+ }
479
+ return true;
480
+ }
481
+ if (property == "animation-timing-function") {
482
+ std::vector<std::string> parts = splitCommas(value);
483
+ style->animations.resize(parts.size());
484
+ for (size_t i = 0; i < parts.size(); i++) {
485
+ TimingFunction timing;
486
+ if (parseTimingFunction(parts[i], &timing)) style->animations[i].timing = timing;
487
+ }
488
+ return true;
489
+ }
490
+ if (property == "animation-iteration-count") {
491
+ std::vector<std::string> parts = splitCommas(value);
492
+ style->animations.resize(parts.size());
493
+ for (size_t i = 0; i < parts.size(); i++) {
494
+ std::string lower = xt_css_lower(parts[i]);
495
+ float count = 0;
496
+ if (lower == "infinite") style->animations[i].iterations = -1.0f;
497
+ else if (xt_css_parse_number(parts[i], &count)) style->animations[i].iterations = count;
498
+ }
499
+ return true;
500
+ }
501
+ if (property == "animation-direction") {
502
+ static const char *const names[] = {"normal", "reverse", "alternate", "alternate-reverse"};
503
+ std::vector<std::string> parts = splitCommas(value);
504
+ style->animations.resize(parts.size());
505
+ for (size_t i = 0; i < parts.size(); i++) {
506
+ std::string lower = xt_css_lower(parts[i]);
507
+ for (int d = 0; d < 4; d++) {
508
+ if (lower == names[d]) style->animations[i].direction = (AnimationDirection)d;
509
+ }
510
+ }
511
+ return true;
512
+ }
513
+ if (property == "animation-fill-mode") {
514
+ static const char *const names[] = {"none", "forwards", "backwards", "both"};
515
+ std::vector<std::string> parts = splitCommas(value);
516
+ style->animations.resize(parts.size());
517
+ for (size_t i = 0; i < parts.size(); i++) {
518
+ std::string lower = xt_css_lower(parts[i]);
519
+ for (int f = 0; f < 4; f++) {
520
+ if (lower == names[f]) style->animations[i].fill = (AnimationFillMode)f;
521
+ }
522
+ }
523
+ return true;
524
+ }
525
+ if (property == "animation-play-state") {
526
+ std::vector<std::string> parts = splitCommas(value);
527
+ style->animations.resize(parts.size());
528
+ for (size_t i = 0; i < parts.size(); i++) {
529
+ style->animations[i].paused = xt_css_lower(parts[i]) == "paused";
530
+ }
531
+ return true;
532
+ }
533
+ return false;
534
+ }
535
+
312
536
  } // namespace xtgui
@@ -0,0 +1,28 @@
1
+ /*
2
+ * xbintsc GUI engine — `gpu` link feature registration bridge.
3
+ *
4
+ * This tiny C translation unit is compiled and linked *only* when the compiler
5
+ * decides the program may compile GLSL (`getContext("webgl…")`,
6
+ * `mix-blend-mode`, or markup built at run time). Its constructor calls
7
+ * `xt_gpu_feature_register` in `gpu_feature.cpp`, which installs the shader
8
+ * table and references glslang/SPIRV-Cross; those references are what make the
9
+ * linker extract the translator members from `gui.a`.
10
+ *
11
+ * When the feature is inactive this object is absent, so nothing references the
12
+ * translator and the table in `gpu_shader.cpp` stays null.
13
+ */
14
+
15
+ extern void xt_gpu_feature_register(void);
16
+
17
+ #if defined(__GNUC__) || defined(__clang__)
18
+ /* clang targeting Windows (the toolchain xbintsc uses there) implements this
19
+ * attribute with a `.CRT$XCU` entry, which the MSVC CRT runs before `main`. */
20
+ __attribute__((constructor)) static void xt_gpu_feature_boot(void) {
21
+ xt_gpu_feature_register();
22
+ }
23
+ #elif defined(_MSC_VER)
24
+ void xt_gpu_feature_boot(void);
25
+ #pragma section(".CRT$XCU", read)
26
+ __declspec(allocate(".CRT$XCU")) static void (*const xt_gpu_feature_ctor)(void) = xt_gpu_feature_boot;
27
+ void xt_gpu_feature_boot(void) { xt_gpu_feature_register(); }
28
+ #endif
@@ -0,0 +1,30 @@
1
+ /*
2
+ * xbintsc GUI engine — `gpu` link feature implementation.
3
+ *
4
+ * This translation unit is the only place in `gui.a` that references glslang and
5
+ * SPIRV-Cross. It is pulled out of the archive only when `gpu_bridge.c` (the
6
+ * feature's C registration object) is linked, which happens only when the
7
+ * compiler detects that the program may compile shaders. Without it the engine
8
+ * sees a null table (`gpu_shader.cpp`) and no glslang member is extracted.
9
+ */
10
+
11
+ #include "gpu_shader_api.h"
12
+ #include "webgl_msl.h"
13
+
14
+ namespace {
15
+
16
+ const xtgui::GpuShaderApi kGpuShaderApi = {
17
+ xtgui::unroll_glsl_pragmas,
18
+ xtgui::scan_glsl,
19
+ xtgui::rewrite_glsl,
20
+ xtgui::compile_glsl,
21
+ xtgui::reflect_uniform_block,
22
+ xtgui::reflect_attributes,
23
+ xtgui::cross_compile_msl,
24
+ };
25
+
26
+ } // namespace
27
+
28
+ extern "C" void xt_gpu_feature_register(void) {
29
+ xtgui::gpu_shader_install(&kGpuShaderApi);
30
+ }
@@ -0,0 +1,22 @@
1
+ /*
2
+ * xbintsc GUI engine — core-side registry for the optional GPU shader table.
3
+ *
4
+ * Kept in `gui.a` and free of any glslang/SPIRV-Cross dependency: the table is
5
+ * installed by the `gpu` feature's bridge when that feature is linked, and stays
6
+ * null otherwise. See `gpu_shader_api.h`.
7
+ */
8
+
9
+ #include "gpu_shader_api.h"
10
+
11
+ namespace xtgui {
12
+ namespace {
13
+
14
+ const GpuShaderApi *g_api = nullptr;
15
+
16
+ } // namespace
17
+
18
+ void gpu_shader_install(const GpuShaderApi *api) { g_api = api; }
19
+
20
+ const GpuShaderApi *gpu_shader_api() { return g_api; }
21
+
22
+ } // namespace xtgui
@@ -0,0 +1,50 @@
1
+ /*
2
+ * xbintsc GUI engine — optional GPU shader translation table.
3
+ *
4
+ * WebGL shaders and the `mix-blend-mode` compositor both need GLSL→SPIR-V
5
+ * translation (glslang), and on Metal a second SPIR-V→MSL pass (SPIRV-Cross).
6
+ * Those are the largest optional third-party dependencies in the GUI engine, so
7
+ * the engine reaches them through this table instead of linking them directly.
8
+ *
9
+ * The implementation lives in `gpu_feature.cpp` and is only pulled out of
10
+ * `gui.a` when the `gpu` link feature's bridge is linked. Without it the table is
11
+ * null: the blend compositor falls back to ordinary painting and WebGL reports a
12
+ * shader compile log, but a DOM/2D-canvas page still runs.
13
+ */
14
+ #ifndef XT_GUI_GPU_SHADER_API_H
15
+ #define XT_GUI_GPU_SHADER_API_H
16
+
17
+ #include <string>
18
+ #include <unordered_map>
19
+ #include <vector>
20
+
21
+ #include "webgl_glsl.h"
22
+
23
+ namespace xtgui {
24
+
25
+ /** The GLSL preprocessing/translation/reflection entry points the engine calls
26
+ * on demand. */
27
+ struct GpuShaderApi {
28
+ std::string (*unroll_glsl_pragmas)(const std::string &source);
29
+ void (*scan_glsl)(const std::string &source, bool fragment, GlslShaderInfo *info);
30
+ std::string (*rewrite_glsl)(const std::string &source, bool fragment,
31
+ const GlslLinkLayout &layout, const GlslShaderInfo &info);
32
+ bool (*compile_glsl)(const std::string &source, bool fragment,
33
+ std::vector<uint32_t> *spirv, std::string *log);
34
+ bool (*reflect_uniform_block)(const std::vector<uint32_t> &spirv,
35
+ std::vector<GlslBlockMember> *out);
36
+ bool (*reflect_attributes)(const std::vector<uint32_t> &spirv,
37
+ std::unordered_map<std::string, int> *out);
38
+ bool (*cross_compile_msl)(const std::vector<uint32_t> &spirv, bool fragment,
39
+ std::string *source, std::string *entry, std::string *log);
40
+ };
41
+
42
+ /** Install the table. Called by the `gpu` feature bridge before `main`. */
43
+ void gpu_shader_install(const GpuShaderApi *api);
44
+
45
+ /** The installed table, or null when the `gpu` feature was not linked. */
46
+ const GpuShaderApi *gpu_shader_api();
47
+
48
+ } // namespace xtgui
49
+
50
+ #endif /* XT_GUI_GPU_SHADER_API_H */
@@ -474,10 +474,34 @@ void xt_style_apply_background(XtStyle *style, const std::string &value) {
474
474
  std::vector<BackgroundLayer> layers;
475
475
  parseLayers(value, &layers, &color, &hasColor, true);
476
476
  style->background_layers = std::move(layers);
477
+ style->background_sizes.clear();
477
478
  if (hasColor) {
478
479
  style->background = color;
479
480
  style->has_background = true;
480
481
  }
481
482
  }
482
483
 
484
+ bool xt_style_apply_background_size(XtStyle *style, const std::string &value) {
485
+ std::vector<BackgroundSize> sizes;
486
+ for (const std::string &part : splitCommas(value)) {
487
+ std::vector<std::string> tokens = splitTokens(part);
488
+ if (tokens.empty()) continue;
489
+ BackgroundSize size;
490
+ for (size_t i = 0; i < tokens.size() && i < 2; i++) {
491
+ std::string token = xt_css_lower(xt_css_trim(tokens[i]));
492
+ Length length{0.0f, Unit::Auto};
493
+ if (token != "auto" && token != "cover" && token != "contain") {
494
+ Length parsed = xt_css_parse_length(token);
495
+ if (parsed.unit != Unit::Invalid && parsed.unit != Unit::None) length = parsed;
496
+ }
497
+ if (i == 0) size.width = length;
498
+ else size.height = length;
499
+ }
500
+ if (tokens.size() == 1) size.height = Length{0.0f, Unit::Auto};
501
+ sizes.push_back(size);
502
+ }
503
+ style->background_sizes = std::move(sizes);
504
+ return true;
505
+ }
506
+
483
507
  } // namespace xtgui
@@ -223,6 +223,29 @@ bool xt_style_apply_grid_property(XtStyle *style, const std::string &property,
223
223
  parseGridLine(value, &style->grid_row_start, &style->grid_row_end);
224
224
  return true;
225
225
  }
226
+ if (property == "grid-area") {
227
+ std::vector<std::string> parts;
228
+ std::string current;
229
+ int depth = 0;
230
+ for (char c : value) {
231
+ if (c == '(') depth++;
232
+ if (c == ')') depth--;
233
+ if (c == '/' && depth == 0) {
234
+ parts.push_back(xt_css_trim(current));
235
+ current.clear();
236
+ } else {
237
+ current.push_back(c);
238
+ }
239
+ }
240
+ parts.push_back(xt_css_trim(current));
241
+ style->grid_row_start = style->grid_column_start = 0;
242
+ style->grid_row_end = style->grid_column_end = 0;
243
+ if (parts.size() >= 1) parseGridLineNumber(parts[0], &style->grid_row_start);
244
+ if (parts.size() >= 2) parseGridLineNumber(parts[1], &style->grid_column_start);
245
+ if (parts.size() >= 3) parseGridLineNumber(parts[2], &style->grid_row_end);
246
+ if (parts.size() >= 4) parseGridLineNumber(parts[3], &style->grid_column_end);
247
+ return true;
248
+ }
226
249
 
227
250
  return false;
228
251
  }
@@ -15,7 +15,7 @@
15
15
 
16
16
  #include "gui_internal.h"
17
17
 
18
- #include "image.h"
18
+ #include "image_api.h"
19
19
  #include "paint.h"
20
20
 
21
21
  #include <stdio.h>
@@ -36,40 +36,9 @@ int g_window_count = 0;
36
36
  * `createWindow`), so the next `createWindow` adopts it. */
37
37
  int g_implicit_window = 0;
38
38
 
39
- /* Optional application icon embedded at compile time by the driver (`--icon` /
40
- * `app.icon`). The symbols live in a small object the driver links into every
41
- * GUI program (an empty object when no icon is configured), so the references
42
- * are plain: `xt_app_icon_size == 0` means "no icon". */
43
- extern "C" {
44
- extern const unsigned char xt_app_icon_data[];
45
- extern const unsigned long long xt_app_icon_size;
46
- extern const char xt_app_icon_format[];
47
- extern const unsigned int xt_app_icon_width;
48
- extern const unsigned int xt_app_icon_height;
49
- }
50
-
51
- /**
52
- * Set the window / taskbar / Dock icon from the embedded PNG, if any. Decoding
53
- * goes through the same stb_image path as `<img>`, so the icon needs no extra
54
- * dependency. Failures are silent: an icon is cosmetic and must never stop the
55
- * program from starting.
56
- */
57
- void xt_gui_apply_icon(SDL_Window *window) {
58
- if (window == NULL) return;
59
- size_t size = (size_t)xt_app_icon_size;
60
- if (size == 0) return;
61
- if (strcmp(xt_app_icon_format, "png") != 0) return;
62
-
63
- xtgui::Image image;
64
- if (!xtgui::xt_image_decode(xt_app_icon_data, size, &image)) return;
65
- if (image.width <= 0 || image.height <= 0) return;
66
-
67
- SDL_Surface *surface = SDL_CreateSurfaceFrom(
68
- image.width, image.height, SDL_PIXELFORMAT_RGBA32, image.pixels.data(), image.width * 4);
69
- if (surface == NULL) return;
70
- SDL_SetWindowIcon(window, surface);
71
- SDL_DestroySurface(surface);
72
- }
39
+ /* The application icon is decoded and applied by the optional `images` feature
40
+ * (`XtImageApi::apply_icon`), which is forced on when an icon is configured.
41
+ * Keeping it there lets a text-only program drop SDL's surface/blit code. */
73
42
 
74
43
  /* -- garbage collector roots ---------------------------------------------- */
75
44
 
@@ -247,7 +216,7 @@ static XtGuiWindow *create_window_record(const char *title, int width, int heigh
247
216
  SDL_DestroyWindow(window);
248
217
  return NULL;
249
218
  }
250
- xt_gui_apply_icon(window);
219
+ if (const XtImageApi *images = xt_image_api()) images->apply_icon(window);
251
220
 
252
221
  int index = g_window_count++;
253
222
  XtGuiWindow *record = &g_windows[index];
@@ -470,7 +439,7 @@ extern "C" xt_value xt_gui_run(int32_t argc, xt_value *argv) {
470
439
  }
471
440
  /* Drop the decoded-image caches (including parsed SVG documents) now that
472
441
  * the GPU textures that referenced them are gone. */
473
- xtgui::xt_image_shutdown();
442
+ if (const XtImageApi *images = xt_image_api()) images->shutdown();
474
443
  g_window_count = 0;
475
444
  return XT_UNDEFINED;
476
445
  }