Skip to content

Commit

Permalink
lua: add program interruption via setjmp/longjmp
Browse files Browse the repository at this point in the history
docs: add docs about util.exit()
  • Loading branch information
and3rson committed Feb 24, 2024
1 parent 2c488e5 commit 2734460
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 39 deletions.
20 changes: 20 additions & 0 deletions docs/lua/util.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ delay(ms)
display.print("Зачекайте півсекунди...")
util.delay(0.5) -- Затримує виконання програми на півсекунди.
display.print("Готово!")
exit()
^^^^^^

.. lua:function:: exit()
Завершує виконання програми.

Приклад використання:

.. code-block:: lua
:linenos:
function _update()
state = controller.get_state()
if state.a.pressed then
util.exit() -- Завершує виконання програми.
end
print('Staying alive!')
end
2 changes: 1 addition & 1 deletion docs/manual/lua.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

.. warning::

Якщо ви визначите ``_update()`` у своїй програмі, то для завершення програми вам потрібно викликати ``os.exit()``. Інакше програма буде виконуватися постійно, поки ви не вимкнете Лілку.
Якщо ви визначите ``_update()`` у своїй програмі, то для завершення програми вам потрібно викликати ``util.exit()``. Інакше програма буде виконуватися постійно, поки ви не вимкнете Лілку.

Крім того, не варто використовувати :lua:func:`util.delay` у програмах, які використовують ``_update()``, оскільки це призведе до заповільнення виконання програми.

Expand Down
Binary file added firmware/main/sdcard/ball.bmp
Binary file not shown.
11 changes: 6 additions & 5 deletions firmware/main/sdcard/movement.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ function _update()
local ball_speed_y = 0

-- Обробляємо введення користувача:
if controller.up.pressed then
state = controller.get_state()
if state.up.pressed then
ball_speed_y = -4
elseif controller.down.pressed then
elseif state.down.pressed then
ball_speed_y = 4
end
if controller.left.pressed then
if state.left.pressed then
ball_speed_x = -4
elseif controller.right.pressed then
elseif state.right.pressed then
ball_speed_x = 4
end
if controller.a.pressed then
if state.a.pressed then
-- Вихід з програми:
util.exit()
end
Expand Down
2 changes: 2 additions & 0 deletions firmware/main/sdcard/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let str = JSON.parse('{"foo": "bar", "baz": {"qux": "quux"}}');
print(str.foo, str.baz);
2 changes: 1 addition & 1 deletion firmware/main/sdcard/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ for i = 10, 1, -1 do

display.render()

util.delay(250)
util.delay(0.25)
end

-- Now, we want to draw random lines & faces really fast directly to display, without any buffering!
Expand Down
10 changes: 10 additions & 0 deletions sdk/lib/lilka/src/lua/lualilka_util.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
#include <csetjmp>

#include "lualilka_util.h"

namespace lilka {

extern jmp_buf stopjmp;

int lualilka_util_delay(lua_State* L) {
float s = luaL_checknumber(L, 1);
delayMicroseconds(s * 1000000);
return 0;
}

int lualilka_util_exit(lua_State* L) {
longjmp(stopjmp, 32); // TODO: 32 marks an "exit" condition
return 0;
}

static const luaL_Reg lualilka_util[] = {
{"delay", lualilka_util_delay},
{"exit", lualilka_util_exit},
{NULL, NULL},
};

Expand Down
85 changes: 53 additions & 32 deletions sdk/lib/lilka/src/lua/luarunner.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <csetjmp>

#include <lua.hpp>
#include "clib/u8g2.h"
#include <lilka.h>
Expand All @@ -11,6 +13,8 @@

namespace lilka {

jmp_buf stopjmp;

int lua_run(String path) {
lilka::serial_log("lua: init libs");

Expand Down Expand Up @@ -70,43 +74,60 @@ int lua_run(String path) {
lua_setfield(L, LUA_REGISTRYINDEX, "bitmaps");

lilka::serial_log("lua: run script");
int retCode = luaL_dofile(L, path.c_str());
if (retCode) {
const char* err = lua_tostring(L, -1);
lilka::ui_alert("Lua", String("Помилка: ") + err);
}

// Check if _update function exists and call it
const uint32_t perfectDelta = 1000 / 30;
uint32_t delta = perfectDelta; // Delta for first frame is always 1/30
while (true) {
uint32_t now = millis();
lua_getglobal(L, "_update");
if (lua_isfunction(L, -1)) {
// Call _update function, passing delta as argument
lua_pushnumber(L, delta);
retCode = lua_pcall(L, 1, 0, 0);
if (retCode) {
const char* err = lua_tostring(L, -1);
lilka::ui_alert("Lua", String("Помилка в _update: ") + err);
break;
}
} else {
// No _update function - we're done
break;
int jmpCode = setjmp(stopjmp);

if (jmpCode == 0) {
// Run script
int retCode = luaL_dofile(L, path.c_str());
if (retCode) {
longjmp(stopjmp, retCode);
}
// Calculate time spent in _update
uint32_t elapsed = millis() - now;
// If we're too fast, delay to keep 30 FPS
if (elapsed < perfectDelta) {
delay(perfectDelta - elapsed);
delta = perfectDelta;
} else {
// If we're too slow, don't delay and set delta to elapsed time
delta = elapsed;

// Check if _update function exists and call it
const uint32_t perfectDelta = 1000 / 30;
uint32_t delta = perfectDelta; // Delta for first frame is always 1/30
while (true) {
uint32_t now = millis();
lua_getglobal(L, "_update");
if (lua_isfunction(L, -1)) {
// Call _update function, passing delta as argument
lua_pushnumber(L, delta);
retCode = lua_pcall(L, 1, 0, 0);
if (retCode) {
// const char* err = lua_tostring(L, -1);
// lilka::ui_alert("Lua", String("Помилка в _update: ") + err);
longjmp(stopjmp, retCode);
}
} else {
// No _update function - we're done
longjmp(stopjmp, 32);
}

// Calculate time spent in _update
uint32_t elapsed = millis() - now;
// If we're too fast, delay to keep 30 FPS
if (elapsed < perfectDelta) {
delay(perfectDelta - elapsed);
delta = perfectDelta;
} else {
// If we're too slow, don't delay and set delta to elapsed time
delta = elapsed;
}
}
}

int retCode = jmpCode;
if (retCode == 32) {
// Normal exit through longjmp
retCode = 0;
}

if (retCode) {
const char* err = lua_tostring(L, -1);
lilka::ui_alert("Lua", String("Помилка: ") + err);
}

lilka::serial_log("lua: cleanup");

// Free bitmaps from registry
Expand Down

0 comments on commit 2734460

Please sign in to comment.