@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,18 @@
1
+ #pragma once
2
+
3
+ namespace gea::platform::storage {
4
+
5
+ // Ensure the persistent file-cache filesystem is mounted; returns true when a
6
+ // writable root is available. Calls the registered mount provider (idempotent —
7
+ // safe on every file op). With no provider registered it returns false, so
8
+ // targets without persistent storage transparently fall back to no caching.
9
+ bool ensureMounted();
10
+
11
+ // Register the platform's mount provider. A target with persistent storage (e.g.
12
+ // a microSD card) calls this at startup with its mount function. Done via
13
+ // explicit registration rather than a weak symbol so the provider's translation
14
+ // unit is guaranteed to be linked in (see the waveshare-p4-7 sdcard_mount.cpp,
15
+ // wired from app_main).
16
+ void setMountProvider(bool (*provider)());
17
+
18
+ } // namespace gea::platform::storage
@@ -0,0 +1,11 @@
1
+ #pragma once
2
+
3
+ namespace gea::platform::power {
4
+
5
+ class Power {
6
+ public:
7
+ static bool init();
8
+ static int batteryPercent();
9
+ };
10
+
11
+ } // namespace gea::platform::power
@@ -0,0 +1,83 @@
1
+ #pragma once
2
+
3
+ // Optional application boot hook. An app that ships native sources can define
4
+ // `gea_app_native_boot` to bring its own hardware up before the runtime claims
5
+ // the display, the radios and the frame loop. It is a weak *declaration* with
6
+ // no default definition: an app that does not provide one leaves the symbol
7
+ // null and Runtime::run() skips the call.
8
+ extern "C" void gea_app_native_boot(void) __attribute__((weak));
9
+
10
+ // Bytes of internal, DMA-capable RAM to hold back for the display across the
11
+ // app's native boot hook. The app hook runs FIRST, by design -- an app that owns
12
+ // hardware needs its large contiguous internal allocations before the panel
13
+ // takes what is left -- but an app that fills internal RAM leaves the display
14
+ // unable to bring its bus up at all: the failure this exists for was 3,236 bytes
15
+ // free, largest block 1,600, and "SPI bus init failed: ESP_ERR_NO_MEM" one
16
+ // millisecond after the framebuffer landed in PSRAM. So Runtime::boot() takes a
17
+ // reserve through gea::platform::memory::Memory before the hook and releases it
18
+ // immediately before Display::init(), which turns an out-of-memory panel into
19
+ // the app giving up its last few kilobytes. 0 disables the reserve, which is
20
+ // every board that has never needed it. Declare it with `gea.defines`.
21
+ //
22
+ // Reserving for the WHOLE hook is the blunt version, and it is wrong for an app
23
+ // whose hook has a mandatory phase and a greedy one. The pedal is the case: its
24
+ // model arenas are a 213,472-byte bin-packing problem against 217,592 free, and
25
+ // holding anything back there makes the packer fail or time out, while the
26
+ // reverb promotion that runs straight afterwards takes 13 KB purely because it
27
+ // is there. Such an app leaves this at 0 and calls holdDisplayReserve() itself,
28
+ // between the two phases. See that method.
29
+ // A board that declares neither a panel nor a canvas has nothing to render to.
30
+ // The runtime then builds no framebuffer, no app tree and no frame loop; the
31
+ // board's own boot hook still runs, so a headless appliance keeps everything
32
+ // that is not the UI. Boards that say nothing keep a display, as they always
33
+ // have.
34
+ #ifndef GEA_EMBEDDED_NO_DISPLAY
35
+ #define GEA_EMBEDDED_NO_DISPLAY 0
36
+ #endif
37
+
38
+ #ifndef GEA_EMBEDDED_DISPLAY_INTERNAL_RESERVE_BYTES
39
+ #define GEA_EMBEDDED_DISPLAY_INTERNAL_RESERVE_BYTES 0
40
+ #endif
41
+
42
+ namespace gea::framework {
43
+
44
+ struct RuntimeOptions {
45
+ int width = 0;
46
+ int height = 0;
47
+ };
48
+
49
+ class Runtime {
50
+ public:
51
+ static void run(const RuntimeOptions &options);
52
+ // Runs the app's native boot hook, once. run() calls this itself, so an app
53
+ // never has to; a target calls it first when it wants the hook to run on a
54
+ // different task than the frame loop. That is not a stylistic choice: the
55
+ // hook is where an app initialises flash-backed things (NVS, a filesystem,
56
+ // its own partitions), and a task that performs a flash operation must be on
57
+ // an internal stack, while a frame loop that only renders can live on an
58
+ // external one. A target that keeps the loop on a PSRAM stack therefore runs
59
+ // the hook on the bring-up task and hands the loop over afterwards.
60
+ static void runNativeBoot();
61
+
62
+ // Bring-up only: services, storage, radios, display, the app's first mount,
63
+ // the frame scheduler -- everything up to but not including the event loop.
64
+ // It touches flash, so it must run on a task whose stack is in INTERNAL RAM;
65
+ // a target that runs the loop on an external stack calls this from its
66
+ // bring-up task and then lets run() do nothing but loop. Idempotent: run()
67
+ // calls it, so a target that does not care never sees a difference.
68
+ static bool boot(const RuntimeOptions &options);
69
+
70
+ // Hands the runtime a block of internal RAM to hold for the display and free
71
+ // immediately before Display::init(). For an app whose native boot hook has
72
+ // a phase that must not see less RAM than it actually has, followed by one
73
+ // that will opportunistically eat whatever is left: take the block with
74
+ // gea::platform::memory::Memory::reserveInternalDma() at the moment in
75
+ // between and pass it here. nullptr is accepted and means no reserve, so a
76
+ // failed reservation needs no check at the call site. Called more than once,
77
+ // the later block is held and the earlier one is freed at once -- the
78
+ // runtime holds exactly one app reserve, in addition to the one
79
+ // GEA_EMBEDDED_DISPLAY_INTERNAL_RESERVE_BYTES asks for.
80
+ static void holdDisplayReserve(void *reserve);
81
+ };
82
+
83
+ } // namespace gea::framework
@@ -0,0 +1,10 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ class AppRunner {
6
+ public:
7
+ static bool initApplication(int width, int height);
8
+ };
9
+
10
+ } // namespace gea::framework::services
@@ -0,0 +1,14 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ class AppState {
6
+ public:
7
+ static bool init();
8
+ static void lock();
9
+ static void unlock();
10
+ static const char *currentAppId();
11
+ static void afterAppInit();
12
+ };
13
+
14
+ } // namespace gea::framework::services
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ class BatteryService {
6
+ public:
7
+ static void update();
8
+ static void start();
9
+ static void poll(int nowMs);
10
+ };
11
+
12
+ } // namespace gea::framework::services
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ class BluetoothService {
6
+ public:
7
+ static void preinitForApp();
8
+ static void notifyConnected();
9
+ static void notifyDisconnected();
10
+ static void notifyBound();
11
+ };
12
+
13
+ } // namespace gea::framework::services
@@ -0,0 +1,76 @@
1
+ #pragma once
2
+
3
+ #include <cstdarg>
4
+ #include <cstdint>
5
+ #include <cstddef>
6
+
7
+ namespace gea::framework::services {
8
+
9
+ class DiagnosticsFrame {
10
+ public:
11
+ virtual ~DiagnosticsFrame() = default;
12
+ virtual bool empty() const = 0;
13
+ virtual unsigned char *payload() = 0;
14
+ virtual int payloadCapacity() const = 0;
15
+ virtual bool queue(int channel, int type, const unsigned char *payload, int payload_len) = 0;
16
+ };
17
+
18
+ class DiagnosticsOutput {
19
+ public:
20
+ virtual ~DiagnosticsOutput() = default;
21
+ virtual int write(const unsigned char *data, int len, int *err_out) = 0;
22
+ };
23
+
24
+ enum class DiagnosticsSendStatus {
25
+ Idle,
26
+ Sent,
27
+ Backpressure,
28
+ Closed,
29
+ };
30
+
31
+ class DiagnosticsConnection {
32
+ public:
33
+ DiagnosticsConnection();
34
+ ~DiagnosticsConnection();
35
+ DiagnosticsConnection(const DiagnosticsConnection &) = delete;
36
+ DiagnosticsConnection &operator=(const DiagnosticsConnection &) = delete;
37
+
38
+ void reset();
39
+ bool hasPendingOutput() const;
40
+ bool wantsImmediatePoll(std::int64_t now_us) const;
41
+ int pollIntervalMs() const;
42
+ int backpressureDelayMs() const;
43
+ void handleIncoming(const char *data, int len);
44
+ DiagnosticsSendStatus flush(DiagnosticsOutput &output, std::int64_t now_us, int *err_out);
45
+ void prepareNextFrame(std::int64_t now_us);
46
+
47
+ private:
48
+ class Impl;
49
+ Impl *impl_;
50
+ };
51
+
52
+ class DiagnosticsServer {
53
+ public:
54
+ static bool begin();
55
+ static int port();
56
+ static void start();
57
+ static void print(const char *fmt, ...);
58
+ static int vprint(const char *fmt, std::va_list args);
59
+ };
60
+
61
+ class HeapProbe {
62
+ public:
63
+ static void log(const char *stage);
64
+ };
65
+
66
+ class StackProbe {
67
+ public:
68
+ static void logCurrentTask(const char *stage);
69
+ };
70
+
71
+ class HeapTaskSummary {
72
+ public:
73
+ static void log(const char *stage);
74
+ };
75
+
76
+ } // namespace gea::framework::services
@@ -0,0 +1,54 @@
1
+ #pragma once
2
+
3
+ #include "services/diagnostics.h"
4
+
5
+ #include <cstdarg>
6
+ #include <cstdint>
7
+
8
+ namespace gea::framework::services {
9
+
10
+ enum DiagnosticsWaitEvent {
11
+ kDiagnosticsWaitNone = 0,
12
+ kDiagnosticsWaitListenerReadable = 1 << 0,
13
+ kDiagnosticsWaitPeerReadable = 1 << 1,
14
+ kDiagnosticsWaitPeerWritable = 1 << 2,
15
+ };
16
+
17
+ constexpr int kDiagnosticsIoWouldBlock = -2;
18
+
19
+ class DiagnosticsPeer : public DiagnosticsOutput {
20
+ public:
21
+ ~DiagnosticsPeer() override = default;
22
+ virtual int read(char *data, int len, int *err_out) = 0;
23
+ virtual void close(const char *reason, int err) = 0;
24
+ };
25
+
26
+ class DiagnosticsListener {
27
+ public:
28
+ virtual ~DiagnosticsListener() = default;
29
+ virtual bool listen(int port) = 0;
30
+ virtual DiagnosticsPeer *accept() = 0;
31
+ virtual int wait(DiagnosticsPeer *peer, bool wait_for_peer_write, int timeout_ms, int *err_out) = 0;
32
+ };
33
+
34
+ using DiagnosticsVPrintSink = int (*)(const char *, std::va_list);
35
+
36
+ class DiagnosticsPlatform {
37
+ public:
38
+ virtual ~DiagnosticsPlatform() = default;
39
+ virtual DiagnosticsListener &listener() = 0;
40
+ virtual void installLogSink(DiagnosticsVPrintSink sink) = 0;
41
+ virtual void startTask(void (*entry)(void *), void *context) = 0;
42
+ virtual std::int64_t nowUs() = 0;
43
+ virtual void sleepMs(int ms) = 0;
44
+ virtual void logInfo(const char *fmt, ...) = 0;
45
+ virtual void logWarn(const char *fmt, ...) = 0;
46
+ virtual void logError(const char *fmt, ...) = 0;
47
+ virtual void logHeapProbe(const char *stage) = 0;
48
+ virtual void logCurrentTaskStackProbe(const char *stage) { (void)stage; }
49
+ virtual void logHeapTaskSummary(const char *stage) { (void)stage; }
50
+ };
51
+
52
+ DiagnosticsPlatform &diagnosticsPlatform();
53
+
54
+ } // namespace gea::framework::services
@@ -0,0 +1,71 @@
1
+ #pragma once
2
+
3
+ #include "event.h"
4
+
5
+ namespace gea::framework::services {
6
+
7
+ class EventQueue {
8
+ public:
9
+ constexpr EventQueue() = default;
10
+ explicit constexpr EventQueue(void *native) : native_(native) {}
11
+
12
+ explicit constexpr operator bool() const { return native_ != nullptr; }
13
+ constexpr void *nativeHandle() const { return native_; }
14
+
15
+ private:
16
+ void *native_ = nullptr;
17
+ };
18
+
19
+ class FrameScheduler {
20
+ public:
21
+ using FrameCallback = void (*)(int timestamp_ms, void *context);
22
+
23
+ struct FrameCallbacks {
24
+ FrameCallback frame = nullptr;
25
+ void *context = nullptr;
26
+ };
27
+
28
+ #ifndef GEA_EMBEDDED_DEFAULT_FRAME_INTERVAL_US
29
+ #define GEA_EMBEDDED_DEFAULT_FRAME_INTERVAL_US 16667
30
+ #endif
31
+
32
+ static constexpr int kDefaultFrameIntervalUs = GEA_EMBEDDED_DEFAULT_FRAME_INTERVAL_US;
33
+ static constexpr int kDefaultFrameIntervalMs = 16;
34
+ static constexpr int kMinFrameIntervalMs = 1;
35
+ static constexpr int kMaxFrameIntervalMs = 1000;
36
+ static constexpr int kFrameIntervalUs = kDefaultFrameIntervalUs;
37
+ static constexpr int kFrameIntervalMs = kDefaultFrameIntervalMs;
38
+ static constexpr int kWatchdogMs = 2000;
39
+ static constexpr int kEventQueueDepth = 32;
40
+
41
+ static EventQueue createEventQueue();
42
+ static EventQueue eventQueue();
43
+ static bool sendEvent(const gea::framework::events::Event &event, int wait_ms = 0);
44
+ static bool receiveEvent(gea::framework::events::Event *event);
45
+ static void start(EventQueue queue);
46
+ static void runFrame(const FrameCallbacks &callbacks);
47
+ // Graceful-overrun catch-up (see runFrame): takeCatchUpRequest() returns+clears
48
+ // whether the last frame overran the budget; drainFramesAndCheckInput() discards
49
+ // the timer's redundant queued frames and returns true if real input is waiting,
50
+ // so the catch-up loop yields to input but not to its own frame posts.
51
+ static bool takeCatchUpRequest();
52
+ static bool drainFramesAndCheckInput();
53
+ // TE-vsync single-clock pacing: when the active display has TE-sync enabled, the
54
+ // panel's TE edge is the ONE frame clock. The TE ISR (teEdgeIsr → notifyVsyncFromISR)
55
+ // becomes the sole frame producer; the scheduler's own timer stops posting frames
56
+ // (it stays only as a dead-TE watchdog), and the runtime loop suppresses its
57
+ // timer/touch frame posts + back-to-back catch-up. This eliminates the second clock
58
+ // (scheduler timer) that raced the TE → unstable 30-44fps pan. No effect on non-vsync
59
+ // apps. setVsyncDriven() is called from the display backend's setVSync().
60
+ static void setVsyncDriven(bool driven);
61
+ static bool vsyncDriven();
62
+ // Post exactly one (coalesced) frame from the panel TE edge ISR. ISR-safe.
63
+ static void notifyVsyncFromISR();
64
+ static void setFrameIntervalMs(int interval_ms);
65
+ static int frameIntervalMs();
66
+ static void setFrameRate(double fps);
67
+ static double frameRate();
68
+ static int nowMs();
69
+ };
70
+
71
+ } // namespace gea::framework::services
@@ -0,0 +1,49 @@
1
+ #pragma once
2
+
3
+ #include "services/diagnostics.h"
4
+
5
+ #include <cstdint>
6
+
7
+ namespace gea::framework::services {
8
+
9
+ class MirrorService {
10
+ public:
11
+ static int beginSnapshot();
12
+ static int beginDiff();
13
+ static int nextRecord(unsigned char *dst, int capacity);
14
+ static void clearDirty();
15
+ };
16
+
17
+ class MirrorTransport {
18
+ public:
19
+ ~MirrorTransport();
20
+
21
+ static int pollIntervalMs();
22
+
23
+ bool enabled() const;
24
+ bool suppressesLogForwarding() const;
25
+ void reset();
26
+ bool handleCommand(char command);
27
+ bool ready(std::int64_t now) const;
28
+ void syncCurrentApp();
29
+ bool ensureBuffer(DiagnosticsFrame &pending);
30
+ void beginIfDue(std::int64_t now, DiagnosticsFrame &pending);
31
+ void appendBatch(std::int64_t now, DiagnosticsFrame &pending);
32
+
33
+ private:
34
+ void enable();
35
+ void disable();
36
+ void endMessage(std::int64_t now);
37
+ void releaseBuffer();
38
+
39
+ bool enabled_ = false;
40
+ bool forceSnapshot_ = true;
41
+ bool messageActive_ = false;
42
+ unsigned char *buffer_ = nullptr;
43
+ int cap_ = 0;
44
+ int heldLen_ = 0;
45
+ std::int64_t nextAt_ = 0;
46
+ char appId_[64] = "";
47
+ };
48
+
49
+ } // namespace gea::framework::services
@@ -0,0 +1,25 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ struct NetworkServicesOptions {
6
+ // Device control and screenshots use the USB command channel. Keep the
7
+ // separate TCP diagnostics server opt-in so its 6 KiB task does not compete
8
+ // with WiFi OTA on RAM-constrained boards.
9
+ bool diagnosticsEnabled = false;
10
+ bool otaEnabled = true;
11
+ };
12
+
13
+ class NetworkServices {
14
+ public:
15
+ // Records the requested service options. If early boot already connected
16
+ // WiFi, starts the servers immediately and returns true. Otherwise the
17
+ // platform WiFi bring-up calls startDeferredServers() after lazy connection.
18
+ static bool start(const NetworkServicesOptions &options);
19
+
20
+ // Starts diagnostics/OTA per the options recorded by start(). Called by the
21
+ // platform WiFi driver once the radio is up and connected.
22
+ static void startDeferredServers();
23
+ };
24
+
25
+ } // namespace gea::framework::services
@@ -0,0 +1,32 @@
1
+ #pragma once
2
+
3
+ #include <cstddef>
4
+ #include <cstdint>
5
+
6
+ namespace gea::framework::services {
7
+
8
+ class OtaServer {
9
+ public:
10
+ enum class Result : std::uint8_t {
11
+ Ok = 0,
12
+ InvalidSize = 1,
13
+ NoPartition = 2,
14
+ BeginFailed = 3,
15
+ WriteFailed = 4,
16
+ SizeMismatch = 5,
17
+ ValidationFailed = 6,
18
+ BootSelectionFailed = 7,
19
+ NotStarted = 8,
20
+ };
21
+
22
+ static bool start();
23
+ static Result beginImage(std::size_t imageSize);
24
+ static Result writeImage(const std::uint8_t *data, std::size_t length);
25
+ static Result finishImage();
26
+ static void abortImage();
27
+ static std::size_t receivedBytes();
28
+ static std::size_t expectedBytes();
29
+ static void rebootSoon();
30
+ };
31
+
32
+ } // namespace gea::framework::services
@@ -0,0 +1,11 @@
1
+ #pragma once
2
+
3
+ namespace gea::framework::services {
4
+
5
+ class RuntimeLog {
6
+ public:
7
+ static void printRuntimeBanner();
8
+ static void appStarted(bool networkReady);
9
+ };
10
+
11
+ } // namespace gea::framework::services
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ namespace gea::framework::services {
6
+
7
+ class StorageService {
8
+ public:
9
+ static bool init();
10
+
11
+ // Persistent key/value strings (NVS "gea" namespace on esp32). getString
12
+ // fills buf and returns true iff the key exists; setString persists + commits.
13
+ // Used for device settings such as the default launch app (read at boot).
14
+ static bool getString(const char *key, char *buf, unsigned capacity);
15
+ static bool setString(const char *key, const char *value);
16
+
17
+ // Whole-store blob persistence for the app-facing `localStorage` (see
18
+ // gea::host::StorageFacade). The facade keeps the live key/value set in RAM
19
+ // and serializes it to one opaque, binary-safe blob; loadKv restores it at
20
+ // boot (empty `out` if none stored), saveKv persists it (an empty blob clears
21
+ // the slot). Backed by an NVS blob on esp32, RAM elsewhere.
22
+ static bool loadKv(std::string &out);
23
+ static void saveKv(const std::string &blob);
24
+ };
25
+
26
+ } // namespace gea::framework::services
@@ -0,0 +1,38 @@
1
+ #pragma once
2
+
3
+ namespace gea::platform::touch {
4
+
5
+ enum class Phase {
6
+ Down = 1,
7
+ Move = 2,
8
+ Up = 3,
9
+ };
10
+
11
+ class Touchscreen {
12
+ public:
13
+ using Observer = void (*)(Phase phase, bool touching, int x, int y);
14
+ // Multi-finger variant: same event plus a 0-based finger index (0 = primary).
15
+ // Controllers that support simultaneous touches (e.g. the GT911) report every
16
+ // finger through this. Single-touch controllers leave it unimplemented; the
17
+ // weak default in touch_runtime.cpp is a no-op, so those targets keep using
18
+ // the single Observer with no code change.
19
+ using PointerObserver = void (*)(Phase phase, bool touching, int x, int y, int pointerId);
20
+
21
+ static void setObserver(Observer observer);
22
+ // Optional: register a multi-finger observer. Default (weak) no-op.
23
+ static void setPointerObserver(PointerObserver observer);
24
+ static bool init();
25
+ // Optional event-loop polling hook. Controllers backed by interrupts or their
26
+ // own driver task use the weak no-op implementation.
27
+ static void poll(int nowMs);
28
+ static int read(int *x, int *y);
29
+ static int readCached(int *x, int *y);
30
+ static void consumeLatestMove(int *x, int *y);
31
+ // Inject a synthetic touch event through the same path as the hardware
32
+ // reader: updates the latest-move cache (so consumeLatestMove returns these
33
+ // coords) and notifies the observer. Used by the debug FIFO so injected
34
+ // drags behave exactly like a real finger.
35
+ static void injectEvent(Phase phase, bool touching, int x, int y);
36
+ };
37
+
38
+ } // namespace gea::platform::touch