From 6b9dbc2a2507d77356fc53e6d56eca5408a60997 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 27 Oct 2025 15:39:18 +1000 Subject: [PATCH] lua: remove the inject_frame API Injecting frame was the first implementation of adding event frames but it has since effectively been replaced by append/prepend_frame which are more predictable and easier to support. In the Lua API injecting frames was only possible within the timer and the only real use-case for this is to inject events that are then also seen by other plugins. But that can be achieved by simply ordering the plugin before the other plugins and using the append/prepend approach. Until we have a real use-case for injecting events let's remove the API so we don't lock ourselves into an API that may not do what it needs to but needs to be supported for a long time. Closes: #1210 Part-of: --- doc/user/lua-plugins.rst | 18 ----- src/libinput-plugin-lua.c | 60 -------------- test/test-plugins-lua.c | 162 -------------------------------------- 3 files changed, 240 deletions(-) diff --git a/doc/user/lua-plugins.rst b/doc/user/lua-plugins.rst index dcd254a4..3e5b84c3 100644 --- a/doc/user/lua-plugins.rst +++ b/doc/user/lua-plugins.rst @@ -604,24 +604,6 @@ methods will be noops. Disconnect the existing callback (if any) for the given event name. See ``EvdevDevice:connect()`` for a list of supported names. -.. function:: EvdevDevice:inject_frame(frame) - - .. warning:: This function is only available from inside a timer callback. - - Inject an :ref:`evdev frame ` into the event stream - for this device. This emulates that same event frame being sent by the kernel - immediately with the current time. - - Assuming three plugins P1, P2 and P3, if P2 injects a frame the frame is - seen by P1, P2 and P3. - - This is rarely the right API to use. Injecting frames at the lowest level - may make other plugins behave unexpectedly. Use ``prepend_frame`` or - ``append_frame`` instead. - - .. warning:: The injected frame will be seen by all plugins, including the - injecting frame. Ensure a guard is in place to prevent recursion. - .. function:: EvdevDevice:prepend_frame(frame) Prepend an :ref:`evdev frame ` for this device diff --git a/src/libinput-plugin-lua.c b/src/libinput-plugin-lua.c index 4ed8dc55..20ddc22e 100644 --- a/src/libinput-plugin-lua.c +++ b/src/libinput-plugin-lua.c @@ -116,7 +116,6 @@ struct libinput_lua_plugin { struct libinput_plugin_timer *timer; bool in_timer_func; - struct list timer_injected_events; }; static struct libinput_lua_plugin * @@ -566,12 +565,6 @@ libinputplugin_gc(lua_State *L) return 0; } -struct timer_injected_event { - struct list link; - struct evdev_frame *frame; - int device_refid; -}; - static void plugin_timer_func(struct libinput_plugin *libinput_plugin, uint64_t now, void *data) { @@ -581,28 +574,7 @@ plugin_timer_func(struct libinput_plugin *libinput_plugin, uint64_t now, void *d lua_rawgeti(L, LUA_REGISTRYINDEX, plugin->timer_expired_refid); lua_pushinteger(L, now); - /* To allow for injecting events */ - plugin->in_timer_func = true; libinput_lua_pcall(plugin, 1, 0); - plugin->in_timer_func = false; - - struct timer_injected_event *injected_event; - list_for_each_safe(injected_event, &plugin->timer_injected_events, link) { - EvdevDevice *device; - list_for_each(device, &plugin->evdev_devices, link) { - if (device->refid == injected_event->device_refid) { - libinput_plugin_inject_evdev_frame( - plugin->parent, - device->device, - injected_event->frame); - break; - } - } - - list_remove(&injected_event->link); - evdev_frame_unref(injected_event->frame); - free(injected_event); - } } static int @@ -951,36 +923,6 @@ evdevdevice_frame(lua_State *L, struct libinput_lua_plugin *plugin, EvdevDevice return frame; } -static int -evdevdevice_inject_frame(lua_State *L) -{ - EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE); - luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected"); - - luaL_checktype(L, 2, LUA_TTABLE); - - /* No refid means we got removed, so quietly - * drop any disconnect call */ - if (device->refid == LUA_NOREF) - return 0; - - struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L); - if (!plugin->in_timer_func) { - return luaL_error(L, "Injecting events only possible in a timer func"); - } - _unref_(evdev_frame) *frame = evdevdevice_frame(L, plugin, device); - - /* Lua is unhappy if we inject an event which calls into our lua state - * immediately so we need to queue this for later when we're out of the timer - * func */ - struct timer_injected_event *event = zalloc(sizeof(*event)); - event->device_refid = device->refid; - event->frame = steal(&frame); - list_insert(&plugin->timer_injected_events, &event->link); - - return 0; -} - static int evdevdevice_prepend_frame(lua_State *L) { @@ -1089,7 +1031,6 @@ static const struct luaL_Reg evdevdevice_vtable[] = { { "set_absinfo", evdevdevice_set_absinfo }, { "connect", evdevdevice_connect }, { "disconnect", evdevdevice_disconnect }, - { "inject_frame", evdevdevice_inject_frame }, { "prepend_frame", evdevdevice_prepend_frame }, { "append_frame", evdevdevice_append_frame }, { "disable_feature", evdevdevice_disable_feature }, @@ -1355,7 +1296,6 @@ libinput_lua_plugin_new_from_path(struct libinput *libinput, const char *path) plugin->device_new_refid = LUA_NOREF; plugin->timer_expired_refid = LUA_NOREF; list_init(&plugin->evdev_devices); - list_init(&plugin->timer_injected_events); _cleanup_(lua_closep) lua_State *L = libinput_lua_plugin_init_lua(libinput, plugin); diff --git a/test/test-plugins-lua.c b/test/test-plugins-lua.c index 9b771cc8..32dbe4b1 100644 --- a/test/test-plugins-lua.c +++ b/test/test-plugins-lua.c @@ -929,164 +929,6 @@ START_TEST(lua_ignore_unsupported_codes) } END_TEST -START_TEST(lua_inject_frame) -{ - bool in_timer = litest_test_param_get_bool(test_env->params, "in_timer"); - _destroy_(tmpdir) *tmpdir = tmpdir_create(NULL); - - /* Plugin 1 swaps left to right */ - const char *lua1 = - "libinput:register({1})\n" - "function frame_handler(device, frame, timestamp)\n" - " for _, e in ipairs(frame) do\n" - " if e.usage == evdev.BTN_LEFT then\n" - " e.usage = evdev.BTN_RIGHT\n" - " end\n" - " end\n" - " return frame\n" - "end\n" - "libinput:connect(\"new-evdev-device\", function(device)\n" - " device:connect(\"evdev-frame\", frame_handler)\n" - "end)\n"; - - /* Plugin 2 injects a left button if a middle button is pressed */ - _autofree_ char *lua2 = strdup_printf( - "libinput:register({1})\n" - "mydev = nil\n" - "function frame_handler(device, frame, timestamp)\n" - " for _, e in ipairs(frame) do\n" - " if e.usage == evdev.BTN_SIDE then\n" - " e.usage = evdev.BTN_EXTRA\n" - " end\n" - " if e.usage == evdev.BTN_MIDDLE then\n" - " log.debug(\"Injecting frame BTN_LEFT value \" .. e.value)\n" - " %sdevice:inject_frame({{ usage = evdev.BTN_LEFT, value = e.value }})\n" - " %slibinput:timer_set_relative(200000)\n" /* commented out - if !in_timer */ - " end\n" - " end\n" - " return frame\n" - "end\n" - "function timer_expired(t)\n" - " log.debug(\"Injecting timer BTN_LEFT\")\n" - " mydev:inject_frame({{ usage = evdev.BTN_LEFT, value = 1 }})\n" - "end\n" - "libinput:connect(\"new-evdev-device\", function(device)\n" - " mydev = device\n" - " device:connect(\"evdev-frame\", frame_handler)\n" - "end)\n" - "libinput:connect(\"timer-expired\", timer_expired)\n", - in_timer ? "-- " : "", - in_timer ? "" : "-- "); - - _autofree_ char *p1 = litest_write_plugin(tmpdir->path, lua1); - _autofree_ char *p2 = litest_write_plugin(tmpdir->path, lua2); - _litest_context_destroy_ struct libinput *li = - litest_create_context_with_plugindir(tmpdir->path); - libinput_plugin_system_load_plugins(li, LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE); - litest_drain_events(li); - - _destroy_(litest_device) *device = litest_add_device(li, LITEST_CYBORG_RAT); - litest_drain_events(li); - litest_dispatch(li); - - litest_log_group("P1 should swap left to right") { - litest_button_click_debounced(device, li, BTN_LEFT, 1); - litest_button_click_debounced(device, li, BTN_LEFT, 0); - litest_dispatch(li); - - litest_assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_RELEASED); - litest_drain_events(li); - } - - litest_log_group("P1/P2 should leave BTN_EXTRA untouched") { - litest_button_click_debounced(device, li, BTN_EXTRA, 1); - litest_button_click_debounced(device, li, BTN_EXTRA, 0); - litest_dispatch(li); - - /* This might be a false positive */ - litest_assert_button_event(li, - BTN_EXTRA, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_EXTRA, - LIBINPUT_BUTTON_STATE_RELEASED); - } - - litest_log_group("P2 should map BTN_SIDE to BTN_EXTRA") { - litest_button_click_debounced(device, li, BTN_SIDE, 1); - litest_button_click_debounced(device, li, BTN_SIDE, 0); - litest_dispatch(li); - litest_timeout_debounce(li); - litest_dispatch(li); - - litest_assert_button_event(li, - BTN_EXTRA, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_EXTRA, - LIBINPUT_BUTTON_STATE_RELEASED); - } - - if (in_timer) { - litest_log_group( - "P2 should inject left on middle via timer, P1 changes that left to right in timer") { - litest_button_click_debounced(device, li, BTN_MIDDLE, 1); - litest_dispatch(li); - - litest_assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_RIGHT, - LIBINPUT_BUTTON_STATE_PRESSED); - - litest_button_click_debounced(device, li, BTN_MIDDLE, 0); - litest_dispatch(li); - litest_assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); - /* We only inject BTN_RIGHT down, so the second inject should - * get filtered */ - litest_assert_empty_queue(li); - } - } else { - litest_log_group("P2 tries to inject during frame, gets unloaded") { - litest_set_log_handler_bug(li); - litest_button_click_debounced(device, li, BTN_MIDDLE, 1); - litest_restore_log_handler(li); - litest_button_click_debounced(device, li, BTN_MIDDLE, 0); - litest_dispatch(li); - - litest_assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_MIDDLE, - LIBINPUT_BUTTON_STATE_RELEASED); - } - - litest_log_group("P2 is unloaded, expect BTN_SIDE") { - litest_button_click_debounced(device, li, BTN_SIDE, 1); - litest_button_click_debounced(device, li, BTN_SIDE, 0); - litest_dispatch(li); - - litest_assert_button_event(li, - BTN_SIDE, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_SIDE, - LIBINPUT_BUTTON_STATE_RELEASED); - } - } -} -END_TEST - enum when { DEVICE_NEW, FIRST_FRAME, @@ -1268,10 +1110,6 @@ TEST_COLLECTION(lua) litest_add_parametrized_no_device(lua_append_prepend_frame, params); } - litest_with_parameters(params, "in_timer", 'b') { - litest_add_parametrized_no_device(lua_inject_frame, params); - } - litest_with_parameters(params, "when", 'I', 2, litest_named_i32(DEVICE_NEW), litest_named_i32(FIRST_FRAME)) {