@typecad/ui 1.0.0-alpha.13 → 1.0.0-alpha.15
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 +1457 -1469
- package/dist/cli.js +22 -19
- package/dist/preview/host-ui-runtime.js +0 -1
- package/dist/ui-engine/model.js +7 -1
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -115
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -182
- package/dist/ui-engine/runtime-header/forward-decls.js +227 -216
- package/dist/ui-engine/runtime-header/init-press-input.js +138 -138
- package/dist/ui-engine/runtime-header/keyboard.js +676 -676
- package/dist/ui-engine/runtime-header/node-draw-body.js +1658 -1656
- package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -259
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -736
- package/dist/ui-engine/runtime-header/scroll-physics.js +4 -4
- package/dist/ui-engine/runtime-header/structs.js +228 -228
- package/dist/ui-engine/runtime-header/text-rendering.js +888 -888
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -118
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +896 -896
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -24
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +11 -11
- package/dist/ui-engine/runtime-header/types-defines.js +110 -110
- package/dist/ui-engine/shadcn-kit.d.ts +1 -1
- package/dist/ui-engine/shadcn-kit.js +4 -0
- package/dist/ui-engine/ui-lowering.d.ts +1 -1
- package/dist/ui-engine/ui-lowering.js +14 -5
- package/dist/ui-engine/ui-registry.js +9 -1
- package/dist/wizard/display-catalog.d.ts +0 -3
- package/dist/wizard/display-catalog.js +3 -12
- package/dist/wizard/index.d.ts +2 -0
- package/dist/wizard/index.js +1 -0
- package/dist/wizard/integration-wizard.d.ts +3 -0
- package/dist/wizard/integration-wizard.js +69 -20
- package/dist/wizard/package-writer.d.ts +20 -0
- package/dist/wizard/package-writer.js +79 -0
- package/package.json +4 -4
- package/src/cli.ts +90 -87
- package/src/preview/host-ui-runtime.ts +0 -1
- package/src/ui-engine/model.ts +6 -1
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -121
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -188
- package/src/ui-engine/runtime-header/forward-decls.ts +238 -227
- package/src/ui-engine/runtime-header/init-press-input.ts +144 -144
- package/src/ui-engine/runtime-header/keyboard.ts +689 -689
- package/src/ui-engine/runtime-header/node-draw-body.ts +1683 -1681
- package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -265
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -742
- package/src/ui-engine/runtime-header/scroll-physics.ts +4 -4
- package/src/ui-engine/runtime-header/structs.ts +234 -234
- package/src/ui-engine/runtime-header/text-rendering.ts +894 -894
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -124
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -902
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -30
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +11 -11
- package/src/ui-engine/runtime-header/types-defines.ts +116 -116
- package/src/ui-engine/shadcn-kit.ts +4 -0
- package/src/ui-engine/ui-lowering.ts +12 -4
- package/src/ui-engine/ui-registry.ts +9 -1
- package/src/wizard/display-catalog.ts +261 -273
- package/src/wizard/index.ts +46 -38
- package/src/wizard/integration-wizard.ts +679 -619
- package/src/wizard/package-writer.ts +98 -0
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
// Slice of the C++ runtime header (original source lines 2442-2561).
|
|
2
|
-
// ui_init, press/release entry points, pin-watch polling.
|
|
3
|
-
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
-
export function emitInitPressInput(): string {
|
|
5
|
-
return `
|
|
6
|
-
// Initial draw: mark all nodes dirty so the first ui_tick renders everything.
|
|
7
|
-
// Called once in setup() before the loop begins.
|
|
8
|
-
// Also seed each text-bound node's buffer from its flash literal so the first
|
|
9
|
-
// strcmp in ui_tick has a valid baseline (no spurious redraw on frame 1).
|
|
10
|
-
static inline void ui_init(void) {
|
|
11
|
-
// Discover <drawer> panels and seed them closed (full-travel offsets, so a
|
|
12
|
-
// closed drawer never paints at its rest position).
|
|
13
|
-
ui_drawer_discover();
|
|
14
|
-
// Allocate the per-node scroll-canvas-OK flag array (once; __ui_node_count
|
|
15
|
-
// is a compile-time constant known by this point). calloc zeroes it — all
|
|
16
|
-
// containers start locked until the scroll-container loop proves their
|
|
17
|
-
// canvas fits.
|
|
18
|
-
if (!__ui_scroll_canvas_ok && __ui_node_count > 0) {
|
|
19
|
-
__ui_scroll_canvas_ok = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
20
|
-
}
|
|
21
|
-
if (!__ui_scroll_render_locked && __ui_node_count > 0) {
|
|
22
|
-
__ui_scroll_render_locked = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
23
|
-
}
|
|
24
|
-
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
25
|
-
__ui_nodes[i].dirty = 1;
|
|
26
|
-
__ui_nodes[i].lastTextHeight = 0;
|
|
27
|
-
__ui_nodes[i].layoutCacheKey = 0;
|
|
28
|
-
if (__ui_nodes[i].scrollable) {
|
|
29
|
-
// Force the first scroll-canvas composition to seed every pixel. After
|
|
30
|
-
// that, a dirty child can be repaired in place without repainting the
|
|
31
|
-
// entire viewport.
|
|
32
|
-
__ui_nodes[i].lastPaintedScrollY = static_cast<int16_t>(
|
|
33
|
-
__ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1));
|
|
34
|
-
}
|
|
35
|
-
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) {
|
|
36
|
-
__ui_nodes[i].lastTextWidth = -1;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
for (uint16_t i = 0; i < __ui_binding_count; i++) {
|
|
40
|
-
if (__ui_bindings[i].prop == PROP_TEXT && __ui_bindings[i].textFn) {
|
|
41
|
-
uint16_t n = __ui_bindings[i].node;
|
|
42
|
-
__ui_nodes[n].hasTextBinding = 1;
|
|
43
|
-
strncpy(__ui_nodes[n].textBuffer, __ui_nodes[n].text ? __ui_nodes[n].text : "", UI_TEXT_BUF);
|
|
44
|
-
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
45
|
-
} else if (__ui_bindings[i].fn) {
|
|
46
|
-
// Prime numeric binding caches once at setup; the first tick can then skip
|
|
47
|
-
// redundant assignments and dirty propagation when values are unchanged.
|
|
48
|
-
__ui_bindings[i].lastValue = __ui_bindings[i].fn();
|
|
49
|
-
__ui_bindings[i].initialized = 1;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
// Seed virtualized-list runtime state. The fn pointers can't be baked into
|
|
53
|
-
// the static node initializer: ui.bindList is resolved AFTER ui.mount lowers
|
|
54
|
-
// the HTML, so the lowering can't see the binding yet. Instead the lowering
|
|
55
|
-
// emits the UIListBinding table (the binding's fn bodies) and ui_init copies
|
|
56
|
-
// the pointers onto each <list> node here, then computes the initial count
|
|
57
|
-
// and contentHeight so the first paint and the scroll clamp bound are correct.
|
|
58
|
-
for (uint16_t b = 0; b < __ui_list_binding_count; b++) {
|
|
59
|
-
uint16_t n = __ui_list_bindings[b].node;
|
|
60
|
-
if (n >= __ui_node_count || !__ui_nodes[n].virtualized) continue;
|
|
61
|
-
__ui_nodes[n].listCountFn = __ui_list_bindings[b].countFn;
|
|
62
|
-
__ui_nodes[n].listItemFn = __ui_list_bindings[b].itemFn;
|
|
63
|
-
__ui_nodes[n].listTapFn = __ui_list_bindings[b].tapFn;
|
|
64
|
-
}
|
|
65
|
-
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
66
|
-
if (!__ui_nodes[i].virtualized || !__ui_nodes[i].listCountFn) continue;
|
|
67
|
-
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
68
|
-
uint16_t cnt = __ui_nodes[i].listCountFn();
|
|
69
|
-
__ui_nodes[i].listCount = cnt;
|
|
70
|
-
__ui_nodes[i].contentHeight = static_cast<int16_t>(static_cast<uint32_t>(cnt) * ih);
|
|
71
|
-
__ui_nodes[i].scrollY = 0;
|
|
72
|
-
__ui_nodes[i].overscrollPx = 0;
|
|
73
|
-
__ui_nodes[i].settling = 0;
|
|
74
|
-
__ui_nodes[i].lastPaintedScrollY = -(__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1);
|
|
75
|
-
}
|
|
76
|
-
ui_build_draw_order();
|
|
77
|
-
ui_build_scroll_owner_table();
|
|
78
|
-
ui_refresh_active_screen_bg_node();
|
|
79
|
-
if (!__ui_scroll_candidates && __ui_node_count > 0) {
|
|
80
|
-
__ui_scroll_candidates = new (std::nothrow) UIScrollPaintCandidate[__ui_node_count];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Debounce: ignore press/release events within 50ms of the last edge.
|
|
85
|
-
// Mechanical switches bounce (multiple edges in ~5-20ms); without this, the
|
|
86
|
-
// transition gets armed/interrupted dozens of times per physical press.
|
|
87
|
-
static volatile uint32_t __ui_last_edge_time = 0;
|
|
88
|
-
#define UI_DEBOUNCE_MS 50
|
|
89
|
-
|
|
90
|
-
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed) {
|
|
91
|
-
__ui_nodes[nodeIdx].value = pressed ? 1 : 0;
|
|
92
|
-
ui_mark_dirty(nodeIdx);
|
|
93
|
-
for (uint16_t i = 0; i < __ui_trans_count; i++) {
|
|
94
|
-
if (__ui_trans[i].node == nodeIdx) {
|
|
95
|
-
__ui_trans[i].prevValue = __ui_trans[i].prop == PROP_FG ? __ui_nodes[nodeIdx].fg : __ui_nodes[nodeIdx].bg;
|
|
96
|
-
__ui_trans[i].targetValue = pressed ? __ui_trans[i].pressedTarget : __ui_trans[i].baseTarget;
|
|
97
|
-
__ui_trans[i].elapsed = 0;
|
|
98
|
-
__ui_trans[i].active = 1;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Press / release entry points that node.onPress(pin) lowers to.
|
|
104
|
-
// On press, arm transitions toward the :pressed target color; on release,
|
|
105
|
-
// arm them back toward the base color (interrupt-and-re-lerp from current).
|
|
106
|
-
static inline void ui_on_press(uint16_t nodeIdx) {
|
|
107
|
-
uint32_t now =
|
|
108
|
-
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
109
|
-
__ui_last_edge_time = now;
|
|
110
|
-
ui_set_pressed(nodeIdx, 1);
|
|
111
|
-
}
|
|
112
|
-
static inline void ui_on_release(uint16_t nodeIdx) {
|
|
113
|
-
uint32_t now =
|
|
114
|
-
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
115
|
-
__ui_last_edge_time = now;
|
|
116
|
-
ui_set_pressed(nodeIdx, 0);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Pin-watch callback type: void fn(void)
|
|
120
|
-
using PinWatchCallback = void (*)(void);
|
|
121
|
-
|
|
122
|
-
struct UIPinWatch {
|
|
123
|
-
uint8_t pin;
|
|
124
|
-
uint8_t lastState; // for edge detection
|
|
125
|
-
PinWatchCallback cb; // fires on falling edge
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Populated by the emit layer from ui.watchPin() calls.
|
|
129
|
-
extern UIPinWatch __ui_pin_watches[];
|
|
130
|
-
extern const uint16_t __ui_pin_watch_count;
|
|
131
|
-
|
|
132
|
-
// Poll all configured pin-watchers. Called at the start of ui_tick each frame.
|
|
133
|
-
// Detects falling edges with natural debounce from the ~16ms frame rate.
|
|
134
|
-
static inline void ui_poll_inputs() {
|
|
135
|
-
for (uint16_t i = 0; i < __ui_pin_watch_count; i++) {
|
|
136
|
-
uint8_t val = digitalRead(__ui_pin_watches[i].pin);
|
|
137
|
-
if (val == LOW && __ui_pin_watches[i].lastState == HIGH) {
|
|
138
|
-
if (__ui_pin_watches[i].cb) __ui_pin_watches[i].cb();
|
|
139
|
-
}
|
|
140
|
-
__ui_pin_watches[i].lastState = val;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
`;
|
|
144
|
-
}
|
|
1
|
+
// Slice of the C++ runtime header (original source lines 2442-2561).
|
|
2
|
+
// ui_init, press/release entry points, pin-watch polling.
|
|
3
|
+
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
|
+
export function emitInitPressInput(): string {
|
|
5
|
+
return `
|
|
6
|
+
// Initial draw: mark all nodes dirty so the first ui_tick renders everything.
|
|
7
|
+
// Called once in setup() before the loop begins.
|
|
8
|
+
// Also seed each text-bound node's buffer from its flash literal so the first
|
|
9
|
+
// strcmp in ui_tick has a valid baseline (no spurious redraw on frame 1).
|
|
10
|
+
static inline void ui_init(void) {
|
|
11
|
+
// Discover <drawer> panels and seed them closed (full-travel offsets, so a
|
|
12
|
+
// closed drawer never paints at its rest position).
|
|
13
|
+
ui_drawer_discover();
|
|
14
|
+
// Allocate the per-node scroll-canvas-OK flag array (once; __ui_node_count
|
|
15
|
+
// is a compile-time constant known by this point). calloc zeroes it — all
|
|
16
|
+
// containers start locked until the scroll-container loop proves their
|
|
17
|
+
// canvas fits.
|
|
18
|
+
if (!__ui_scroll_canvas_ok && __ui_node_count > 0) {
|
|
19
|
+
__ui_scroll_canvas_ok = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
20
|
+
}
|
|
21
|
+
if (!__ui_scroll_render_locked && __ui_node_count > 0) {
|
|
22
|
+
__ui_scroll_render_locked = new (std::nothrow) uint8_t[__ui_node_count]();
|
|
23
|
+
}
|
|
24
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
25
|
+
__ui_nodes[i].dirty = 1;
|
|
26
|
+
__ui_nodes[i].lastTextHeight = 0;
|
|
27
|
+
__ui_nodes[i].layoutCacheKey = 0;
|
|
28
|
+
if (__ui_nodes[i].scrollable) {
|
|
29
|
+
// Force the first scroll-canvas composition to seed every pixel. After
|
|
30
|
+
// that, a dirty child can be repaired in place without repainting the
|
|
31
|
+
// entire viewport.
|
|
32
|
+
__ui_nodes[i].lastPaintedScrollY = static_cast<int16_t>(
|
|
33
|
+
__ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1));
|
|
34
|
+
}
|
|
35
|
+
if (__ui_nodes[i].kind == NODE_PROGRESS || __ui_nodes[i].kind == NODE_RANGE) {
|
|
36
|
+
__ui_nodes[i].lastTextWidth = -1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (uint16_t i = 0; i < __ui_binding_count; i++) {
|
|
40
|
+
if (__ui_bindings[i].prop == PROP_TEXT && __ui_bindings[i].textFn) {
|
|
41
|
+
uint16_t n = __ui_bindings[i].node;
|
|
42
|
+
__ui_nodes[n].hasTextBinding = 1;
|
|
43
|
+
strncpy(__ui_nodes[n].textBuffer, __ui_nodes[n].text ? __ui_nodes[n].text : "", UI_TEXT_BUF);
|
|
44
|
+
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
45
|
+
} else if (__ui_bindings[i].fn) {
|
|
46
|
+
// Prime numeric binding caches once at setup; the first tick can then skip
|
|
47
|
+
// redundant assignments and dirty propagation when values are unchanged.
|
|
48
|
+
__ui_bindings[i].lastValue = __ui_bindings[i].fn();
|
|
49
|
+
__ui_bindings[i].initialized = 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Seed virtualized-list runtime state. The fn pointers can't be baked into
|
|
53
|
+
// the static node initializer: ui.bindList is resolved AFTER ui.mount lowers
|
|
54
|
+
// the HTML, so the lowering can't see the binding yet. Instead the lowering
|
|
55
|
+
// emits the UIListBinding table (the binding's fn bodies) and ui_init copies
|
|
56
|
+
// the pointers onto each <list> node here, then computes the initial count
|
|
57
|
+
// and contentHeight so the first paint and the scroll clamp bound are correct.
|
|
58
|
+
for (uint16_t b = 0; b < __ui_list_binding_count; b++) {
|
|
59
|
+
uint16_t n = __ui_list_bindings[b].node;
|
|
60
|
+
if (n >= __ui_node_count || !__ui_nodes[n].virtualized) continue;
|
|
61
|
+
__ui_nodes[n].listCountFn = __ui_list_bindings[b].countFn;
|
|
62
|
+
__ui_nodes[n].listItemFn = __ui_list_bindings[b].itemFn;
|
|
63
|
+
__ui_nodes[n].listTapFn = __ui_list_bindings[b].tapFn;
|
|
64
|
+
}
|
|
65
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
66
|
+
if (!__ui_nodes[i].virtualized || !__ui_nodes[i].listCountFn) continue;
|
|
67
|
+
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
68
|
+
uint16_t cnt = __ui_nodes[i].listCountFn();
|
|
69
|
+
__ui_nodes[i].listCount = cnt;
|
|
70
|
+
__ui_nodes[i].contentHeight = static_cast<int16_t>(static_cast<uint32_t>(cnt) * ih);
|
|
71
|
+
__ui_nodes[i].scrollY = 0;
|
|
72
|
+
__ui_nodes[i].overscrollPx = 0;
|
|
73
|
+
__ui_nodes[i].settling = 0;
|
|
74
|
+
__ui_nodes[i].lastPaintedScrollY = -(__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1);
|
|
75
|
+
}
|
|
76
|
+
ui_build_draw_order();
|
|
77
|
+
ui_build_scroll_owner_table();
|
|
78
|
+
ui_refresh_active_screen_bg_node();
|
|
79
|
+
if (!__ui_scroll_candidates && __ui_node_count > 0) {
|
|
80
|
+
__ui_scroll_candidates = new (std::nothrow) UIScrollPaintCandidate[__ui_node_count];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Debounce: ignore press/release events within 50ms of the last edge.
|
|
85
|
+
// Mechanical switches bounce (multiple edges in ~5-20ms); without this, the
|
|
86
|
+
// transition gets armed/interrupted dozens of times per physical press.
|
|
87
|
+
static volatile uint32_t __ui_last_edge_time = 0;
|
|
88
|
+
#define UI_DEBOUNCE_MS 50
|
|
89
|
+
|
|
90
|
+
static inline void ui_set_pressed(uint16_t nodeIdx, uint8_t pressed) {
|
|
91
|
+
__ui_nodes[nodeIdx].value = pressed ? 1 : 0;
|
|
92
|
+
ui_mark_dirty(nodeIdx);
|
|
93
|
+
for (uint16_t i = 0; i < __ui_trans_count; i++) {
|
|
94
|
+
if (__ui_trans[i].node == nodeIdx) {
|
|
95
|
+
__ui_trans[i].prevValue = __ui_trans[i].prop == PROP_FG ? __ui_nodes[nodeIdx].fg : __ui_nodes[nodeIdx].bg;
|
|
96
|
+
__ui_trans[i].targetValue = pressed ? __ui_trans[i].pressedTarget : __ui_trans[i].baseTarget;
|
|
97
|
+
__ui_trans[i].elapsed = 0;
|
|
98
|
+
__ui_trans[i].active = 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Press / release entry points that node.onPress(pin) lowers to.
|
|
104
|
+
// On press, arm transitions toward the :pressed target color; on release,
|
|
105
|
+
// arm them back toward the base color (interrupt-and-re-lerp from current).
|
|
106
|
+
static inline void ui_on_press(uint16_t nodeIdx) {
|
|
107
|
+
uint32_t now = __tc_now_ms();
|
|
108
|
+
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
109
|
+
__ui_last_edge_time = now;
|
|
110
|
+
ui_set_pressed(nodeIdx, 1);
|
|
111
|
+
}
|
|
112
|
+
static inline void ui_on_release(uint16_t nodeIdx) {
|
|
113
|
+
uint32_t now = __tc_now_ms();
|
|
114
|
+
if (now - __ui_last_edge_time < UI_DEBOUNCE_MS) return;
|
|
115
|
+
__ui_last_edge_time = now;
|
|
116
|
+
ui_set_pressed(nodeIdx, 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Pin-watch callback type: void fn(void)
|
|
120
|
+
using PinWatchCallback = void (*)(void);
|
|
121
|
+
|
|
122
|
+
struct UIPinWatch {
|
|
123
|
+
uint8_t pin;
|
|
124
|
+
uint8_t lastState; // for edge detection
|
|
125
|
+
PinWatchCallback cb; // fires on falling edge
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Populated by the emit layer from ui.watchPin() calls.
|
|
129
|
+
extern UIPinWatch __ui_pin_watches[];
|
|
130
|
+
extern const uint16_t __ui_pin_watch_count;
|
|
131
|
+
|
|
132
|
+
// Poll all configured pin-watchers. Called at the start of ui_tick each frame.
|
|
133
|
+
// Detects falling edges with natural debounce from the ~16ms frame rate.
|
|
134
|
+
static inline void ui_poll_inputs() {
|
|
135
|
+
for (uint16_t i = 0; i < __ui_pin_watch_count; i++) {
|
|
136
|
+
uint8_t val = digitalRead(__ui_pin_watches[i].pin);
|
|
137
|
+
if (val == LOW && __ui_pin_watches[i].lastState == HIGH) {
|
|
138
|
+
if (__ui_pin_watches[i].cb) __ui_pin_watches[i].cb();
|
|
139
|
+
}
|
|
140
|
+
__ui_pin_watches[i].lastState = val;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
}
|