#include #include #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(::millis()); } esp32lua::MemoryInfo Sys::memory() const { esp32lua::MemoryInfo info; info.freeBytes = static_cast(ESP.getFreeHeap()); info.totalBytes = static_cast(ESP.getHeapSize()); info.largestFreeBlock = static_cast( 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& 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(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