Add an internal plugin system to libinput

This adds the scaffolding for an internal plugin architecture. No such
plugin currently exists and any plugin implemented against this
architecture merely is plugin-like in the event flow, not actually
external to libinput.

The goal of this architecture is to have more segmented processing
of the event stream from devices to modify that stream before libinput
ever sees it. Right now libinput looks at e.g. a tablet packet and then
determines whether the tool was correctly in proximity, etc.
With this architecture we can have a plugin that modifies the event
stream that the tool is *always* correctly in proximity and the tablet
backend itself merely needs to handle the correct case.

The event flow will thus logically change from:
   evdev device -> backend dispatch
to
   evdev device -> plugin1 -> plugin2 -> backend dispatch

The plugin API is more expansive than we will use immediately, it is the
result of several different implementation branches that all require
different functionality.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1217>
This commit is contained in:
Peter Hutterer 2025-04-22 10:25:55 +10:00 committed by Marge Bot
parent 7e6a9f322f
commit 3958ba44c2
8 changed files with 643 additions and 11 deletions

View file

@ -335,7 +335,7 @@ src_libfilter = [
'src/filter-trackpoint-flat.c', 'src/filter-trackpoint-flat.c',
] ]
libfilter = static_library('filter', src_libfilter, libfilter = static_library('filter', src_libfilter,
dependencies : [dep_udev, dep_libwacom], dependencies : [dep_udev, dep_libwacom, dep_libevdev],
include_directories : includes_include) include_directories : includes_include)
dep_libfilter = declare_dependency(link_with : libfilter) dep_libfilter = declare_dependency(link_with : libfilter)
@ -374,6 +374,7 @@ config_h.set10('EVENT_DEBUGGING', get_option('internal-event-debugging'))
install_headers('src/libinput.h') install_headers('src/libinput.h')
src_libinput = src_libfilter + [ src_libinput = src_libfilter + [
'src/libinput.c', 'src/libinput.c',
'src/libinput-plugin.c',
'src/libinput-private-config.c', 'src/libinput-private-config.c',
'src/evdev.c', 'src/evdev.c',
'src/evdev-debounce.c', 'src/evdev-debounce.c',

View file

@ -1120,19 +1120,25 @@ evdev_device_dispatch_one(struct evdev_device *device,
} }
static inline void static inline void
evdev_device_dispatch_frame(struct evdev_device *device, evdev_device_dispatch_frame(struct libinput *libinput,
struct evdev_device *device,
struct evdev_frame *frame) struct evdev_frame *frame)
{ {
size_t nevents; size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents); struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
libinput_plugin_system_notify_evdev_frame(&libinput->plugin_system,
&device->base,
frame);
for (size_t i = 0; i < nevents; i++) { for (size_t i = 0; i < nevents; i++) {
evdev_device_dispatch_one(device, &events[i], evdev_frame_get_time(frame)); evdev_device_dispatch_one(device, &events[i], evdev_frame_get_time(frame));
} }
} }
static int static int
evdev_sync_device(struct evdev_device *device) evdev_sync_device(struct libinput *libinput,
struct evdev_device *device)
{ {
struct input_event ev; struct input_event ev;
int rc; int rc;
@ -1149,7 +1155,7 @@ evdev_sync_device(struct evdev_device *device)
evdev_frame_append_input_event(frame, &ev); evdev_frame_append_input_event(frame, &ev);
} while (rc == LIBEVDEV_READ_STATUS_SYNC); } while (rc == LIBEVDEV_READ_STATUS_SYNC);
evdev_device_dispatch_frame(device, frame); evdev_device_dispatch_frame(libinput, device, frame);
return rc == -EAGAIN ? 0 : rc; return rc == -EAGAIN ? 0 : rc;
} }
@ -1211,10 +1217,10 @@ evdev_device_dispatch(void *data)
evdev_log_bug_libinput(device, evdev_log_bug_libinput(device,
"event frame overflow, discarding events.\n"); "event frame overflow, discarding events.\n");
} }
evdev_device_dispatch_frame(device, frame); evdev_device_dispatch_frame(libinput, device, frame);
evdev_frame_reset(frame); evdev_frame_reset(frame);
rc = evdev_sync_device(device); rc = evdev_sync_device(libinput, device);
if (rc == 0) if (rc == 0)
rc = LIBEVDEV_READ_STATUS_SUCCESS; rc = LIBEVDEV_READ_STATUS_SUCCESS;
} else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
@ -1228,7 +1234,7 @@ evdev_device_dispatch(void *data)
"event frame overflow, discarding events.\n"); "event frame overflow, discarding events.\n");
} }
if (ev.type == EV_SYN && ev.code == SYN_REPORT) { if (ev.type == EV_SYN && ev.code == SYN_REPORT) {
evdev_device_dispatch_frame(device, frame); evdev_device_dispatch_frame(libinput, device, frame);
evdev_frame_reset(frame); evdev_frame_reset(frame);
} }
} else if (rc == -ENODEV) { } else if (rc == -ENODEV) {
@ -1240,7 +1246,7 @@ evdev_device_dispatch(void *data)
/* This should never happen, the kernel flushes only on SYN_REPORT */ /* This should never happen, the kernel flushes only on SYN_REPORT */
if (evdev_frame_get_count(frame) > 1) { if (evdev_frame_get_count(frame) > 1) {
evdev_log_bug_kernel(device, "event frame missing SYN_REPORT, forcing frame.\n"); evdev_log_bug_kernel(device, "event frame missing SYN_REPORT, forcing frame.\n");
evdev_device_dispatch_frame(device, frame); evdev_device_dispatch_frame(libinput, device, frame);
} }
if (rc != -EAGAIN && rc != -EINTR) { if (rc != -EAGAIN && rc != -EINTR) {
@ -2538,17 +2544,22 @@ evdev_device_create(struct libinput_seat *seat,
evdev_pre_configure_model_quirks(device); evdev_pre_configure_model_quirks(device);
libinput_plugin_system_notify_device_new(&libinput->plugin_system,
&device->base,
device->evdev,
device->udev_device);
device->dispatch = evdev_configure_device(device); device->dispatch = evdev_configure_device(device);
if (device->dispatch == NULL || device->seat_caps == EVDEV_DEVICE_NO_CAPABILITIES) if (device->dispatch == NULL || device->seat_caps == EVDEV_DEVICE_NO_CAPABILITIES)
goto err; goto err_notify;
device->source = device->source =
libinput_add_fd(libinput, fd, evdev_device_dispatch, device); libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
if (!device->source) if (!device->source)
goto err; goto err_notify;
if (!evdev_set_device_group(device, udev_device)) if (!evdev_set_device_group(device, udev_device))
goto err; goto err_notify;
list_insert(seat->devices_list.prev, &device->base.link); list_insert(seat->devices_list.prev, &device->base.link);
@ -2556,6 +2567,10 @@ evdev_device_create(struct libinput_seat *seat,
return device; return device;
err_notify:
libinput_plugin_system_notify_device_ignored(&libinput->plugin_system,
&device->base);
err: err:
if (fd >= 0) { if (fd >= 0) {
close_restricted(libinput, fd); close_restricted(libinput, fd);

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
#pragma once
#include "config.h"
#include "libinput-plugin.h"
void
libinput_plugin_run(struct libinput_plugin *plugin);
void
libinput_plugin_notify_device_new(struct libinput_plugin *plugin,
struct libinput_device *device,
struct libevdev *evdev,
struct udev_device *udev_device);
void
libinput_plugin_notify_device_added(struct libinput_plugin *plugin,
struct libinput_device *device);
void
libinput_plugin_notify_device_ignored(struct libinput_plugin *plugin,
struct libinput_device *device);
void
libinput_plugin_notify_device_removed(struct libinput_plugin *plugin,
struct libinput_device *device);
void
libinput_plugin_notify_evdev_frame(struct libinput_plugin *plugin,
struct libinput_device *device,
struct evdev_frame *frame);

View file

@ -0,0 +1,84 @@
/*
* 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.
*/
#pragma once
#include "config.h"
#include <stdbool.h>
#include <libudev.h>
#include <libevdev/libevdev.h>
#include "evdev-frame.h"
#include "util-list.h"
#include "libinput.h"
struct libinput;
struct libinput_plugin;
struct libinput_plugin_system {
char **directories; /* NULL once loaded == true */
struct list plugins;
struct list removed_plugins;
};
void
libinput_plugin_system_init(struct libinput_plugin_system *system);
void
libinput_plugin_system_destroy(struct libinput_plugin_system *system);
void
libinput_plugin_system_run(struct libinput_plugin_system *system);
void
libinput_plugin_system_register_plugin(struct libinput_plugin_system *system,
struct libinput_plugin *plugin);
void
libinput_plugin_system_unregister_plugin(struct libinput_plugin_system *system,
struct libinput_plugin *plugin);
void
libinput_plugin_system_notify_device_new(struct libinput_plugin_system *system,
struct libinput_device *device,
struct libevdev *evdev,
struct udev_device *udev);
void
libinput_plugin_system_notify_device_added(struct libinput_plugin_system *system,
struct libinput_device *device);
void
libinput_plugin_system_notify_device_removed(struct libinput_plugin_system *system,
struct libinput_device *device);
void
libinput_plugin_system_notify_device_ignored(struct libinput_plugin_system *system,
struct libinput_device *device);
void
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
struct libinput_device *device,
struct evdev_frame *frame);

319
src/libinput-plugin.c Normal file
View file

@ -0,0 +1,319 @@
/*
* 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 <stdbool.h>
#include "util-files.h"
#include "util-list.h"
#include "libinput-plugin.h"
#include "libinput-plugin-private.h"
#include "libinput-plugin-system.h"
#include "libinput-util.h"
#include "libinput-private.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;
};
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);
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) {
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;
}
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_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);
}
void
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
struct libinput_device *device,
struct evdev_frame *frame)
{
struct libinput_plugin *plugin;
list_for_each_safe(plugin, &system->plugins, link) {
libinput_plugin_notify_evdev_frame(plugin, device, frame);
}
libinput_plugin_system_drop_unregistered_plugins(system);
}

146
src/libinput-plugin.h Normal file
View file

@ -0,0 +1,146 @@
/*
* 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.
*/
#pragma once
#include "config.h"
#include <stdbool.h>
#include <libudev.h>
#include <libevdev/libevdev.h>
/* Forward declarations instead of #includes to make
* this header self-contained (bindgen, etc.) */
struct evdev_frame;
struct libinput;
struct libinput_device;
struct libinput_plugin;
enum libinput_log_priority;
#define plugin_log_debug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
#define plugin_log_info(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
#define plugin_log_error(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
#define plugin_log_bug_kernel(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
#define plugin_log_bug_libinput(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
#define plugin_log_bug_client(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
#define plugin_log_bug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "plugin bug: " __VA_ARGS__)
void
plugin_log_msg(struct libinput_plugin *plugin,
enum libinput_log_priority priority,
const char *format,
...);
struct libinput_plugin_interface {
void (*run)(struct libinput_plugin *plugin);
/**
* Notification that the plugin is about to be destroyed.
* When this function is called, the plugin has already
* been unregistered. The plugin should free any
* resources allocated but not the struct libinput_plugin
* itself.
*/
void (*destroy)(struct libinput_plugin *plugin);
/**
* Notification about a newly added device that has **not** yet
* been added by libinput as struct libinput_device.
*/
void (*device_new)(struct libinput_plugin *plugin,
struct libinput_device *device,
struct libevdev *evdev,
struct udev_device *udev_device);
/**
* Notification that a device (previously announced with device_new)
* was ignored by libinput and was **never** added as struct
* libinput_device.
*
* If a device was added (device_added) then this callback will
* not be called for that device.
*/
void (*device_ignored)(struct libinput_plugin *plugin,
struct libinput_device *device);
/**
* Notification that a device was added to libinput. Called
* after the device_new callback if the device matches libinput's
* expectations.
*/
void (*device_added)(struct libinput_plugin *plugin,
struct libinput_device *device);
/**
* Notification that a previously added device was removed.
*/
void (*device_removed)(struct libinput_plugin *plugin,
struct libinput_device *device);
/**
* Notification that a device submitted a frame event.
*/
void (*evdev_frame)(struct libinput_plugin *plugin,
struct libinput_device *device,
struct evdev_frame *frame);
};
/**
* Returns a new plugin with the given interface and, optionally,
* the user data. The returned plugin has a refcount of at least 1
* and must be unref'd by the caller.
* Should an error occur, the plugin must be unregistered by
* the caller:
*
* ```
* struct libinput_plugin *plugin = libinput_plugin_new(libinput, ...);
* if (some_error_condition) {
* libinput_plugin_unregister(plugin);
* }
* libinput_plugin_unref(plugin);
* ```
*/
struct libinput_plugin *
libinput_plugin_new(struct libinput *libinput,
const char *name,
const struct libinput_plugin_interface *interface,
void *user_data);
const char *
libinput_plugin_get_name(struct libinput_plugin *plugin);
struct libinput *
libinput_plugin_get_context(struct libinput_plugin *plugin);
void
libinput_plugin_unregister(struct libinput_plugin *plugin);
void
libinput_plugin_set_user_data(struct libinput_plugin *plugin,
void *user_data);
void *
libinput_plugin_get_user_data(struct libinput_plugin *plugin);
struct libinput_plugin *
libinput_plugin_ref(struct libinput_plugin *plugin);
struct libinput_plugin *
libinput_plugin_unref(struct libinput_plugin *plugin);
#ifdef DEFINE_UNREF_CLEANUP_FUNC
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
#endif

View file

@ -39,6 +39,7 @@
#include "libinput.h" #include "libinput.h"
#include "libinput-log.h" #include "libinput-log.h"
#include "libinput-plugin-system.h"
#include "libinput-private-config.h" #include "libinput-private-config.h"
#include "libinput-util.h" #include "libinput-util.h"
#include "libinput-version.h" #include "libinput-version.h"
@ -221,6 +222,8 @@ struct libinput {
bool quirks_initialized; bool quirks_initialized;
struct quirks_context *quirks; struct quirks_context *quirks;
struct libinput_plugin_system plugin_system;
#if HAVE_LIBWACOM #if HAVE_LIBWACOM
struct { struct {
WacomDeviceDatabase *db; WacomDeviceDatabase *db;

View file

@ -1879,6 +1879,8 @@ libinput_init(struct libinput *libinput,
list_init(&libinput->device_group_list); list_init(&libinput->device_group_list);
list_init(&libinput->tool_list); list_init(&libinput->tool_list);
libinput_plugin_system_init(&libinput->plugin_system);
if (libinput_timer_subsys_init(libinput) != 0) { if (libinput_timer_subsys_init(libinput) != 0) {
free(libinput->events); free(libinput->events);
close(libinput->epoll_fd); close(libinput->epoll_fd);
@ -1977,6 +1979,8 @@ libinput_unref(struct libinput *libinput)
free(libinput->events); free(libinput->events);
libinput_plugin_system_destroy(&libinput->plugin_system);
list_for_each_safe(seat, &libinput->seat_list, link) { list_for_each_safe(seat, &libinput->seat_list, link) {
list_for_each_safe(device, list_for_each_safe(device,
&seat->devices_list, &seat->devices_list,
@ -2345,6 +2349,9 @@ post_device_event(struct libinput_device *device,
void void
notify_added_device(struct libinput_device *device) notify_added_device(struct libinput_device *device)
{ {
struct libinput *libinput = device->seat->libinput;
libinput_plugin_system_notify_device_added(&libinput->plugin_system, device);
struct libinput_event_device_notify *added_device_event; struct libinput_event_device_notify *added_device_event;
added_device_event = zalloc(sizeof *added_device_event); added_device_event = zalloc(sizeof *added_device_event);
@ -2363,6 +2370,9 @@ notify_added_device(struct libinput_device *device)
void void
notify_removed_device(struct libinput_device *device) notify_removed_device(struct libinput_device *device)
{ {
struct libinput *libinput = device->seat->libinput;
libinput_plugin_system_notify_device_removed(&libinput->plugin_system, device);
struct libinput_event_device_notify *removed_device_event; struct libinput_event_device_notify *removed_device_event;
removed_device_event = zalloc(sizeof *removed_device_event); removed_device_event = zalloc(sizeof *removed_device_event);