Files
slate32/src/host/providers_sys.cpp
T
evan b3b5a9b2a3 feat(settings): persist the theme through the settings binding
Keeps one writer for saved state: ui.setTheme() writes through
settings.setTheme() and then reloads the palette C cannot see.
2026-08-04 20:44:13 -04:00

144 lines
4.4 KiB
C++

#include <Arduino.h>
#include <esp_heap_caps.h>
#include "../net.h"
#include "../settings.h"
#include "lua_host.h"
#include "providers.h"
namespace slate {
using esp32lua::Status;
void Log::write(esp32lua::LogLevel level, const std::string& message) {
const char* tag =
level == esp32lua::LogLevel::Error
? "error"
: (level == esp32lua::LogLevel::Info ? "info" : "debug");
Serial.printf("[lua] %s: %s\n", tag, message.c_str());
}
int32_t Settings::rotation() const { return settings.rotation; }
// Applies the rotation itself, because a setter that needs a follow-up call is
// a bug in the setter: the frame, the viewport and the bar all move together or
// not at all.
Status Settings::setRotation(int32_t degrees) {
if (!settings.setRotation(degrees))
return Status::failure("rotation must be 0, 90, 180 or 270");
host.applyRotation();
return settings.save() ? Status::success()
: Status::failure("cannot save settings");
}
std::string Settings::timezone() const { return settings.timezone.c_str(); }
Status Settings::setTimezone(const std::string& timezone) {
if (timezone.empty() || timezone.size() > 48)
return Status::failure("timezone must be 1 to 48 characters");
settings.timezone = timezone.c_str();
net::applyTimezone();
return settings.save() ? Status::success()
: Status::failure("cannot save settings");
}
std::string Settings::theme() const { return settings.theme.c_str(); }
// Stores the name only. ui.setTheme() owns applying it, because the palette and
// the repaint it drives exist solely in Lua.
Status Settings::setTheme(const std::string& theme) {
if (theme.empty() || theme.size() > 16)
return Status::failure("theme must be 1 to 16 characters");
settings.theme = theme.c_str();
return settings.save() ? Status::success()
: Status::failure("cannot save settings");
}
int32_t Sys::millis() const { return static_cast<int32_t>(::millis()); }
esp32lua::MemoryInfo Sys::memory() const {
esp32lua::MemoryInfo info;
info.freeBytes = static_cast<int32_t>(ESP.getFreeHeap());
info.totalBytes = static_cast<int32_t>(ESP.getHeapSize());
info.largestFreeBlock = static_cast<int32_t>(
heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT));
return info;
}
bool Sys::isClockSynced() const { return net::clockSynced(); }
bool Touch::touch(int32_t& x, int32_t& y) const {
if (!panel.touched())
return false;
int16_t mappedX = 0, mappedY = 0;
host.mapTouch(panel.getPoint(), mappedX, mappedY);
x = mappedX;
y = mappedY;
return true;
}
bool Touch::rawTouch(int32_t& x, int32_t& y) const {
if (!panel.touched())
return false;
const TS_Point point = panel.getPoint();
// The controller's axes are swapped relative to the panel, which calibration
// undoes.
x = point.y;
y = point.x;
return true;
}
bool Touch::isTouched() const { return panel.touched(); }
Status Touch::setCalibration(int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
settings.touchX0 = x0;
settings.touchY0 = y0;
settings.touchX1 = x1;
settings.touchY1 = y1;
return settings.save() ? Status::success()
: Status::failure("cannot save settings");
}
// A fixed slot table rather than a heap list: the whole point of the arena
// elsewhere is that a Lua app cannot fragment the heap, and timers are created
// from Lua.
Status Timer::schedule(esp32lua::TimerId id, int32_t intervalMs,
bool repeating) {
for (int at = 0; at < SLOTS; at++) {
if (slots[at].id != 0)
continue;
slots[at].id = id;
slots[at].dueMs = ::millis() + intervalMs;
slots[at].intervalMs = intervalMs;
slots[at].repeating = repeating;
return Status::success();
}
return Status::failure("no timer slot available");
}
void Timer::cancel(esp32lua::TimerId id) {
for (int at = 0; at < SLOTS; at++) {
if (slots[at].id == id)
slots[at] = Slot();
}
}
void Timer::collectDue(uint32_t nowMs, std::vector<esp32lua::TimerId>& due) {
for (int at = 0; at < SLOTS; at++) {
if (slots[at].id == 0)
continue;
// Signed comparison, so a deadline still pending across the millis() wrap
// is not "due".
if (static_cast<int32_t>(nowMs - slots[at].dueMs) < 0)
continue;
due.push_back(slots[at].id);
if (slots[at].repeating) {
slots[at].dueMs = nowMs + slots[at].intervalMs;
} else {
slots[at] = Slot();
}
}
}
} // namespace slate