@geastack/core 0.1.38 → 0.1.39-uart.1
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/include/display_present.h +22 -5
- package/include/image.h +8 -0
- package/include/platform/uart.h +58 -0
- package/index.d.ts +37 -0
- package/package.json +3 -3
- package/runtime/host.ts +35 -0
- package/runtime-surface.ts +2 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include "canvas.h"
|
|
4
4
|
#include "display.h"
|
|
5
|
+
#include "image.h"
|
|
5
6
|
|
|
6
7
|
#include <algorithm>
|
|
7
8
|
#include <array>
|
|
@@ -284,6 +285,13 @@ inline void addCommandBounds(const Command &command, Rect *rects, int *count, in
|
|
|
284
285
|
addRect(rects, count, capacity, commandBounds(command, width, height), width, height);
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// An app-registered buffer (native surface, ImageData565, video frame) is
|
|
289
|
+
// rewritten in place, so equal pointers say nothing about equal pixels.
|
|
290
|
+
inline bool imagePixelsStable(const Command &command)
|
|
291
|
+
{
|
|
292
|
+
return !gea::framework::graphics::ImageStore::instance().pixelsMayChange(command.pixels);
|
|
293
|
+
}
|
|
294
|
+
|
|
287
295
|
inline bool commandsEqual(const Command &a, const Command &b)
|
|
288
296
|
{
|
|
289
297
|
if (a.type != b.type || a.alpha != b.alpha) return false;
|
|
@@ -321,15 +329,15 @@ inline bool commandsEqual(const Command &a, const Command &b)
|
|
|
321
329
|
sizeof(gea::framework::graphics::TriangleEntry)) == 0;
|
|
322
330
|
}
|
|
323
331
|
case Type::DrawImage:
|
|
324
|
-
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
|
|
332
|
+
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
|
|
325
333
|
a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight && a.x == b.x && a.y == b.y;
|
|
326
334
|
case Type::DrawImageScaled:
|
|
327
335
|
case Type::DrawImageRotated90CW:
|
|
328
|
-
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
|
|
336
|
+
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
|
|
329
337
|
a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight &&
|
|
330
338
|
a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h;
|
|
331
339
|
case Type::DrawImageTiledX:
|
|
332
|
-
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
|
|
340
|
+
return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
|
|
333
341
|
a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight &&
|
|
334
342
|
a.x == b.x && a.y == b.y && a.w == b.w;
|
|
335
343
|
case Type::FillText:
|
|
@@ -574,8 +582,17 @@ inline bool frameHasOpaqueBase(const Frame &frame, int width, int height)
|
|
|
574
582
|
const Command &first = frame.commands.front();
|
|
575
583
|
using Type = gea::platform::display::DisplayPresentCommandType;
|
|
576
584
|
if (first.type == Type::Clear) return true;
|
|
577
|
-
if (first.
|
|
578
|
-
|
|
585
|
+
if (first.alpha != 255) return false;
|
|
586
|
+
Rect bounds;
|
|
587
|
+
if (first.type == Type::FillRectRgb565) {
|
|
588
|
+
bounds = rectFromBox(first.x, first.y, first.w, first.h);
|
|
589
|
+
} else if (first.type == Type::DrawImage && first.alphaPixels == nullptr) {
|
|
590
|
+
// A full-panel opaque image (putImageData of a whole-screen buffer)
|
|
591
|
+
// paints every pixel, the same as a full-panel fill.
|
|
592
|
+
bounds = rectFromBox(first.x, first.y, first.srcWidth, first.srcHeight);
|
|
593
|
+
} else {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
579
596
|
return bounds.x0 <= 0 && bounds.y0 <= 0 && bounds.x1 >= width - 1 && bounds.y1 >= height - 1;
|
|
580
597
|
}
|
|
581
598
|
|
package/include/image.h
CHANGED
|
@@ -61,6 +61,9 @@ struct ImageSlot {
|
|
|
61
61
|
pixel::native_t *pixels = nullptr;
|
|
62
62
|
std::uint8_t *alpha = nullptr;
|
|
63
63
|
bool ownsPixels = false;
|
|
64
|
+
// Registered by the app (registerBuffer): the app writes these pixels in
|
|
65
|
+
// place between frames, so the same pointer does not mean the same image.
|
|
66
|
+
bool mutablePixels = false;
|
|
64
67
|
|
|
65
68
|
void *gifDecoder = nullptr;
|
|
66
69
|
std::uint8_t *gifData = nullptr;
|
|
@@ -98,6 +101,11 @@ public:
|
|
|
98
101
|
// camera capture, which hands over a freshly-allocated RGBA8888 still.
|
|
99
102
|
int registerBuffer(pixel::native_t *pixels, int width, int height, int preferredSlot = -1, bool takeOwnership = false);
|
|
100
103
|
|
|
104
|
+
// True when `pixels` point into a registerBuffer() image, whose content the
|
|
105
|
+
// app rewrites in place. The present diff never treats such an image as
|
|
106
|
+
// unchanged from the previous frame.
|
|
107
|
+
bool pixelsMayChange(const pixel::native_t *pixels) const;
|
|
108
|
+
|
|
101
109
|
int width(int id) const;
|
|
102
110
|
int height(int id) const;
|
|
103
111
|
int frameCount(int id) const;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
// Target-owned implementation. Unsupported platforms report unavailable.
|
|
7
|
+
extern "C" {
|
|
8
|
+
bool gea_uart_open(int port, int tx, int rx, int baud, int rxBytes, int txBytes)
|
|
9
|
+
__attribute__((weak));
|
|
10
|
+
bool gea_uart_is_open(int port) __attribute__((weak));
|
|
11
|
+
void gea_uart_close(int port) __attribute__((weak));
|
|
12
|
+
int gea_uart_read(int port, void *bytes, std::size_t capacity)
|
|
13
|
+
__attribute__((weak));
|
|
14
|
+
int gea_uart_write(int port, const void *bytes, std::size_t length)
|
|
15
|
+
__attribute__((weak));
|
|
16
|
+
int gea_uart_available(int port) __attribute__((weak));
|
|
17
|
+
int gea_uart_writable(int port) __attribute__((weak));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
namespace gea::platform::uart {
|
|
21
|
+
struct Config {
|
|
22
|
+
int tx;
|
|
23
|
+
int rx;
|
|
24
|
+
int baudRate;
|
|
25
|
+
int rxBufferSize = 4096;
|
|
26
|
+
int txBufferSize = 4096;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class Uart {
|
|
30
|
+
public:
|
|
31
|
+
static bool open(int port, const Config &config) {
|
|
32
|
+
return gea_uart_open &&
|
|
33
|
+
gea_uart_open(port, config.tx, config.rx, config.baudRate,
|
|
34
|
+
config.rxBufferSize, config.txBufferSize);
|
|
35
|
+
}
|
|
36
|
+
static bool isOpen(int port) {
|
|
37
|
+
return gea_uart_is_open && gea_uart_is_open(port);
|
|
38
|
+
}
|
|
39
|
+
static void close(int port) {
|
|
40
|
+
if (gea_uart_close)
|
|
41
|
+
gea_uart_close(port);
|
|
42
|
+
}
|
|
43
|
+
// Nonblocking. -1 means unavailable/closed/error; 0 means no progress.
|
|
44
|
+
static int read(int port, void *bytes, std::size_t capacity) {
|
|
45
|
+
return gea_uart_read ? gea_uart_read(port, bytes, capacity) : -1;
|
|
46
|
+
}
|
|
47
|
+
// Enqueues the complete buffer, or returns 0 if it cannot fit yet.
|
|
48
|
+
static int write(int port, const void *bytes, std::size_t length) {
|
|
49
|
+
return gea_uart_write ? gea_uart_write(port, bytes, length) : -1;
|
|
50
|
+
}
|
|
51
|
+
static int available(int port) {
|
|
52
|
+
return gea_uart_available ? gea_uart_available(port) : -1;
|
|
53
|
+
}
|
|
54
|
+
static int writable(int port) {
|
|
55
|
+
return gea_uart_writable ? gea_uart_writable(port) : -1;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
} // namespace gea::platform::uart
|
package/index.d.ts
CHANGED
|
@@ -1023,6 +1023,16 @@ export interface CanvasRenderingContext2D {
|
|
|
1023
1023
|
createImageData565(width: number, height: number): ImageData565
|
|
1024
1024
|
/** Draw an `ImageData565` with its top-left corner at (dx, dy): one blit. */
|
|
1025
1025
|
putImageData(image: ImageData565, dx: number, dy: number): void
|
|
1026
|
+
/** Draw only the dirty rectangle of an `ImageData565`, at (dx + dirtyX, dy + dirtyY). */
|
|
1027
|
+
putImageData(
|
|
1028
|
+
image: ImageData565,
|
|
1029
|
+
dx: number,
|
|
1030
|
+
dy: number,
|
|
1031
|
+
dirtyX: number,
|
|
1032
|
+
dirtyY: number,
|
|
1033
|
+
dirtyWidth: number,
|
|
1034
|
+
dirtyHeight: number,
|
|
1035
|
+
): void
|
|
1026
1036
|
flush(): void
|
|
1027
1037
|
beginBatch(): void
|
|
1028
1038
|
endBatch(): void
|
|
@@ -1223,6 +1233,33 @@ export interface GpioController {
|
|
|
1223
1233
|
|
|
1224
1234
|
export declare const Gpio: GpioController
|
|
1225
1235
|
|
|
1236
|
+
export interface UartOptions {
|
|
1237
|
+
tx: number
|
|
1238
|
+
rx: number
|
|
1239
|
+
baudRate: number
|
|
1240
|
+
/** Receive/transmit queue bytes. Defaults to 4096 each. */
|
|
1241
|
+
rxBufferSize?: number
|
|
1242
|
+
txBufferSize?: number
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
/** Hardware UART, 8 data bits, no parity, one stop bit (8N1). */
|
|
1246
|
+
export interface UartController {
|
|
1247
|
+
/** Claims a free controller and pins. False on conflict/invalid configuration. */
|
|
1248
|
+
open(port: number, options: UartOptions): boolean
|
|
1249
|
+
isOpen(port: number): boolean
|
|
1250
|
+
close(port: number): void
|
|
1251
|
+
/** Nonblocking read into the supplied view; returns bytes read, or -1 on error. */
|
|
1252
|
+
read(port: number, buffer: Uint8Array): number
|
|
1253
|
+
/** Nonblocking whole-buffer enqueue; 0 means backpressure, -1 means error. */
|
|
1254
|
+
write(port: number, buffer: Uint8Array): number
|
|
1255
|
+
/** Queued readable bytes, or -1 when unavailable. */
|
|
1256
|
+
available(port: number): number
|
|
1257
|
+
/** Writable queue capacity, or -1 when unavailable. */
|
|
1258
|
+
writable(port: number): number
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
export declare const Uart: UartController
|
|
1262
|
+
|
|
1226
1263
|
export interface LedController {
|
|
1227
1264
|
// A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
|
|
1228
1265
|
// modern ESP32 modules.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geastack/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39-uart.1",
|
|
4
4
|
"description": "GeaStack app API, reactivity headers, runtime sources, and type declarations.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@babel/types": "^7.23.0",
|
|
66
66
|
"@geajs/core": "1.5.1",
|
|
67
67
|
"@geajs/vite-plugin": "1.5.0",
|
|
68
|
-
"@geastack/compiler": "^1.0.
|
|
69
|
-
"@geastack/geatsc-plugin-gea": "
|
|
68
|
+
"@geastack/compiler": "^1.0.25",
|
|
69
|
+
"@geastack/geatsc-plugin-gea": "0.1.21-uart.1",
|
|
70
70
|
"opentype.js": "^2.0.0",
|
|
71
71
|
"vite": "^8.0.0"
|
|
72
72
|
}
|
package/runtime/host.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// fails the representation coverage guard for the whole `Geolocation` literal
|
|
7
7
|
// below. lib.dom's GeolocationPositionError also carries PERMISSION_DENIED /
|
|
8
8
|
// POSITION_UNAVAILABLE / TIMEOUT, which our host object never sets.
|
|
9
|
+
import type { UartOptions } from '../index'
|
|
9
10
|
import type {
|
|
10
11
|
CanvasRenderingContext2D,
|
|
11
12
|
GeolocationCoordinates,
|
|
@@ -361,6 +362,15 @@ declare const __gea_Gpio: {
|
|
|
361
362
|
write(pin: number, level: boolean): boolean
|
|
362
363
|
read(pin: number): boolean
|
|
363
364
|
}
|
|
365
|
+
declare const __gea_Uart: {
|
|
366
|
+
open(port: number, tx: number, rx: number, baud: number, rxBytes: number, txBytes: number): boolean
|
|
367
|
+
isOpen(port: number): boolean
|
|
368
|
+
close(port: number): void
|
|
369
|
+
read(port: number, buffer: Uint8Array): number
|
|
370
|
+
write(port: number, buffer: Uint8Array): number
|
|
371
|
+
available(port: number): number
|
|
372
|
+
writable(port: number): number
|
|
373
|
+
}
|
|
364
374
|
declare const __gea_Led: {
|
|
365
375
|
// A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
|
|
366
376
|
// modern ESP32 modules. set() is the one-call default (attach if needed,
|
|
@@ -995,6 +1005,31 @@ export const Gpio = {
|
|
|
995
1005
|
return __gea_Gpio.read(pin)
|
|
996
1006
|
}
|
|
997
1007
|
}
|
|
1008
|
+
export const Uart = {
|
|
1009
|
+
open(port: number, options: UartOptions): boolean {
|
|
1010
|
+
if (typeof __gea_Uart === 'undefined') return false
|
|
1011
|
+
return __gea_Uart.open(port, options.tx, options.rx, options.baudRate,
|
|
1012
|
+
options.rxBufferSize ?? 4096, options.txBufferSize ?? 4096)
|
|
1013
|
+
},
|
|
1014
|
+
isOpen(port: number): boolean {
|
|
1015
|
+
return typeof __gea_Uart !== 'undefined' && __gea_Uart.isOpen(port)
|
|
1016
|
+
},
|
|
1017
|
+
close(port: number): void {
|
|
1018
|
+
if (typeof __gea_Uart !== 'undefined') __gea_Uart.close(port)
|
|
1019
|
+
},
|
|
1020
|
+
read(port: number, buffer: Uint8Array): number {
|
|
1021
|
+
return typeof __gea_Uart !== 'undefined' ? __gea_Uart.read(port, buffer) : -1
|
|
1022
|
+
},
|
|
1023
|
+
write(port: number, buffer: Uint8Array): number {
|
|
1024
|
+
return typeof __gea_Uart !== 'undefined' ? __gea_Uart.write(port, buffer) : -1
|
|
1025
|
+
},
|
|
1026
|
+
available(port: number): number {
|
|
1027
|
+
return typeof __gea_Uart !== 'undefined' ? __gea_Uart.available(port) : -1
|
|
1028
|
+
},
|
|
1029
|
+
writable(port: number): number {
|
|
1030
|
+
return typeof __gea_Uart !== 'undefined' ? __gea_Uart.writable(port) : -1
|
|
1031
|
+
},
|
|
1032
|
+
}
|
|
998
1033
|
export const Led = {
|
|
999
1034
|
set(pin: number, r: number, g: number, b: number): boolean {
|
|
1000
1035
|
return __gea_Led.set(pin, r, g, b)
|