b3b5a9b2a3
Keeps one writer for saved state: ui.setTheme() writes through settings.setTheme() and then reloads the palette C cannot see.
144 lines
4.4 KiB
C++
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
|