Skip to content

Commit 799f2b0

Browse files
committed
config, lua: run event listeners in coroutines
This makes waywall.sleep() and any other future asynchronous functions usable within event listeners.
1 parent f2440c8 commit 799f2b0

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

doc/02_waywall_sleep.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ can be executed while the current execution context is paused.
77
Calling this function forbids keybind handlers from marking an input as
88
non-consumed. See [Input consumption] for more details.
99

10-
> [!NOTE]
11-
> Currently, it is only allowed to call this function from within a keybind
12-
> handler. Support for calling this function from an event listener may be added
13-
> at a later date.
14-
1510
### Arguments
1611

1712
- `ms`: number

waywall/config/api.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,66 @@ l_sleep(lua_State *L) {
892892
return lua_yield(L, 0);
893893
}
894894

895+
static int
896+
l_spawn(lua_State *L) {
897+
static constexpr int IDX_FUNC = 1;
898+
899+
// Prologue
900+
struct config_vm *vm = config_vm_from(L);
901+
struct wrap *wrap = config_vm_get_wrap(vm);
902+
if (!wrap) {
903+
return luaL_error(L, STARTUP_ERRMSG("sleep"));
904+
}
905+
906+
if (lua_type(L, IDX_FUNC) != LUA_TFUNCTION) {
907+
return luaL_error(L, "expected a function, got '%s'", luaL_typename(L, IDX_FUNC));
908+
}
909+
910+
size_t argc = lua_gettop(L) - IDX_FUNC;
911+
912+
lua_State *coro = lua_newthread(vm->L); // stack: argc + 2
913+
config_vm_coro_add(coro, nullptr);
914+
915+
// The function and its arguments need to be moved to the coroutine's stack, but we need to keep
916+
// a reference to the newly created coroutine on the stack of the caller so that the coroutine
917+
// is not immediately garbage collected in the event of an error.
918+
lua_pop(L, 1); // stack: argc + 1
919+
lua_xmove(L, coro, argc + 1); // stack: 0 (coroutine: argc + 1)
920+
lua_pushthread(coro); // stack: 0 (coroutine: argc + 2)
921+
lua_xmove(coro, L, 1); // stack: 1 (coroutine: argc + 1)
922+
923+
int ret = lua_resume(coro, argc);
924+
925+
switch (ret) {
926+
case LUA_YIELD:
927+
// There needs to be a waker associated with the coroutine or else it will never be
928+
// resumed.
929+
if (config_vm_coro_get(coro)) {
930+
return 0;
931+
}
932+
933+
config_vm_coro_del(coro);
934+
return luaL_error(L, "invalid yield from coroutine");
935+
case 0:
936+
// The coroutine finished immediately without yielding, so it can be deleted from the
937+
// coroutine table.
938+
config_vm_coro_del(coro);
939+
break;
940+
default:
941+
// An error occurred. The spawned coroutine is still on the stack of the coroutine which
942+
// called spawn so the error message will not get garbage collected.
943+
944+
config_vm_coro_del(coro);
945+
if (lua_type(coro, -1) == LUA_TSTRING) {
946+
return luaL_error(L, lua_tostring(coro, -1));
947+
} else {
948+
return luaL_error(L, "unknown (%d): %s", ret, lua_tostring(coro, -1));
949+
}
950+
}
951+
952+
return 0;
953+
}
954+
895955
static int
896956
l_state(lua_State *L) {
897957
static constexpr int IDX_STATE = 1;
@@ -1215,6 +1275,7 @@ static const struct luaL_Reg lua_lib[] = {
12151275
{"log_error", l_log_error},
12161276
{"register", l_register},
12171277
{"setenv", l_setenv},
1278+
{"spawn", l_spawn},
12181279
{nullptr, nullptr},
12191280
};
12201281

waywall/lua/api.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ local function event_handler(name)
1111

1212
priv.register(name, function()
1313
for listener, _ in pairs(listeners) do
14-
local ok, result = pcall(listener)
14+
local ok, result = pcall(priv.spawn, listener)
1515
if not ok then
16-
priv.log_error("failed to call event listener (" .. name .. "): " .. result)
16+
priv.log_error(
17+
"failed to create event listener coroutine (" .. name .. "): " .. result
18+
)
1719
end
1820
end
1921
end)

0 commit comments

Comments
 (0)