@geastack/core 0.1.38 → 0.1.40
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/events.cpp +2 -0
- package/gea_app_entry.cpp +2 -2
- package/gea_sources.mjs +4 -3
- package/include/audio.h +11 -1
- package/include/audio_oscillator.h +52 -0
- package/include/camera.h +21 -0
- package/include/captive_dns.h +71 -0
- package/include/display_present.h +88 -33
- package/include/events.h +31 -3
- package/include/gea/embedded.h +2 -0
- package/include/graphics/font.h +19 -0
- package/include/image.h +47 -1
- package/include/input.h +12 -1
- package/include/platform/atomic_file.h +72 -0
- package/include/platform/file_cache.h +6 -3
- package/include/power.h +2 -0
- package/include/services/frame_scheduler.h +6 -0
- package/include/wifi.h +60 -45
- package/index.d.ts +233 -44
- package/package.json +3 -3
- package/runtime/audio-worklet.ts +7 -11
- package/runtime/host.ts +132 -77
- package/runtime/images.ts +5 -0
- package/runtime-surface.ts +1 -0
- package/runtime.cpp +23 -5
- package/scripts/bitmap-font-source.mjs +155 -0
- package/scripts/build-gea-vite-geatsc.mjs +97 -6
- package/scripts/gea-embedded-asset-policy.mjs +38 -0
- package/scripts/gea-embedded-compat-transform.mjs +4 -2
- package/scripts/gea-vite-module-graph-plugin.mjs +64 -15
- package/scripts/generate-gea-embedded-assets.mjs +17 -3
- package/scripts/generate-gea-embedded-fonts.mjs +986 -670
package/include/image.h
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "pixel.h"
|
|
4
4
|
|
|
5
5
|
#include <cstdint>
|
|
6
|
+
#include <utility>
|
|
6
7
|
|
|
7
8
|
#if GEA_EMBEDDED_SINGLE_THREAD_IMAGE_STORE
|
|
8
9
|
namespace gea::framework::graphics {
|
|
@@ -26,7 +27,10 @@ using ImageStoreMutex = std::mutex;
|
|
|
26
27
|
// tile-grid app (maps) keep ~4 screens of decoded tiles resident in PSRAM for
|
|
27
28
|
// instant pan-back, on top of its persistent SD cache. ~128 KiB/tile (RGB565
|
|
28
29
|
// 256x256) when full → ~12 MiB; the P4 has ample PSRAM.
|
|
29
|
-
|
|
30
|
+
#ifndef GEA_EMBEDDED_MAX_IMAGES
|
|
31
|
+
#define GEA_EMBEDDED_MAX_IMAGES 96
|
|
32
|
+
#endif
|
|
33
|
+
inline constexpr int kImageMax = GEA_EMBEDDED_MAX_IMAGES;
|
|
30
34
|
|
|
31
35
|
enum class ImageFit {
|
|
32
36
|
Fill = 0,
|
|
@@ -61,6 +65,9 @@ struct ImageSlot {
|
|
|
61
65
|
pixel::native_t *pixels = nullptr;
|
|
62
66
|
std::uint8_t *alpha = nullptr;
|
|
63
67
|
bool ownsPixels = false;
|
|
68
|
+
// Registered by the app (registerBuffer): the app writes these pixels in
|
|
69
|
+
// place between frames, so the same pointer does not mean the same image.
|
|
70
|
+
bool mutablePixels = false;
|
|
64
71
|
|
|
65
72
|
void *gifDecoder = nullptr;
|
|
66
73
|
std::uint8_t *gifData = nullptr;
|
|
@@ -98,6 +105,11 @@ public:
|
|
|
98
105
|
// camera capture, which hands over a freshly-allocated RGBA8888 still.
|
|
99
106
|
int registerBuffer(pixel::native_t *pixels, int width, int height, int preferredSlot = -1, bool takeOwnership = false);
|
|
100
107
|
|
|
108
|
+
// True when `pixels` point into a registerBuffer() image, whose content the
|
|
109
|
+
// app rewrites in place. The present diff never treats such an image as
|
|
110
|
+
// unchanged from the previous frame.
|
|
111
|
+
bool pixelsMayChange(const pixel::native_t *pixels) const;
|
|
112
|
+
|
|
101
113
|
int width(int id) const;
|
|
102
114
|
int height(int id) const;
|
|
103
115
|
int frameCount(int id) const;
|
|
@@ -111,6 +123,10 @@ public:
|
|
|
111
123
|
const pixel::native_t *framePixels(int id, int frame) const;
|
|
112
124
|
const pixel::native_t *currentPixels(int id) const;
|
|
113
125
|
const std::uint8_t *currentAlpha(int id) const;
|
|
126
|
+
// Pins keep decoded pixels alive for nodes and retained render commands.
|
|
127
|
+
bool retain(int id);
|
|
128
|
+
void release(int id);
|
|
129
|
+
int idForPixels(const pixel::native_t *pixels) const;
|
|
114
130
|
void dispose(int id);
|
|
115
131
|
void disposeAll();
|
|
116
132
|
ImageFormat detectFormat(const std::uint8_t *data, int length) const;
|
|
@@ -142,6 +158,8 @@ private:
|
|
|
142
158
|
|
|
143
159
|
ImageSlot images_[kImageMax]{};
|
|
144
160
|
bool used_[kImageMax]{};
|
|
161
|
+
unsigned references_[kImageMax]{};
|
|
162
|
+
bool retired_[kImageMax]{};
|
|
145
163
|
// Guards slot ALLOCATION and DISPOSAL only (reservation-style): the async
|
|
146
164
|
// tile loader decodes on the second core while the frame task blits and
|
|
147
165
|
// evicts. A decode reserves its slot under the lock, then fills it
|
|
@@ -151,4 +169,32 @@ private:
|
|
|
151
169
|
mutable ImageStoreMutex slotMutex_;
|
|
152
170
|
};
|
|
153
171
|
|
|
172
|
+
// Copyable ownership for retained commands; raw pixel pointers alone do not
|
|
173
|
+
// keep a decoded slot alive.
|
|
174
|
+
class ImageReference {
|
|
175
|
+
public:
|
|
176
|
+
ImageReference() = default;
|
|
177
|
+
explicit ImageReference(int id) : id_(ImageStore::instance().retain(id) ? id : -1) {}
|
|
178
|
+
ImageReference(const ImageReference &other) : ImageReference(other.id_) {}
|
|
179
|
+
ImageReference(ImageReference &&other) noexcept : id_(other.id_) { other.id_ = -1; }
|
|
180
|
+
~ImageReference() { ImageStore::instance().release(id_); }
|
|
181
|
+
ImageReference &operator=(const ImageReference &other) {
|
|
182
|
+
if (this != &other) {
|
|
183
|
+
ImageReference next(other);
|
|
184
|
+
std::swap(id_, next.id_);
|
|
185
|
+
}
|
|
186
|
+
return *this;
|
|
187
|
+
}
|
|
188
|
+
ImageReference &operator=(ImageReference &&other) noexcept {
|
|
189
|
+
if (this != &other) {
|
|
190
|
+
ImageStore::instance().release(id_);
|
|
191
|
+
id_ = other.id_;
|
|
192
|
+
other.id_ = -1;
|
|
193
|
+
}
|
|
194
|
+
return *this;
|
|
195
|
+
}
|
|
196
|
+
private:
|
|
197
|
+
int id_ = -1;
|
|
198
|
+
};
|
|
199
|
+
|
|
154
200
|
} // namespace gea::framework::graphics
|
package/include/input.h
CHANGED
|
@@ -27,7 +27,18 @@ int consumeRotaryDelta();
|
|
|
27
27
|
// `keydown` event. keyCode 0 is ignored.
|
|
28
28
|
void queueKeyDown(int keyCode);
|
|
29
29
|
|
|
30
|
+
// Queue a release in the same ordered stream as key presses.
|
|
31
|
+
void queueKeyUp(int keyCode);
|
|
32
|
+
|
|
33
|
+
struct HardwareKeyEvent {
|
|
34
|
+
int keyCode;
|
|
35
|
+
bool pressed;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Consume the oldest press or release without changing their order.
|
|
39
|
+
bool consumeKeyEvent(HardwareKeyEvent &event);
|
|
40
|
+
|
|
30
41
|
// Reads and clears the oldest pending key press. Returns 0 when none pending.
|
|
31
42
|
int consumeKeyDown();
|
|
32
43
|
|
|
33
|
-
}
|
|
44
|
+
} // namespace gea::framework::input
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include <cerrno>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <cstdio>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <sys/stat.h>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
namespace gea::platform::storage {
|
|
12
|
+
|
|
13
|
+
struct FileOperations {
|
|
14
|
+
using Handle = std::FILE *;
|
|
15
|
+
Handle open(const std::string &path) {
|
|
16
|
+
return std::fopen(path.c_str(), "wb");
|
|
17
|
+
}
|
|
18
|
+
std::size_t write(Handle file, const std::vector<std::uint8_t> &bytes) {
|
|
19
|
+
return std::fwrite(bytes.data(), 1, bytes.size(), file);
|
|
20
|
+
}
|
|
21
|
+
bool close(Handle file) { return std::fclose(file) == 0; }
|
|
22
|
+
bool move(const std::string &from, const std::string &to) {
|
|
23
|
+
return std::rename(from.c_str(), to.c_str()) == 0;
|
|
24
|
+
}
|
|
25
|
+
bool remove(const std::string &path) {
|
|
26
|
+
return std::remove(path.c_str()) == 0 || errno == ENOENT;
|
|
27
|
+
}
|
|
28
|
+
bool destinationExists(const std::string &path) const {
|
|
29
|
+
struct stat info{};
|
|
30
|
+
return ::stat(path.c_str(), &info) == 0;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// POSIX/FAT can replace a destination with rename; SPIFFS requires moving it
|
|
35
|
+
// aside first. Never truncate the current file while writing its replacement.
|
|
36
|
+
// If the second rename fails, restore the backup (and retain it on disk even
|
|
37
|
+
// when the filesystem also rejects that recovery rename).
|
|
38
|
+
template <typename Operations>
|
|
39
|
+
bool writeFileAtomically(const std::string &path,
|
|
40
|
+
const std::vector<std::uint8_t> &bytes,
|
|
41
|
+
Operations &files) {
|
|
42
|
+
const auto temporary = path + ".tmp";
|
|
43
|
+
auto file = files.open(temporary);
|
|
44
|
+
if (!file)
|
|
45
|
+
return false;
|
|
46
|
+
const bool written = files.write(file, bytes) == bytes.size();
|
|
47
|
+
const bool closed = files.close(file);
|
|
48
|
+
if (!written || !closed) {
|
|
49
|
+
files.remove(temporary);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (files.move(temporary, path))
|
|
53
|
+
return true;
|
|
54
|
+
if (!files.destinationExists(path)) {
|
|
55
|
+
files.remove(temporary);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const auto backup = path + ".bak";
|
|
59
|
+
if (!files.remove(backup) || !files.move(path, backup)) {
|
|
60
|
+
files.remove(temporary);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (!files.move(temporary, path)) {
|
|
64
|
+
files.move(backup, path);
|
|
65
|
+
files.remove(temporary);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
files.remove(backup);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
} // namespace gea::platform::storage
|
|
@@ -8,11 +8,14 @@ namespace gea::platform::storage {
|
|
|
8
8
|
// targets without persistent storage transparently fall back to no caching.
|
|
9
9
|
bool ensureMounted();
|
|
10
10
|
|
|
11
|
-
// Register the platform's mount provider. A target with persistent storage
|
|
12
|
-
// a microSD card) calls this at startup with its mount function. Done via
|
|
11
|
+
// Register the platform's mount provider. A target with persistent storage
|
|
12
|
+
// (e.g. a microSD card) calls this at startup with its mount function. Done via
|
|
13
13
|
// explicit registration rather than a weak symbol so the provider's translation
|
|
14
14
|
// unit is guaranteed to be linked in (see the waveshare-p4-7 sdcard_mount.cpp,
|
|
15
15
|
// wired from app_main).
|
|
16
16
|
void setMountProvider(bool (*provider)());
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
// Used by built-in flash storage only when no target-specific provider exists.
|
|
19
|
+
void setFallbackMountProvider(bool (*provider)());
|
|
20
|
+
|
|
21
|
+
} // namespace gea::platform::storage
|
package/include/power.h
CHANGED
|
@@ -65,6 +65,12 @@ public:
|
|
|
65
65
|
static int frameIntervalMs();
|
|
66
66
|
static void setFrameRate(double fps);
|
|
67
67
|
static double frameRate();
|
|
68
|
+
#if defined(ESP_PLATFORM) && defined(GEA_NATIVE_DEBUGGER) && GEA_NATIVE_DEBUGGER
|
|
69
|
+
// Debug cap applies to timer, TE and input-driven frames. Application FPS
|
|
70
|
+
// changes cannot exceed it; zero restores normal scheduling.
|
|
71
|
+
static void setDebuggerFrameRate(int fps);
|
|
72
|
+
static int debuggerFrameRate();
|
|
73
|
+
#endif
|
|
68
74
|
static int nowMs();
|
|
69
75
|
};
|
|
70
76
|
|
package/include/wifi.h
CHANGED
|
@@ -7,61 +7,76 @@
|
|
|
7
7
|
namespace gea::framework::network {
|
|
8
8
|
|
|
9
9
|
struct WifiNetwork {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
std::string ssid;
|
|
11
|
+
int rssi = 0;
|
|
12
|
+
bool secured = false;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
class WifiDriver {
|
|
16
16
|
public:
|
|
17
|
-
|
|
17
|
+
virtual ~WifiDriver() = default;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
virtual bool init() = 0;
|
|
20
|
+
virtual bool enabled() const = 0;
|
|
21
|
+
virtual void setEnabled(bool enabled) = 0;
|
|
22
|
+
virtual bool connected() const = 0;
|
|
23
|
+
virtual int rssi() = 0;
|
|
24
|
+
virtual std::string ssid() = 0;
|
|
25
|
+
virtual std::string ip() const = 0;
|
|
26
|
+
virtual std::string mac() = 0;
|
|
27
|
+
virtual void configure(const std::string &ssid,
|
|
28
|
+
const std::string &password) = 0;
|
|
29
|
+
virtual bool startAccessPoint(const std::string &, const std::string &) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
virtual void stopAccessPoint() {}
|
|
33
|
+
virtual std::string accessPointMac() { return {}; }
|
|
34
|
+
virtual bool startCaptivePortal() { return false; }
|
|
35
|
+
virtual void stopCaptivePortal() {}
|
|
36
|
+
virtual void scan() = 0;
|
|
37
|
+
virtual bool scanning() const = 0;
|
|
38
|
+
virtual int scanCount() const = 0;
|
|
39
|
+
virtual WifiNetwork networkAt(int index) const = 0;
|
|
40
|
+
virtual std::vector<WifiNetwork> scanResults() const = 0;
|
|
41
|
+
virtual void setWebState(bool connected, const char *ssid, const char *ip,
|
|
42
|
+
int rssi);
|
|
43
|
+
virtual void setWebScanCount(int count);
|
|
44
|
+
virtual void setWebScanEntry(int index, const char *ssid, int rssi,
|
|
45
|
+
bool secured);
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
class WifiAdapter {
|
|
39
49
|
public:
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
static void setDriver(WifiDriver *driver);
|
|
51
|
+
static WifiDriver *driver();
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
bool init() const;
|
|
54
|
+
bool enabled() const;
|
|
55
|
+
void setEnabled(bool enabled) const;
|
|
56
|
+
bool connected() const;
|
|
57
|
+
int rssi() const;
|
|
58
|
+
std::string ssid() const;
|
|
59
|
+
std::string ip() const;
|
|
60
|
+
std::string mac() const;
|
|
61
|
+
void configure(const std::string &ssid, const std::string &password) const;
|
|
62
|
+
bool startAccessPoint(const std::string &ssid,
|
|
63
|
+
const std::string &password) const;
|
|
64
|
+
void stopAccessPoint() const;
|
|
65
|
+
std::string accessPointMac() const;
|
|
66
|
+
bool startCaptivePortal() const;
|
|
67
|
+
void stopCaptivePortal() const;
|
|
68
|
+
void scan() const;
|
|
69
|
+
bool scanning() const;
|
|
70
|
+
int scanCount() const;
|
|
71
|
+
WifiNetwork networkAt(int index) const;
|
|
72
|
+
std::vector<WifiNetwork> scanResults() const;
|
|
73
|
+
void setWebState(bool connected, const char *ssid, const char *ip,
|
|
74
|
+
int rssi) const;
|
|
75
|
+
void setWebScanCount(int count) const;
|
|
76
|
+
void setWebScanEntry(int index, const char *ssid, int rssi,
|
|
77
|
+
bool secured) const;
|
|
60
78
|
};
|
|
61
79
|
|
|
62
|
-
inline WifiAdapter wifi()
|
|
63
|
-
{
|
|
64
|
-
return WifiAdapter{};
|
|
65
|
-
}
|
|
80
|
+
inline WifiAdapter wifi() { return WifiAdapter{}; }
|
|
66
81
|
|
|
67
|
-
}
|
|
82
|
+
} // namespace gea::framework::network
|