Files
slate32/src/settings.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

129 lines
3.5 KiB
C++

#include "settings.h"
#include <SD.h>
extern "C" {
#include <lauxlib.h>
#include <lua.h>
}
Settings settings;
static constexpr const char* PATH = "/settings.lua";
static constexpr const char* TEMP_PATH = "/settings.tmp";
static constexpr const char* BACKUP_PATH = "/settings.bak";
static int16_t fieldOr(lua_State* L, const char* key, int16_t fallback) {
lua_getfield(L, -1, key);
int16_t value = lua_isinteger(L, -1) ? lua_tointeger(L, -1) : fallback;
lua_pop(L, 1);
return value;
}
static String stringFieldOr(lua_State* L, const char* key,
const String& fallback) {
lua_getfield(L, -1, key);
String value = lua_isstring(L, -1) ? lua_tostring(L, -1) : fallback;
lua_pop(L, 1);
return value;
}
static String luaString(const String& value) {
String out = "\"";
for (size_t i = 0; i < value.length(); i++) {
unsigned char c = value[i];
if (c == '\\' || c == '\"') {
out += '\\';
out += (char)c;
} else if (c >= 32 && c < 127) {
out += (char)c;
} else {
char escaped[5];
snprintf(escaped, sizeof(escaped), "\\%03u", c);
out += escaped;
}
}
out += '\"';
return out;
}
bool Settings::setRotation(int16_t degrees) {
if (degrees % 90 != 0 || degrees < 0 || degrees > 270)
return false;
rotation = degrees;
return true;
}
bool Settings::load() {
File f = SD.open(PATH);
if (!f)
return false;
String source = f.readString();
f.close();
lua_State* L = luaL_newstate();
if (!L)
return false;
bool ok =
luaL_loadbuffer(L, source.c_str(), source.length(), PATH) == LUA_OK &&
lua_pcall(L, 0, 1, 0) == LUA_OK && lua_istable(L, -1);
if (ok) {
rotation = fieldOr(L, "rotation", rotation);
timezone = stringFieldOr(L, "timezone", timezone);
theme = stringFieldOr(L, "theme", theme);
lua_getfield(L, -1, "touch");
if (lua_istable(L, -1)) {
touchX0 = fieldOr(L, "x0", touchX0);
touchY0 = fieldOr(L, "y0", touchY0);
touchX1 = fieldOr(L, "x1", touchX1);
touchY1 = fieldOr(L, "y1", touchY1);
}
lua_pop(L, 1);
lua_getfield(L, -1, "wifi");
if (lua_istable(L, -1)) {
wifiSsid = stringFieldOr(L, "ssid", wifiSsid);
wifiPassword = stringFieldOr(L, "password", wifiPassword);
}
lua_pop(L, 1);
}
lua_close(L);
return ok;
}
bool Settings::save() const {
String source = "return {\n rotation = " + String(rotation) + ",\n";
source += " timezone = " + luaString(timezone) + ",\n";
source += " theme = " + luaString(theme) + ",\n";
source += " touch = { x0 = " + String(touchX0) +
", y0 = " + String(touchY0) + ", x1 = " + String(touchX1) +
", y1 = " + String(touchY1) + " },\n";
source += " wifi = { ssid = " + luaString(wifiSsid) +
", password = " + luaString(wifiPassword) + " },\n}\n";
if (SD.exists(TEMP_PATH))
SD.remove(TEMP_PATH);
File f = SD.open(TEMP_PATH, FILE_WRITE);
if (!f)
return false;
bool ok = f.write(reinterpret_cast<const uint8_t*>(source.c_str()),
source.length()) == source.length();
f.close();
if (!ok) {
SD.remove(TEMP_PATH);
return false;
}
if (SD.exists(BACKUP_PATH))
SD.remove(BACKUP_PATH);
bool hadSettings = SD.exists(PATH);
if (hadSettings && !SD.rename(PATH, BACKUP_PATH))
return false;
if (SD.rename(TEMP_PATH, PATH)) {
if (hadSettings)
SD.remove(BACKUP_PATH);
return true;
}
if (hadSettings)
SD.rename(BACKUP_PATH, PATH);
return false;
}