Files
slate32/src/lua/bindings/sys.cpp
T
evan 6f2d618b8d feat: http bindings matching crosspoint-reader, plus generated Lua stubs
The http table copies crosspoint-reader's signatures exactly -- get/head/delete/post/
patch returning (body|nil, status), download taking maxBytes/expectedSize/sha256, the
same 50000 byte body cap and the same -1 for a request that never left the device --
so a script that talks to a server runs on either firmware. docs/lua-api-parity.md
records that, and every other place the two APIs agree, differ for a reason, or differ
because nobody noticed.

Two crosspoint behaviours are deliberately not copied. It reinterprets a string in
argument 2 of a GET as a request body, which turns a mistyped headers table into a
silent protocol error. More seriously it calls setInsecure() on every request, so TLS
is encrypted but unauthenticated on the very path a firmware update would use; this
verifies against the root bundle already sitting in the framework, and the emulator
confirms expired.badssl.com is refused while a wrong sha256 deletes the file.

Downloading exposed two failures worth naming. A 2KB read buffer on the stack tripped
the loop task's canary because a TLS handshake had already spent it, and the
hand-rolled read loop spun forever on a stream that stopped producing -- HTTPClient's
own writeToStream handles both, so the loop is gone and the loop task gets 16KB.

scripts/gen_lua_stubs.py generates stubs/esp32lcd.lua in the same LuaLS format
crosspoint uses, reading annotations off the luaL_Reg tables so a module's docs sit
with its registration. make test runs --check, which crosspoint's copy never wired up.
2026-08-01 17:33:47 -04:00

173 lines
5.5 KiB
C++

// Process control and persisted settings, plus the one-function `log` table.
#include "../../net.h"
#include "../../settings.h"
#include "../bindings.h"
#include "../lua_app.h"
static int l_sys_millis(lua_State* L) {
lua_pushinteger(L, millis());
return 1;
}
static int l_sys_exit(lua_State* L) {
app(L)->requestExit();
return 0;
}
static int l_sys_launch(lua_State* L) {
app(L)->requestLaunch(luaL_checkstring(L, 1));
app(L)->requestExit();
return 0;
}
static int l_sys_getRotation(lua_State* L) {
lua_pushinteger(L, settings.rotation);
return 1;
}
// Persisted, unlike gui.setRotation() which only changes the current frame.
static int l_sys_setRotation(lua_State* L) {
if (!settings.setRotation(luaL_checkinteger(L, 1))) {
lua_pushboolean(L, false);
return 1;
}
app(L)->tft.setRotation(settings.rotationIndex());
lua_pushboolean(L, settings.save());
return 1;
}
static int l_sys_getTheme(lua_State* L) {
lua_pushstring(L, settings.theme.c_str());
return 1;
}
// The firmware only stores the name; /lib/theme.lua decides what it looks like.
static int l_sys_setTheme(lua_State* L) {
size_t length;
const char* name = luaL_checklstring(L, 1, &length);
if (!length || length > 32) {
lua_pushboolean(L, false);
return 1;
}
settings.theme = String(name, length);
lua_pushboolean(L, settings.save());
return 1;
}
static int l_sys_getTimezone(lua_State* L) {
lua_pushstring(L, settings.timezone.c_str());
return 1;
}
// Takes a POSIX TZ rule, not a zone name: /lib/timezones.lua is only a picker, so a
// zone missing from that list is still reachable by writing the rule.
static int l_sys_setTimezone(lua_State* L) {
size_t length;
const char* tz = luaL_checklstring(L, 1, &length);
if (!length || length > 48) {
lua_pushboolean(L, false);
return 1;
}
settings.timezone = String(tz, length);
net::applyTimezone();
lua_pushboolean(L, settings.save());
return 1;
}
static int l_sys_setCalibration(lua_State* L) {
settings.touchX0 = luaL_checkinteger(L, 1);
settings.touchY0 = luaL_checkinteger(L, 2);
settings.touchX1 = luaL_checkinteger(L, 3);
settings.touchY1 = luaL_checkinteger(L, 4);
lua_pushboolean(L, settings.save());
return 1;
}
// Lua's os.time()/os.date() work off the same system clock, so this is the only
// binding a clock UI needs: it says whether that clock means anything yet.
static int l_sys_clockSynced(lua_State* L) {
lua_pushboolean(L, net::clockSynced());
return 1;
}
static int logAt(lua_State* L, const char* level) {
Serial.printf("[lua:%s] %s\n", level, luaL_checkstring(L, 1));
return 0;
}
static int l_log_debug(lua_State* L) { return logAt(L, "debug"); }
static int l_log_info(lua_State* L) { return logAt(L, "info"); }
static int l_log_error(lua_State* L) { return logAt(L, "error"); }
// Parity with crosspoint: sleeping is the one thing a script cannot express itself,
// since the runtime owns the loop.
static int l_sys_delay(lua_State* L) {
delay(luaL_checkinteger(L, 1));
return 0;
}
void registerSys(lua_State* L) {
static const luaL_Reg lib[] = {
// --- Milliseconds since boot.
// @return integer
{"millis", l_sys_millis},
// --- Blocks for the given time.
// @param ms integer
{"delay", l_sys_delay},
// --- Ends this app and returns to the launcher.
{"exit", l_sys_exit},
// --- Ends this app and starts another one.
// @param path string Absolute path to the app's main.lua.
{"launch", l_sys_launch},
// --- Saved screen rotation in degrees clockwise.
// @return integer
{"getRotation", l_sys_getRotation},
// --- Rotates the screen and saves it.
// @param degrees integer 0, 90, 180 or 270.
// @return boolean Whether the setting was saved.
{"setRotation", l_sys_setRotation},
// --- Name of the active theme in /lib/theme.lua.
// @return string
{"getTheme", l_sys_getTheme},
// --- Selects a theme by name and saves it.
// @param name string
// @return boolean Whether the setting was saved.
{"setTheme", l_sys_setTheme},
// --- Stores touch calibration, in the panel's unrotated frame.
// @param x0 integer Raw reading at the left edge.
// @param y0 integer Raw reading at the top edge.
// @param x1 integer Raw reading at the right edge.
// @param y1 integer Raw reading at the bottom edge.
// @return boolean Whether the setting was saved.
{"setCalibration", l_sys_setCalibration},
// --- Whether SNTP has answered. Until it has, os.time() is only a build-time floor.
// @return boolean
{"clockSynced", l_sys_clockSynced},
// --- Active POSIX timezone rule.
// @return string
{"getTimezone", l_sys_getTimezone},
// --- Sets the timezone from a POSIX TZ rule and saves it.
// @param tz string For example EST5EDT,M3.2.0,M11.1.0.
// @return boolean Whether the setting was saved.
{"setTimezone", l_sys_setTimezone},
{nullptr, nullptr}};
luaL_newlib(L, lib);
lua_setglobal(L, "sys");
// Too small to deserve its own translation unit.
static const luaL_Reg logLib[] = {
// --- Writes a debug line to the serial log.
// @param message string
{"debug", l_log_debug},
// --- Writes an info line to the serial log.
// @param message string
{"info", l_log_info},
// --- Writes an error line to the serial log.
// @param message string
{"error", l_log_error},
{nullptr, nullptr}};
luaL_newlib(L, logLib);
lua_setglobal(L, "log");
}