@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/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
- inline constexpr int kImageMax = 96;
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
- } // namespace gea::framework::input
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 (e.g.
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
- } // namespace gea::platform::storage
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
@@ -6,6 +6,8 @@ class Power {
6
6
  public:
7
7
  static bool init();
8
8
  static int batteryPercent();
9
+ static bool charging();
10
+ static bool vibrate(double durationMs, int strength);
9
11
  };
10
12
 
11
13
  } // namespace gea::platform::power
@@ -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
- std::string ssid;
11
- int rssi = 0;
12
- bool secured = false;
10
+ std::string ssid;
11
+ int rssi = 0;
12
+ bool secured = false;
13
13
  };
14
14
 
15
15
  class WifiDriver {
16
16
  public:
17
- virtual ~WifiDriver() = default;
17
+ virtual ~WifiDriver() = default;
18
18
 
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, const std::string &password) = 0;
28
- virtual void scan() = 0;
29
- virtual bool scanning() const = 0;
30
- virtual int scanCount() const = 0;
31
- virtual WifiNetwork networkAt(int index) const = 0;
32
- virtual std::vector<WifiNetwork> scanResults() const = 0;
33
- virtual void setWebState(bool connected, const char *ssid, const char *ip, int rssi);
34
- virtual void setWebScanCount(int count);
35
- virtual void setWebScanEntry(int index, const char *ssid, int rssi, bool secured);
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
- static void setDriver(WifiDriver *driver);
41
- static WifiDriver *driver();
50
+ static void setDriver(WifiDriver *driver);
51
+ static WifiDriver *driver();
42
52
 
43
- bool init() const;
44
- bool enabled() const;
45
- void setEnabled(bool enabled) const;
46
- bool connected() const;
47
- int rssi() const;
48
- std::string ssid() const;
49
- std::string ip() const;
50
- std::string mac() const;
51
- void configure(const std::string &ssid, const std::string &password) const;
52
- void scan() const;
53
- bool scanning() const;
54
- int scanCount() const;
55
- WifiNetwork networkAt(int index) const;
56
- std::vector<WifiNetwork> scanResults() const;
57
- void setWebState(bool connected, const char *ssid, const char *ip, int rssi) const;
58
- void setWebScanCount(int count) const;
59
- void setWebScanEntry(int index, const char *ssid, int rssi, bool secured) const;
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
- } // namespace gea::framework::network
82
+ } // namespace gea::framework::network