mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 15:00:05 +01:00
plugin: add ability to queue more events in the evdev_frame callback
This adds a event queue pointer to each plugin that is set when the plugin's evdev_frame() callback is invoked. If the plugin calls libinput_plugin_queue_event_frame() during the callback the given event frame is appended to an event frame list (starting with the current frame). Once the callback completes, that frame list is passed to the next plugin and each frame is replayed on the next plugin. In the case of multiple plugins queueing events this effectively builds a tree of frames which each level of the tree representing one plugin. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1217>
This commit is contained in:
parent
38d92a96b9
commit
7137eb9702
2 changed files with 267 additions and 1 deletions
|
|
@ -44,6 +44,11 @@ struct libinput_plugin {
|
||||||
bool registered;
|
bool registered;
|
||||||
|
|
||||||
const struct libinput_plugin_interface *interface;
|
const struct libinput_plugin_interface *interface;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct list *after;
|
||||||
|
struct list *before;
|
||||||
|
} event_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBINPUT_ATTRIBUTE_PRINTF(3, 4)
|
LIBINPUT_ATTRIBUTE_PRINTF(3, 4)
|
||||||
|
|
@ -146,6 +151,79 @@ libinput_plugin_get_context(struct libinput_plugin *plugin)
|
||||||
return plugin->libinput;
|
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));
|
||||||
|
if (!event)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
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
|
void
|
||||||
libinput_plugin_run(struct libinput_plugin *plugin)
|
libinput_plugin_run(struct libinput_plugin *plugin)
|
||||||
{
|
{
|
||||||
|
|
@ -306,14 +384,154 @@ libinput_plugin_system_notify_device_ignored(struct libinput_plugin_system *syst
|
||||||
libinput_plugin_system_drop_unregistered_plugins(system);
|
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)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
"%u.%03u ----------------- EV_SYN ----------------- +%ums\n",
|
||||||
|
time / 1000,
|
||||||
|
time % 1000,
|
||||||
|
time - last_time);
|
||||||
|
|
||||||
|
last_time = time;
|
||||||
|
break;
|
||||||
|
case EVDEV_MSC_SERIAL:
|
||||||
|
log_debug(libinput,
|
||||||
|
"%u.%03u %-16s %-16s %#010x\n",
|
||||||
|
time / 1000,
|
||||||
|
time % 1000,
|
||||||
|
evdev_event_get_type_name(e),
|
||||||
|
evdev_event_get_code_name(e),
|
||||||
|
e->value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_debug(libinput,
|
||||||
|
"%u.%03u %-16s %-20s %4d\n",
|
||||||
|
time / 1000,
|
||||||
|
time % 1000,
|
||||||
|
evdev_event_get_type_name(e),
|
||||||
|
evdev_event_get_code_name(e),
|
||||||
|
e->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
|
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
|
||||||
struct libinput_device *device,
|
struct libinput_device *device,
|
||||||
struct evdev_frame *frame)
|
struct evdev_frame *frame)
|
||||||
{
|
{
|
||||||
|
/* 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);
|
||||||
|
|
||||||
struct libinput_plugin *plugin;
|
struct libinput_plugin *plugin;
|
||||||
list_for_each_safe(plugin, &system->plugins, link) {
|
list_for_each_safe(plugin, &system->plugins, link) {
|
||||||
libinput_plugin_notify_evdev_frame(plugin, device, frame);
|
/* 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
|
||||||
|
log_debug(libinput_device_get_context(device),
|
||||||
|
"Plugin %s processing frame for device %s\n",
|
||||||
|
plugin->name,
|
||||||
|
libinput_device_get_name(event->device));
|
||||||
|
print_frame(libinput_device_get_context(device), event->frame);
|
||||||
|
#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);
|
libinput_plugin_system_drop_unregistered_plugins(system);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,3 +144,51 @@ libinput_plugin_unref(struct libinput_plugin *plugin);
|
||||||
#ifdef DEFINE_UNREF_CLEANUP_FUNC
|
#ifdef DEFINE_UNREF_CLEANUP_FUNC
|
||||||
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
|
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue an event frame for the next plugin in sequence, after
|
||||||
|
* the current event frame being processed.
|
||||||
|
*
|
||||||
|
* This function can only be called from within the evdev_frame()
|
||||||
|
* callback and may be used to queue new frames. If called multiple
|
||||||
|
* times with frames F1, F2, ... while processing the current frame C,
|
||||||
|
* frame sequence to be passed to the next plugin is
|
||||||
|
* C, F1, F2, ...
|
||||||
|
*
|
||||||
|
* Assuming several plugins P1, P2, P3, and P4 and an event frame E,
|
||||||
|
* and P2 and P3 calling this function with an queued event frame Q1 and Q2,
|
||||||
|
* respectively.
|
||||||
|
*
|
||||||
|
* - P1: receives E, optionally modifies E
|
||||||
|
* - P2: receives E, appends Q1, optionally modifies E
|
||||||
|
* - P3: receives E, appends Q2, optionally modifies E
|
||||||
|
* - P3: receives Q1, optionally modifies Q1
|
||||||
|
* - P4: receives E, optionally modifies E
|
||||||
|
* - P4: receives Q2, optionally modifies Q2
|
||||||
|
* - P4: receives Q1, optionally modifies Q1
|
||||||
|
*
|
||||||
|
* Once plugin processing is complete, the event sequence passed
|
||||||
|
* back to libinput is thus [E, Q2, Q1].
|
||||||
|
*
|
||||||
|
* To discard the original event frame, the plugin needs to
|
||||||
|
* call evdev_frame_reset() on the frame passed to it.
|
||||||
|
*
|
||||||
|
* It is a plugin bug to call this function from outside the
|
||||||
|
* evdev_frame() callback.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
libinput_plugin_append_evdev_frame(struct libinput_plugin *libinput,
|
||||||
|
struct libinput_device *device,
|
||||||
|
struct evdev_frame *frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identical to libinput_plugin_append_evdev_frame(), but prepends
|
||||||
|
* the event frame to the current event frame being processed.
|
||||||
|
* If called multiple times with frames F1, F2, ... while processing
|
||||||
|
* the current frame C, frame sequence to be passed to the next
|
||||||
|
* plugin is F1, F2, ..., C
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
libinput_plugin_prepend_evdev_frame(struct libinput_plugin *libinput,
|
||||||
|
struct libinput_device *device,
|
||||||
|
struct evdev_frame *frame);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue