xbintsc 0.3.18 → 0.3.35

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 (194) hide show
  1. package/README.md +33 -0
  2. package/dist/src/cli/main.js +124 -30
  3. package/dist/src/cli/main.js.map +1 -1
  4. package/dist/src/codegen/generator/calls/access.js +8 -1
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/state.js +20 -0
  7. package/dist/src/codegen/generator/state.js.map +1 -1
  8. package/dist/src/codegen/generator/tables.d.ts +6 -0
  9. package/dist/src/codegen/generator/tables.js +38 -0
  10. package/dist/src/codegen/generator/tables.js.map +1 -1
  11. package/dist/src/driver/bundler/commonjs.d.ts +52 -0
  12. package/dist/src/driver/bundler/commonjs.js +493 -0
  13. package/dist/src/driver/bundler/commonjs.js.map +1 -0
  14. package/dist/src/driver/bundler/graph.d.ts +2 -1
  15. package/dist/src/driver/bundler/graph.js +32 -3
  16. package/dist/src/driver/bundler/graph.js.map +1 -1
  17. package/dist/src/driver/bundler/merge.d.ts +2 -1
  18. package/dist/src/driver/bundler/merge.js +16 -2
  19. package/dist/src/driver/bundler/merge.js.map +1 -1
  20. package/dist/src/driver/bundler/types.d.ts +8 -0
  21. package/dist/src/driver/compiler.d.ts +8 -1
  22. package/dist/src/driver/compiler.js +75 -7
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/config.d.ts +53 -0
  25. package/dist/src/driver/config.js +141 -0
  26. package/dist/src/driver/config.js.map +1 -0
  27. package/dist/src/driver/icon.d.ts +62 -0
  28. package/dist/src/driver/icon.js +151 -0
  29. package/dist/src/driver/icon.js.map +1 -0
  30. package/dist/src/driver/index.d.ts +4 -0
  31. package/dist/src/driver/index.js +4 -0
  32. package/dist/src/driver/index.js.map +1 -1
  33. package/dist/src/driver/mac-bundle.d.ts +41 -0
  34. package/dist/src/driver/mac-bundle.js +123 -0
  35. package/dist/src/driver/mac-bundle.js.map +1 -0
  36. package/dist/src/driver/win-icon.d.ts +27 -0
  37. package/dist/src/driver/win-icon.js +80 -0
  38. package/dist/src/driver/win-icon.js.map +1 -0
  39. package/dist/src/extensions/gui/html.d.ts +25 -0
  40. package/dist/src/extensions/gui/html.js +203 -0
  41. package/dist/src/extensions/gui/html.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +11 -0
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/registry.d.ts +29 -0
  45. package/dist/src/extensions/registry.js +14 -0
  46. package/dist/src/extensions/registry.js.map +1 -1
  47. package/dist/src/index.d.ts +1 -1
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/lexer/scanner/identifiers.js +4 -12
  50. package/dist/src/lexer/scanner/identifiers.js.map +1 -1
  51. package/dist/src/parser/statements.js +17 -4
  52. package/dist/src/parser/statements.js.map +1 -1
  53. package/dist/tests/cli/main.test.js +46 -1
  54. package/dist/tests/cli/main.test.js.map +1 -1
  55. package/dist/tests/driver/bundler-resolve.test.d.ts +1 -0
  56. package/dist/tests/driver/bundler-resolve.test.js +230 -0
  57. package/dist/tests/driver/bundler-resolve.test.js.map +1 -0
  58. package/dist/tests/driver/config.test.d.ts +1 -0
  59. package/dist/tests/driver/config.test.js +124 -0
  60. package/dist/tests/driver/config.test.js.map +1 -0
  61. package/dist/tests/driver/icon.test.d.ts +1 -0
  62. package/dist/tests/driver/icon.test.js +421 -0
  63. package/dist/tests/driver/icon.test.js.map +1 -0
  64. package/dist/tests/driver/modules.test.js +177 -0
  65. package/dist/tests/driver/modules.test.js.map +1 -1
  66. package/dist/tests/e2e/fetch.test.d.ts +12 -0
  67. package/dist/tests/e2e/fetch.test.js +272 -0
  68. package/dist/tests/e2e/fetch.test.js.map +1 -0
  69. package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
  70. package/dist/tests/e2e/gui-dom.test.js +102 -0
  71. package/dist/tests/e2e/gui-dom.test.js.map +1 -0
  72. package/dist/tests/e2e/gui-events.test.d.ts +5 -0
  73. package/dist/tests/e2e/gui-events.test.js +60 -0
  74. package/dist/tests/e2e/gui-events.test.js.map +1 -0
  75. package/dist/tests/e2e/gui-helpers.d.ts +31 -0
  76. package/dist/tests/e2e/gui-helpers.js +74 -0
  77. package/dist/tests/e2e/gui-helpers.js.map +1 -0
  78. package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
  79. package/dist/tests/e2e/gui-layout.test.js +88 -0
  80. package/dist/tests/e2e/gui-layout.test.js.map +1 -0
  81. package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
  82. package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
  83. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
  84. package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
  85. package/dist/tests/e2e/gui-paint.test.js +111 -0
  86. package/dist/tests/e2e/gui-paint.test.js.map +1 -0
  87. package/dist/tests/e2e/gui-script.test.d.ts +5 -0
  88. package/dist/tests/e2e/gui-script.test.js +78 -0
  89. package/dist/tests/e2e/gui-script.test.js.map +1 -0
  90. package/dist/tests/e2e/gui-style.test.d.ts +5 -0
  91. package/dist/tests/e2e/gui-style.test.js +175 -0
  92. package/dist/tests/e2e/gui-style.test.js.map +1 -0
  93. package/dist/tests/e2e/icon.test.d.ts +7 -0
  94. package/dist/tests/e2e/icon.test.js +80 -0
  95. package/dist/tests/e2e/icon.test.js.map +1 -0
  96. package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
  97. package/dist/tests/e2e/npm-modules.test.js +47 -1
  98. package/dist/tests/e2e/npm-modules.test.js.map +1 -1
  99. package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
  100. package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
  101. package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
  102. package/dist/tests/extensions/gui-index.test.d.ts +1 -0
  103. package/dist/tests/extensions/gui-index.test.js +49 -0
  104. package/dist/tests/extensions/gui-index.test.js.map +1 -0
  105. package/dist/tests/extensions/gui.test.d.ts +1 -0
  106. package/dist/tests/extensions/gui.test.js +177 -0
  107. package/dist/tests/extensions/gui.test.js.map +1 -0
  108. package/dist/tests/extensions/registry.test.js +36 -0
  109. package/dist/tests/extensions/registry.test.js.map +1 -1
  110. package/dist/tests/parser/statements.test.js +11 -0
  111. package/dist/tests/parser/statements.test.js.map +1 -1
  112. package/dist/tests/public-api.test.d.ts +1 -0
  113. package/dist/tests/public-api.test.js +31 -0
  114. package/dist/tests/public-api.test.js.map +1 -0
  115. package/package.json +1 -1
  116. package/runtime/ext_gui/css.cpp +4 -517
  117. package/runtime/ext_gui/css_internal.h +27 -0
  118. package/runtime/ext_gui/css_match.cpp +195 -0
  119. package/runtime/ext_gui/css_selector.cpp +207 -0
  120. package/runtime/ext_gui/css_sheet.cpp +129 -0
  121. package/runtime/ext_gui/document.cpp +90 -0
  122. package/runtime/ext_gui/document.h +23 -0
  123. package/runtime/ext_gui/dom.cpp +43 -403
  124. package/runtime/ext_gui/dom.h +7 -0
  125. package/runtime/ext_gui/dom_api.cpp +164 -0
  126. package/runtime/ext_gui/dom_api.h +12 -0
  127. package/runtime/ext_gui/dom_api_document.cpp +96 -0
  128. package/runtime/ext_gui/dom_api_events.cpp +123 -0
  129. package/runtime/ext_gui/dom_api_internal.h +135 -0
  130. package/runtime/ext_gui/dom_api_node.cpp +329 -0
  131. package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
  132. package/runtime/ext_gui/dom_api_proto.cpp +130 -0
  133. package/runtime/ext_gui/dom_api_style.cpp +192 -0
  134. package/runtime/ext_gui/dom_internal.h +39 -0
  135. package/runtime/ext_gui/dom_parse.cpp +348 -0
  136. package/runtime/ext_gui/dom_serialize.cpp +54 -0
  137. package/runtime/ext_gui/gui.cpp +61 -245
  138. package/runtime/ext_gui/gui_engine.h +54 -0
  139. package/runtime/ext_gui/gui_events.cpp +294 -0
  140. package/runtime/ext_gui/gui_internal.h +29 -0
  141. package/runtime/ext_gui/layout.cpp +6 -597
  142. package/runtime/ext_gui/layout_flex.cpp +200 -0
  143. package/runtime/ext_gui/layout_flow.cpp +191 -0
  144. package/runtime/ext_gui/layout_flow_internal.h +149 -0
  145. package/runtime/ext_gui/layout_inline.cpp +181 -0
  146. package/runtime/ext_gui/layout_internal.h +20 -0
  147. package/runtime/ext_gui/renderer.cpp +15 -679
  148. package/runtime/ext_gui/renderer_image.cpp +233 -0
  149. package/runtime/ext_gui/renderer_internal.h +112 -0
  150. package/runtime/ext_gui/renderer_shaders.h +108 -0
  151. package/runtime/ext_gui/renderer_text.cpp +342 -0
  152. package/runtime/ext_gui/script.cpp +63 -0
  153. package/runtime/ext_gui/style.cpp +12 -660
  154. package/runtime/ext_gui/style_internal.h +33 -0
  155. package/runtime/ext_gui/style_print.cpp +113 -0
  156. package/runtime/ext_gui/style_ua.cpp +98 -0
  157. package/runtime/ext_gui/style_values.cpp +468 -0
  158. package/runtime/ext_gui/window.cpp +60 -0
  159. package/runtime/rt.h +6 -0
  160. package/runtime/rt_internal.h +4 -0
  161. package/runtime/xt_builtins.c +1 -0
  162. package/runtime/xt_containers/access.inc +32 -1
  163. package/runtime/xt_fetch/buffer.inc +64 -0
  164. package/runtime/xt_fetch/headers.inc +312 -0
  165. package/runtime/xt_fetch/request.inc +386 -0
  166. package/runtime/xt_fetch/response.inc +107 -0
  167. package/runtime/xt_fetch/socket.inc +72 -0
  168. package/runtime/xt_fetch/url.inc +108 -0
  169. package/runtime/xt_fetch.c +39 -0
  170. package/runtime/xt_stdlib2/set.inc +1 -1
  171. package/runtime/xt_typed_array.c +614 -0
  172. package/src/cli/main.ts +137 -29
  173. package/src/codegen/generator/calls/access.ts +9 -1
  174. package/src/codegen/generator/state.ts +20 -0
  175. package/src/codegen/generator/tables.ts +40 -0
  176. package/src/driver/bundler/commonjs.ts +553 -0
  177. package/src/driver/bundler/graph.ts +35 -2
  178. package/src/driver/bundler/merge.ts +18 -1
  179. package/src/driver/bundler/types.ts +8 -0
  180. package/src/driver/compiler.ts +85 -7
  181. package/src/driver/config.ts +173 -0
  182. package/src/driver/icon.ts +184 -0
  183. package/src/driver/index.ts +30 -0
  184. package/src/driver/mac-bundle.ts +156 -0
  185. package/src/driver/win-icon.ts +96 -0
  186. package/src/extensions/gui/html.ts +236 -0
  187. package/src/extensions/gui/index.ts +11 -0
  188. package/src/extensions/registry.ts +39 -0
  189. package/src/index.ts +1 -1
  190. package/src/lexer/scanner/identifiers.ts +4 -11
  191. package/src/parser/statements.ts +17 -4
  192. package/dist/tests/e2e/gui.test.d.ts +0 -12
  193. package/dist/tests/e2e/gui.test.js +0 -493
  194. package/dist/tests/e2e/gui.test.js.map +0 -1
@@ -8,25 +8,65 @@
8
8
  *
9
9
  * The engine is deliberately free of any compiler knowledge: it only uses the
10
10
  * public runtime ABI (`rt.h`).
11
+ *
12
+ * Input dispatch and per-frame rendering live in `gui_events.cpp`; the shared
13
+ * process state is declared in `gui_internal.h`.
11
14
  */
12
15
 
13
- #include "gui_engine.h"
16
+ #include "gui_internal.h"
17
+
18
+ #include "image.h"
14
19
  #include "paint.h"
15
20
 
16
21
  #include <stdio.h>
17
22
  #include <string.h>
18
23
 
19
- #define XT_GUI_MAX_WINDOWS 64
24
+ namespace guidev {
25
+
26
+ SDL_GPUDevice *g_device = NULL;
27
+ int g_sdl_initialized = 0;
28
+ int g_quit = 0;
29
+ XtGuiWindow g_windows[XT_GUI_MAX_WINDOWS];
30
+ int g_window_count = 0;
31
+
32
+ /* Optional application icon embedded at compile time by the driver (`--icon` /
33
+ * `app.icon`). The symbols live in a small object the driver links into every
34
+ * GUI program (an empty object when no icon is configured), so the references
35
+ * are plain: `xt_app_icon_size == 0` means "no icon". */
36
+ extern "C" {
37
+ extern const unsigned char xt_app_icon_data[];
38
+ extern const unsigned long long xt_app_icon_size;
39
+ extern const char xt_app_icon_format[];
40
+ extern const unsigned int xt_app_icon_width;
41
+ extern const unsigned int xt_app_icon_height;
42
+ }
20
43
 
21
- static SDL_GPUDevice *g_device = NULL;
22
- static int g_sdl_initialized = 0;
23
- static int g_quit = 0;
24
- static XtGuiWindow g_windows[XT_GUI_MAX_WINDOWS];
25
- static int g_window_count = 0;
44
+ /**
45
+ * Set the window / taskbar / Dock icon from the embedded PNG, if any. Decoding
46
+ * goes through the same stb_image path as `<img>`, so the icon needs no extra
47
+ * dependency. Failures are silent: an icon is cosmetic and must never stop the
48
+ * program from starting.
49
+ */
50
+ void xt_gui_apply_icon(SDL_Window *window) {
51
+ if (window == NULL) return;
52
+ size_t size = (size_t)xt_app_icon_size;
53
+ if (size == 0) return;
54
+ if (strcmp(xt_app_icon_format, "png") != 0) return;
55
+
56
+ xtgui::Image image;
57
+ if (!xtgui::xt_image_decode(xt_app_icon_data, size, &image)) return;
58
+ if (image.width <= 0 || image.height <= 0) return;
59
+
60
+ SDL_Surface *surface = SDL_CreateSurfaceFrom(
61
+ image.width, image.height, SDL_PIXELFORMAT_RGBA32, image.pixels.data(), image.width * 4);
62
+ if (surface == NULL) return;
63
+ SDL_SetWindowIcon(window, surface);
64
+ SDL_DestroySurface(surface);
65
+ }
26
66
 
27
67
  /* -- initialisation ------------------------------------------------------- */
28
68
 
29
- static int xt_gui_ensure_init(void) {
69
+ static int ensure_init(void) {
30
70
  if (g_sdl_initialized) return 1;
31
71
  if (!SDL_Init(SDL_INIT_VIDEO)) {
32
72
  fprintf(stderr, "xt_gui: SDL_Init failed: %s\n", SDL_GetError());
@@ -48,7 +88,7 @@ static int xt_gui_ensure_init(void) {
48
88
 
49
89
  /* -- window lookup -------------------------------------------------------- */
50
90
 
51
- static XtGuiWindow *xt_gui_window_by_object(xt_value object) {
91
+ static XtGuiWindow *window_by_object(xt_value object) {
52
92
  xt_value index_value = xt_object_get_cstr(object, "__xt_gui_index");
53
93
  if (!XT_IS_NUMBER(index_value)) return NULL;
54
94
  int index = (int)xt_to_number(index_value);
@@ -56,39 +96,21 @@ static XtGuiWindow *xt_gui_window_by_object(xt_value object) {
56
96
  return &g_windows[index];
57
97
  }
58
98
 
59
- XtGuiWindow *xt_gui_window_from_this(xt_value thisValue) {
60
- return xt_gui_window_by_object(thisValue);
61
- }
62
-
63
- /* -- events --------------------------------------------------------------- */
99
+ } // namespace guidev
64
100
 
65
- void xt_gui_emit(XtGuiWindow *win, const char *event) {
66
- xt_gui_emit_payload(win, event, XT_UNDEFINED);
67
- }
101
+ using namespace guidev;
68
102
 
69
- void xt_gui_emit_payload(XtGuiWindow *win, const char *event, xt_value payload) {
70
- if (win == NULL || !XT_IS_OBJECT(win->object)) return;
71
- xt_value listeners = xt_object_get_cstr(win->object, "__xt_gui_events");
72
- if (!XT_IS_OBJECT(listeners)) return;
73
- xt_value array = xt_object_get(listeners, xt_string_from_cstr(event));
74
- if (!XT_IS_ARRAY(array)) return;
75
- xt_value args[1];
76
- int32_t count = 0;
77
- if (!XT_IS_UNDEFINED(payload)) {
78
- args[0] = payload;
79
- count = 1;
80
- }
81
- int32_t listener_count = (int32_t)xt_to_number(xt_array_length(array));
82
- for (int32_t i = 0; i < listener_count; i++) {
83
- xt_value fn = xt_array_get(array, xt_number((double)i));
84
- if (XT_IS_FUNCTION(fn)) xt_call_with_this(fn, win->object, count, args);
85
- }
103
+ XtGuiWindow *xt_gui_window_from_this(xt_value thisValue) {
104
+ return window_by_object(thisValue);
86
105
  }
87
106
 
88
107
  void xt_gui_quit_window(XtGuiWindow *win) {
89
108
  if (win == NULL || !win->open) return;
90
109
  win->open = 0;
91
110
  xt_gui_emit(win, "close");
111
+ win->animation_frames.clear();
112
+ xt_gui_handles_reset(win);
113
+ win->document_object = XT_UNDEFINED;
92
114
  win->document.reset();
93
115
  win->html.clear();
94
116
  if (g_device != NULL) {
@@ -111,215 +133,6 @@ void xt_gui_window_viewport(XtGuiWindow *win, float *width, float *height) {
111
133
  if (height != NULL) *height = (float)h;
112
134
  }
113
135
 
114
- static XtGuiWindow *xt_gui_find_by_window_id(SDL_WindowID id) {
115
- SDL_Window *window = SDL_GetWindowFromID(id);
116
- if (window == NULL) return NULL;
117
- for (int i = 0; i < g_window_count; i++) {
118
- if (g_windows[i].window == window) return &g_windows[i];
119
- }
120
- return NULL;
121
- }
122
-
123
- /* -- input events --------------------------------------------------------- */
124
-
125
- /** Deepest element under (x, y), or NULL. */
126
- static const xtgui::Node *xt_gui_hit_node(XtGuiWindow *win, float x, float y) {
127
- if (win->document == nullptr) return nullptr;
128
- const xtgui::LayoutBox *box = win->document->layout().hitTest(x, y);
129
- const xtgui::Node *node = box != nullptr ? box->node : nullptr;
130
- while (node != nullptr && !node->isElement()) node = node->parent;
131
- return node;
132
- }
133
-
134
- /** Deepest element under (x, y) as a CSS-like descriptor, or an empty string. */
135
- std::string xt_gui_hit_test(XtGuiWindow *win, float x, float y) {
136
- const xtgui::Node *node = xt_gui_hit_node(win, x, y);
137
- return node == nullptr ? std::string() : xtgui::xt_dom_describe(node);
138
- }
139
-
140
- /** Update the hovered element from the pointer position; marks paint dirty. */
141
- static void xt_gui_update_hover(XtGuiWindow *win, float x, float y) {
142
- if (win->document == nullptr) return;
143
- if (win->document->setHover(xt_gui_hit_node(win, x, y))) win->geometry.dirty = 1;
144
- }
145
-
146
- static xt_value xt_gui_pointer_payload(const char *type, XtGuiWindow *win, float x, float y,
147
- int button, int clicks) {
148
- xt_value payload = xt_object_new();
149
- xt_object_set(payload, xt_string_from_cstr("type"), xt_string_from_cstr(type));
150
- xt_object_set(payload, xt_string_from_cstr("x"), xt_number(x));
151
- xt_object_set(payload, xt_string_from_cstr("y"), xt_number(y));
152
- xt_object_set(payload, xt_string_from_cstr("clientX"), xt_number(x));
153
- xt_object_set(payload, xt_string_from_cstr("clientY"), xt_number(y));
154
- xt_object_set(payload, xt_string_from_cstr("button"), xt_number(button));
155
- xt_object_set(payload, xt_string_from_cstr("clicks"), xt_number(clicks));
156
- std::string target = xt_gui_hit_test(win, x, y);
157
- if (!target.empty()) {
158
- xt_object_set(payload, xt_string_from_cstr("target"), xt_string_from_cstr(target.c_str()));
159
- }
160
- return payload;
161
- }
162
-
163
- void xt_gui_dispatch_pointer(XtGuiWindow *win, const char *type, float x, float y, int button,
164
- int clicks) {
165
- if (win == NULL || !win->open) return;
166
- xt_gui_update_hover(win, x, y);
167
- if (strcmp(type, "mousedown") == 0 && win->document != nullptr) {
168
- if (win->document->setFocus(xt_gui_hit_node(win, x, y))) win->geometry.dirty = 1;
169
- }
170
- xt_gui_emit_payload(win, type, xt_gui_pointer_payload(type, win, x, y, button, clicks));
171
- }
172
-
173
- void xt_gui_dispatch_wheel(XtGuiWindow *win, float x, float y, float delta_x, float delta_y) {
174
- if (win == NULL || !win->open) return;
175
- xt_value payload = xt_gui_pointer_payload("wheel", win, x, y, -1, 0);
176
- xt_object_set(payload, xt_string_from_cstr("deltaX"), xt_number(delta_x));
177
- xt_object_set(payload, xt_string_from_cstr("deltaY"), xt_number(delta_y));
178
- xt_gui_emit_payload(win, "wheel", payload);
179
- }
180
-
181
- static xt_value xt_gui_key_payload(const char *type, const char *key, const char *code, bool repeat,
182
- bool ctrl, bool shift, bool alt, bool meta) {
183
- xt_value payload = xt_object_new();
184
- xt_object_set(payload, xt_string_from_cstr("type"), xt_string_from_cstr(type));
185
- xt_object_set(payload, xt_string_from_cstr("key"), xt_string_from_cstr(key));
186
- xt_object_set(payload, xt_string_from_cstr("code"), xt_string_from_cstr(code));
187
- xt_object_set(payload, xt_string_from_cstr("repeat"), repeat ? XT_TRUE : XT_FALSE);
188
- xt_object_set(payload, xt_string_from_cstr("ctrl"), ctrl ? XT_TRUE : XT_FALSE);
189
- xt_object_set(payload, xt_string_from_cstr("shift"), shift ? XT_TRUE : XT_FALSE);
190
- xt_object_set(payload, xt_string_from_cstr("alt"), alt ? XT_TRUE : XT_FALSE);
191
- xt_object_set(payload, xt_string_from_cstr("meta"), meta ? XT_TRUE : XT_FALSE);
192
- return payload;
193
- }
194
-
195
- void xt_gui_dispatch_key(XtGuiWindow *win, const char *type, const char *key, const char *code) {
196
- if (win == NULL || !win->open) return;
197
- xt_gui_emit_payload(win, type, xt_gui_key_payload(type, key, code, false, false, false, false, false));
198
- }
199
-
200
- static void xt_gui_handle_event(const SDL_Event *event) {
201
- if (event->type == SDL_EVENT_QUIT) {
202
- g_quit = 1;
203
- return;
204
- }
205
- if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
206
- XtGuiWindow *win = xt_gui_find_by_window_id(event->window.windowID);
207
- if (win != NULL && win->open) xt_gui_quit_window(win);
208
- return;
209
- }
210
- if (event->type == SDL_EVENT_WINDOW_RESIZED) {
211
- XtGuiWindow *win = xt_gui_find_by_window_id(event->window.windowID);
212
- if (win == NULL || !win->open) return;
213
- win->width = event->window.data1;
214
- win->height = event->window.data2;
215
- if (win->document != NULL) win->document->restyle((float)win->width, (float)win->height);
216
- win->geometry.dirty = 1;
217
- return;
218
- }
219
-
220
- if (event->type == SDL_EVENT_MOUSE_MOTION) {
221
- XtGuiWindow *win = xt_gui_find_by_window_id(event->motion.windowID);
222
- xt_gui_dispatch_pointer(win, "mousemove", event->motion.x, event->motion.y, -1, 0);
223
- return;
224
- }
225
- if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
226
- XtGuiWindow *win = xt_gui_find_by_window_id(event->button.windowID);
227
- /* SDL numbers buttons 1/2/3 (left/middle/right); DOM uses 0/1/2. */
228
- xt_gui_dispatch_pointer(win, "mousedown", event->button.x, event->button.y,
229
- (int)event->button.button - 1, event->button.clicks);
230
- return;
231
- }
232
- if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
233
- XtGuiWindow *win = xt_gui_find_by_window_id(event->button.windowID);
234
- if (win == NULL || !win->open) return;
235
- int button = (int)event->button.button - 1;
236
- xt_gui_dispatch_pointer(win, "mouseup", event->button.x, event->button.y, button,
237
- event->button.clicks);
238
- if (button == 0) {
239
- xt_gui_dispatch_pointer(win, "click", event->button.x, event->button.y, button,
240
- event->button.clicks);
241
- }
242
- return;
243
- }
244
- if (event->type == SDL_EVENT_MOUSE_WHEEL) {
245
- XtGuiWindow *win = xt_gui_find_by_window_id(event->wheel.windowID);
246
- /* DOM `deltaY` grows downwards; SDL's grows away from the user. */
247
- xt_gui_dispatch_wheel(win, event->wheel.mouse_x, event->wheel.mouse_y, event->wheel.x,
248
- -event->wheel.y);
249
- return;
250
- }
251
- if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) {
252
- XtGuiWindow *win = xt_gui_find_by_window_id(event->key.windowID);
253
- if (win == NULL || !win->open) return;
254
- const char *type = event->type == SDL_EVENT_KEY_DOWN ? "keydown" : "keyup";
255
- xt_gui_emit_payload(win, type,
256
- xt_gui_key_payload(type, SDL_GetKeyName(event->key.key),
257
- SDL_GetScancodeName(event->key.scancode),
258
- event->key.repeat, (event->key.mod & SDL_KMOD_CTRL) != 0,
259
- (event->key.mod & SDL_KMOD_SHIFT) != 0,
260
- (event->key.mod & SDL_KMOD_ALT) != 0,
261
- (event->key.mod & SDL_KMOD_GUI) != 0));
262
- return;
263
- }
264
- }
265
-
266
- /* -- rendering ------------------------------------------------------------ */
267
-
268
- void xt_gui_render_window(XtGuiWindow *win) {
269
- if (win == NULL || !win->open || win->window == NULL || g_device == NULL) return;
270
- float viewport_width = 0;
271
- float viewport_height = 0;
272
- xt_gui_window_viewport(win, &viewport_width, &viewport_height);
273
-
274
- /* Drive CSS transitions. */
275
- double now = (double)SDL_GetTicks();
276
- double delta = win->last_frame_ms > 0.0 ? now - win->last_frame_ms : 0.0;
277
- win->last_frame_ms = now;
278
- if (win->document != nullptr && win->document->advance(delta)) win->geometry.dirty = 1;
279
-
280
- /* Rebuild the geometry only when the document or viewport changed. */
281
- bool can_paint = xt_gui_renderer_ensure(g_device, win->window);
282
- if (can_paint && win->geometry.dirty && win->document != nullptr) {
283
- xtgui::DisplayList list;
284
- xtgui::xt_paint_build(win->document->layout().root(), list);
285
- xt_gui_geometry_upload(g_device, &win->geometry, list);
286
- float density = SDL_GetWindowPixelDensity(win->window);
287
- xt_gui_text_upload(g_device, &win->text_geometry, list, density > 0.0f ? density : 1.0f);
288
- xt_gui_image_upload(g_device, &win->image_geometry, list);
289
- win->geometry.dirty = 0;
290
- }
291
-
292
- SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(g_device);
293
- if (cmd == NULL) return;
294
- SDL_GPUTexture *swapchain = NULL;
295
- SDL_AcquireGPUSwapchainTexture(cmd, win->window, &swapchain, NULL, NULL);
296
- bool firstFrame = false;
297
- if (swapchain != NULL) {
298
- SDL_GPUColorTargetInfo target;
299
- memset(&target, 0, sizeof(target));
300
- target.texture = swapchain;
301
- target.clear_color.r = win->background[0];
302
- target.clear_color.g = win->background[1];
303
- target.clear_color.b = win->background[2];
304
- target.clear_color.a = win->background[3];
305
- target.load_op = SDL_GPU_LOADOP_CLEAR;
306
- target.store_op = SDL_GPU_STOREOP_STORE;
307
- SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(cmd, &target, 1, NULL);
308
- if (can_paint) {
309
- xt_gui_renderer_draw(cmd, pass, &win->geometry, viewport_width, viewport_height);
310
- xt_gui_text_draw(cmd, pass, &win->text_geometry, viewport_width, viewport_height);
311
- xt_gui_image_draw(cmd, pass, &win->image_geometry, viewport_width, viewport_height);
312
- }
313
- SDL_EndGPURenderPass(pass);
314
- firstFrame = !win->ready;
315
- win->ready = 1;
316
- }
317
- SDL_SubmitGPUCommandBuffer(cmd);
318
- /* The first presented frame marks the window ready. Emitting after the
319
- * submit lets a `ready` handler safely close/destroy the window. */
320
- if (firstFrame) xt_gui_emit(win, "ready");
321
- }
322
-
323
136
  /* -- createWindow(options) ------------------------------------------------ */
324
137
 
325
138
  static const char *xt_gui_string_or(xt_value value, const char *fallback) {
@@ -333,7 +146,7 @@ static int xt_gui_int_or(xt_value value, int fallback) {
333
146
  }
334
147
 
335
148
  extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
336
- if (!xt_gui_ensure_init()) return XT_UNDEFINED;
149
+ if (!ensure_init()) return XT_UNDEFINED;
337
150
  if (g_window_count >= XT_GUI_MAX_WINDOWS) {
338
151
  fprintf(stderr, "xt_gui: too many windows (max %d)\n", XT_GUI_MAX_WINDOWS);
339
152
  return XT_UNDEFINED;
@@ -364,6 +177,7 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
364
177
  SDL_DestroyWindow(window);
365
178
  return XT_UNDEFINED;
366
179
  }
180
+ xt_gui_apply_icon(window);
367
181
 
368
182
  int index = g_window_count++;
369
183
  XtGuiWindow *record = &g_windows[index];
@@ -378,6 +192,8 @@ extern "C" xt_value xt_gui_create_window(int32_t argc, xt_value *argv) {
378
192
  record->text_geometry = XtGuiGeometry();
379
193
  record->image_geometry = XtGuiGeometry();
380
194
  record->last_frame_ms = 0.0;
195
+ record->animation_frames.clear();
196
+ record->next_animation_frame_id = 1;
381
197
  record->background[0] = 0.08f;
382
198
  record->background[1] = 0.09f;
383
199
  record->background[2] = 0.11f;
@@ -408,7 +224,7 @@ extern "C" xt_value xt_gui_run(int32_t argc, xt_value *argv) {
408
224
  int had_event = 0;
409
225
  while (SDL_PollEvent(&event)) {
410
226
  had_event = 1;
411
- xt_gui_handle_event(&event);
227
+ handle_event(&event);
412
228
  }
413
229
 
414
230
  int open = 0;
@@ -22,6 +22,8 @@ extern "C" {
22
22
  xt_value xt_gui_create_window(int32_t argc, xt_value *argv);
23
23
  xt_value xt_gui_run(int32_t argc, xt_value *argv);
24
24
  xt_value xt_gui_quit(int32_t argc, xt_value *argv);
25
+ /** `__registerScript(id, fn)`: record an AOT-compiled `<script>` body. */
26
+ xt_value xt_register_script(int32_t argc, xt_value *argv);
25
27
 
26
28
  #ifdef __cplusplus
27
29
  } /* extern "C" */
@@ -30,10 +32,26 @@ xt_value xt_gui_quit(int32_t argc, xt_value *argv);
30
32
 
31
33
  #include <memory>
32
34
  #include <string>
35
+ #include <unordered_map>
36
+ #include <vector>
33
37
 
34
38
  #include "document.h"
35
39
  #include "renderer.h"
36
40
 
41
+ /** One element-level event listener registered through `addEventListener`. */
42
+ struct XtGuiNodeListener {
43
+ std::string type;
44
+ xt_value fn;
45
+ bool once;
46
+ bool capture;
47
+ };
48
+
49
+ /** One pending `requestAnimationFrame` callback. */
50
+ struct XtGuiAnimationFrame {
51
+ int id;
52
+ xt_value fn;
53
+ };
54
+
37
55
  /* One live window. `object` is the JavaScript-visible handle; the record is
38
56
  * addressed from it through the hidden `__xt_gui_index` property. */
39
57
  struct XtGuiWindow {
@@ -50,10 +68,43 @@ struct XtGuiWindow {
50
68
  float background[4];
51
69
  int width;
52
70
  int height;
71
+
72
+ /* -- DOM object model (element handles + events) ----------------------- */
73
+ /** `document` handle for this window (lazily created). */
74
+ xt_value document_object = XT_UNDEFINED;
75
+ /** Bumped on every `loadHTML`; stale handles no-op. */
76
+ double doc_generation = 1.0;
77
+ /** Node -> table index / cached handle, so identity is stable. */
78
+ std::vector<xtgui::Node *> node_order;
79
+ std::unordered_map<const xtgui::Node *, int> node_index;
80
+ std::unordered_map<const xtgui::Node *, xt_value> node_handles;
81
+ /** Element-level listeners, keyed by node. */
82
+ std::unordered_map<const xtgui::Node *, std::vector<XtGuiNodeListener>> node_listeners;
83
+ /** Set by a DOM mutation; consumed by the frame loop before repainting. */
84
+ int struct_dirty = 0;
85
+ /** Callbacks queued for the next frame, plus the id counter. */
86
+ std::vector<XtGuiAnimationFrame> animation_frames;
87
+ int next_animation_frame_id = 1;
53
88
  };
54
89
 
55
90
  /** Resolve a window handle (`this`) to its record, or NULL for a foreign value. */
56
91
  XtGuiWindow *xt_gui_window_from_this(xt_value thisValue);
92
+ /** The `document` handle for `win` (created on first use). */
93
+ xt_value xt_gui_document_handle(XtGuiWindow *win);
94
+ /** Node handle for `node` (created and cached on first use). */
95
+ xt_value xt_gui_node_handle(XtGuiWindow *win, const xtgui::Node *node);
96
+ /** Drop every cached handle/listener and bump the document generation. */
97
+ void xt_gui_handles_reset(XtGuiWindow *win);
98
+ /** Deliver `type` to element listeners along the propagation path. */
99
+ void xt_gui_emit_dom_event(XtGuiWindow *win, const char *type, const xtgui::Node *target,
100
+ xt_value legacy_payload);
101
+ /** Synthesise a DOM event whose target is `target` (used by element.click()). */
102
+ void xt_gui_dispatch_to_node(XtGuiWindow *win, const xtgui::Node *target, const char *type);
103
+ /** Apply any pending DOM mutation (restyle/relayout) before a synchronous read. */
104
+ void xt_gui_flush_dom(XtGuiWindow *win);
105
+ /** Run every registered `<script data-xt-id>` body found in the document, in
106
+ * document order, passing `(windowHandle, documentHandle)`. */
107
+ void xt_gui_run_scripts(XtGuiWindow *win);
57
108
  /** Shared prototype carrying the window methods. */
58
109
  xt_value xt_gui_window_proto(void);
59
110
  /** Release GPU claim + destroy the window and mark the record closed. */
@@ -64,6 +115,9 @@ void xt_gui_emit(XtGuiWindow *win, const char *event);
64
115
  void xt_gui_emit_payload(XtGuiWindow *win, const char *event, xt_value payload);
65
116
  /** Acquire a swapchain frame for `win` and clear it (placeholder paint). */
66
117
  void xt_gui_render_window(XtGuiWindow *win);
118
+ /** Run every callback queued with `requestAnimationFrame` for the frame at
119
+ * `timestamp_ms`, then clear the queue. */
120
+ void xt_gui_run_animation_frames(XtGuiWindow *win, double timestamp_ms);
67
121
  /** Current logical window size (CSS viewport), in pixels. */
68
122
  void xt_gui_window_viewport(XtGuiWindow *win, float *width, float *height);
69
123
  /** CSS-like descriptor of the deepest element at (x, y), or `""` (test/debug). */