@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,290 @@
1
+ #pragma once
2
+
3
+ #include "pixel.h"
4
+
5
+ #include <cstddef>
6
+ #include <cstdint>
7
+
8
+ namespace gea::framework::graphics {
9
+
10
+ class RasterizedFont;
11
+
12
+ struct ClipRect {
13
+ int x0 = 0;
14
+ int y0 = 0;
15
+ int x1 = 0;
16
+ int y1 = 0;
17
+ };
18
+
19
+ struct CanvasDirtyRect {
20
+ int x0 = 0;
21
+ int y0 = 0;
22
+ int x1 = -1;
23
+ int y1 = -1;
24
+ };
25
+
26
+ // Interleaved circle record used by fillCirclesRgb565WorldYSorted. Field order
27
+ // is (y, x, color) so sorting an array of these by ::y is a simple
28
+ // std::sort with a single comparator on the first field. The display_present
29
+ // pipeline allocates these in INTERNAL RAM, sorts in place, and hands the
30
+ // pointer directly to Canvas — no deinterleave step.
31
+ struct CircleEntry {
32
+ std::uint16_t y;
33
+ std::uint16_t x;
34
+ pixel::native_t color;
35
+ };
36
+
37
+ // Interleaved opaque-triangle record used by the batched triangle present
38
+ // command (one command per frame instead of one per triangle — the per-command
39
+ // record/dispatch overhead dominated at a few hundred triangles). Kept in
40
+ // PAINTER'S order (depth-sorted by the producer), so the rasterizer may only
41
+ // band-reject per entry, never reorder. rowY0/rowY1 cache the vertical extent
42
+ // for that reject. int16 coordinates: producers clamp to +/-30000.
43
+ struct TriangleEntry {
44
+ std::int16_t x0;
45
+ std::int16_t y0;
46
+ std::int16_t x1;
47
+ std::int16_t y1;
48
+ std::int16_t x2;
49
+ std::int16_t y2;
50
+ pixel::native_t color;
51
+ std::int16_t rowY0;
52
+ std::int16_t rowY1;
53
+ };
54
+
55
+ class Canvas {
56
+ public:
57
+ static constexpr int kMaxClipDepth = 32;
58
+ static constexpr int kMaxDirtyRects = 24;
59
+ static void setAntialiasSamples(int samples);
60
+ static int antialiasSamples();
61
+ // Large-static-text sprite cache opt-in (default off). When enabled, text at
62
+ // font.sizePx() >= 40 is rasterized to a coverage sprite once and blitted on
63
+ // subsequent frames instead of re-running the glyph rasterizer. Toggled from
64
+ // TS via Display.setTextRasterCache(boolean).
65
+ static void setTextRasterCacheEnabled(bool enabled);
66
+ static bool textRasterCacheEnabled();
67
+ // Pre-packed glyph stamping opt-in (packed 4bpp targets; no-op elsewhere).
68
+ // The app declares text always draws over ONE solid color (0xRRGGBB);
69
+ // glyphs are then pre-blended+packed once and stamped as byte copies — no
70
+ // per-pixel destination read, no blend. Negative disables. Toggled from TS
71
+ // via Display.setTextSolidBackdrop(number).
72
+ static void setTextSolidBackdrop(int rrggbb);
73
+
74
+ // `pixel::native_t` is this target's framebuffer pixel (RGB565 16-bit or
75
+ // RGBA8888 32-bit), fixed at compile time — no runtime format here.
76
+ void bindPixels(pixel::native_t *pixels, int width, int height);
77
+ void bindPixels(pixel::native_t *pixels, int width, int height, int stridePixels);
78
+
79
+ #if GEA_EMBEDDED_DISPLAY_ROTATE_LANDSCAPE
80
+ // Bind a PHYSICAL landscape buffer while presenting a LOGICAL portrait
81
+ // coordinate system. The apps + renderer keep drawing in logical portrait
82
+ // (logicalWidth x logicalHeight); every pixel write is rotated 90° into the
83
+ // physical landscape buffer, whose dimensions are (logicalHeight x
84
+ // logicalWidth). A logical pixel (lx, ly) addresses physical column ly, row
85
+ // (logicalWidth-1)-lx — the same map the papers3/T5 flush-time transpose used,
86
+ // so the Canvas IS the panel buffer and no separate transpose pass is needed.
87
+ // Additive and compile-gated: only built when GEA_EMBEDDED_DISPLAY_ROTATE_
88
+ // LANDSCAPE is defined (papers3), so every other target is byte-for-byte
89
+ // unaffected. Supports both packed grayscale and native RGB framebuffers.
90
+ void bindPixelsRotatedLandscape(pixel::native_t *physPixels, int logicalWidth, int logicalHeight);
91
+ #endif
92
+
93
+ pixel::native_t *pixels();
94
+ const pixel::native_t *pixels() const;
95
+ int width() const;
96
+ int height() const;
97
+ int strideBytes() const;
98
+
99
+ // Software-scroll register: rotates the logical → physical row mapping
100
+ // inside a scroll region without moving any pixels. A pixel write at
101
+ // logical row Y lands at the physical FB row determined by the region:
102
+ // - outside the region (y < regionY or y >= regionY+regionH): no
103
+ // translation, physical row = y.
104
+ // - inside the region: physical row = regionY + ((y − regionY +
105
+ // scrollOffsetY) mod regionH). Wraps inside the region's row range
106
+ // so the region acts as a ring buffer along Y.
107
+ // regionH = 0 means "no region active" — every pixel writes through.
108
+ // A scroll becomes a single integer update on scrollOffsetY rather than
109
+ // an H × W memcpy.
110
+ void setScrollRegion(int regionY, int regionH, int scrollOffsetY);
111
+ int scrollOffsetY() const { return scrollOffsetY_; }
112
+ int scrollRegionY() const { return regionY_; }
113
+ int scrollRegionH() const { return regionH_; }
114
+ int rowToPhysical(int y) const {
115
+ if (regionH_ <= 0) return y;
116
+ if (y < regionY_ || y >= regionY_ + regionH_) return y;
117
+ int rel = (y - regionY_) + scrollOffsetY_;
118
+ rel %= regionH_;
119
+ if (rel < 0) rel += regionH_;
120
+ return regionY_ + rel;
121
+ }
122
+ #if GEA_PIXEL_STORAGE_PACKED
123
+ // Base of a physical row's packed bytes (sub-byte framebuffers only — GRAY4
124
+ // at 2 px/byte, GRAY2 at 4 px/byte; strideBytes_ carries the depth).
125
+ std::uint8_t *packedRow(int phys) {
126
+ return reinterpret_cast<std::uint8_t *>(pixels_) + static_cast<std::size_t>(phys) * strideBytes_;
127
+ }
128
+ const std::uint8_t *packedRow(int phys) const {
129
+ return reinterpret_cast<const std::uint8_t *>(pixels_) + static_cast<std::size_t>(phys) * strideBytes_;
130
+ }
131
+ #endif
132
+ #if GEA_EMBEDDED_DISPLAY_ROTATE_LANDSCAPE
133
+ // Landscape-rotation primitives (see bindPixelsRotatedLandscape). A LOGICAL
134
+ // pixel (lx, ly) maps to physical row (width_-1)-lx, physical column ly. These
135
+ // are the SINGLE place the rotation transform lives; every rotated Canvas draw
136
+ // path routes its pixel writes through them, so the transform is verified once.
137
+ bool landscapeRot_ = false;
138
+ int rotPhysRow(int lx) const { return (width_ - 1) - lx; }
139
+ void rotSet(int lx, int ly, pixel::native_t v) {
140
+ #if GEA_PIXEL_STORAGE_PACKED
141
+ pixel::packed::set(packedRow(rotPhysRow(lx)), ly, v);
142
+ #else
143
+ pixels_[rotPhysRow(lx) * stride_ + ly] = v;
144
+ #endif
145
+ }
146
+ void rotBlend(int lx, int ly, pixel::native_t fg, int alpha) {
147
+ #if GEA_PIXEL_STORAGE_PACKED
148
+ pixel::packed::blendPixel(packedRow(rotPhysRow(lx)), ly, fg, alpha);
149
+ #else
150
+ pixel::native_t &dst = pixels_[rotPhysRow(lx) * stride_ + ly];
151
+ dst = pixel::blendNative(fg, dst, alpha);
152
+ #endif
153
+ }
154
+ pixel::native_t rotGet(int lx, int ly) const {
155
+ #if GEA_PIXEL_STORAGE_PACKED
156
+ return pixel::packed::get(packedRow(rotPhysRow(lx)), ly);
157
+ #else
158
+ return pixels_[rotPhysRow(lx) * stride_ + ly];
159
+ #endif
160
+ }
161
+ #endif
162
+
163
+ void resetDirty();
164
+ void markDirty(int x0, int y0, int x1, int y1);
165
+ bool dirty(int *x0, int *y0, int *x1, int *y1) const;
166
+ int dirtyRects(CanvasDirtyRect *rects, int capacity) const;
167
+
168
+ void resetClip();
169
+ void pushClip(int x, int y, int w, int h);
170
+ void popClip();
171
+ void currentClip(int *x0, int *y0, int *x1, int *y1) const;
172
+
173
+ void setGlobalAlpha(std::uint8_t alpha);
174
+ std::uint8_t globalAlpha() const;
175
+
176
+ void clear(pixel::native_t color);
177
+ void clearRect(int x, int y, int w, int h);
178
+ void scrollRect(int x, int y, int w, int h, int dx, int dy);
179
+ void fillRect(int x, int y, int w, int h, pixel::native_t color);
180
+ void fillRectOpaque(int x, int y, int w, int h, pixel::native_t color);
181
+ void strokeRect(int x, int y, int w, int h, pixel::native_t color);
182
+ void fillCircle(int cx, int cy, int r, pixel::native_t color);
183
+ void fillCirclesRgb565(const std::uint16_t *xs, const std::uint16_t *ys, int count, int r, const pixel::native_t *colors);
184
+ void fillCirclesRgb565Uniform(const std::uint16_t *xs, const std::uint16_t *ys, int count, int r, pixel::native_t color);
185
+ void fillCirclesRgb565WorldYSorted(const CircleEntry *circles, int count,
186
+ int r,
187
+ int yBandMinWorld, int yBandMaxWorld,
188
+ int xOffset, int yOffset);
189
+ void fillRoundedRectBoxesRgb565(const std::int16_t *xs, const std::int16_t *ys, int count,
190
+ int w, int h, int tl, int tr, int br, int bl,
191
+ const pixel::native_t *colors);
192
+ void strokeCircle(int cx, int cy, int r, pixel::native_t color);
193
+ void drawLine(int x0, int y0, int x1, int y1, pixel::native_t color);
194
+ void drawArc(int cx, int cy, int r, int start_deg, int end_deg, pixel::native_t color);
195
+ void fillTriangle(int x0, int y0, int x1, int y1, int x2, int y2, pixel::native_t color);
196
+ void fillTriangleOpaque(int x0, int y0, int x1, int y1, int x2, int y2, pixel::native_t color);
197
+ // Opaque triangle BATCH with per-row span occlusion. Entries arrive
198
+ // farthest-first (painter's depth order); this walks them nearest-first and
199
+ // fills only pixels no nearer triangle has already covered — each pixel
200
+ // written once, occluded spans cost a coverage test instead of a fill. Same
201
+ // visible result as drawing the batch back-to-front with fillTriangleOpaque,
202
+ // but without the overdraw. Coords are world; ox/oy shift into this canvas's
203
+ // chunk-local space (like the fillTriangleOpaque caller does). Valid only for
204
+ // OPAQUE, depth-sorted batches. Coverage lives in per-core static scratch, so
205
+ // two Canvases on different cores may run this concurrently.
206
+ void fillTrianglesOpaqueOccluded(const TriangleEntry *tris, int count, int ox, int oy);
207
+ void drawText(const char *text, int x, int y, pixel::native_t color, float scale);
208
+ void drawTextFont(const char *text, int x, int y, pixel::native_t color, int font_id);
209
+ void drawTextFontFamily(const char *text, int x, int y, pixel::native_t color, int family_id, int size_px);
210
+ // Rotated 90deg for panel-native landscape rendering: the anchor and the
211
+ // text advance are in LANDSCAPE coordinates; every glyph pixel writes to
212
+ // the portrait panel address (panel_x = ly, panel_y = height-1-lx). The
213
+ // canvas must be bound to the FULL portrait buffer.
214
+ void drawTextFontFamilyRotated90(const char *text, int lx, int ly, pixel::native_t color, int family_id, int size_px);
215
+ void drawTextFontFamilyOnBackground(const char *text, int x, int y, pixel::native_t color,
216
+ int family_id, int size_px, pixel::native_t background);
217
+ int measureTextFontFamily(const char *text, int family_id, int size_px);
218
+ int measureTextInkCenterFontFamily(const char *text, int family_id, int size_px);
219
+ static void measureTextFont(const char *text, int max_width, int font_id, int *out_w, int *out_h);
220
+ void fillRoundedRect(int x, int y, int w, int h, int tl, int tr, int br, int bl, pixel::native_t color);
221
+ void strokeRoundedRect(int x, int y, int w, int h, int tl, int tr, int br, int bl, int lw, pixel::native_t color);
222
+ // Image sources are native_t (same format as the framebuffer for this
223
+ // target), so blits are a native copy/blend with no per-pixel conversion.
224
+ void drawImage(const pixel::native_t *src, const std::uint8_t *alpha, int src_w, int src_h, int dx, int dy);
225
+ #if GEA_PIXEL_STORAGE_PACKED
226
+ // Blit a packed source (a grayscale canvas surface, stored in the same
227
+ // sub-byte packing as the framebuffer) straight in — a direct same-depth
228
+ // copy, no unpack/repack. Clipped.
229
+ void blitPacked(const std::uint8_t *src, int src_w, int src_h, int dx, int dy);
230
+ #endif
231
+ void drawImage(const pixel::native_t *src, const std::uint8_t *alpha, int src_w, int src_h,
232
+ int dx, int dy, int dst_w, int dst_h);
233
+ void drawImageRotated90CW(const pixel::native_t *src, const std::uint8_t *alpha, int src_w, int src_h,
234
+ int dx, int dy, int dst_w, int dst_h);
235
+ void drawImageRounded(const pixel::native_t *src, const std::uint8_t *alpha, int src_w, int src_h,
236
+ int dx, int dy, int dst_w, int dst_h, int tl, int tr, int br, int bl);
237
+ void drawImageTiledX(const pixel::native_t *src, const std::uint8_t *alpha, int src_w, int src_h,
238
+ int dx, int dy, int width);
239
+
240
+ private:
241
+ void writePixel(int x, int y, pixel::native_t fg);
242
+ void writePixelAlpha(int x, int y, pixel::native_t fg, std::uint8_t alpha);
243
+ // Native-pixel writers: the colour is already this target's native pixel
244
+ // (e.g. an image source pixel), so there is no colour conversion — clip +
245
+ // global-alpha + dirty handling only. writePixel/writePixelAlpha are the
246
+ // RGB565-colour entry points and convert once via pixel::toNative().
247
+ void writePixelNative(int x, int y, pixel::native_t c);
248
+ void writePixelNativeAlpha(int x, int y, pixel::native_t c, std::uint8_t alpha);
249
+ // Fill `count` native pixels at physical row `phys` with an RGB565 colour
250
+ // (converted once to native). fillSpanGlobalAlpha honours globalAlpha_.
251
+ void fillSpanColor(int phys, int x0, int count, pixel::native_t color);
252
+ void fillSpanGlobalAlpha(int phys, int x0, int count, pixel::native_t color);
253
+ void markDirtyClipped(int x0, int y0, int x1, int y1);
254
+ void addDirtyRect(int x0, int y0, int x1, int y1);
255
+ bool fillCircleNoDirty(int cx, int cy, int r, pixel::native_t color);
256
+ void fillQuarterCircle(int cx, int cy, int r, int quadrant, pixel::native_t color);
257
+ void fillCircleBox(int x, int y, int size, pixel::native_t color);
258
+ void drawRasterizedText(const char *text, int x, int y, pixel::native_t color, const RasterizedFont &font);
259
+ // Blit a pre-rasterized glyph-coverage sprite (text-relative coords, origin at
260
+ // pen x/y, offset by minLX/minLY) honouring the current clip, global alpha and
261
+ // dirty accounting. Runs of fully-opaque coverage use the fast span fill.
262
+ // Used by the large-static-text sprite cache to avoid re-running the scattered
263
+ // flash-atlas glyph rasterizer every frame. cov is w*h coverage bytes; rowX0/
264
+ // rowX1 are per-row ink extents (rowX1 < rowX0 marks an empty row).
265
+ void blitGlyphSprite(const std::uint8_t *cov, const std::int16_t *rowX0, const std::int16_t *rowX1,
266
+ const std::uint8_t *rleVal, const std::uint16_t *rleLen, const std::int32_t *rleRowStart,
267
+ int minLX, int minLY, int w, int h, int x, int y, pixel::native_t color);
268
+
269
+ int width_ = 0;
270
+ int height_ = 0;
271
+ int stride_ = 0; // row stride in pixels (>= width_, may be padded)
272
+ int strideBytes_ = 0;
273
+ int regionY_ = 0; // scroll region origin row (logical)
274
+ int regionH_ = 0; // scroll region height; 0 = no region active
275
+ int scrollOffsetY_ = 0; // current rotation within region; see rowToPhysical()
276
+ pixel::native_t *pixels_ = nullptr;
277
+
278
+ int dirtyX0_ = 0;
279
+ int dirtyY0_ = 0;
280
+ int dirtyX1_ = -1;
281
+ int dirtyY1_ = -1;
282
+ CanvasDirtyRect dirtyRects_[kMaxDirtyRects] = {};
283
+ int dirtyRectCount_ = 0;
284
+ std::uint8_t globalAlpha_ = 255;
285
+
286
+ ClipRect clipStack_[kMaxClipDepth] = {};
287
+ int clipDepth_ = 0;
288
+ };
289
+
290
+ } // namespace gea::framework::graphics
@@ -0,0 +1,390 @@
1
+ #pragma once
2
+
3
+ #include "pixel.h"
4
+
5
+ #include <cstdint>
6
+ #include <cstddef>
7
+
8
+ namespace gea::framework::graphics {
9
+ struct CircleEntry; // canvas.h; only referenced by pointer here
10
+ struct TriangleEntry; // canvas.h; only referenced by pointer here
11
+ class Canvas;
12
+ } // namespace gea::framework::graphics
13
+
14
+ namespace gea::platform::display {
15
+
16
+ #ifndef GEA_EMBEDDED_DISPLAY_WIDTH
17
+ #define GEA_EMBEDDED_DISPLAY_WIDTH 410
18
+ #endif
19
+
20
+ #ifndef GEA_EMBEDDED_DISPLAY_HEIGHT
21
+ #define GEA_EMBEDDED_DISPLAY_HEIGHT 502
22
+ #endif
23
+
24
+ #ifndef GEA_EMBEDDED_DISPLAY_NATIVE_WIDTH
25
+ #define GEA_EMBEDDED_DISPLAY_NATIVE_WIDTH GEA_EMBEDDED_DISPLAY_WIDTH
26
+ #endif
27
+
28
+ #ifndef GEA_EMBEDDED_DISPLAY_NATIVE_HEIGHT
29
+ #define GEA_EMBEDDED_DISPLAY_NATIVE_HEIGHT GEA_EMBEDDED_DISPLAY_HEIGHT
30
+ #endif
31
+
32
+ inline constexpr int kWidth = GEA_EMBEDDED_DISPLAY_WIDTH;
33
+ inline constexpr int kHeight = GEA_EMBEDDED_DISPLAY_HEIGHT;
34
+ inline constexpr int kNativeWidth = GEA_EMBEDDED_DISPLAY_NATIVE_WIDTH;
35
+ inline constexpr int kNativeHeight = GEA_EMBEDDED_DISPLAY_NATIVE_HEIGHT;
36
+ inline constexpr int kConsoleFontWidth = 8;
37
+ inline constexpr int kConsoleFontHeight = 16;
38
+ inline constexpr int kConsoleFontScale = 3;
39
+ inline constexpr int kConsoleGlyphWidth = kConsoleFontWidth * kConsoleFontScale;
40
+ inline constexpr int kConsoleGlyphHeight = kConsoleFontHeight * kConsoleFontScale;
41
+ inline constexpr int kConsoleColumns = kWidth / kConsoleGlyphWidth;
42
+ inline constexpr int kConsoleRows = kHeight / kConsoleGlyphHeight;
43
+
44
+ struct DisplayFlushPerfStats {
45
+ int64_t totalUs = 0;
46
+ int64_t setWindowUs = 0;
47
+ int64_t slotWaitUs = 0;
48
+ int64_t copyUs = 0;
49
+ int64_t rasterUs = 0;
50
+ int64_t byteSwapUs = 0;
51
+ int64_t txUs = 0;
52
+ int64_t completeWaitUs = 0;
53
+ int64_t entryWaitUs = 0;
54
+ int64_t chunkWaitUs = 0;
55
+ int64_t tailWaitUs = 0;
56
+ int callCount = 0;
57
+ int chunkCount = 0;
58
+ int pixelCount = 0;
59
+ };
60
+
61
+ struct DisplayFlushStageDetail {
62
+ int x0 = 0;
63
+ int y0 = 0;
64
+ int x1 = -1;
65
+ int y1 = -1;
66
+ int row = 0;
67
+ int rows = 0;
68
+ int pixels = 0;
69
+ };
70
+
71
+ enum class DisplayPresentCommandType : uint8_t {
72
+ Clear,
73
+ FillRectRgb565,
74
+ StrokeRectRgb565,
75
+ FillTriangleRgb565,
76
+ FillCircleRgb565,
77
+ StrokeCircleRgb565,
78
+ FillCirclesRgb565,
79
+ DrawImage,
80
+ DrawImageScaled,
81
+ DrawImageRotated90CW,
82
+ DrawImageTiledX,
83
+ FillText,
84
+ FillTrianglesRgb565
85
+ };
86
+
87
+ struct DisplayPresentCommand {
88
+ DisplayPresentCommandType type = DisplayPresentCommandType::Clear;
89
+ union {
90
+ struct {
91
+ gea::framework::graphics::pixel::native_t color;
92
+ } clear;
93
+ struct {
94
+ int x;
95
+ int y;
96
+ int w;
97
+ int h;
98
+ gea::framework::graphics::pixel::native_t color;
99
+ uint8_t alpha;
100
+ } fillRectRgb565;
101
+ struct {
102
+ int x;
103
+ int y;
104
+ int w;
105
+ int h;
106
+ gea::framework::graphics::pixel::native_t color;
107
+ uint8_t alpha;
108
+ } strokeRectRgb565;
109
+ struct {
110
+ int x0;
111
+ int y0;
112
+ int x1;
113
+ int y1;
114
+ int x2;
115
+ int y2;
116
+ gea::framework::graphics::pixel::native_t color;
117
+ uint8_t alpha;
118
+ } fillTriangleRgb565;
119
+ struct {
120
+ int x;
121
+ int y;
122
+ int radius;
123
+ gea::framework::graphics::pixel::native_t color;
124
+ uint8_t alpha;
125
+ } fillCircleRgb565;
126
+ struct {
127
+ int x;
128
+ int y;
129
+ int radius;
130
+ gea::framework::graphics::pixel::native_t color;
131
+ uint8_t alpha;
132
+ } strokeCircleRgb565;
133
+ struct {
134
+ const uint16_t *xs;
135
+ const uint16_t *ys;
136
+ const gea::framework::graphics::pixel::native_t *colors;
137
+ int count;
138
+ int radius;
139
+ uint8_t alpha;
140
+ } fillCirclesRgb565;
141
+ struct {
142
+ // Entries pre-packed (and depth-ordered) by the canvas layer.
143
+ const gea::framework::graphics::TriangleEntry *entries;
144
+ int count;
145
+ uint8_t alpha;
146
+ } fillTrianglesRgb565;
147
+ struct {
148
+ const gea::framework::graphics::pixel::native_t *pixels;
149
+ const uint8_t *alphaPixels;
150
+ int srcWidth;
151
+ int srcHeight;
152
+ int x;
153
+ int y;
154
+ uint8_t alpha;
155
+ } drawImage;
156
+ struct {
157
+ const gea::framework::graphics::pixel::native_t *pixels;
158
+ const uint8_t *alphaPixels;
159
+ int srcWidth;
160
+ int srcHeight;
161
+ int x;
162
+ int y;
163
+ int w;
164
+ int h;
165
+ uint8_t alpha;
166
+ // Corner radius applied to the DESTINATION box, all four corners.
167
+ // min(w,h)/2 is a circle, which is how an opaque (alpha-free) sprite
168
+ // gets a round mask without carrying an alpha plane: the blit skips
169
+ // the outside-the-shape pixels instead of blending them.
170
+ int radius;
171
+ } drawImageScaled;
172
+ struct {
173
+ const gea::framework::graphics::pixel::native_t *pixels;
174
+ const uint8_t *alphaPixels;
175
+ int srcWidth;
176
+ int srcHeight;
177
+ int x;
178
+ int y;
179
+ int w;
180
+ int h;
181
+ uint8_t alpha;
182
+ } drawImageRotated90CW;
183
+ struct {
184
+ const gea::framework::graphics::pixel::native_t *pixels;
185
+ const uint8_t *alphaPixels;
186
+ int srcWidth;
187
+ int srcHeight;
188
+ int x;
189
+ int y;
190
+ int w;
191
+ uint8_t alpha;
192
+ } drawImageTiledX;
193
+ struct {
194
+ const char *text;
195
+ int x;
196
+ int y;
197
+ gea::framework::graphics::pixel::native_t color;
198
+ float scale;
199
+ int fontFamilyId;
200
+ int fontSizePx;
201
+ uint8_t alpha;
202
+ } fillText;
203
+ };
204
+ };
205
+
206
+ // Inclusive rect, half-open is fine if x1 < x0 (treated as empty by the
207
+ // driver). Used for multi-rect framebuffer flush.
208
+ struct DisplayFlushRect {
209
+ int x0;
210
+ int y0;
211
+ int x1;
212
+ int y1;
213
+ };
214
+
215
+ // The raster callback fills a NATIVE pixel row for a streamed region. This is
216
+ // native_t (identical to uint16_t on RGB565 targets — a no-op there); on a
217
+ // grayscale GRAY4/GRAY2 target it is the 1-byte gray value, so no RGB565 is
218
+ // dragged through the streaming path.
219
+ using DisplayStreamRasterFn = void (*)(gea::framework::graphics::pixel::native_t *pixels,
220
+ int width,
221
+ int height,
222
+ int origin_x,
223
+ int origin_y,
224
+ void *user);
225
+
226
+ class Display {
227
+ public:
228
+ static bool init();
229
+ static bool start();
230
+ static gea::framework::graphics::Canvas *canvas();
231
+ // True when canvas() is the panel's scanout framebuffer (no logical->panel
232
+ // rotation): writes to it appear on the next refresh with no flush/rotate.
233
+ // False when an orientation forces a separate logical framebuffer that must
234
+ // be PPA-rotated to the panel each flush. Defaults to false on targets that
235
+ // don't override it.
236
+ static bool framebufferIsPanelDirect();
237
+ // If the panel has a portrait scanout buffer a producer (e.g. the camera) can
238
+ // PPA/DMA straight into for a self-owned rect — no off-screen compose + re-blit —
239
+ // return it plus its width/height and true. False when the UI composes off-screen
240
+ // and a flush/rotate is required. Default false; only the DSI panel overrides it.
241
+ static bool panelScanoutSurface(uint16_t **out_buffer, int *out_width, int *out_height);
242
+ // One-pass camera→panel target. Given a logical rect (the viewfinder), returns the
243
+ // live panel scanout buffer + its pic dims, the panel rect that logical rect maps to
244
+ // under the current orientation, and the orientation's rotation in 90° steps (0..3).
245
+ // A producer (the camera) composes its own source rotation with out_rot_steps and
246
+ // scales+rotates straight into the panel in ONE PPA pass — no off-screen rotate-to-
247
+ // panel second pass. Available in any orientation. Default false on targets without it.
248
+ // out_flip is set to 1 when the producer covers the whole panel and a second
249
+ // framebuffer exists: out_buffer is then the non-scanned (back) buffer, and the
250
+ // producer must call flipPanelToBack() after writing it to swap it in tear-free at
251
+ // vblank. out_flip = 0 means out_buffer is the live scanned buffer (write in place).
252
+ static bool panelDirectTarget(int logical_x, int logical_y, int logical_w, int logical_h,
253
+ uint16_t **out_buffer, int *out_buffer_w, int *out_buffer_h,
254
+ int *out_panel_x, int *out_panel_y, int *out_panel_w, int *out_panel_h,
255
+ int *out_rot_steps, int *out_flip);
256
+ // Swap the back framebuffer (just written by a full-panel producer) in at the next
257
+ // vblank. No-op on single-framebuffer targets. The pointer overload flips the
258
+ // exact buffer returned by panelDirectTarget(); use it when a long producer could
259
+ // let the writable-buffer choice change before the flip.
260
+ static void flipPanelToBack();
261
+ static void flipPanelToBack(uint16_t *buffer);
262
+ static bool copySnapshotRgb565(uint16_t *dst, int pixel_capacity, int *width, int *height, bool presented);
263
+ // Same snapshot, but in the board's NATIVE sub-byte-PACKED storage (GRAY4/
264
+ // GRAY2 targets only -- GEA_PIXEL_STORAGE_PACKED) instead of expanded RGB565.
265
+ // A packed panel's true framebuffer is a quarter (GRAY4) or eighth (GRAY2)
266
+ // the size of an RGB565 expansion of the same pixels; a caller that needs
267
+ // RGB565 (the wire format screenshot commands emit) allocates this small
268
+ // buffer and expands per-pixel itself while it encodes, instead of asking
269
+ // the device to hold a full RGB565-sized allocation it may not have. `dst`
270
+ // is a flat, unpadded packed pixel STREAM: pixel index i (row-major,
271
+ // i = y*width+x) sits at pixel::packed::byteIndex(i)/shiftFor(i), with no
272
+ // per-row alignment gap -- so byte_capacity need only be
273
+ // pixel::packed::rowBytes(width*height). Only implemented on
274
+ // GEA_PIXEL_STORAGE_PACKED boards; RGB565/8888 targets never call it.
275
+ static bool copySnapshotPacked(uint8_t *dst, int byte_capacity, int *width, int *height, bool presented);
276
+ static int countNonBlackPixels(bool presented);
277
+ static void clear();
278
+ static void clearNoFlush();
279
+ static void print(const char *text);
280
+ static void flush();
281
+ // Transmit `count` framebuffer rects to the panel inside one driver
282
+ // entry. Each rect is independently chunked / windowed (we can't merge
283
+ // without re-transmitting between rects), but we share the call boundary
284
+ // so callers that already track multiple dirty rects (tree_render's
285
+ // per-region replay loop) don't pay 16× the per-call overhead. After a
286
+ // successful return the canvas dirty rect is cleared; passing rects
287
+ // outside the framebuffer is silently clipped.
288
+ static void flushRects(const DisplayFlushRect *rects, int count, bool allowPerChunkDrain = true);
289
+ // Fused flush: instead of copying each window from the framebuffer, invoke `raster`
290
+ // to generate each DMA chunk in place (the render layer rebinds the draw canvas onto
291
+ // the chunk buffer and replays the dirty display-list into it). Skips the PSRAM
292
+ // framebuffer round-trip and overlaps rasterization with DMA. Call
293
+ // rebindCanvasToFramebuffer() afterward to restore the canvas binding.
294
+ static void flushRectsRasterized(const DisplayFlushRect *rects, int count, DisplayStreamRasterFn raster, void *user, bool allowPerChunkDrain = true);
295
+ static void rebindCanvasToFramebuffer();
296
+ static bool streamRect(int x, int y, int w, int h, DisplayStreamRasterFn raster, void *user);
297
+ static bool present(const DisplayPresentCommand *commands, int command_count);
298
+ static void setFlushConfig(int chunk_rows, int queue_depth);
299
+ // Present render scale: 1 = native, 2 = the app renders into a half-panel
300
+ // viewport and present() 2x-upscales it to the panel (quarters fill/overdraw).
301
+ // Per-present, so it never affects full-res apps sharing the present path.
302
+ // Only the ESP32 backend implements the upscale; other targets no-op.
303
+ static void setPresentScale(int scale);
304
+ // Reserve `bytes` of internal RAM by shrinking the elastic staging budget
305
+ // (release with reserveInternal(0)). Used by the connectivity layer so WiFi/
306
+ // BLE bring-up has headroom; the staging resize is applied on the frame task.
307
+ static void reserveInternal(std::size_t bytes);
308
+ // Apply a pending reserveInternal() staging resize. Call once per frame on the
309
+ // frame task: the resize otherwise only lands inside a content flush, so a
310
+ // static screen (no dirty region to flush) would never free the reserved RAM.
311
+ static void applyPendingInternalReserve();
312
+ static int flushChunkRows();
313
+ static int flushQueueDepth();
314
+ static int flushBufferBytes();
315
+ static void pushClip(int x, int y, int w, int h);
316
+ static void popClip();
317
+ static void resetClip();
318
+ static void setAlpha(uint8_t alpha);
319
+ static uint8_t alpha();
320
+ static void setAA(int samples);
321
+ static int aa();
322
+ static int brightness();
323
+ static void setBrightness(int brightness_percent);
324
+ // High-brightness mode: a separate, higher ceiling than the 0-100 brightness
325
+ // range (a sunlight boost on AMOLED controllers that have one). Deliberately
326
+ // a toggle, not brightness > 100, so a slider can never reach it. Returns
327
+ // false where the panel has no such mode.
328
+ static bool setHighBrightnessMode(bool enabled);
329
+ static bool highBrightnessMode();
330
+ // Opt-in tearing sync (TE/VBlank). Off by default. When enabled, the frame
331
+ // scheduler calls vsyncWaitForFrame() at the top of each frame so the app's
332
+ // draw+flush lands inside the panel's VBlank lead window. No-op on targets
333
+ // that don't wire a TE line. Toggled from TS via Display.setVSync(boolean).
334
+ static void setVSync(bool on);
335
+ static bool vsyncEnabled();
336
+ // Mark the next present as a full-screen damage-all: skip the dirty-rect diff
337
+ // and the persistent previous-frame copy for that frame. Toggled from TS via
338
+ // Display.invalidate() while panning. No-op on targets without a present diff.
339
+ static void invalidate();
340
+ static void vsyncWaitForFrame();
341
+ static void clip(int *x0, int *y0, int *x1, int *y1);
342
+ static void fillRect(int x, int y, int w, int h, gea::framework::graphics::pixel::native_t color);
343
+ static void scrollRect(int x, int y, int w, int h, int dx, int dy);
344
+ // Drop any active software-scroll-register state. Called by Tree::clear()
345
+ // when the tree is torn down (app switch / mount of a fresh tree) so the
346
+ // next app's drawing isn't translated through the previous app's region.
347
+ // No-op on platforms / native-test stubs that don't implement the
348
+ // software scroll register.
349
+ static void resetScrollRegion();
350
+ static void strokeRect(int x, int y, int w, int h, gea::framework::graphics::pixel::native_t color);
351
+ static void fillCircle(int cx, int cy, int r, gea::framework::graphics::pixel::native_t color);
352
+ static void strokeCircle(int cx, int cy, int r, gea::framework::graphics::pixel::native_t color);
353
+ static void drawLine(int x0, int y0, int x1, int y1, gea::framework::graphics::pixel::native_t color);
354
+ static void drawArc(int cx, int cy, int r, int start_deg, int end_deg, gea::framework::graphics::pixel::native_t color);
355
+ static void fillTriangle(int x0, int y0, int x1, int y1, int x2, int y2, gea::framework::graphics::pixel::native_t color);
356
+ static void drawText(const char *text, int x, int y, gea::framework::graphics::pixel::native_t color, float scale);
357
+ static void drawTextFont(const char *text, int x, int y, gea::framework::graphics::pixel::native_t color, int font_id);
358
+ static void drawTextFontFamily(const char *text, int x, int y, gea::framework::graphics::pixel::native_t color, int family_id, int size_px);
359
+ static void setPixel(int x, int y, gea::framework::graphics::pixel::native_t color);
360
+ static void fillRoundedRect(int x, int y, int w, int h, int tl, int tr, int br, int bl, gea::framework::graphics::pixel::native_t color);
361
+ static void fillRoundedRectBoxesRgb565(const int16_t *xs, const int16_t *ys, int count,
362
+ int w, int h, int tl, int tr, int br, int bl,
363
+ const gea::framework::graphics::pixel::native_t *colors);
364
+ static void strokeRoundedRect(int x, int y, int w, int h, int tl, int tr, int br, int bl, int lw, gea::framework::graphics::pixel::native_t color);
365
+ static void blitImage(const gea::framework::graphics::pixel::native_t *src, const uint8_t *alpha, int src_w, int src_h, int dx, int dy);
366
+ static void blitImageScaled(const gea::framework::graphics::pixel::native_t *src, const uint8_t *alpha, int src_w, int src_h, int dx, int dy, int dst_w, int dst_h);
367
+ static void setWorldOverlay(const uint16_t *world_pixels, int world_width, int world_height, int panel_top, int panel_height);
368
+ static void setWorldScroll(int scroll_x);
369
+ static void flushStatsRead(int64_t *total_us, int *call_count, int *pixel_count);
370
+ static DisplayFlushPerfStats flushPerfStatsRead();
371
+ // Cumulative flush odometer, never reset per frame -- lets a caller watch for
372
+ // the panel going quiet after an interaction (flushPerfStatsRead is zeroed
373
+ // every frame, so it cannot answer that).
374
+ static void flushOdometerRead(uint32_t &calls, uint64_t &pixels);
375
+ // Present-path diagnostics for GEADEV (counters + last tile-shape
376
+ // ineligibility); boards without a batched present leave outputs zero.
377
+ static void presentPathDebug(int *calls, int *direct, int *general, int *rejected,
378
+ int *tileShapeFailKind, int *tileShapeFailType, int *tileShapeFailCount);
379
+ static void landFrameDebug(int *total, int *align, int *raster, int *text, int *flip);
380
+ static void landPanDebug(int *detect, int *kick, int *strips, int *wait, int *interior);
381
+ static void flushStatsReset();
382
+ // Diagnostic: name of the current display flush sub-stage, plus the chunk
383
+ // or region index in flight. Returned name is a static string. Safe to
384
+ // call from any task (atomic load).
385
+ static const char *flushStageName();
386
+ static int flushStageChunk();
387
+ static DisplayFlushStageDetail flushStageDetail();
388
+ };
389
+
390
+ } // namespace gea::platform::display