/* * Copyright © 2025 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include "util-files.h" #include "util-list.h" #include "libinput-plugin.h" #include "libinput-plugin-private.h" #include "libinput-plugin-system.h" #include "timer.h" #include "libinput-util.h" #include "libinput-private.h" #include "libinput-plugin-tablet-double-tool.h" #include "libinput-plugin-tablet-forced-tool.h" #include "libinput-plugin-tablet-proximity-timer.h" #include "evdev-plugin.h" struct libinput_plugin { struct libinput *libinput; char *name; int refcount; struct list link; void *user_data; bool registered; const struct libinput_plugin_interface *interface; struct list timers; struct { struct list *after; struct list *before; } event_queue; }; struct libinput_plugin_timer { int refcount; struct list link; struct libinput_plugin *plugin; struct libinput_timer timer; void (*func)(struct libinput_plugin *plugin, uint64_t now, void *user_data); void *user_data; }; LIBINPUT_ATTRIBUTE_PRINTF(3, 4) void plugin_log_msg(struct libinput_plugin *plugin, enum libinput_log_priority priority, const char *format, ...) { if (!log_is_logged(plugin->libinput, priority)) return; _autofree_ char *prefix = strdup_printf("Plugin:%-15s - ", plugin->name); va_list args; va_start(args, format); _autofree_ char *message = strdup_vprintf(format, args); va_end(args); log_msg(plugin->libinput, priority, "%s%s", prefix, message); } struct libinput_plugin * libinput_plugin_new(struct libinput *libinput, const char *name, const struct libinput_plugin_interface *interface, void *user_data) { struct libinput_plugin *plugin = zalloc(sizeof(*plugin)); plugin->registered = true; plugin->libinput = libinput; plugin->refcount = 1; plugin->interface = interface; plugin->user_data = user_data; plugin->name = strdup(name); list_init(&plugin->timers); libinput_plugin_system_register_plugin(&libinput->plugin_system, plugin); return plugin; } void libinput_plugin_unregister(struct libinput_plugin *plugin) { struct libinput *libinput = plugin->libinput; if (!plugin->registered) return; plugin->registered = false; libinput_plugin_system_unregister_plugin(&libinput->plugin_system, plugin); } struct libinput_plugin * libinput_plugin_ref(struct libinput_plugin *plugin) { assert(plugin->refcount > 0); ++plugin->refcount; return plugin; } struct libinput_plugin * libinput_plugin_unref(struct libinput_plugin *plugin) { assert(plugin->refcount > 0); if (--plugin->refcount == 0) { struct libinput_plugin_timer *timer; list_for_each_safe(timer, &plugin->timers, link) { libinput_plugin_timer_cancel(timer); libinput_plugin_timer_unref(timer); } list_remove(&plugin->link); if (plugin->interface->destroy) plugin->interface->destroy(plugin); free(plugin->name); free(plugin); } return NULL; } void libinput_plugin_set_user_data(struct libinput_plugin *plugin, void *user_data) { plugin->user_data = user_data; } void * libinput_plugin_get_user_data(struct libinput_plugin *plugin) { return plugin->user_data; } const char * libinput_plugin_get_name(struct libinput_plugin *plugin) { return plugin->name; } struct libinput * libinput_plugin_get_context(struct libinput_plugin *plugin) { return plugin->libinput; } struct plugin_queued_event { struct list link; struct evdev_frame *frame; /* owns a ref */ struct libinput_device *device; /* owns a ref */ }; static void plugin_queued_event_destroy(struct plugin_queued_event *event) { evdev_frame_unref(event->frame); libinput_device_unref(event->device); list_remove(&event->link); free(event); } static inline struct plugin_queued_event * plugin_queued_event_new(struct evdev_frame *frame, struct libinput_device *device) { struct plugin_queued_event *event = zalloc(sizeof(*event)); event->frame = evdev_frame_ref(frame); event->device = libinput_device_ref(device); return event; } static void libinput_plugin_queue_evdev_frame(struct list *queue, const char *func, struct libinput_plugin *plugin, struct libinput_device *device, struct evdev_frame *frame) { if (queue == NULL) { plugin_log_bug(plugin, "%s() called outside evdev_frame processing\n", func); libinput_plugin_unregister(plugin); return; } _unref_(evdev_frame) *clone = evdev_frame_clone(frame); struct plugin_queued_event *event = plugin_queued_event_new(clone, device); list_take_append(queue, event, link); } void libinput_plugin_append_evdev_frame(struct libinput_plugin *plugin, struct libinput_device *device, struct evdev_frame *frame) { libinput_plugin_queue_evdev_frame(plugin->event_queue.after, __func__, plugin, device, frame); } void libinput_plugin_prepend_evdev_frame(struct libinput_plugin *plugin, struct libinput_device *device, struct evdev_frame *frame) { libinput_plugin_queue_evdev_frame(plugin->event_queue.before, __func__, plugin, device, 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) { if (plugin->interface->run) plugin->interface->run(plugin); } void libinput_plugin_notify_device_new(struct libinput_plugin *plugin, struct libinput_device *device, struct libevdev *evdev, struct udev_device *udev_device) { if (plugin->interface->device_new) plugin->interface->device_new(plugin, device, evdev, udev_device); } void libinput_plugin_notify_device_added(struct libinput_plugin *plugin, struct libinput_device *device) { if (plugin->interface->device_added) plugin->interface->device_added(plugin, device); } void libinput_plugin_notify_device_ignored(struct libinput_plugin *plugin, struct libinput_device *device) { if (plugin->interface->device_ignored) plugin->interface->device_ignored(plugin, device); } void libinput_plugin_notify_device_removed(struct libinput_plugin *plugin, struct libinput_device *device) { if (plugin->interface->device_removed) plugin->interface->device_removed(plugin, device); } void libinput_plugin_notify_evdev_frame(struct libinput_plugin *plugin, struct libinput_device *device, struct evdev_frame *frame) { if (plugin->interface->evdev_frame) plugin->interface->evdev_frame(plugin, device, frame); } void libinput_plugin_system_run(struct libinput_plugin_system *system) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_run(plugin); } } void libinput_plugin_system_register_plugin(struct libinput_plugin_system *system, struct libinput_plugin *plugin) { libinput_plugin_ref(plugin); list_append(&system->plugins, &plugin->link); } void libinput_plugin_system_unregister_plugin(struct libinput_plugin_system *system, struct libinput_plugin *plugin) { struct libinput_plugin *p; list_for_each(p, &system->plugins, link) { if (p == plugin) { list_remove(&plugin->link); list_append(&system->removed_plugins, &plugin->link); return; } } } static void libinput_plugin_system_drop_unregistered_plugins(struct libinput_plugin_system *system) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->removed_plugins, link) { list_remove(&plugin->link); list_init(&plugin->link); /* allow list_remove in unref */ libinput_plugin_unref(plugin); } } void libinput_plugin_system_init(struct libinput_plugin_system *system) { list_init(&system->plugins); list_init(&system->removed_plugins); } void libinput_plugin_system_load_internal_plugins(struct libinput *libinput, struct libinput_plugin_system *system) { /* FIXME: this should really be one of the first in the sequence * so plugins don't have to take care of this? */ libinput_tablet_plugin_forced_tool(libinput); libinput_tablet_plugin_double_tool(libinput); libinput_tablet_plugin_proximity_timer(libinput); /* Our own event dispatch is implemented as mini-plugin, * guarantee this one to always be last (and after any * other plugins have run so none of the devices are * actually connected to anything yet */ libinput_evdev_dispatch_plugin(libinput); } void libinput_plugin_system_destroy(struct libinput_plugin_system *system) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_unregister(plugin); } libinput_plugin_system_drop_unregistered_plugins(system); strv_free(system->directories); } void libinput_plugin_system_notify_device_new(struct libinput_plugin_system *system, struct libinput_device *device, struct libevdev *evdev, struct udev_device *udev_device) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_notify_device_new(plugin, device, evdev, udev_device); } libinput_plugin_system_drop_unregistered_plugins(system); } void libinput_plugin_system_notify_device_added(struct libinput_plugin_system *system, struct libinput_device *device) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_notify_device_added(plugin, device); } libinput_plugin_system_drop_unregistered_plugins(system); } void libinput_plugin_system_notify_device_removed(struct libinput_plugin_system *system, struct libinput_device *device) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_notify_device_removed(plugin, device); } libinput_plugin_system_drop_unregistered_plugins(system); } void libinput_plugin_system_notify_device_ignored(struct libinput_plugin_system *system, struct libinput_device *device) { struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { libinput_plugin_notify_device_ignored(plugin, device); } libinput_plugin_system_drop_unregistered_plugins(system); } static void libinput_plugin_process_frame(struct libinput_plugin *plugin, struct libinput_device *device, struct evdev_frame *frame, struct list *queued_events) { struct list before_events = LIST_INIT(before_events); struct list after_events = LIST_INIT(after_events); plugin->event_queue.before = &before_events; plugin->event_queue.after = &after_events; if (plugin->interface->evdev_frame) plugin->interface->evdev_frame(plugin, device, frame); plugin->event_queue.before = NULL; plugin->event_queue.after = NULL; list_chain(queued_events, &before_events); if (!evdev_frame_is_empty(frame)) { struct plugin_queued_event *event = plugin_queued_event_new(frame, device); list_take_append(queued_events, event, link); } list_chain(queued_events, &after_events); } _unused_ static inline void print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *prefix) { static uint32_t offset = 0; static uint32_t last_time = 0; uint32_t time = evdev_frame_get_time(frame) / 1000; if (offset == 0) { offset = time; last_time = time - offset; } time -= offset; size_t nevents; struct evdev_event *events = evdev_frame_get_events(frame, &nevents); for (size_t i = 0; i < nevents; i++) { struct evdev_event *e = &events[i]; switch (evdev_usage_enum(e->usage)) { case EVDEV_SYN_REPORT: log_debug(libinput, "%s%u.%03u ----------------- EV_SYN ----------------- +%ums\n", prefix, time / 1000, time % 1000, time - last_time); last_time = time; break; case EVDEV_MSC_SERIAL: log_debug(libinput, "%s%u.%03u %-16s %-16s %#010x\n", prefix, time / 1000, time % 1000, evdev_event_get_type_name(e), evdev_event_get_code_name(e), e->value); break; default: log_debug(libinput, "%s%u.%03u %-16s %-20s %4d\n", prefix, time / 1000, time % 1000, evdev_event_get_type_name(e), evdev_event_get_code_name(e), e->value); break; } } } static void plugin_system_notify_evdev_frame(struct libinput_plugin_system *system, struct libinput_device *device, struct evdev_frame *frame, struct libinput_plugin *sender_plugin) { /* This is messy because a single event frame may cause * *each* plugin to generate multiple event frames for potentially * different devices and replaying is basically breadth-first traversal. * * So we have our event (passed in as 'frame') and we create a queue. * Each plugin then creates a new event list from each frame in the * queue. */ struct plugin_queued_event *our_event = plugin_queued_event_new(frame, device); struct list queued_events = LIST_INIT(queued_events); list_take_insert(&queued_events, our_event, link); uint64_t frame_time = evdev_frame_get_time(frame); bool delay = !!sender_plugin; struct libinput_plugin *plugin; list_for_each_safe(plugin, &system->plugins, link) { /* We start processing *after* the sender plugin. sender_plugin * is only set if we're queuing (not injecting) events from * a plugin timer func */ if (delay) { delay = plugin != sender_plugin; continue; } /* The list of queued events for the *next* plugin */ struct list next_events = LIST_INIT(next_events); /* Iterate through the current list of queued events, pass * each through to the plugin and remove it from the current * list. The plugin may generate a new event list (possibly * containing our frame but not our queued_event directly) * and that list becomes the event list for the next plugin. */ struct plugin_queued_event *event; list_for_each_safe(event, &queued_events, link) { struct list next = LIST_INIT(next); if (evdev_frame_get_time(event->frame) == 0) evdev_frame_set_time(event->frame, frame_time); #ifdef EVENT_DEBUGGING _autofree_ char *prefix = strdup_printf("plugin %-25s - %s:", plugin->name, libinput_device_get_name(event->device)); print_frame(libinput_device_get_context(device), event->frame, prefix); #endif libinput_plugin_process_frame(plugin, event->device, event->frame, &next); list_chain(&next_events, &next); plugin_queued_event_destroy(event); } assert(list_empty(&queued_events)); list_chain(&queued_events, &next_events); if (list_empty(&queued_events)) { /* No more events to process, stop here */ break; } } /* Our own evdev plugin is last and discards the event for us */ if (!list_empty(&queued_events)) { log_bug_libinput(libinput_device_get_context(device), "Events left over to replay after last plugin\n"); } libinput_plugin_system_drop_unregistered_plugins(system); } void libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system, struct libinput_device *device, struct evdev_frame *frame) { plugin_system_notify_evdev_frame(system, device, frame, NULL); } static void plugin_timer_func(uint64_t now, void *data) { struct libinput_plugin_timer *timer = data; struct libinput_plugin *plugin = timer->plugin; struct libinput *libinput = plugin->libinput; if (!timer->func) return; struct list before_events = LIST_INIT(before_events); struct list after_events = LIST_INIT(after_events); plugin->event_queue.before = &before_events; plugin->event_queue.after = &after_events; timer->func(plugin, now, timer->user_data); plugin->event_queue.before = NULL; plugin->event_queue.after = NULL; list_chain(&before_events, &after_events); struct plugin_queued_event *event; list_for_each_safe(event, &before_events, link) { plugin_system_notify_evdev_frame(&libinput->plugin_system, event->device, event->frame, plugin); plugin_queued_event_destroy(event); } } struct libinput_plugin_timer * libinput_plugin_timer_new(struct libinput_plugin *plugin, const char *name, void (*func)(struct libinput_plugin *plugin, uint64_t now, void *data), void *data) { struct libinput_plugin_timer *timer = zalloc(sizeof(*timer)); _autofree_ char *timer_name = strdup_printf("%s-%s", plugin->name, name); timer->plugin = plugin; timer->refcount = 2; /* one for the caller, one for our list */ timer->func = func; timer->user_data = data; libinput_timer_init(&timer->timer, plugin->libinput, timer_name, plugin_timer_func, timer); list_append(&plugin->timers, &timer->link); return timer; } void libinput_plugin_timer_set_user_data(struct libinput_plugin_timer *timer, void *user_data) { timer->user_data = user_data; } void * libinput_plugin_timer_get_user_data(struct libinput_plugin_timer *timer) { return timer->user_data; } struct libinput_plugin_timer * libinput_plugin_timer_ref(struct libinput_plugin_timer *timer) { assert(timer->refcount > 0); ++timer->refcount; return timer; } struct libinput_plugin_timer * libinput_plugin_timer_unref(struct libinput_plugin_timer *timer) { assert(timer->refcount > 0); if (--timer->refcount == 0) { libinput_timer_cancel(&timer->timer); libinput_timer_destroy(&timer->timer); list_remove(&timer->link); free(timer); } return NULL; } /* Set timer expire time, in absolute us CLOCK_MONOTONIC */ void libinput_plugin_timer_set(struct libinput_plugin_timer *timer, uint64_t expire) { libinput_timer_set(&timer->timer, expire); } void libinput_plugin_timer_cancel(struct libinput_plugin_timer *timer) { libinput_timer_cancel(&timer->timer); }