@geastack/core 0.1.23

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 (73) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +36 -0
  3. package/app_frame_perf.cpp +77 -0
  4. package/css.d.ts +1 -0
  5. package/events.cpp +67 -0
  6. package/gea_app_entry.cpp +317 -0
  7. package/gea_sources.mjs +126 -0
  8. package/gea_sources.sh +36 -0
  9. package/include/app.h +60 -0
  10. package/include/apps.h +46 -0
  11. package/include/audio.h +90 -0
  12. package/include/ble.h +212 -0
  13. package/include/bluetooth/hid_profile.h +92 -0
  14. package/include/camera.h +107 -0
  15. package/include/canvas.h +290 -0
  16. package/include/display.h +390 -0
  17. package/include/display_present.h +869 -0
  18. package/include/event.h +32 -0
  19. package/include/events.h +116 -0
  20. package/include/gea/embedded.h +13 -0
  21. package/include/gea/reactive_runtime.h +32 -0
  22. package/include/gea.h +14 -0
  23. package/include/gea_perf_config.h +96 -0
  24. package/include/geaos/abi.h +165 -0
  25. package/include/geolocation.h +126 -0
  26. package/include/gpio.h +61 -0
  27. package/include/graphics/font.h +122 -0
  28. package/include/i2c.h +20 -0
  29. package/include/image.h +156 -0
  30. package/include/imu.h +23 -0
  31. package/include/input.h +33 -0
  32. package/include/memory.h +52 -0
  33. package/include/pixel.h +907 -0
  34. package/include/platform/file_cache.h +18 -0
  35. package/include/power.h +11 -0
  36. package/include/runtime.h +83 -0
  37. package/include/services/app_runner.h +10 -0
  38. package/include/services/app_state.h +14 -0
  39. package/include/services/battery_service.h +12 -0
  40. package/include/services/bluetooth_service.h +13 -0
  41. package/include/services/diagnostics.h +76 -0
  42. package/include/services/diagnostics_platform.h +54 -0
  43. package/include/services/frame_scheduler.h +71 -0
  44. package/include/services/mirror.h +49 -0
  45. package/include/services/network_services.h +25 -0
  46. package/include/services/ota.h +32 -0
  47. package/include/services/runtime_log.h +11 -0
  48. package/include/services/storage_service.h +26 -0
  49. package/include/touch.h +38 -0
  50. package/include/ui/signal.h +149 -0
  51. package/include/wifi.h +67 -0
  52. package/index.d.ts +1940 -0
  53. package/jsx.d.ts +1 -0
  54. package/package.json +70 -0
  55. package/runtime/ble.ts +49 -0
  56. package/runtime/color.ts +21 -0
  57. package/runtime/compiler.ts +43 -0
  58. package/runtime/host.ts +1083 -0
  59. package/runtime/images.ts +129 -0
  60. package/runtime/primitives.ts +49 -0
  61. package/runtime-surface.ts +180 -0
  62. package/runtime.cpp +431 -0
  63. package/runtime.ts +34 -0
  64. package/scripts/build-gea-vite-geatsc.mjs +4514 -0
  65. package/scripts/dotenv-defines.mjs +98 -0
  66. package/scripts/gea-bundle-type-hints.mjs +546 -0
  67. package/scripts/gea-embedded-compat-staging.mjs +17 -0
  68. package/scripts/gea-embedded-compat-transform.mjs +427 -0
  69. package/scripts/gea-vite-module-graph-plugin.mjs +578 -0
  70. package/scripts/generate-gea-embedded-assets.mjs +296 -0
  71. package/scripts/generate-gea-embedded-fonts.mjs +1252 -0
  72. package/scripts/generate-installed-app-plan.mjs +55 -0
  73. package/scripts/native-object-cache.sh +244 -0
@@ -0,0 +1,32 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::events {
4
+
5
+ enum class EventType {
6
+ Touch,
7
+ Frame,
8
+ Timeout,
9
+ SettingsToggle,
10
+ };
11
+
12
+ enum class TouchPhase {
13
+ Down = 1,
14
+ Move = 2,
15
+ Up = 3,
16
+ };
17
+
18
+ struct Event {
19
+ EventType type = EventType::Frame;
20
+ TouchPhase touchPhase = TouchPhase::Down;
21
+ bool touching = false;
22
+ int data = 0;
23
+ int x = 0;
24
+ int y = 0;
25
+ // Multi-touch pointer index. 0 = primary pointer (full gesture: scroll,
26
+ // click, focus, keyboard). >=1 = additional simultaneous fingers, which fire
27
+ // per-node touch/pointer/press events only (e.g. holding a move button while
28
+ // tapping jump). Single-touch sources (ESP32 panel, mouse) always use 0.
29
+ int pointerId = 0;
30
+ };
31
+
32
+ } // namespace gea::framework::events
@@ -0,0 +1,116 @@
1
+ #pragma once
2
+
3
+ #include "event.h"
4
+
5
+ #include <cstdint>
6
+ #include <functional>
7
+
8
+ namespace gea::framework::events {
9
+
10
+ enum class PointerEventType {
11
+ TouchStart,
12
+ TouchMove,
13
+ TouchEnd,
14
+ Click,
15
+ Input,
16
+ KeyDown,
17
+ Rotary
18
+ };
19
+
20
+ enum class EventPhase {
21
+ None = 0,
22
+ Capturing = 1,
23
+ AtTarget = 2,
24
+ Bubbling = 3
25
+ };
26
+
27
+ class EventTarget {
28
+ public:
29
+ explicit EventTarget(int nodeId = -1) : nodeId_(nodeId) {}
30
+
31
+ int id() const { return nodeId_; }
32
+ bool valid() const { return nodeId_ >= 0; }
33
+ const char *getAttribute(const char *name) const;
34
+ const char *dataset(const char *name) const;
35
+ int pressId() const;
36
+ int pressValue() const;
37
+
38
+ private:
39
+ int nodeId_;
40
+ };
41
+
42
+ struct TouchPoint {
43
+ int identifier = 1;
44
+ EventTarget target{};
45
+ int screenX = 0;
46
+ int screenY = 0;
47
+ int clientX = 0;
48
+ int clientY = 0;
49
+ int pageX = 0;
50
+ int pageY = 0;
51
+ };
52
+
53
+ struct PointerEvent {
54
+ PointerEventType type;
55
+ EventTarget target{};
56
+ EventTarget currentTarget{};
57
+ int targetId = -1;
58
+ int currentTargetId = -1;
59
+ int pointerId = 0;
60
+ int x = 0;
61
+ int y = 0;
62
+ int clientX = 0;
63
+ int clientY = 0;
64
+ int pageX = 0;
65
+ int pageY = 0;
66
+ int screenX = 0;
67
+ int screenY = 0;
68
+ int pressId = -1;
69
+ int pressValue = -1;
70
+ int keyCode = 0;
71
+ int delta = 0;
72
+ bool primary = true;
73
+ bool bubbles = true;
74
+ bool cancelable = true;
75
+ bool defaultPrevented = false;
76
+ bool propagationStopped = false;
77
+ EventPhase eventPhase = EventPhase::None;
78
+ TouchPoint touches[1]{};
79
+ TouchPoint targetTouches[1]{};
80
+ TouchPoint changedTouches[1]{};
81
+ int touchesLength = 0;
82
+ int targetTouchesLength = 0;
83
+ int changedTouchesLength = 0;
84
+
85
+ const char *typeName() const;
86
+ void preventDefault();
87
+ void stopPropagation();
88
+ };
89
+
90
+ using EventListener = std::function<void(PointerEvent &)>;
91
+ using EventListenerId = std::uint64_t;
92
+ inline constexpr EventListenerId kInvalidEventListenerId = 0;
93
+
94
+ class TouchRuntime {
95
+ public:
96
+ static bool start();
97
+ static void poll(int nowMs);
98
+ static void setDispatchEnabled(bool enabled);
99
+ static bool dispatchEnabled();
100
+ static void queueTouchEvent(TouchPhase phase, bool touching, int x, int y, int pointerId = 0);
101
+ // Panel-physical -> logical (orientation-rotated) coordinate mapping.
102
+ static void transformTouchToLogical(int *x, int *y);
103
+ // Returns true if the event may have changed what's on screen (a real
104
+ // down/move/up that fired handlers), so the caller can render immediately
105
+ // instead of waiting for the next timer frame. Redundant moves (finger held
106
+ // still, same coords) return false.
107
+ static bool dispatchEvent(const Event &event);
108
+ // True if a finger is currently down AND fired a down/move/up within the last
109
+ // `windowMs` ms. Lets the frame loop keep rendering back-to-back through a drag
110
+ // (like momentum's overrun catch-up) instead of stalling for the next touch
111
+ // event after each frame. Goes false on a static hold so there's no busy-render.
112
+ static bool gestureActiveWithin(int nowMs, int windowMs);
113
+ static void resetGestureState();
114
+ };
115
+
116
+ } // namespace gea::framework::events
@@ -0,0 +1,13 @@
1
+ // Gea embedded framework.
2
+ //
3
+ // Generated app code targets the framework UI, canvas, services, and host
4
+ // device classes directly. Platform glue stays below this layer.
5
+
6
+ #pragma once
7
+
8
+ // Full framework umbrella = host platform layer + the UI engine (document) and
9
+ // reactive (signal) layers. App/engine code includes this; host package code
10
+ // includes gea/embedded-host.h instead (no engine/reactive dependency).
11
+ #include "gea/embedded-host.h"
12
+ #include "ui/document.h"
13
+ #include "ui/signal.h"
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // @geastack/core — reactive-apply runtime support.
4
+ //
5
+ // Source fragment fused into a program's translation unit by the gea plugin
6
+ // (geatsc-plugin-gea inserts `#include "gea/reactive_runtime.h"` right after the
7
+ // runtime include, ONLY when the program uses reactivity). NOT self-contained:
8
+ // it relies on the JS-language runtime already being in scope (gea_cpp_value,
9
+ // gea_cpp_key, gea_cpp_queue_microtask, gea::runtime::coerce::to_boolean — all
10
+ // defined earlier in the TU via runtime_pch.h). This is the reactivity FEATURE,
11
+ // so it lives in the framework (GPL), separate from the GPL+RLE language runtime.
12
+
13
+ #pragma once
14
+
15
+ static void __attribute__((noinline)) gea_cpp_run_reactive_apply(const std::shared_ptr<std::function<void()>> &apply) {
16
+ if (apply && *apply) (*apply)();
17
+ }
18
+ static void __attribute__((noinline)) gea_cpp_schedule_reactive_apply(
19
+ const std::shared_ptr<std::function<void()>> &apply,
20
+ const std::shared_ptr<bool> &pending
21
+ ) {
22
+ if (!pending || *pending) return;
23
+ *pending = true;
24
+ gea_cpp_queue_microtask([apply, pending]() mutable -> void {
25
+ if (pending) *pending = false;
26
+ gea_cpp_run_reactive_apply(apply);
27
+ });
28
+ }
29
+ // (Removed gea_cpp_bind_direct_reactive_apply — the dead "observe-direct"
30
+ // fast-path of the dynamic-store fallback. Its `__sym_GEA_OBSERVE_DIRECT` gate
31
+ // had no setter anywhere in source, so it always returned false and every caller
32
+ // fell through to the typed-store path. Deleted 2026-06-27.)
package/include/gea.h ADDED
@@ -0,0 +1,14 @@
1
+ #pragma once
2
+
3
+ #include "app.h"
4
+ #include "apps.h"
5
+ #include "audio.h"
6
+ #include "ble.h"
7
+ #include "display.h"
8
+ #include "i2c.h"
9
+ #include "image.h"
10
+ #include "imu.h"
11
+ #include "power.h"
12
+ #include "runtime.h"
13
+ #include "touch.h"
14
+ #include "wifi.h"
@@ -0,0 +1,96 @@
1
+ #pragma once
2
+
3
+ // ============================================================================
4
+ // ONE master switch for ALL gea per-frame perf instrumentation.
5
+ //
6
+ // GEA_EMBEDDED_PERF
7
+ //
8
+ // governs every perf subsystem at once — frame/phase timing, the esp32
9
+ // scheduler harvest + "perf:"/"perf-lite:" lines, per-op refresh & canvas 2D
10
+ // stats, the requestAnimationFrame queue timing, and heap diagnostics. When it
11
+ // is 0, ALL of that is compiled out: no esp_timer/chrono reads, no counter
12
+ // increments, no stat resets, no log output — zero machinery, zero cost.
13
+ //
14
+ // When it is 1 (the development default), each subsystem flag below takes its
15
+ // own value, so a build can still enable just one line (e.g. set
16
+ // GEA_FRAME_PERF_LOG and leave the rest) for targeted profiling.
17
+ //
18
+ // A production target flips perf off in ONE place — its CMakeLists / sdkconfig
19
+ // sets GEA_EMBEDDED_PERF=0 — instead of remembering to zero each sub-flag.
20
+ //
21
+ // This header is the single source of truth for the sub-flag defaults; it is
22
+ // included by the lib perf consumers directly and by the esp32 perf consumers
23
+ // transitively through memory_config.h. The build's own -D defines (seen before
24
+ // any header) still win for an individual flag; the master override below then
25
+ // forces every flag off when the master is off, regardless of those -D values.
26
+ // ============================================================================
27
+
28
+ #ifndef GEA_EMBEDDED_PERF
29
+ #define GEA_EMBEDDED_PERF 1
30
+ #endif
31
+
32
+ // --- individual subsystem flags (consulted only when the master is on) ------
33
+
34
+ // App "gea.perf:" rolling-window line + its per-phase timing (gea_app_entry.cpp).
35
+ #ifndef GEA_FRAME_PERF_LOG
36
+ #define GEA_FRAME_PERF_LOG 1
37
+ #endif
38
+
39
+ // esp32 frame-scheduler per-frame harvest + "perf:" line (frame_scheduler.cpp).
40
+ #ifndef GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LOG
41
+ #define GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LOG 1
42
+ #endif
43
+
44
+ // esp32 frame-scheduler compact "perf-lite:" line.
45
+ #ifndef GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LITE
46
+ #define GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LITE 0
47
+ #endif
48
+
49
+ // Per-op refresh timing (ScopedRefreshStat; refresh_perf.h + tree_render/render).
50
+ #ifndef GEA_EMBEDDED_UI_REFRESH_PERF
51
+ #define GEA_EMBEDDED_UI_REFRESH_PERF 1
52
+ #endif
53
+
54
+ // Per-op canvas 2D timing (canvas_element.cpp).
55
+ #ifndef GEA_EMBEDDED_CANVAS_PERF_DETAIL
56
+ #define GEA_EMBEDDED_CANVAS_PERF_DETAIL 0
57
+ #endif
58
+
59
+ // requestAnimationFrame queue timing (host/timers.cpp).
60
+ #ifndef GEA_EMBEDDED_RAF_PERF
61
+ #define GEA_EMBEDDED_RAF_PERF 1
62
+ #endif
63
+
64
+ // Heap diagnostics logging (memory_config.h consumers).
65
+ #ifndef GEA_EMBEDDED_HEAP_DIAGNOSTICS_LOG
66
+ #define GEA_EMBEDDED_HEAP_DIAGNOSTICS_LOG 0
67
+ #endif
68
+
69
+ // The esp32 frame scheduler's "geafps: PRODFPS=" line. Deliberately NOT under
70
+ // the master switch below: its whole purpose is to report the true frame rate
71
+ // of a build with GEA_EMBEDDED_PERF=0, where every other perf line is stripped.
72
+ // It does need a switch of its own, though, because it is otherwise one INFO
73
+ // line per second for the life of the device -- which overwrites an app's log
74
+ // ring and drowns a serial capture within minutes. On by default so no existing
75
+ // board changes behaviour.
76
+ #ifndef GEA_EMBEDDED_FRAME_SCHEDULER_FPS_LOG
77
+ #define GEA_EMBEDDED_FRAME_SCHEDULER_FPS_LOG 1
78
+ #endif
79
+
80
+ // --- master OFF forces every subsystem OFF ----------------------------------
81
+ #if !GEA_EMBEDDED_PERF
82
+ #undef GEA_FRAME_PERF_LOG
83
+ #define GEA_FRAME_PERF_LOG 0
84
+ #undef GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LOG
85
+ #define GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LOG 0
86
+ #undef GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LITE
87
+ #define GEA_EMBEDDED_FRAME_SCHEDULER_PERF_LITE 0
88
+ #undef GEA_EMBEDDED_UI_REFRESH_PERF
89
+ #define GEA_EMBEDDED_UI_REFRESH_PERF 0
90
+ #undef GEA_EMBEDDED_CANVAS_PERF_DETAIL
91
+ #define GEA_EMBEDDED_CANVAS_PERF_DETAIL 0
92
+ #undef GEA_EMBEDDED_RAF_PERF
93
+ #define GEA_EMBEDDED_RAF_PERF 0
94
+ #undef GEA_EMBEDDED_HEAP_DIAGNOSTICS_LOG
95
+ #define GEA_EMBEDDED_HEAP_DIAGNOSTICS_LOG 0
96
+ #endif
@@ -0,0 +1,165 @@
1
+ #pragma once
2
+ //
3
+ // geaos app ABI — the stable boundary between a dynamically-loaded gea app and
4
+ // the geaos core + backends. See docs/geaos-app-platform.md.
5
+ //
6
+ // This is a **C ABI on purpose**: the loader boundary must not depend on C++
7
+ // name mangling or C++ ABI stability. Apps are authored in TSX/C++, but the
8
+ // surface they bind to is this plain-C table of function pointers.
9
+ //
10
+ // BINDING (same surface, two transports):
11
+ // - esp32 (in-process): the core fills this table with thin thunks over the
12
+ // `gea::framework::*Backend` classes — effectively the jump table the
13
+ // loader patches. Zero IPC.
14
+ // - Linux (cross-process): the core fills it with proxy thunks that marshal
15
+ // to the geaos compositor process. Real isolation.
16
+ //
17
+ // VERSIONING: bump GEAOS_ABI_VERSION on any layout/semantics change. The app
18
+ // records the version it was built against; the loader refuses a mismatch.
19
+ //
20
+ // STRINGS: UTF-8. Inbound strings are caller-owned `const char *`. Outbound
21
+ // strings are written into a caller-provided buffer: pass `*len` = capacity,
22
+ // it is overwritten with the written length (excluding the NUL). NUMBERS use
23
+ // gea's value model (double) except explicit sizes/handles. A NULL function
24
+ // pointer (or NULL sub-table) means "unavailable on this target / not granted".
25
+
26
+ #include <stdint.h>
27
+ #include <stdbool.h>
28
+
29
+ #ifdef __cplusplus
30
+ extern "C" {
31
+ #endif
32
+
33
+ #define GEAOS_ABI_VERSION 1u
34
+
35
+ // --- capabilities -----------------------------------------------------------
36
+ // Declared in the app manifest; the loader acquires/grants the matching
37
+ // per-target resources (e.g. esp32 brings up the WiFi/BLE backends only when
38
+ // granted — see docs §9) and leaves the sub-table NULL when not granted.
39
+ typedef enum {
40
+ GEAOS_CAP_NONE = 0,
41
+ GEAOS_CAP_WIFI = 1u << 0,
42
+ GEAOS_CAP_BLE = 1u << 1,
43
+ GEAOS_CAP_SENSORS = 1u << 2,
44
+ GEAOS_CAP_CAMERA = 1u << 3,
45
+ GEAOS_CAP_AUDIO = 1u << 4,
46
+ GEAOS_CAP_STORAGE = 1u << 5,
47
+ } GeaosCapability;
48
+ typedef uint32_t GeaosCapabilityMask;
49
+
50
+ // --- service tables (mirror gea::framework::*Backend) -----------------------
51
+
52
+ typedef struct GeaosSystemApi {
53
+ void (*log)(const char *msg);
54
+ double (*now_ms)(void);
55
+ uint32_t (*abi_version)(void);
56
+ } GeaosSystemApi;
57
+
58
+ typedef struct GeaosMemoryApi {
59
+ double (*internal_free)(void);
60
+ double (*internal_largest_free_block)(void);
61
+ double (*internal_minimum_free)(void);
62
+ double (*psram_free)(void);
63
+ } GeaosMemoryApi;
64
+
65
+ typedef struct GeaosDisplayApi {
66
+ double (*brightness)(void);
67
+ void (*set_brightness)(double v);
68
+ // The reactive UI render path (tree mutation + present) is provided by the
69
+ // shared core runtime, not per-call here. Its exact placement relative to a
70
+ // dynamically-loaded app is the Phase 2 decision (doc §5/§13); this sub-table
71
+ // is the device-level display control only.
72
+ } GeaosDisplayApi;
73
+
74
+ typedef struct GeaosInputApi {
75
+ bool (*take_back_pressed)(void); // one-shot: reads and clears the flag
76
+ } GeaosInputApi;
77
+
78
+ typedef struct GeaosWifiApi {
79
+ bool (*enabled)(void);
80
+ void (*set_enabled)(bool enabled);
81
+ bool (*connected)(void);
82
+ double (*rssi)(void);
83
+ void (*ssid)(char *out, int *len);
84
+ void (*ip)(char *out, int *len);
85
+ void (*mac)(char *out, int *len);
86
+ void (*configure)(const char *ssid, const char *password);
87
+ void (*start_scan)(void);
88
+ bool (*scanning)(void);
89
+ double (*scan_count)(void);
90
+ void (*scan_ssid_at)(double index, char *out, int *len);
91
+ double (*scan_rssi_at)(double index);
92
+ bool (*scan_secured_at)(double index);
93
+ } GeaosWifiApi;
94
+
95
+ typedef struct GeaosBluetoothApi {
96
+ void (*init)(const char *device_name, double appearance, const char *mac);
97
+ bool (*enabled)(void);
98
+ void (*set_enabled)(bool enabled);
99
+ void (*start_advertising)(void);
100
+ void (*stop_advertising)(void);
101
+ bool (*connected)(void);
102
+ bool (*bound)(void);
103
+ double (*battery_level)(void);
104
+ void (*key_tap)(double hid_code);
105
+ void (*key_down)(double modifier, double hid_code);
106
+ void (*key_up)(void);
107
+ void (*mouse_move)(double dx, double dy, double buttons, double wheel);
108
+ void (*mouse_click)(double button);
109
+ } GeaosBluetoothApi;
110
+
111
+ typedef struct GeaosSensorsApi {
112
+ void (*init)(void);
113
+ void (*close)(void);
114
+ double (*tilt_x)(void);
115
+ double (*tilt_y)(void);
116
+ double (*accel_x)(void);
117
+ double (*accel_y)(void);
118
+ double (*accel_z)(void);
119
+ double (*gyro_x)(void);
120
+ double (*gyro_y)(void);
121
+ double (*gyro_z)(void);
122
+ } GeaosSensorsApi;
123
+
124
+ // The aggregate syscall table handed to the app at init. A sub-table is NULL
125
+ // when the target lacks it or the capability was not granted.
126
+ typedef struct GeaosServices {
127
+ uint32_t abi_version; // == GEAOS_ABI_VERSION the core implements
128
+ const GeaosSystemApi *system;
129
+ const GeaosMemoryApi *memory;
130
+ const GeaosDisplayApi *display;
131
+ const GeaosInputApi *input;
132
+ const GeaosWifiApi *wifi; // NULL unless GEAOS_CAP_WIFI granted
133
+ const GeaosBluetoothApi *bluetooth; // NULL unless GEAOS_CAP_BLE granted
134
+ const GeaosSensorsApi *sensors; // NULL unless GEAOS_CAP_SENSORS granted
135
+ // camera / audio / storage / power: formalized in Phase 3 as the surface settles.
136
+ } GeaosServices;
137
+
138
+ // --- app lifecycle (the app implements; the loader drives) ------------------
139
+ // Mirrors gea::framework::app::Application (init/frame/toggleSettings) plus a
140
+ // teardown hook the dynamic model needs for clean unload.
141
+ typedef struct GeaosAppLifecycle {
142
+ void (*init)(const GeaosServices *services, int width, int height, double device_pixel_ratio);
143
+ void (*frame)(int timestamp_ms);
144
+ void (*toggle_settings)(void); // may be NULL
145
+ void (*teardown)(void); // called before unload; may be NULL
146
+ } GeaosAppLifecycle;
147
+
148
+ // --- app descriptor (the app exports exactly one) ---------------------------
149
+ typedef struct GeaosAppDescriptor {
150
+ uint32_t abi_version; // GEAOS_ABI_VERSION the app was built for
151
+ const char *id; // stable app id
152
+ const char *name; // display name
153
+ GeaosCapabilityMask capabilities; // services the app needs
154
+ GeaosAppLifecycle lifecycle;
155
+ } GeaosAppDescriptor;
156
+
157
+ // Each app exports this single symbol. The loader resolves it (esp32: via the
158
+ // fixed-slot/jump-table binding; Linux: dlsym / the IPC handshake), checks
159
+ // abi_version + capabilities, then drives the lifecycle.
160
+ #define GEAOS_APP_ENTRY_SYMBOL "geaos_app_descriptor"
161
+ const GeaosAppDescriptor *geaos_app_descriptor(void);
162
+
163
+ #ifdef __cplusplus
164
+ } // extern "C"
165
+ #endif
@@ -0,0 +1,126 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::geolocation {
4
+
5
+ namespace detail {
6
+
7
+ // Read a numeric/bool field off a boxed record, keeping the fallback when the
8
+ // field is absent. The value-bridges below run only at dynamic boundaries
9
+ // (module-boundary records, boxed callbacks) — typed lowering never calls
10
+ // them; they exist so the generated record converters compile (same pattern
11
+ // as host/media.h).
12
+ template <typename T, typename Value>
13
+ T fieldOr(const Value &value, const char *name, T fallback)
14
+ {
15
+ if constexpr (requires { value.record_get_literal(name); }) {
16
+ const auto field = value.record_get_literal(name);
17
+ if constexpr (requires { field.is_nullish(); static_cast<T>(field); }) {
18
+ if (!field.is_nullish()) return static_cast<T>(field);
19
+ }
20
+ }
21
+ return fallback;
22
+ }
23
+
24
+ } // namespace detail
25
+
26
+ struct GeolocationCoordinates {
27
+ double latitude = 0.0;
28
+ double longitude = 0.0;
29
+ double altitude = 0.0;
30
+ double accuracy = -1.0;
31
+ double altitudeAccuracy = -1.0;
32
+ double heading = -1.0;
33
+ double speed = -1.0;
34
+
35
+ template <typename Value>
36
+ static GeolocationCoordinates __gea_native_from_value(const Value &value)
37
+ {
38
+ GeolocationCoordinates out{};
39
+ out.latitude = detail::fieldOr(value, "latitude", out.latitude);
40
+ out.longitude = detail::fieldOr(value, "longitude", out.longitude);
41
+ out.altitude = detail::fieldOr(value, "altitude", out.altitude);
42
+ out.accuracy = detail::fieldOr(value, "accuracy", out.accuracy);
43
+ out.altitudeAccuracy = detail::fieldOr(value, "altitudeAccuracy", out.altitudeAccuracy);
44
+ out.heading = detail::fieldOr(value, "heading", out.heading);
45
+ out.speed = detail::fieldOr(value, "speed", out.speed);
46
+ return out;
47
+ }
48
+ };
49
+
50
+ struct GeolocationPosition {
51
+ GeolocationCoordinates coords;
52
+ double timestamp = 0.0;
53
+ bool hasFix = false;
54
+
55
+ template <typename Value>
56
+ static GeolocationPosition __gea_native_from_value(const Value &value)
57
+ {
58
+ GeolocationPosition out{};
59
+ if constexpr (requires { value.record_get_literal("coords"); }) {
60
+ const auto coords = value.record_get_literal("coords");
61
+ if constexpr (requires { coords.is_nullish(); }) {
62
+ if (!coords.is_nullish()) out.coords = GeolocationCoordinates::__gea_native_from_value(coords);
63
+ }
64
+ }
65
+ out.timestamp = detail::fieldOr(value, "timestamp", out.timestamp);
66
+ out.hasFix = detail::fieldOr(value, "hasFix", out.hasFix);
67
+ return out;
68
+ }
69
+ };
70
+
71
+ struct GeolocationPositionError {
72
+ double code = 2.0;
73
+ const char *message = "Position unavailable";
74
+
75
+ // `message` keeps its default: a boxed record's string has no storage a
76
+ // stable const char* could borrow from.
77
+ template <typename Value>
78
+ static GeolocationPositionError __gea_native_from_value(const Value &value)
79
+ {
80
+ GeolocationPositionError out{};
81
+ out.code = detail::fieldOr(value, "code", out.code);
82
+ return out;
83
+ }
84
+ };
85
+
86
+ struct GeolocationOptions {
87
+ bool enableHighAccuracy = false;
88
+ double timeout = 0.0;
89
+ double maximumAge = 0.0;
90
+
91
+ template <typename Value>
92
+ static GeolocationOptions __gea_native_from_value(const Value &value)
93
+ {
94
+ GeolocationOptions out{};
95
+ out.enableHighAccuracy = detail::fieldOr(value, "enableHighAccuracy", out.enableHighAccuracy);
96
+ out.timeout = detail::fieldOr(value, "timeout", out.timeout);
97
+ out.maximumAge = detail::fieldOr(value, "maximumAge", out.maximumAge);
98
+ return out;
99
+ }
100
+ };
101
+
102
+ class GeolocationDriver {
103
+ public:
104
+ virtual ~GeolocationDriver() = default;
105
+
106
+ virtual bool init() = 0;
107
+ virtual bool hasFix() const = 0;
108
+ virtual GeolocationPosition currentPosition() const = 0;
109
+ };
110
+
111
+ class GeolocationAdapter {
112
+ public:
113
+ static void setDriver(GeolocationDriver *driver);
114
+ static GeolocationDriver *driver();
115
+
116
+ bool init() const;
117
+ bool hasFix() const;
118
+ GeolocationPosition currentPosition() const;
119
+ };
120
+
121
+ inline GeolocationAdapter geolocation()
122
+ {
123
+ return GeolocationAdapter{};
124
+ }
125
+
126
+ } // namespace gea::framework::geolocation
package/include/gpio.h ADDED
@@ -0,0 +1,61 @@
1
+ #pragma once
2
+
3
+ // Digital pins, and a single-wire addressable LED strand.
4
+ //
5
+ // A board with no display still has to be able to say something. Until now the
6
+ // only outputs an app could reach were the panel and the log, which left a bare
7
+ // module — the exact hardware headless support was added for — with nothing it
8
+ // could drive. These two are the smallest primitives that fix that: a pin you
9
+ // can set, and the RGB LED that is the one output almost every devkit ships.
10
+ //
11
+ // Platforms without the hardware implement these as no-ops that report failure,
12
+ // so an app written against them still runs everywhere; it simply lights
13
+ // nothing. `ok` is therefore "this board did it", not "the call was valid".
14
+
15
+ namespace gea::platform::gpio {
16
+
17
+ class Gpio {
18
+ public:
19
+ // Configure before use. Returns false when the pin is not a usable GPIO on
20
+ // this chip, or when the platform has no GPIO at all.
21
+ static bool configureOutput(int pin);
22
+ static bool configureInput(int pin, bool pull_up);
23
+ static bool write(int pin, bool level);
24
+ // Reads low as false. A pin that was never configured, or a platform with no
25
+ // GPIO, also reads false — callers that care must check configureInput().
26
+ static bool read(int pin);
27
+ };
28
+
29
+ // WS2812-family LED on one data pin: the "onboard RGB LED" of most ESP32-S3
30
+ // devkits. Colours are staged by setPixel and only reach the strand on show(),
31
+ // because the protocol rewrites the whole strand in one timed burst.
32
+ class AddressableLed {
33
+ public:
34
+ // The default way to turn one LED on or off: attaches the pin if it is not
35
+ // already attached, writes the colour and clocks it out, in one call. A
36
+ // board's "onboard LED" is a single WS2812 on one pin, and that case should
37
+ // not cost three calls and a piece of retained state to express. Black
38
+ // (0, 0, 0) is off.
39
+ static bool set(int pin, int r, int g, int b);
40
+
41
+ // Dark. The same thing as set(pin, 0, 0, 0) -- a WS2812 has no separate
42
+ // "off", black IS off -- spelled as its own verb because that is what a
43
+ // caller looks for, and because writing three zeros to mean "off" reads as
44
+ // an accident at the call site. The channel stays attached, so turning the
45
+ // LED back on costs one transmit and no re-allocation.
46
+ static bool off(int pin);
47
+
48
+ // The multi-pixel path, for a real strand where one transmit per FRAME beats
49
+ // one per pixel: attach once, setPixel each element, show to clock the whole
50
+ // buffer out. `set` is exactly these three for a strand of one.
51
+ static bool attach(int pin, int count);
52
+ static bool setPixel(int index, int r, int g, int b);
53
+ static bool show();
54
+
55
+ // Give the RMT channel back. The ESP32-S3 has four TX channels, so a strand
56
+ // an app has finished with should not hold one, and the pin can then be
57
+ // driven as a plain output again.
58
+ static void detach();
59
+ };
60
+
61
+ } // namespace gea::platform::gpio