mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-04-18 12:00:38 +02:00
plugin: wrap timers for use by plugins
Wrapping timers means plugins we can set up the event queue for a plugin when the timer triggers, allowing plugins to queue events during the timer func. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1217>
This commit is contained in:
parent
e69c0b9bfb
commit
5698e3c12f
2 changed files with 224 additions and 11 deletions
|
|
@ -31,6 +31,8 @@
|
|||
#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"
|
||||
|
||||
|
|
@ -45,12 +47,23 @@ struct libinput_plugin {
|
|||
|
||||
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,
|
||||
|
|
@ -85,6 +98,7 @@ libinput_plugin_new(struct libinput *libinput,
|
|||
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);
|
||||
|
||||
|
|
@ -117,6 +131,12 @@ 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);
|
||||
|
|
@ -170,8 +190,6 @@ 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);
|
||||
|
|
@ -470,10 +488,11 @@ print_frame(struct libinput *libinput,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
|
||||
struct libinput_device *device,
|
||||
struct evdev_frame *frame)
|
||||
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
|
||||
|
|
@ -490,8 +509,19 @@ libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
|
|||
|
||||
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);
|
||||
|
||||
|
|
@ -538,3 +568,118 @@ libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <libudev.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
|
|
@ -150,10 +151,10 @@ DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
|
|||
* 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, ...
|
||||
* callback or within a plugin's timer func 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,
|
||||
|
|
@ -174,7 +175,26 @@ DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
|
|||
* 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.
|
||||
* evdev_frame() callback or a timer callback.
|
||||
*
|
||||
* If called within a plugin's timer callback, any frames generated by
|
||||
* the plugin will only be seen by plugins after this plugin. These
|
||||
* frames will be processed in the usual evdev_fame() callback and there
|
||||
* is no indication that the events were queued from within a timer
|
||||
* callback. Using the above example:
|
||||
*
|
||||
* - P1: <idle>
|
||||
* - P2: timer callback invoked, appends Q1
|
||||
* - P3: receives Q1, appends Q2, optionally modifies Q1
|
||||
* - P4: receives Q2, optionally modifies Q2
|
||||
* - P4: receives Q1, optionally modifies Q1
|
||||
*
|
||||
* Because there is no current frame during a timer callback
|
||||
* libinput_plugin_append_evdev_frame() and
|
||||
* libinput_plugin_prepend_evdev_frame() are functionally equivalent.
|
||||
* If both functions are used, all events from
|
||||
* libinput_plugin_prepend_evdev_frame() will be queued before
|
||||
* events from libinput_plugin_append_evdev_frame().
|
||||
*/
|
||||
void
|
||||
libinput_plugin_append_evdev_frame(struct libinput_plugin *libinput,
|
||||
|
|
@ -192,3 +212,51 @@ void
|
|||
libinput_plugin_prepend_evdev_frame(struct libinput_plugin *libinput,
|
||||
struct libinput_device *device,
|
||||
struct evdev_frame *frame);
|
||||
|
||||
/**
|
||||
* Create a new timer for the given plugin.
|
||||
*
|
||||
* The timer needs to be set with libinput_plugin_timer_set()
|
||||
* before it can be used.
|
||||
*
|
||||
* The refcount of the returned timer is at least 1, and the caller
|
||||
* must call libinput_plugin_timer_unref() to release it.
|
||||
*
|
||||
* The func given is the callback invoked when the timer expires.
|
||||
* It is passed the user_data given here or in a subsequent
|
||||
* call to libinput_plugin_timer_set_user_data().
|
||||
*
|
||||
* Note that event generating inside a timer is subject to
|
||||
* specific behaviors, see the documentation
|
||||
* to libinput_plugin_append_evdev_frame(), libinput_plugin_prepend_evdev_frame()
|
||||
* and libinput_plugin_inject_evdev_frame() for details.
|
||||
*/
|
||||
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 *user_data),
|
||||
void *user_data);
|
||||
|
||||
struct libinput_plugin_timer *
|
||||
libinput_plugin_timer_ref(struct libinput_plugin_timer *timer);
|
||||
|
||||
struct libinput_plugin_timer *
|
||||
libinput_plugin_timer_unref(struct libinput_plugin_timer *timer);
|
||||
|
||||
#ifdef DEFINE_UNREF_CLEANUP_FUNC
|
||||
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin_timer);
|
||||
#endif
|
||||
|
||||
/* Set timer expire time, in absolute us CLOCK_MONOTONIC */
|
||||
void
|
||||
libinput_plugin_timer_set(struct libinput_plugin_timer *timer,
|
||||
uint64_t expire);
|
||||
|
||||
void
|
||||
libinput_plugin_timer_set_user_data(struct libinput_plugin_timer *timer,
|
||||
void *user_data);
|
||||
void *
|
||||
libinput_plugin_timer_get_user_data(struct libinput_plugin_timer *timer);
|
||||
|
||||
void
|
||||
libinput_plugin_timer_cancel(struct libinput_plugin_timer *timer);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue