plugins: Add the ability for a plugin to inject evdev frames

This adds a callback for plugins (and lua plugins) to inject an evdev
frame into the event stream. Unlike the prepend/append functions
this one injects the event at the bottom of the plugin stack as if
the device had sent the frame right then and there.

There's a drawback though: the event frame isn't marked as synthetic
so it cannot be identified without having a guard in the plugin so
the same frame is not reprocessed.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1217>
This commit is contained in:
Peter Hutterer 2025-05-22 19:40:43 +10:00 committed by Marge Bot
parent 5698e3c12f
commit 83222a0798
4 changed files with 78 additions and 0 deletions

View file

@ -1136,6 +1136,16 @@ evdev_device_dispatch_frame(struct libinput *libinput,
}
}
static inline void
libinput_device_dispatch_frame(struct libinput_device *device,
struct evdev_frame *frame)
{
struct libinput *libinput = libinput_device_get_context(device);
struct evdev_device *dev = evdev_device(device);
evdev_device_dispatch_frame(libinput, dev, frame);
}
static int
evdev_sync_device(struct libinput *libinput,
struct evdev_device *device)
@ -2563,6 +2573,8 @@ evdev_device_create(struct libinput_seat *seat,
list_insert(seat->devices_list.prev, &device->base.link);
device->base.inject_evdev_frame = libinput_device_dispatch_frame;
evdev_notify_added_device(device);
return device;

View file

@ -242,6 +242,15 @@ libinput_plugin_prepend_evdev_frame(struct libinput_plugin *plugin,
frame);
}
void
libinput_plugin_inject_evdev_frame(struct libinput_plugin *plugin,
struct libinput_device *device,
struct evdev_frame *frame)
{
if (device->inject_evdev_frame)
device->inject_evdev_frame(device, frame);
}
void
libinput_plugin_run(struct libinput_plugin *plugin)
{

View file

@ -146,6 +146,60 @@ libinput_plugin_unref(struct libinput_plugin *plugin);
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
#endif
/**
* Inject a new event frame from the given plugin. This
* frame is treated as if it was just sent by the kernel's
* event node and is processed immediately, interrupting
* any other processing from this device.
*
* This function can be called any time but unlike
* libinput_plugin_append_evdev_frame() and
* libinput_plugin_prepend_evdev_frame() it starts
* processing the frame at the bottom of the plugin
* stack.
*
* The injected event will also be sent to
* the current plugin, a guard needs to be in
* place to prevent recursion.
*
* Injecting events may cause other plugins to
* behave unexpectedly (see below). In almost all cases
* it is better to use libinput_plugin_append_evdev_frame()
* or libinput_plugin_prepend_evdev_frame() to only
* affect the plugins logically *after* this
* plugin.
*
* Assuming several plugins P1, P2, and P3 and
* an event frame E, and P2 calling this function
* with an injected event I:
* - P1: receives E, optionally modifies E
* - P2: receives E, injects I
* - P1: receives I, optionally modifies I
* - P2: receives I, optionally modifies I
* - P3: receives I, optionally modifies I
* - P2: continues processing E, optionally modifies E
* - P3: receives E, optionally modifies E
*
* For P1 the injected event I is received after E,
* for P3 the injected event I is received before E.
*
* An example for event injection being harmful:
* A plugin may monitor tablet proximity state and prepent
* proximity-in events if the tablet does not send proximity-in
* events. This plugin stops monitoring events once it sees correct
* proximity-in events.
* If another plugin were to inject a proximity event (e.g. to fake a
* different tool coming into proximity), the plugin would stop
* monitoring. Future proximity events from the tablet will then
* have the wrong proximity.
* This can be avoided by appending or prepending the events instead
* of injecting them.
*/
void
libinput_plugin_inject_evdev_frame(struct libinput_plugin *libinput,
struct libinput_device *device,
struct evdev_frame *frame);
/**
* Queue an event frame for the next plugin in sequence, after
* the current event frame being processed.

View file

@ -494,6 +494,9 @@ struct libinput_device {
void *user_data;
int refcount;
struct libinput_device_config config;
void (*inject_evdev_frame)(struct libinput_device *device,
struct evdev_frame *frame);
};
enum libinput_tablet_tool_axis {