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.
- package/README.md +33 -0
- package/dist/src/cli/main.js +124 -30
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +8 -1
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/state.js +20 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +6 -0
- package/dist/src/codegen/generator/tables.js +38 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/commonjs.d.ts +52 -0
- package/dist/src/driver/bundler/commonjs.js +493 -0
- package/dist/src/driver/bundler/commonjs.js.map +1 -0
- package/dist/src/driver/bundler/graph.d.ts +2 -1
- package/dist/src/driver/bundler/graph.js +32 -3
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.d.ts +2 -1
- package/dist/src/driver/bundler/merge.js +16 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/bundler/types.d.ts +8 -0
- package/dist/src/driver/compiler.d.ts +8 -1
- package/dist/src/driver/compiler.js +75 -7
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +53 -0
- package/dist/src/driver/config.js +141 -0
- package/dist/src/driver/config.js.map +1 -0
- package/dist/src/driver/icon.d.ts +62 -0
- package/dist/src/driver/icon.js +151 -0
- package/dist/src/driver/icon.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +4 -0
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/mac-bundle.d.ts +41 -0
- package/dist/src/driver/mac-bundle.js +123 -0
- package/dist/src/driver/mac-bundle.js.map +1 -0
- package/dist/src/driver/win-icon.d.ts +27 -0
- package/dist/src/driver/win-icon.js +80 -0
- package/dist/src/driver/win-icon.js.map +1 -0
- package/dist/src/extensions/gui/html.d.ts +25 -0
- package/dist/src/extensions/gui/html.js +203 -0
- package/dist/src/extensions/gui/html.js.map +1 -0
- package/dist/src/extensions/gui/index.js +11 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/registry.d.ts +29 -0
- package/dist/src/extensions/registry.js +14 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lexer/scanner/identifiers.js +4 -12
- package/dist/src/lexer/scanner/identifiers.js.map +1 -1
- package/dist/src/parser/statements.js +17 -4
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/tests/cli/main.test.js +46 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/bundler-resolve.test.d.ts +1 -0
- package/dist/tests/driver/bundler-resolve.test.js +230 -0
- package/dist/tests/driver/bundler-resolve.test.js.map +1 -0
- package/dist/tests/driver/config.test.d.ts +1 -0
- package/dist/tests/driver/config.test.js +124 -0
- package/dist/tests/driver/config.test.js.map +1 -0
- package/dist/tests/driver/icon.test.d.ts +1 -0
- package/dist/tests/driver/icon.test.js +421 -0
- package/dist/tests/driver/icon.test.js.map +1 -0
- package/dist/tests/driver/modules.test.js +177 -0
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.d.ts +12 -0
- package/dist/tests/e2e/fetch.test.js +272 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -0
- package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
- package/dist/tests/e2e/gui-dom.test.js +102 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -0
- package/dist/tests/e2e/gui-events.test.d.ts +5 -0
- package/dist/tests/e2e/gui-events.test.js +60 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +31 -0
- package/dist/tests/e2e/gui-helpers.js +74 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -0
- package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
- package/dist/tests/e2e/gui-layout.test.js +88 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -0
- package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
- package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
- package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
- package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
- package/dist/tests/e2e/gui-paint.test.js +111 -0
- package/dist/tests/e2e/gui-paint.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.d.ts +5 -0
- package/dist/tests/e2e/gui-script.test.js +78 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -0
- package/dist/tests/e2e/gui-style.test.d.ts +5 -0
- package/dist/tests/e2e/gui-style.test.js +175 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -0
- package/dist/tests/e2e/icon.test.d.ts +7 -0
- package/dist/tests/e2e/icon.test.js +80 -0
- package/dist/tests/e2e/icon.test.js.map +1 -0
- package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
- package/dist/tests/e2e/npm-modules.test.js +47 -1
- package/dist/tests/e2e/npm-modules.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
- package/dist/tests/extensions/gui-index.test.d.ts +1 -0
- package/dist/tests/extensions/gui-index.test.js +49 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.d.ts +1 -0
- package/dist/tests/extensions/gui.test.js +177 -0
- package/dist/tests/extensions/gui.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +36 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/dist/tests/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/dist/tests/public-api.test.d.ts +1 -0
- package/dist/tests/public-api.test.js +31 -0
- package/dist/tests/public-api.test.js.map +1 -0
- package/package.json +1 -1
- package/runtime/ext_gui/css.cpp +4 -517
- package/runtime/ext_gui/css_internal.h +27 -0
- package/runtime/ext_gui/css_match.cpp +195 -0
- package/runtime/ext_gui/css_selector.cpp +207 -0
- package/runtime/ext_gui/css_sheet.cpp +129 -0
- package/runtime/ext_gui/document.cpp +90 -0
- package/runtime/ext_gui/document.h +23 -0
- package/runtime/ext_gui/dom.cpp +43 -403
- package/runtime/ext_gui/dom.h +7 -0
- package/runtime/ext_gui/dom_api.cpp +164 -0
- package/runtime/ext_gui/dom_api.h +12 -0
- package/runtime/ext_gui/dom_api_document.cpp +96 -0
- package/runtime/ext_gui/dom_api_events.cpp +123 -0
- package/runtime/ext_gui/dom_api_internal.h +135 -0
- package/runtime/ext_gui/dom_api_node.cpp +329 -0
- package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
- package/runtime/ext_gui/dom_api_proto.cpp +130 -0
- package/runtime/ext_gui/dom_api_style.cpp +192 -0
- package/runtime/ext_gui/dom_internal.h +39 -0
- package/runtime/ext_gui/dom_parse.cpp +348 -0
- package/runtime/ext_gui/dom_serialize.cpp +54 -0
- package/runtime/ext_gui/gui.cpp +61 -245
- package/runtime/ext_gui/gui_engine.h +54 -0
- package/runtime/ext_gui/gui_events.cpp +294 -0
- package/runtime/ext_gui/gui_internal.h +29 -0
- package/runtime/ext_gui/layout.cpp +6 -597
- package/runtime/ext_gui/layout_flex.cpp +200 -0
- package/runtime/ext_gui/layout_flow.cpp +191 -0
- package/runtime/ext_gui/layout_flow_internal.h +149 -0
- package/runtime/ext_gui/layout_inline.cpp +181 -0
- package/runtime/ext_gui/layout_internal.h +20 -0
- package/runtime/ext_gui/renderer.cpp +15 -679
- package/runtime/ext_gui/renderer_image.cpp +233 -0
- package/runtime/ext_gui/renderer_internal.h +112 -0
- package/runtime/ext_gui/renderer_shaders.h +108 -0
- package/runtime/ext_gui/renderer_text.cpp +342 -0
- package/runtime/ext_gui/script.cpp +63 -0
- package/runtime/ext_gui/style.cpp +12 -660
- package/runtime/ext_gui/style_internal.h +33 -0
- package/runtime/ext_gui/style_print.cpp +113 -0
- package/runtime/ext_gui/style_ua.cpp +98 -0
- package/runtime/ext_gui/style_values.cpp +468 -0
- package/runtime/ext_gui/window.cpp +60 -0
- package/runtime/rt.h +6 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_builtins.c +1 -0
- package/runtime/xt_containers/access.inc +32 -1
- package/runtime/xt_fetch/buffer.inc +64 -0
- package/runtime/xt_fetch/headers.inc +312 -0
- package/runtime/xt_fetch/request.inc +386 -0
- package/runtime/xt_fetch/response.inc +107 -0
- package/runtime/xt_fetch/socket.inc +72 -0
- package/runtime/xt_fetch/url.inc +108 -0
- package/runtime/xt_fetch.c +39 -0
- package/runtime/xt_stdlib2/set.inc +1 -1
- package/runtime/xt_typed_array.c +614 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/state.ts +20 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +35 -2
- package/src/driver/bundler/merge.ts +18 -1
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +85 -7
- package/src/driver/config.ts +173 -0
- package/src/driver/icon.ts +184 -0
- package/src/driver/index.ts +30 -0
- package/src/driver/mac-bundle.ts +156 -0
- package/src/driver/win-icon.ts +96 -0
- package/src/extensions/gui/html.ts +236 -0
- package/src/extensions/gui/index.ts +11 -0
- package/src/extensions/registry.ts +39 -0
- package/src/index.ts +1 -1
- package/src/lexer/scanner/identifiers.ts +4 -11
- package/src/parser/statements.ts +17 -4
- package/dist/tests/e2e/gui.test.d.ts +0 -12
- package/dist/tests/e2e/gui.test.js +0 -493
- package/dist/tests/e2e/gui.test.js.map +0 -1
package/runtime/ext_gui/gui.cpp
CHANGED
|
@@ -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 "
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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 *
|
|
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
|
-
|
|
60
|
-
return xt_gui_window_by_object(thisValue);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* -- events --------------------------------------------------------------- */
|
|
99
|
+
} // namespace guidev
|
|
64
100
|
|
|
65
|
-
|
|
66
|
-
xt_gui_emit_payload(win, event, XT_UNDEFINED);
|
|
67
|
-
}
|
|
101
|
+
using namespace guidev;
|
|
68
102
|
|
|
69
|
-
|
|
70
|
-
|
|
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 (!
|
|
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
|
-
|
|
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). */
|