@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.
- package/LICENSE +202 -0
- package/README.md +36 -0
- package/app_frame_perf.cpp +77 -0
- package/css.d.ts +1 -0
- package/events.cpp +67 -0
- package/gea_app_entry.cpp +317 -0
- package/gea_sources.mjs +126 -0
- package/gea_sources.sh +36 -0
- package/include/app.h +60 -0
- package/include/apps.h +46 -0
- package/include/audio.h +90 -0
- package/include/ble.h +212 -0
- package/include/bluetooth/hid_profile.h +92 -0
- package/include/camera.h +107 -0
- package/include/canvas.h +290 -0
- package/include/display.h +390 -0
- package/include/display_present.h +869 -0
- package/include/event.h +32 -0
- package/include/events.h +116 -0
- package/include/gea/embedded.h +13 -0
- package/include/gea/reactive_runtime.h +32 -0
- package/include/gea.h +14 -0
- package/include/gea_perf_config.h +96 -0
- package/include/geaos/abi.h +165 -0
- package/include/geolocation.h +126 -0
- package/include/gpio.h +61 -0
- package/include/graphics/font.h +122 -0
- package/include/i2c.h +20 -0
- package/include/image.h +156 -0
- package/include/imu.h +23 -0
- package/include/input.h +33 -0
- package/include/memory.h +52 -0
- package/include/pixel.h +907 -0
- package/include/platform/file_cache.h +18 -0
- package/include/power.h +11 -0
- package/include/runtime.h +83 -0
- package/include/services/app_runner.h +10 -0
- package/include/services/app_state.h +14 -0
- package/include/services/battery_service.h +12 -0
- package/include/services/bluetooth_service.h +13 -0
- package/include/services/diagnostics.h +76 -0
- package/include/services/diagnostics_platform.h +54 -0
- package/include/services/frame_scheduler.h +71 -0
- package/include/services/mirror.h +49 -0
- package/include/services/network_services.h +25 -0
- package/include/services/ota.h +32 -0
- package/include/services/runtime_log.h +11 -0
- package/include/services/storage_service.h +26 -0
- package/include/touch.h +38 -0
- package/include/ui/signal.h +149 -0
- package/include/wifi.h +67 -0
- package/index.d.ts +1940 -0
- package/jsx.d.ts +1 -0
- package/package.json +70 -0
- package/runtime/ble.ts +49 -0
- package/runtime/color.ts +21 -0
- package/runtime/compiler.ts +43 -0
- package/runtime/host.ts +1083 -0
- package/runtime/images.ts +129 -0
- package/runtime/primitives.ts +49 -0
- package/runtime-surface.ts +180 -0
- package/runtime.cpp +431 -0
- package/runtime.ts +34 -0
- package/scripts/build-gea-vite-geatsc.mjs +4514 -0
- package/scripts/dotenv-defines.mjs +98 -0
- package/scripts/gea-bundle-type-hints.mjs +546 -0
- package/scripts/gea-embedded-compat-staging.mjs +17 -0
- package/scripts/gea-embedded-compat-transform.mjs +427 -0
- package/scripts/gea-vite-module-graph-plugin.mjs +578 -0
- package/scripts/generate-gea-embedded-assets.mjs +296 -0
- package/scripts/generate-gea-embedded-fonts.mjs +1252 -0
- package/scripts/generate-installed-app-plan.mjs +55 -0
- package/scripts/native-object-cache.sh +244 -0
package/runtime/host.ts
ADDED
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
// These names ALSO exist as lib.dom globals, so leaving them unimported does
|
|
2
|
+
// not fail to compile — it silently binds the DOM interfaces instead of ours.
|
|
3
|
+
// That is not cosmetic: lib.dom's GeolocationCoordinates/GeolocationPosition
|
|
4
|
+
// carry `toJSON(): any`, a callable member, so geatsc's record identity
|
|
5
|
+
// declines the shape and every method returning one seals `unresolved`, which
|
|
6
|
+
// fails the representation coverage guard for the whole `Geolocation` literal
|
|
7
|
+
// below. lib.dom's GeolocationPositionError also carries PERMISSION_DENIED /
|
|
8
|
+
// POSITION_UNAVAILABLE / TIMEOUT, which our host object never sets.
|
|
9
|
+
import type {
|
|
10
|
+
CanvasRenderingContext2D,
|
|
11
|
+
GeolocationCoordinates,
|
|
12
|
+
GeolocationPosition,
|
|
13
|
+
GeolocationPositionError,
|
|
14
|
+
PositionOptions,
|
|
15
|
+
TouchSample,
|
|
16
|
+
CameraCaptureOptions,
|
|
17
|
+
CameraClip,
|
|
18
|
+
CameraDevice,
|
|
19
|
+
CameraExposureMode,
|
|
20
|
+
CameraExposureOptions,
|
|
21
|
+
CameraFacing,
|
|
22
|
+
CameraFlashMode,
|
|
23
|
+
CameraFocusMode,
|
|
24
|
+
CameraFocusOptions,
|
|
25
|
+
CameraOpenOptions,
|
|
26
|
+
CameraPhoto,
|
|
27
|
+
CameraRecordOptions,
|
|
28
|
+
CameraWhiteBalanceMode,
|
|
29
|
+
CameraWhiteBalanceOptions,
|
|
30
|
+
EmbeddedMemoryConfig,
|
|
31
|
+
EmbeddedMemoryStats,
|
|
32
|
+
HttpReply,
|
|
33
|
+
AudioContext
|
|
34
|
+
} from '../index'
|
|
35
|
+
|
|
36
|
+
// These 17 used to be declared HERE as well as in `../index`, byte for byte.
|
|
37
|
+
// A duplicate is not free: the record-alias candidate registry is keyed by
|
|
38
|
+
// NAME across every file in the program and the last writer wins, so one
|
|
39
|
+
// declaration silently shadowed the other and the struct emitted for a literal
|
|
40
|
+
// stopped matching the struct emitted for its annotation. Re-exported so
|
|
41
|
+
// existing `from './runtime/host'` importers keep working.
|
|
42
|
+
export type {
|
|
43
|
+
CameraCaptureOptions,
|
|
44
|
+
CameraClip,
|
|
45
|
+
CameraDevice,
|
|
46
|
+
CameraExposureMode,
|
|
47
|
+
CameraExposureOptions,
|
|
48
|
+
CameraFacing,
|
|
49
|
+
CameraFlashMode,
|
|
50
|
+
CameraFocusMode,
|
|
51
|
+
CameraFocusOptions,
|
|
52
|
+
CameraOpenOptions,
|
|
53
|
+
CameraPhoto,
|
|
54
|
+
CameraRecordOptions,
|
|
55
|
+
CameraWhiteBalanceMode,
|
|
56
|
+
CameraWhiteBalanceOptions,
|
|
57
|
+
EmbeddedMemoryConfig,
|
|
58
|
+
EmbeddedMemoryStats,
|
|
59
|
+
HttpReply
|
|
60
|
+
} from '../index'
|
|
61
|
+
|
|
62
|
+
declare const apps: {
|
|
63
|
+
launch(appId: string): number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare const image: {
|
|
67
|
+
loadBytes(bytes: Uint8Array): number
|
|
68
|
+
loadBytesOpaque(bytes: Uint8Array): number
|
|
69
|
+
width(id: number): number
|
|
70
|
+
height(id: number): number
|
|
71
|
+
frameCount(id: number): number
|
|
72
|
+
isAnimated(id: number): boolean
|
|
73
|
+
setPlaying(id: number, playing: number): void
|
|
74
|
+
seek(id: number, frame: number): void
|
|
75
|
+
dispose(id: number): void
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare const navigator: {
|
|
79
|
+
geolocation: {
|
|
80
|
+
hasFix(): boolean
|
|
81
|
+
currentPosition(): GeolocationPosition
|
|
82
|
+
coords(): GeolocationCoordinates
|
|
83
|
+
latitude(): number
|
|
84
|
+
longitude(): number
|
|
85
|
+
accuracy(): number
|
|
86
|
+
getCurrentPosition(
|
|
87
|
+
success: (position: GeolocationPosition) => void,
|
|
88
|
+
error?: (error: GeolocationPositionError) => void,
|
|
89
|
+
options?: PositionOptions
|
|
90
|
+
): void
|
|
91
|
+
watchPosition(
|
|
92
|
+
success: (position: GeolocationPosition) => void,
|
|
93
|
+
error?: (error: GeolocationPositionError) => void,
|
|
94
|
+
options?: PositionOptions
|
|
95
|
+
): number
|
|
96
|
+
clearWatch(id: number): void
|
|
97
|
+
}
|
|
98
|
+
wifi: {
|
|
99
|
+
enabled(): boolean
|
|
100
|
+
setEnabled(enabled: boolean): void
|
|
101
|
+
connected(): boolean
|
|
102
|
+
rssi(): number
|
|
103
|
+
ssid(): string
|
|
104
|
+
ip(): string
|
|
105
|
+
mac(): string
|
|
106
|
+
configure(ssid: string, password: string): void
|
|
107
|
+
waitForConnection(timeoutMs: number): boolean
|
|
108
|
+
startScan(): void
|
|
109
|
+
scanning(): boolean
|
|
110
|
+
scanCount(): number
|
|
111
|
+
scanSsidAt(index: number): string
|
|
112
|
+
scanRssiAt(index: number): number
|
|
113
|
+
scanSecuredAt(index: number): boolean
|
|
114
|
+
}
|
|
115
|
+
bluetooth: {
|
|
116
|
+
keyboard: {
|
|
117
|
+
tap(hidCode: number): void
|
|
118
|
+
down(modifier: number, hidCode: number): void
|
|
119
|
+
up(): void
|
|
120
|
+
}
|
|
121
|
+
mouse: {
|
|
122
|
+
move(dx: number, dy: number, buttons?: number, wheel?: number): void
|
|
123
|
+
click(button: number): void
|
|
124
|
+
}
|
|
125
|
+
midi: {
|
|
126
|
+
enable(): void
|
|
127
|
+
bound(): boolean
|
|
128
|
+
send(bytes: number[]): void
|
|
129
|
+
startScan(): void
|
|
130
|
+
stopScan(): void
|
|
131
|
+
scanning(): boolean
|
|
132
|
+
scanCount(): number
|
|
133
|
+
scanNameAt(index: number): string
|
|
134
|
+
connect(index: number): void
|
|
135
|
+
disconnect(): void
|
|
136
|
+
}
|
|
137
|
+
hidHost: {
|
|
138
|
+
startScan(): void
|
|
139
|
+
stopScan(): void
|
|
140
|
+
scanning(): boolean
|
|
141
|
+
scanCount(): number
|
|
142
|
+
scanNameAt(index: number): string
|
|
143
|
+
connect(index: number): void
|
|
144
|
+
disconnect(): void
|
|
145
|
+
bound(): boolean
|
|
146
|
+
reportCount(): number
|
|
147
|
+
reportIdAt(index: number): number
|
|
148
|
+
reportLenAt(index: number): number
|
|
149
|
+
reportByteAt(index: number, byteIndex: number): number
|
|
150
|
+
clearReports(): void
|
|
151
|
+
}
|
|
152
|
+
connections: {
|
|
153
|
+
count(): number
|
|
154
|
+
kindAt(index: number): number
|
|
155
|
+
nameAt(index: number): string
|
|
156
|
+
}
|
|
157
|
+
config: {
|
|
158
|
+
setDocument(bytes: number[]): void
|
|
159
|
+
pendingLength(): number
|
|
160
|
+
pendingByteAt(index: number): number
|
|
161
|
+
consumePending(): void
|
|
162
|
+
pairing(): boolean
|
|
163
|
+
pairCode(): string
|
|
164
|
+
dismissPairing(): void
|
|
165
|
+
pushActivity(index: number): void
|
|
166
|
+
}
|
|
167
|
+
enabled(): boolean
|
|
168
|
+
setEnabled(enabled: boolean): void
|
|
169
|
+
connected(): boolean
|
|
170
|
+
bound(): boolean
|
|
171
|
+
batteryLevel(): number
|
|
172
|
+
mac(): string
|
|
173
|
+
deviceName(): string
|
|
174
|
+
init(deviceName: string, appearance?: number, macAddress?: string): void
|
|
175
|
+
startAdvertising(): void
|
|
176
|
+
stopAdvertising(): void
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
declare const __gea_Accelerometer: {
|
|
181
|
+
readonly x: number
|
|
182
|
+
readonly y: number
|
|
183
|
+
readonly z: number
|
|
184
|
+
readonly accelerationX: number
|
|
185
|
+
readonly accelerationY: number
|
|
186
|
+
readonly accelerationZ: number
|
|
187
|
+
readonly gyroscopeX: number
|
|
188
|
+
readonly gyroscopeY: number
|
|
189
|
+
readonly gyroscopeZ: number
|
|
190
|
+
readonly tiltX: number
|
|
191
|
+
readonly tiltY: number
|
|
192
|
+
start(): void
|
|
193
|
+
close(): void
|
|
194
|
+
calibrateBias(): void
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
declare const __gea_Camera: {
|
|
198
|
+
readonly width: number
|
|
199
|
+
readonly height: number
|
|
200
|
+
readonly orientation: number
|
|
201
|
+
readonly facing: string
|
|
202
|
+
readonly deviceCount: number
|
|
203
|
+
isAvailable(): boolean
|
|
204
|
+
hasPermission(): boolean
|
|
205
|
+
requestPermission(): boolean
|
|
206
|
+
open(facing: string, width: number, height: number): boolean
|
|
207
|
+
close(): void
|
|
208
|
+
isOpen(): boolean
|
|
209
|
+
draw(x: number, y: number, destWidth: number, destHeight: number): void
|
|
210
|
+
capture(): number
|
|
211
|
+
captureMirrored(): number
|
|
212
|
+
startRecording(path: string, fps: number): boolean
|
|
213
|
+
stopRecording(): number
|
|
214
|
+
isRecording(): boolean
|
|
215
|
+
setFlash(mode: string): void
|
|
216
|
+
setZoom(factor: number): void
|
|
217
|
+
setMirror(mirror: boolean): void
|
|
218
|
+
setExposure(mode: string, bias: number, iso: number, durationMs: number): void
|
|
219
|
+
setWhiteBalance(mode: string, temperature: number, tint: number): void
|
|
220
|
+
setFocus(mode: string, pointX: number, pointY: number): void
|
|
221
|
+
setTorch(mode: string, level: number): void
|
|
222
|
+
deviceIdAt(index: number): string
|
|
223
|
+
deviceFacingAt(index: number): string
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The audio surface is NATIVE. The geatsc gea plugin maps `AudioContext`,
|
|
227
|
+
// `AudioDestinationNode`, `AudioParam` and `OscillatorNode` to
|
|
228
|
+
// `gea::host::…` handles by NAME (host-shims.ts `nativeTypes`), so the
|
|
229
|
+
// `Embedded`-prefixed copies that used to live here matched nothing and were
|
|
230
|
+
// lowered as ordinary records instead. `AudioDestinationNode` is an EMPTY
|
|
231
|
+
// interface, and an empty record mints no shape at all — its field reached
|
|
232
|
+
// emission with no frozen representation and the whole build failed closed.
|
|
233
|
+
// Use the published names so the native mapping applies.
|
|
234
|
+
export type { AudioContext as EmbeddedAudioContext } from '../index'
|
|
235
|
+
|
|
236
|
+
declare const __gea_audioContext: AudioContext
|
|
237
|
+
declare const __gea_Audio: {
|
|
238
|
+
getVolume(): number
|
|
239
|
+
setVolume(volume: number): void
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type EmbeddedDisplayOrientation = 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary'
|
|
243
|
+
|
|
244
|
+
type EmbeddedDisplayOrientationSupport = EmbeddedDisplayOrientation | 'portrait' | 'landscape' | 'all'
|
|
245
|
+
|
|
246
|
+
type EmbeddedDisplayPixelFormat = 'rgb565' | 'rgb888' | 'rgb8888' | 'argb8888'
|
|
247
|
+
|
|
248
|
+
declare const __gea_Display: {
|
|
249
|
+
readonly ctx: CanvasRenderingContext2D
|
|
250
|
+
readonly width: number
|
|
251
|
+
readonly height: number
|
|
252
|
+
readonly nativeWidth: number
|
|
253
|
+
readonly nativeHeight: number
|
|
254
|
+
orientation: EmbeddedDisplayOrientation
|
|
255
|
+
supportedOrientations: EmbeddedDisplayOrientationSupport[]
|
|
256
|
+
autoRotate: boolean
|
|
257
|
+
pixelFormat: EmbeddedDisplayPixelFormat
|
|
258
|
+
readonly panelPixelFormat: EmbeddedDisplayPixelFormat
|
|
259
|
+
readonly supportedPixelFormats: EmbeddedDisplayPixelFormat[]
|
|
260
|
+
getBrightness(): number
|
|
261
|
+
setBrightness(brightness: number): void
|
|
262
|
+
getOrientation(): EmbeddedDisplayOrientation
|
|
263
|
+
setOrientation(orientation: EmbeddedDisplayOrientation): void
|
|
264
|
+
getSupportedOrientations(): EmbeddedDisplayOrientationSupport[]
|
|
265
|
+
setSupportedOrientations(orientations: EmbeddedDisplayOrientationSupport | EmbeddedDisplayOrientationSupport[]): void
|
|
266
|
+
getAutoRotate(): boolean
|
|
267
|
+
setAutoRotate(enabled: boolean): void
|
|
268
|
+
setVSync(on: boolean): void
|
|
269
|
+
setTextRasterCache(on: boolean): void
|
|
270
|
+
// Declare that text always draws over ONE solid color (0xRRGGBB). On packed
|
|
271
|
+
// 4-bit grayscale targets (e-paper) each glyph is then pre-blended against
|
|
272
|
+
// that backdrop once and stamped as straight byte copies -- no per-pixel
|
|
273
|
+
// framebuffer read or blend. Glyphs whose color equals the backdrop (e.g.
|
|
274
|
+
// inverted rows) automatically fall back to the true blend. Pass a negative
|
|
275
|
+
// value to disable. No-op on full-color targets.
|
|
276
|
+
//
|
|
277
|
+
// Declared in `index.d.ts` and implemented by the engine
|
|
278
|
+
// (`packages/host/include/host/display.h`, `packages/engine/canvas.cpp`)
|
|
279
|
+
// since it was added; this surface -- the one a real build type-checks
|
|
280
|
+
// against -- had been left out, so `e-reader`'s own `index.tsx` did not
|
|
281
|
+
// compile against the framework it ships with.
|
|
282
|
+
setTextSolidBackdrop(rrggbb: number): void
|
|
283
|
+
// Mark the next present as a full-screen change ("damage-all"). The present
|
|
284
|
+
// then skips the dirty-rect diff and its persistent previous-frame copy --
|
|
285
|
+
// both exist only to flush less when little changed, which is pure waste
|
|
286
|
+
// during a full-screen pan (the diff would conclude "everything changed"
|
|
287
|
+
// and full-flush anyway). Call it once per frame while actively
|
|
288
|
+
// panning/animating the whole screen; stop calling it when motion settles
|
|
289
|
+
// so idle gets the cheap diff back.
|
|
290
|
+
invalidate(): void
|
|
291
|
+
getPixelFormat(): EmbeddedDisplayPixelFormat
|
|
292
|
+
setPixelFormat(format: EmbeddedDisplayPixelFormat): void
|
|
293
|
+
getPanelPixelFormat(): EmbeddedDisplayPixelFormat
|
|
294
|
+
getSupportedPixelFormats(): EmbeddedDisplayPixelFormat[]
|
|
295
|
+
getDevicePixelRatio(): number
|
|
296
|
+
setDevicePixelRatio(devicePixelRatio: number): void
|
|
297
|
+
getFrameIntervalMs(): number
|
|
298
|
+
setFrameIntervalMs(intervalMs: number): void
|
|
299
|
+
getFrameRate(): number
|
|
300
|
+
setFrameRate(fps: number): void
|
|
301
|
+
setAA(samples: number): void
|
|
302
|
+
setFlushConfig(config: { rows: number; depth: number }): void
|
|
303
|
+
setMemoryConfig(config: { commandBufferCommands?: number; backgroundCache?: boolean }): void
|
|
304
|
+
// E-paper boards only (no-op elsewhere): refresh policy + custom waveforms.
|
|
305
|
+
// LUTs are the panel's raw 159-byte waveform tables; [] restores the vendor
|
|
306
|
+
// default. fastStreakWindowMs: 0 disables the fast waveform entirely.
|
|
307
|
+
setEpaperRefreshConfig(config: {
|
|
308
|
+
fullRefreshEveryPartials?: number
|
|
309
|
+
fullRefreshHardCapPartials?: number
|
|
310
|
+
fastStreakWindowMs?: number
|
|
311
|
+
partialLut?: number[]
|
|
312
|
+
fastLut?: number[]
|
|
313
|
+
// 4-level grayscale on supported e-paper panels (SSD1681): full refresh,
|
|
314
|
+
// 4 reflectance levels instead of 1-bit dither. Omit to leave unchanged.
|
|
315
|
+
grayscale?: boolean
|
|
316
|
+
// Whether a full-screen-covering partial (an e-reader page turn) is
|
|
317
|
+
// promoted to a flashing full refresh. Omit to leave unchanged.
|
|
318
|
+
fullOnCover?: boolean
|
|
319
|
+
}): void
|
|
320
|
+
// Force a full (flashing, anti-ghost) refresh of the current frame now.
|
|
321
|
+
epaperFullRefresh(): void
|
|
322
|
+
}
|
|
323
|
+
declare const __gea_Memory: {
|
|
324
|
+
internalFree(): number
|
|
325
|
+
internalLargestFreeBlock(): number
|
|
326
|
+
internalMinimumFree(): number
|
|
327
|
+
psramFree(): number
|
|
328
|
+
currentTaskStackHighWaterMark(): number
|
|
329
|
+
geaMainStackBytes(): number
|
|
330
|
+
geaInitStackBytes(): number
|
|
331
|
+
appFrameStackWords(): number
|
|
332
|
+
appFrameStackBytes(): number
|
|
333
|
+
displayFlushConfiguredRows(): number
|
|
334
|
+
displayFlushConfiguredDepth(): number
|
|
335
|
+
displayFlushBufferMaxBytes(): number
|
|
336
|
+
displayFlushRows(): number
|
|
337
|
+
displayFlushDepth(): number
|
|
338
|
+
displayFlushBufferBytes(): number
|
|
339
|
+
allocationSramCount(): number
|
|
340
|
+
allocationPsramCount(): number
|
|
341
|
+
allocationSramBytes(): number
|
|
342
|
+
allocationPsramBytes(): number
|
|
343
|
+
allocationSramPeakBytes(): number
|
|
344
|
+
allocationPsramPeakBytes(): number
|
|
345
|
+
}
|
|
346
|
+
declare const __gea_Input: {
|
|
347
|
+
// Reads-and-clears the one-shot back-button-pressed flag. Set by the
|
|
348
|
+
// platform launcher-button task (BOOT short press on ESP32) and by any
|
|
349
|
+
// application's `_settings_toggle()` stub when a `SettingsToggle` event
|
|
350
|
+
// is dispatched. Apps that own UI overlays (Settings panel) poll this in
|
|
351
|
+
// their rAF loop and dismiss the overlay if visible.
|
|
352
|
+
consumeBackButton(): boolean
|
|
353
|
+
}
|
|
354
|
+
declare const __gea_Gpio: {
|
|
355
|
+
// Digital pins, for the wiring a board exposes that no other facade covers:
|
|
356
|
+
// an LED, a relay, a button that is not the launcher button. Each call
|
|
357
|
+
// answers "did this board do it" — a pin the chip does not have reports
|
|
358
|
+
// false rather than throwing.
|
|
359
|
+
configureOutput(pin: number): boolean
|
|
360
|
+
configureInput(pin: number, pullUp: boolean): boolean
|
|
361
|
+
write(pin: number, level: boolean): boolean
|
|
362
|
+
read(pin: number): boolean
|
|
363
|
+
}
|
|
364
|
+
declare const __gea_Led: {
|
|
365
|
+
// A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
|
|
366
|
+
// modern ESP32 modules. set() is the one-call default (attach if needed,
|
|
367
|
+
// write, transmit); off() is set() with black. show() returns once the strand
|
|
368
|
+
// has latched the buffer, so a true means the LED is showing what was asked
|
|
369
|
+
// for rather than that the write was queued.
|
|
370
|
+
set(pin: number, r: number, g: number, b: number): boolean
|
|
371
|
+
off(pin: number): boolean
|
|
372
|
+
attach(pin: number, count: number): boolean
|
|
373
|
+
setPixel(index: number, r: number, g: number, b: number): boolean
|
|
374
|
+
show(): boolean
|
|
375
|
+
detach(): void
|
|
376
|
+
}
|
|
377
|
+
declare const __gea_Clock: {
|
|
378
|
+
// Milliseconds since the Unix epoch from the platform real-time clock
|
|
379
|
+
// (gettimeofday). Reflects a host-set time (GEADEV SETTIME); unlike
|
|
380
|
+
// Date.now() — monotonic on this ESP32 build — it tracks real wall-clock
|
|
381
|
+
// time once synced. Returns a small value before the clock is ever set.
|
|
382
|
+
epochMs(): number
|
|
383
|
+
}
|
|
384
|
+
declare const __gea_Profiler: {
|
|
385
|
+
// Monotonic microseconds from the platform high-resolution timer.
|
|
386
|
+
nowUs(): number
|
|
387
|
+
}
|
|
388
|
+
declare const __gea_Battery: {
|
|
389
|
+
// Battery charge percentage (0-100) from the platform PMU.
|
|
390
|
+
level(): number
|
|
391
|
+
}
|
|
392
|
+
declare const __gea_Notify: {
|
|
393
|
+
// Transient host->app notification channel. text() is the latest message;
|
|
394
|
+
// seq() increments on each post so an app can detect a new one.
|
|
395
|
+
text(): string
|
|
396
|
+
seq(): number
|
|
397
|
+
}
|
|
398
|
+
declare const __gea_DeviceControl: {
|
|
399
|
+
// Run a host shell command, return combined stdout+stderr (macOS companion).
|
|
400
|
+
exec(command: string): string
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// The `__gea_embedded_touch` global this used to reach for is registered by
|
|
404
|
+
// NOTHING in this repository — `touch.read()` has always returned the zero
|
|
405
|
+
// sample on every board. Reaching for it was not free, though: the
|
|
406
|
+
// `globalThis as EmbeddedHostGlobals` cast minted a file-scope record out of
|
|
407
|
+
// the entire global surface, and the hook's own `read()` is a callable member,
|
|
408
|
+
// which D0 record identity declines by design (a callable key is coarser than
|
|
409
|
+
// the physical ABI). The field therefore reached emission with no frozen
|
|
410
|
+
// representation authority and `widenValueClassFieldForFileScope` failed
|
|
411
|
+
// closed, taking every embedded build down with it.
|
|
412
|
+
//
|
|
413
|
+
// Keep the public surface (`export declare const touch: TouchHost`) and the
|
|
414
|
+
// observable behaviour exactly as they were. When a board does grow a raw
|
|
415
|
+
// touch source, give it a real ambient `declare const __gea_*` host object
|
|
416
|
+
// like every other one in this file rather than a globalThis intersection.
|
|
417
|
+
|
|
418
|
+
export const defaults = {}
|
|
419
|
+
|
|
420
|
+
export const touch = {
|
|
421
|
+
read(): TouchSample {
|
|
422
|
+
return { touching: false, x: 0, y: 0 }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export const Apps = {
|
|
427
|
+
launch(appId: string): number {
|
|
428
|
+
return apps.launch(appId)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Case-variant aliases for host accessors. Keep these lazy: direct top-level
|
|
433
|
+
// `navigator.*` aliases survive bundling as bare reads even in Display-only
|
|
434
|
+
// apps, which bloats embedded canvas demos.
|
|
435
|
+
export const BLE: typeof navigator.bluetooth = {
|
|
436
|
+
keyboard: {
|
|
437
|
+
tap(hidCode: number): void {
|
|
438
|
+
navigator.bluetooth.keyboard.tap(hidCode)
|
|
439
|
+
},
|
|
440
|
+
down(modifier: number, hidCode: number): void {
|
|
441
|
+
navigator.bluetooth.keyboard.down(modifier, hidCode)
|
|
442
|
+
},
|
|
443
|
+
up(): void {
|
|
444
|
+
navigator.bluetooth.keyboard.up()
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
mouse: {
|
|
448
|
+
move(dx: number, dy: number, buttons?: number, wheel?: number): void {
|
|
449
|
+
navigator.bluetooth.mouse.move(dx, dy, buttons, wheel)
|
|
450
|
+
},
|
|
451
|
+
click(button: number): void {
|
|
452
|
+
navigator.bluetooth.mouse.click(button)
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
// BLE-MIDI (MIDI over GATT). `enable()` registers the MIDI service +
|
|
456
|
+
// advertises its UUID; `send()` transmits ONE already-framed BLE-MIDI
|
|
457
|
+
// packet (header + timestamp + MIDI bytes) as a notification.
|
|
458
|
+
midi: {
|
|
459
|
+
enable(): void {
|
|
460
|
+
navigator.bluetooth.midi.enable()
|
|
461
|
+
},
|
|
462
|
+
bound(): boolean {
|
|
463
|
+
return navigator.bluetooth.midi.bound()
|
|
464
|
+
},
|
|
465
|
+
send(bytes: number[]): void {
|
|
466
|
+
navigator.bluetooth.midi.send(bytes)
|
|
467
|
+
},
|
|
468
|
+
// Central role (scan for WIDI adapters / BLE-MIDI pedals and connect as
|
|
469
|
+
// a GATT client). Scan results follow the WiFi scan polling idiom.
|
|
470
|
+
startScan(): void {
|
|
471
|
+
navigator.bluetooth.midi.startScan()
|
|
472
|
+
},
|
|
473
|
+
stopScan(): void {
|
|
474
|
+
navigator.bluetooth.midi.stopScan()
|
|
475
|
+
},
|
|
476
|
+
scanning(): boolean {
|
|
477
|
+
return navigator.bluetooth.midi.scanning()
|
|
478
|
+
},
|
|
479
|
+
scanCount(): number {
|
|
480
|
+
return navigator.bluetooth.midi.scanCount()
|
|
481
|
+
},
|
|
482
|
+
scanNameAt(index: number): string {
|
|
483
|
+
return navigator.bluetooth.midi.scanNameAt(index)
|
|
484
|
+
},
|
|
485
|
+
connect(index: number): void {
|
|
486
|
+
navigator.bluetooth.midi.connect(index)
|
|
487
|
+
},
|
|
488
|
+
disconnect(): void {
|
|
489
|
+
navigator.bluetooth.midi.disconnect()
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
// HID host role: this device is the central; a remote HID peripheral (a
|
|
493
|
+
// keyboard/macro pad like the XPPen ACK05) is the input source. Raw input
|
|
494
|
+
// reports queue driver-side; the app polls and decodes them itself.
|
|
495
|
+
hidHost: {
|
|
496
|
+
startScan(): void {
|
|
497
|
+
navigator.bluetooth.hidHost.startScan()
|
|
498
|
+
},
|
|
499
|
+
stopScan(): void {
|
|
500
|
+
navigator.bluetooth.hidHost.stopScan()
|
|
501
|
+
},
|
|
502
|
+
scanning(): boolean {
|
|
503
|
+
return navigator.bluetooth.hidHost.scanning()
|
|
504
|
+
},
|
|
505
|
+
scanCount(): number {
|
|
506
|
+
return navigator.bluetooth.hidHost.scanCount()
|
|
507
|
+
},
|
|
508
|
+
scanNameAt(index: number): string {
|
|
509
|
+
return navigator.bluetooth.hidHost.scanNameAt(index)
|
|
510
|
+
},
|
|
511
|
+
connect(index: number): void {
|
|
512
|
+
navigator.bluetooth.hidHost.connect(index)
|
|
513
|
+
},
|
|
514
|
+
disconnect(): void {
|
|
515
|
+
navigator.bluetooth.hidHost.disconnect()
|
|
516
|
+
},
|
|
517
|
+
bound(): boolean {
|
|
518
|
+
return navigator.bluetooth.hidHost.bound()
|
|
519
|
+
},
|
|
520
|
+
reportCount(): number {
|
|
521
|
+
return navigator.bluetooth.hidHost.reportCount()
|
|
522
|
+
},
|
|
523
|
+
reportIdAt(index: number): number {
|
|
524
|
+
return navigator.bluetooth.hidHost.reportIdAt(index)
|
|
525
|
+
},
|
|
526
|
+
reportLenAt(index: number): number {
|
|
527
|
+
return navigator.bluetooth.hidHost.reportLenAt(index)
|
|
528
|
+
},
|
|
529
|
+
reportByteAt(index: number, byteIndex: number): number {
|
|
530
|
+
return navigator.bluetooth.hidHost.reportByteAt(index, byteIndex)
|
|
531
|
+
},
|
|
532
|
+
clearReports(): void {
|
|
533
|
+
navigator.bluetooth.hidHost.clearReports()
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
// Live connection registry across every concurrent BLE role. kind: 0 =
|
|
537
|
+
// HID central peer (desktop), 1 = MIDI central peer (DAW), 2 = HID
|
|
538
|
+
// peripheral we host (macro pad), 3 = BLE-MIDI peripheral we drive
|
|
539
|
+
// (pedal/WIDI adapter).
|
|
540
|
+
connections: {
|
|
541
|
+
count(): number {
|
|
542
|
+
return navigator.bluetooth.connections.count()
|
|
543
|
+
},
|
|
544
|
+
kindAt(index: number): number {
|
|
545
|
+
return navigator.bluetooth.connections.kindAt(index)
|
|
546
|
+
},
|
|
547
|
+
nameAt(index: number): string {
|
|
548
|
+
return navigator.bluetooth.connections.nameAt(index)
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
// Config service (custom GATT). A Web Bluetooth portal live-programs the
|
|
552
|
+
// device: it reads the app's current config blob and, once paired with the
|
|
553
|
+
// 4-digit code shown on-device, writes a new one. `setDocument` publishes
|
|
554
|
+
// the blob the portal reads back; the app polls `pendingLength` each frame
|
|
555
|
+
// and drains a committed inbound blob via `pendingByteAt`/`consumePending`.
|
|
556
|
+
// The pairing trio drives the on-device pairing overlay.
|
|
557
|
+
config: {
|
|
558
|
+
setDocument(bytes: number[]): void {
|
|
559
|
+
navigator.bluetooth.config.setDocument(bytes)
|
|
560
|
+
},
|
|
561
|
+
pendingLength(): number {
|
|
562
|
+
return navigator.bluetooth.config.pendingLength()
|
|
563
|
+
},
|
|
564
|
+
pendingByteAt(index: number): number {
|
|
565
|
+
return navigator.bluetooth.config.pendingByteAt(index)
|
|
566
|
+
},
|
|
567
|
+
consumePending(): void {
|
|
568
|
+
navigator.bluetooth.config.consumePending()
|
|
569
|
+
},
|
|
570
|
+
pairing(): boolean {
|
|
571
|
+
return navigator.bluetooth.config.pairing()
|
|
572
|
+
},
|
|
573
|
+
pairCode(): string {
|
|
574
|
+
return navigator.bluetooth.config.pairCode()
|
|
575
|
+
},
|
|
576
|
+
dismissPairing(): void {
|
|
577
|
+
navigator.bluetooth.config.dismissPairing()
|
|
578
|
+
},
|
|
579
|
+
// Device -> portal activity push: when a control fires on the device, tell
|
|
580
|
+
// any subscribed portal which control (by index) lit up, so the portal can
|
|
581
|
+
// highlight it live. No-op on the device when no portal is subscribed.
|
|
582
|
+
pushActivity(index: number): void {
|
|
583
|
+
navigator.bluetooth.config.pushActivity(index)
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
enabled(): boolean {
|
|
587
|
+
return navigator.bluetooth.enabled()
|
|
588
|
+
},
|
|
589
|
+
setEnabled(enabled: boolean): void {
|
|
590
|
+
navigator.bluetooth.setEnabled(enabled)
|
|
591
|
+
},
|
|
592
|
+
connected(): boolean {
|
|
593
|
+
return navigator.bluetooth.connected()
|
|
594
|
+
},
|
|
595
|
+
bound(): boolean {
|
|
596
|
+
return navigator.bluetooth.bound()
|
|
597
|
+
},
|
|
598
|
+
batteryLevel(): number {
|
|
599
|
+
return navigator.bluetooth.batteryLevel()
|
|
600
|
+
},
|
|
601
|
+
mac(): string {
|
|
602
|
+
return navigator.bluetooth.mac()
|
|
603
|
+
},
|
|
604
|
+
deviceName(): string {
|
|
605
|
+
return navigator.bluetooth.deviceName()
|
|
606
|
+
},
|
|
607
|
+
init(deviceName: string, appearance?: number, macAddress?: string): void {
|
|
608
|
+
navigator.bluetooth.init(deviceName, appearance, macAddress)
|
|
609
|
+
},
|
|
610
|
+
startAdvertising(): void {
|
|
611
|
+
navigator.bluetooth.startAdvertising()
|
|
612
|
+
},
|
|
613
|
+
stopAdvertising(): void {
|
|
614
|
+
navigator.bluetooth.stopAdvertising()
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
export const bluetooth = BLE
|
|
618
|
+
export const WiFi: typeof navigator.wifi = {
|
|
619
|
+
enabled(): boolean {
|
|
620
|
+
return navigator.wifi.enabled()
|
|
621
|
+
},
|
|
622
|
+
setEnabled(enabled: boolean): void {
|
|
623
|
+
navigator.wifi.setEnabled(enabled)
|
|
624
|
+
},
|
|
625
|
+
connected(): boolean {
|
|
626
|
+
return navigator.wifi.connected()
|
|
627
|
+
},
|
|
628
|
+
rssi(): number {
|
|
629
|
+
return navigator.wifi.rssi()
|
|
630
|
+
},
|
|
631
|
+
ssid(): string {
|
|
632
|
+
return navigator.wifi.ssid()
|
|
633
|
+
},
|
|
634
|
+
ip(): string {
|
|
635
|
+
return navigator.wifi.ip()
|
|
636
|
+
},
|
|
637
|
+
mac(): string {
|
|
638
|
+
return navigator.wifi.mac()
|
|
639
|
+
},
|
|
640
|
+
configure(ssid: string, password: string): void {
|
|
641
|
+
navigator.wifi.configure(ssid, password)
|
|
642
|
+
},
|
|
643
|
+
waitForConnection(timeoutMs: number): boolean {
|
|
644
|
+
return navigator.wifi.waitForConnection(timeoutMs)
|
|
645
|
+
},
|
|
646
|
+
startScan(): void {
|
|
647
|
+
navigator.wifi.startScan()
|
|
648
|
+
},
|
|
649
|
+
scanning(): boolean {
|
|
650
|
+
return navigator.wifi.scanning()
|
|
651
|
+
},
|
|
652
|
+
scanCount(): number {
|
|
653
|
+
return navigator.wifi.scanCount()
|
|
654
|
+
},
|
|
655
|
+
scanSsidAt(index: number): string {
|
|
656
|
+
return navigator.wifi.scanSsidAt(index)
|
|
657
|
+
},
|
|
658
|
+
scanRssiAt(index: number): number {
|
|
659
|
+
return navigator.wifi.scanRssiAt(index)
|
|
660
|
+
},
|
|
661
|
+
scanSecuredAt(index: number): boolean {
|
|
662
|
+
return navigator.wifi.scanSecuredAt(index)
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
export const wifi = WiFi
|
|
666
|
+
|
|
667
|
+
// Device-hosted HTTP server. On hardware, geatsc lowers `http.createServer(...)`
|
|
668
|
+
// to the native esp_http_server facade (packages/core/host/http.cpp); this JS
|
|
669
|
+
// stub is the web/simulator fallback — browsers can't open a listening socket,
|
|
670
|
+
// so it accepts the handler and reports a no-op server (listen() returns false).
|
|
671
|
+
export interface HttpIncomingMessage {
|
|
672
|
+
method: string
|
|
673
|
+
url: string
|
|
674
|
+
path: string
|
|
675
|
+
query: string
|
|
676
|
+
}
|
|
677
|
+
export interface HttpServerInstance {
|
|
678
|
+
listen(port: number): boolean
|
|
679
|
+
close(): void
|
|
680
|
+
id(): number
|
|
681
|
+
}
|
|
682
|
+
export const http = {
|
|
683
|
+
createServer(_handler: (req: HttpIncomingMessage) => HttpReply): HttpServerInstance {
|
|
684
|
+
return {
|
|
685
|
+
listen(_port: number): boolean {
|
|
686
|
+
return false
|
|
687
|
+
},
|
|
688
|
+
close(): void {},
|
|
689
|
+
id(): number {
|
|
690
|
+
return 0
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
close(_handle: number): void {}
|
|
695
|
+
}
|
|
696
|
+
export const Geolocation: typeof navigator.geolocation = {
|
|
697
|
+
hasFix(): boolean {
|
|
698
|
+
return navigator.geolocation.hasFix()
|
|
699
|
+
},
|
|
700
|
+
currentPosition(): GeolocationPosition {
|
|
701
|
+
return navigator.geolocation.currentPosition()
|
|
702
|
+
},
|
|
703
|
+
coords(): GeolocationCoordinates {
|
|
704
|
+
return navigator.geolocation.coords()
|
|
705
|
+
},
|
|
706
|
+
latitude(): number {
|
|
707
|
+
return navigator.geolocation.latitude()
|
|
708
|
+
},
|
|
709
|
+
longitude(): number {
|
|
710
|
+
return navigator.geolocation.longitude()
|
|
711
|
+
},
|
|
712
|
+
accuracy(): number {
|
|
713
|
+
return navigator.geolocation.accuracy()
|
|
714
|
+
},
|
|
715
|
+
getCurrentPosition(
|
|
716
|
+
success: (position: GeolocationPosition) => void,
|
|
717
|
+
error?: (error: GeolocationPositionError) => void,
|
|
718
|
+
options?: PositionOptions
|
|
719
|
+
): void {
|
|
720
|
+
navigator.geolocation.getCurrentPosition(success, error, options)
|
|
721
|
+
},
|
|
722
|
+
watchPosition(
|
|
723
|
+
success: (position: GeolocationPosition) => void,
|
|
724
|
+
error?: (error: GeolocationPositionError) => void,
|
|
725
|
+
options?: PositionOptions
|
|
726
|
+
): number {
|
|
727
|
+
return navigator.geolocation.watchPosition(success, error, options)
|
|
728
|
+
},
|
|
729
|
+
clearWatch(id: number): void {
|
|
730
|
+
navigator.geolocation.clearWatch(id)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
export const geolocation = Geolocation
|
|
734
|
+
// `__gea_Accelerometer` is only injected by the vite plugin when an app actually
|
|
735
|
+
// uses the accelerometer (the imu-capability gate keys on `Accelerometer.` calls).
|
|
736
|
+
// Referencing it unconditionally here threw `ReferenceError: __gea_Accelerometer
|
|
737
|
+
// is not defined` at module load for every app that doesn't use it — blanking the
|
|
738
|
+
// whole page in the browser runtime. `typeof` on the bare identifier is safe
|
|
739
|
+
// whether or not the global was injected, and apps that do use it still get the
|
|
740
|
+
// real binding.
|
|
741
|
+
export const Accelerometer =
|
|
742
|
+
typeof __gea_Accelerometer !== 'undefined' ? __gea_Accelerometer : (undefined as unknown as typeof __gea_Accelerometer)
|
|
743
|
+
|
|
744
|
+
// Remembers the most recent recording sink so stopRecording can report it on
|
|
745
|
+
// the resolved CameraClip (the host only returns a duration).
|
|
746
|
+
let cameraRecordingPath = ''
|
|
747
|
+
|
|
748
|
+
// Live preview + still-capture + recording + controls API. Same shape on every
|
|
749
|
+
// target that ships a Camera backend (esp32-p4, geaos; iOS follows). The
|
|
750
|
+
// `<camera>` JSX element provides the matching CSS-sized preview surface; the
|
|
751
|
+
// optional <CameraView /> wrapper lives in @geastack/elements.
|
|
752
|
+
export const Camera = {
|
|
753
|
+
isAvailable(): boolean {
|
|
754
|
+
return __gea_Camera.isAvailable()
|
|
755
|
+
},
|
|
756
|
+
hasPermission(): boolean {
|
|
757
|
+
return __gea_Camera.hasPermission()
|
|
758
|
+
},
|
|
759
|
+
requestPermission(): Promise<boolean> {
|
|
760
|
+
return Promise.resolve(__gea_Camera.requestPermission())
|
|
761
|
+
},
|
|
762
|
+
open(options?: CameraOpenOptions | CameraFacing): boolean {
|
|
763
|
+
if (typeof options === 'string') return __gea_Camera.open(options, 0, 0)
|
|
764
|
+
if (!options) return __gea_Camera.open('back', 0, 0)
|
|
765
|
+
const facing = options.facing ?? 'back'
|
|
766
|
+
const width = options.width ?? 0
|
|
767
|
+
const height = options.height ?? 0
|
|
768
|
+
return __gea_Camera.open(facing, width, height)
|
|
769
|
+
},
|
|
770
|
+
close(): void {
|
|
771
|
+
__gea_Camera.close()
|
|
772
|
+
},
|
|
773
|
+
isOpen(): boolean {
|
|
774
|
+
return __gea_Camera.isOpen()
|
|
775
|
+
},
|
|
776
|
+
get facing(): CameraFacing {
|
|
777
|
+
return __gea_Camera.facing as CameraFacing
|
|
778
|
+
},
|
|
779
|
+
get width(): number {
|
|
780
|
+
return __gea_Camera.width
|
|
781
|
+
},
|
|
782
|
+
get height(): number {
|
|
783
|
+
return __gea_Camera.height
|
|
784
|
+
},
|
|
785
|
+
get orientation(): number {
|
|
786
|
+
return __gea_Camera.orientation
|
|
787
|
+
},
|
|
788
|
+
draw(x: number, y: number, destWidth?: number, destHeight?: number): void {
|
|
789
|
+
__gea_Camera.draw(x, y, destWidth ?? 0, destHeight ?? 0)
|
|
790
|
+
},
|
|
791
|
+
capture(options?: CameraCaptureOptions): Promise<CameraPhoto> {
|
|
792
|
+
const id = options?.mirror ? __gea_Camera.captureMirrored() : __gea_Camera.capture()
|
|
793
|
+
const photo: CameraPhoto = {
|
|
794
|
+
imageId: id,
|
|
795
|
+
width: __gea_Camera.width,
|
|
796
|
+
height: __gea_Camera.height,
|
|
797
|
+
orientation: __gea_Camera.orientation,
|
|
798
|
+
dispose(): void {
|
|
799
|
+
image.dispose(id)
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return Promise.resolve(photo)
|
|
803
|
+
},
|
|
804
|
+
capturePhoto(options?: CameraCaptureOptions): Promise<CameraPhoto> {
|
|
805
|
+
return this.capture(options)
|
|
806
|
+
},
|
|
807
|
+
startRecording(options: CameraRecordOptions): Promise<void> {
|
|
808
|
+
cameraRecordingPath = options.path
|
|
809
|
+
__gea_Camera.startRecording(options.path, options.fps ?? 0)
|
|
810
|
+
return Promise.resolve()
|
|
811
|
+
},
|
|
812
|
+
stopRecording(): Promise<CameraClip> {
|
|
813
|
+
// durationMs is -1 when no recording was active; callers can check it.
|
|
814
|
+
const durationMs = __gea_Camera.stopRecording()
|
|
815
|
+
const clip: CameraClip = { durationMs, path: cameraRecordingPath }
|
|
816
|
+
return Promise.resolve(clip)
|
|
817
|
+
},
|
|
818
|
+
isRecording(): boolean {
|
|
819
|
+
return __gea_Camera.isRecording()
|
|
820
|
+
},
|
|
821
|
+
setFlash(mode: CameraFlashMode): void {
|
|
822
|
+
__gea_Camera.setFlash(mode)
|
|
823
|
+
},
|
|
824
|
+
setZoom(factor: number): void {
|
|
825
|
+
__gea_Camera.setZoom(factor)
|
|
826
|
+
},
|
|
827
|
+
setMirror(mirror: boolean): void {
|
|
828
|
+
__gea_Camera.setMirror(mirror)
|
|
829
|
+
},
|
|
830
|
+
setExposure(options: CameraExposureOptions): void {
|
|
831
|
+
__gea_Camera.setExposure(options.mode ?? 'continuous', options.bias ?? 0, options.iso ?? 0, options.durationMs ?? 0)
|
|
832
|
+
},
|
|
833
|
+
setWhiteBalance(options: CameraWhiteBalanceOptions): void {
|
|
834
|
+
__gea_Camera.setWhiteBalance(options.mode ?? 'auto', options.temperature ?? 0, options.tint ?? 0)
|
|
835
|
+
},
|
|
836
|
+
setFocus(options: CameraFocusOptions): void {
|
|
837
|
+
__gea_Camera.setFocus(options.mode ?? 'continuous', options.point?.x ?? 0, options.point?.y ?? 0)
|
|
838
|
+
},
|
|
839
|
+
setTorch(mode: 'off' | 'on' | 'auto', level?: number): void {
|
|
840
|
+
__gea_Camera.setTorch(mode, level ?? 0)
|
|
841
|
+
},
|
|
842
|
+
switchCamera(target: CameraFacing | string): boolean {
|
|
843
|
+
if (this.isOpen()) __gea_Camera.close()
|
|
844
|
+
return __gea_Camera.open(target, 0, 0)
|
|
845
|
+
},
|
|
846
|
+
getDevices(): CameraDevice[] {
|
|
847
|
+
const count = __gea_Camera.deviceCount
|
|
848
|
+
const out: CameraDevice[] = []
|
|
849
|
+
for (let i = 0; i < count; i++) {
|
|
850
|
+
out.push({
|
|
851
|
+
id: __gea_Camera.deviceIdAt(i),
|
|
852
|
+
facing: __gea_Camera.deviceFacingAt(i) as CameraFacing
|
|
853
|
+
})
|
|
854
|
+
}
|
|
855
|
+
return out
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Same guard as Accelerometer above: these embedded host globals are not injected
|
|
860
|
+
// in the browser runtime (which renders via real web APIs), so a bare top-level
|
|
861
|
+
// reference ReferenceErrors at module load and blanks the page. `typeof` keeps the
|
|
862
|
+
// real binding on embedded targets where the plugin injects them.
|
|
863
|
+
export const audioContext: AudioContext =
|
|
864
|
+
typeof __gea_audioContext !== 'undefined' ? __gea_audioContext : (undefined as unknown as AudioContext)
|
|
865
|
+
// Method wrapper, NOT the `typeof … ? … : undefined as …` guard the
|
|
866
|
+
// property-bearing host objects above use. Two reasons. (1) The guard exists
|
|
867
|
+
// only to stop a bare top-level reference from ReferenceError-ing at module
|
|
868
|
+
// load in the browser runtime; a reference inside a method body is evaluated
|
|
869
|
+
// on call, so it needs no guard — which is why `Memory`/`Input`/`Clock` below
|
|
870
|
+
// are already written this way. (2) `typeof __gea_Audio` is an object whose
|
|
871
|
+
// every member is callable, and the conditional over it has no plannable
|
|
872
|
+
// representation ("coverage gap for ConditionalExpression"). The plugin cannot
|
|
873
|
+
// paper over that with a `hostNamespaces` entry either: the name `Audio` is
|
|
874
|
+
// already claimed by `embeddedHostClasses` for the DOM `new Audio()`
|
|
875
|
+
// constructor (gea::host::HTMLAudioElement).
|
|
876
|
+
export const Audio = {
|
|
877
|
+
getVolume(): number {
|
|
878
|
+
return __gea_Audio.getVolume()
|
|
879
|
+
},
|
|
880
|
+
setVolume(volume: number): void {
|
|
881
|
+
__gea_Audio.setVolume(volume)
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
export const Display = typeof __gea_Display !== 'undefined' ? __gea_Display : (undefined as unknown as typeof __gea_Display)
|
|
885
|
+
export const Memory = {
|
|
886
|
+
internalFree(): number {
|
|
887
|
+
return __gea_Memory.internalFree()
|
|
888
|
+
},
|
|
889
|
+
internalLargestFreeBlock(): number {
|
|
890
|
+
return __gea_Memory.internalLargestFreeBlock()
|
|
891
|
+
},
|
|
892
|
+
internalMinimumFree(): number {
|
|
893
|
+
return __gea_Memory.internalMinimumFree()
|
|
894
|
+
},
|
|
895
|
+
psramFree(): number {
|
|
896
|
+
return __gea_Memory.psramFree()
|
|
897
|
+
},
|
|
898
|
+
currentTaskStackHighWaterMark(): number {
|
|
899
|
+
return __gea_Memory.currentTaskStackHighWaterMark()
|
|
900
|
+
},
|
|
901
|
+
geaMainStackBytes(): number {
|
|
902
|
+
return __gea_Memory.geaMainStackBytes()
|
|
903
|
+
},
|
|
904
|
+
geaInitStackBytes(): number {
|
|
905
|
+
return __gea_Memory.geaInitStackBytes()
|
|
906
|
+
},
|
|
907
|
+
appFrameStackWords(): number {
|
|
908
|
+
return __gea_Memory.appFrameStackWords()
|
|
909
|
+
},
|
|
910
|
+
appFrameStackBytes(): number {
|
|
911
|
+
return __gea_Memory.appFrameStackBytes()
|
|
912
|
+
},
|
|
913
|
+
displayFlushConfiguredRows(): number {
|
|
914
|
+
return __gea_Memory.displayFlushConfiguredRows()
|
|
915
|
+
},
|
|
916
|
+
displayFlushConfiguredDepth(): number {
|
|
917
|
+
return __gea_Memory.displayFlushConfiguredDepth()
|
|
918
|
+
},
|
|
919
|
+
displayFlushBufferMaxBytes(): number {
|
|
920
|
+
return __gea_Memory.displayFlushBufferMaxBytes()
|
|
921
|
+
},
|
|
922
|
+
displayFlushRows(): number {
|
|
923
|
+
return __gea_Memory.displayFlushRows()
|
|
924
|
+
},
|
|
925
|
+
displayFlushDepth(): number {
|
|
926
|
+
return __gea_Memory.displayFlushDepth()
|
|
927
|
+
},
|
|
928
|
+
displayFlushBufferBytes(): number {
|
|
929
|
+
return __gea_Memory.displayFlushBufferBytes()
|
|
930
|
+
},
|
|
931
|
+
allocationSramCount(): number {
|
|
932
|
+
return __gea_Memory.allocationSramCount()
|
|
933
|
+
},
|
|
934
|
+
allocationPsramCount(): number {
|
|
935
|
+
return __gea_Memory.allocationPsramCount()
|
|
936
|
+
},
|
|
937
|
+
allocationSramBytes(): number {
|
|
938
|
+
return __gea_Memory.allocationSramBytes()
|
|
939
|
+
},
|
|
940
|
+
allocationPsramBytes(): number {
|
|
941
|
+
return __gea_Memory.allocationPsramBytes()
|
|
942
|
+
},
|
|
943
|
+
allocationSramPeakBytes(): number {
|
|
944
|
+
return __gea_Memory.allocationSramPeakBytes()
|
|
945
|
+
},
|
|
946
|
+
allocationPsramPeakBytes(): number {
|
|
947
|
+
return __gea_Memory.allocationPsramPeakBytes()
|
|
948
|
+
},
|
|
949
|
+
stats(): EmbeddedMemoryStats {
|
|
950
|
+
return {
|
|
951
|
+
internalFree: __gea_Memory.internalFree(),
|
|
952
|
+
internalLargestFreeBlock: __gea_Memory.internalLargestFreeBlock(),
|
|
953
|
+
internalMinimumFree: __gea_Memory.internalMinimumFree(),
|
|
954
|
+
psramFree: __gea_Memory.psramFree(),
|
|
955
|
+
currentTaskStackHighWaterMark: __gea_Memory.currentTaskStackHighWaterMark(),
|
|
956
|
+
allocationSramCount: __gea_Memory.allocationSramCount(),
|
|
957
|
+
allocationPsramCount: __gea_Memory.allocationPsramCount(),
|
|
958
|
+
allocationSramBytes: __gea_Memory.allocationSramBytes(),
|
|
959
|
+
allocationPsramBytes: __gea_Memory.allocationPsramBytes(),
|
|
960
|
+
allocationSramPeakBytes: __gea_Memory.allocationSramPeakBytes(),
|
|
961
|
+
allocationPsramPeakBytes: __gea_Memory.allocationPsramPeakBytes()
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
config(): EmbeddedMemoryConfig {
|
|
965
|
+
return {
|
|
966
|
+
geaMainStackBytes: __gea_Memory.geaMainStackBytes(),
|
|
967
|
+
geaInitStackBytes: __gea_Memory.geaInitStackBytes(),
|
|
968
|
+
appFrameStackWords: __gea_Memory.appFrameStackWords(),
|
|
969
|
+
appFrameStackBytes: __gea_Memory.appFrameStackBytes(),
|
|
970
|
+
displayFlushConfiguredRows: __gea_Memory.displayFlushConfiguredRows(),
|
|
971
|
+
displayFlushConfiguredDepth: __gea_Memory.displayFlushConfiguredDepth(),
|
|
972
|
+
displayFlushBufferMaxBytes: __gea_Memory.displayFlushBufferMaxBytes(),
|
|
973
|
+
displayFlushRows: __gea_Memory.displayFlushRows(),
|
|
974
|
+
displayFlushDepth: __gea_Memory.displayFlushDepth(),
|
|
975
|
+
displayFlushBufferBytes: __gea_Memory.displayFlushBufferBytes()
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
export const Input = {
|
|
980
|
+
consumeBackButton(): boolean {
|
|
981
|
+
return __gea_Input.consumeBackButton()
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
export const Gpio = {
|
|
985
|
+
configureOutput(pin: number): boolean {
|
|
986
|
+
return __gea_Gpio.configureOutput(pin)
|
|
987
|
+
},
|
|
988
|
+
configureInput(pin: number, pullUp: boolean): boolean {
|
|
989
|
+
return __gea_Gpio.configureInput(pin, pullUp)
|
|
990
|
+
},
|
|
991
|
+
write(pin: number, level: boolean): boolean {
|
|
992
|
+
return __gea_Gpio.write(pin, level)
|
|
993
|
+
},
|
|
994
|
+
read(pin: number): boolean {
|
|
995
|
+
return __gea_Gpio.read(pin)
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
export const Led = {
|
|
999
|
+
set(pin: number, r: number, g: number, b: number): boolean {
|
|
1000
|
+
return __gea_Led.set(pin, r, g, b)
|
|
1001
|
+
},
|
|
1002
|
+
off(pin: number): boolean {
|
|
1003
|
+
return __gea_Led.off(pin)
|
|
1004
|
+
},
|
|
1005
|
+
attach(pin: number, count: number): boolean {
|
|
1006
|
+
return __gea_Led.attach(pin, count)
|
|
1007
|
+
},
|
|
1008
|
+
setPixel(index: number, r: number, g: number, b: number): boolean {
|
|
1009
|
+
return __gea_Led.setPixel(index, r, g, b)
|
|
1010
|
+
},
|
|
1011
|
+
show(): boolean {
|
|
1012
|
+
return __gea_Led.show()
|
|
1013
|
+
},
|
|
1014
|
+
detach(): void {
|
|
1015
|
+
__gea_Led.detach()
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
export const Clock = {
|
|
1019
|
+
epochMs(): number {
|
|
1020
|
+
return __gea_Clock.epochMs()
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
// Same method-wrapper reasoning as `Audio` above: the host global is only
|
|
1024
|
+
// touched when the method runs, and the conditional form has no plannable
|
|
1025
|
+
// representation. The `Date.now()` fallback is preserved for the runtimes that
|
|
1026
|
+
// do not inject the global.
|
|
1027
|
+
export const Profiler = {
|
|
1028
|
+
nowUs(): number {
|
|
1029
|
+
if (typeof __gea_Profiler === 'undefined') return Date.now() * 1000
|
|
1030
|
+
return __gea_Profiler.nowUs()
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
export const Battery = {
|
|
1034
|
+
level(): number {
|
|
1035
|
+
return __gea_Battery.level()
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
export const Notify = {
|
|
1039
|
+
text(): string {
|
|
1040
|
+
return __gea_Notify.text()
|
|
1041
|
+
},
|
|
1042
|
+
seq(): number {
|
|
1043
|
+
return __gea_Notify.seq()
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
export const DeviceControl = {
|
|
1047
|
+
exec(command: string): string {
|
|
1048
|
+
return __gea_DeviceControl.exec(command)
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// Method wrapper for the same two reasons as `Audio`/`Profiler` above: a
|
|
1052
|
+
// reference inside a method body cannot ReferenceError at module load, and the
|
|
1053
|
+
// `typeof … ? … : undefined as …` conditional over an all-callable object type
|
|
1054
|
+
// has no plannable representation.
|
|
1055
|
+
export const imageHost = {
|
|
1056
|
+
loadBytes(bytes: Uint8Array): number {
|
|
1057
|
+
return image.loadBytes(bytes)
|
|
1058
|
+
},
|
|
1059
|
+
loadBytesOpaque(bytes: Uint8Array): number {
|
|
1060
|
+
return image.loadBytesOpaque(bytes)
|
|
1061
|
+
},
|
|
1062
|
+
width(id: number): number {
|
|
1063
|
+
return image.width(id)
|
|
1064
|
+
},
|
|
1065
|
+
height(id: number): number {
|
|
1066
|
+
return image.height(id)
|
|
1067
|
+
},
|
|
1068
|
+
frameCount(id: number): number {
|
|
1069
|
+
return image.frameCount(id)
|
|
1070
|
+
},
|
|
1071
|
+
isAnimated(id: number): boolean {
|
|
1072
|
+
return image.isAnimated(id)
|
|
1073
|
+
},
|
|
1074
|
+
setPlaying(id: number, playing: number): void {
|
|
1075
|
+
image.setPlaying(id, playing)
|
|
1076
|
+
},
|
|
1077
|
+
seek(id: number, frame: number): void {
|
|
1078
|
+
image.seek(id, frame)
|
|
1079
|
+
},
|
|
1080
|
+
dispose(id: number): void {
|
|
1081
|
+
image.dispose(id)
|
|
1082
|
+
}
|
|
1083
|
+
}
|