diff --git a/src/evdev.c b/src/evdev.c index 78c77740..9b87a0d0 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -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; diff --git a/src/libinput-plugin.c b/src/libinput-plugin.c index 93085523..ba8167ae 100644 --- a/src/libinput-plugin.c +++ b/src/libinput-plugin.c @@ -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) { diff --git a/src/libinput-plugin.h b/src/libinput-plugin.h index 655aa207..fc2defb2 100644 --- a/src/libinput-plugin.h +++ b/src/libinput-plugin.h @@ -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. diff --git a/src/libinput-private.h b/src/libinput-private.h index 11c5440d..ecc372ac 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -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 {