diff --git a/include/luak/kiwmi_lua_callback.h b/include/luak/kiwmi_lua_callback.h index f829c69..3a13169 100644 --- a/include/luak/kiwmi_lua_callback.h +++ b/include/luak/kiwmi_lua_callback.h @@ -16,7 +16,10 @@ struct kiwmi_lua_callback { struct wl_list link; struct kiwmi_server *server; int callback_ref; - struct wl_listener listener; + union { + struct wl_event_source *event_source; + struct wl_listener listener; + }; }; int luaK_kiwmi_lua_callback_new(lua_State *L); diff --git a/include/luak/luak.h b/include/luak/luak.h index 8c61944..a35ad55 100644 --- a/include/luak/luak.h +++ b/include/luak/luak.h @@ -11,12 +11,14 @@ #include #include +#include #include "server.h" struct kiwmi_lua { lua_State *L; int objects; + struct wl_list scheduled_callbacks; struct wl_global *global; }; diff --git a/kiwmi/luak/kiwmi_server.c b/kiwmi/luak/kiwmi_server.c index 559adb6..880d792 100644 --- a/kiwmi/luak/kiwmi_server.c +++ b/kiwmi/luak/kiwmi_server.c @@ -82,6 +82,60 @@ l_kiwmi_server_quit(lua_State *L) return 0; } +static int +kiwmi_server_schedule_handler(void *data) +{ + struct kiwmi_lua_callback *lc = data; + lua_State *L = lc->server->lua->L; + + lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref); + lua_pushvalue(L, -1); + if (lua_pcall(L, 1, 0, 0)) { + wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1)); + } + + wl_event_source_remove(lc->event_source); + + luaL_unref(L, LUA_REGISTRYINDEX, lc->callback_ref); + + wl_list_remove(&lc->link); + free(lc); + + return 0; +} + +static int +l_kiwmi_server_schedule(lua_State *L) +{ + struct kiwmi_object *obj = + *(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server"); + luaL_checktype(L, 2, LUA_TNUMBER); // delay + luaL_checktype(L, 3, LUA_TFUNCTION); // callback + + struct kiwmi_server *server = obj->object; + + struct kiwmi_lua_callback *lc = malloc(sizeof(*lc)); + if (!lc) { + return luaL_error(L, "failed to allocate kiwmi_lua_callback"); + } + + int delay = lua_tonumber(L, 2); + + lc->event_source = wl_event_loop_add_timer(server->wl_event_loop, kiwmi_server_schedule_handler, lc); + + if (wl_event_source_timer_update(lc->event_source, delay) < 0) { + free(lc); + return luaL_error(L, "failed to arm timer"); + } + + lc->server = server; + lc->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + wl_list_insert(&server->lua->scheduled_callbacks, &lc->link); + + return 0; +} + static int l_kiwmi_server_spawn(lua_State *L) { @@ -159,6 +213,7 @@ static const luaL_Reg kiwmi_server_methods[] = { {"focused_view", l_kiwmi_server_focused_view}, {"on", luaK_callback_register_dispatch}, {"quit", l_kiwmi_server_quit}, + {"schedule", l_kiwmi_server_schedule}, {"spawn", l_kiwmi_server_spawn}, {"stop_interactive", l_kiwmi_server_stop_interactive}, {"view_at", l_kiwmi_server_view_at}, diff --git a/kiwmi/luak/luak.c b/kiwmi/luak/luak.c index 06127f8..8d7748d 100644 --- a/kiwmi/luak/luak.c +++ b/kiwmi/luak/luak.c @@ -179,6 +179,8 @@ luaK_create(struct kiwmi_server *server) luaL_openlibs(L); + wl_list_init(&lua->scheduled_callbacks); + // init object registry lua_newtable(L); lua->objects = luaL_ref(L, LUA_REGISTRYINDEX); @@ -248,5 +250,13 @@ luaK_destroy(struct kiwmi_lua *lua) { lua_close(lua->L); + struct kiwmi_lua_callback *lc; + struct kiwmi_lua_callback *tmp; + wl_list_for_each_safe (lc, tmp, &lua->scheduled_callbacks, link) { + wl_event_source_remove(lc->event_source); + wl_list_remove(&lc->link); + free(lc); + } + free(lua); } diff --git a/lua_docs.md b/lua_docs.md index 9d72425..a587deb 100644 --- a/lua_docs.md +++ b/lua_docs.md @@ -29,6 +29,11 @@ Used to register event listeners. Quit kiwmi. +#### kiwmi:schedule(delay, callback) + +Call `callback` after `delay` ms. +Callback get passed itself, so that it can easily reregister itself. + #### kiwmi:spawn(command) Spawn a new process.