mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 12:40:05 +01:00
path: add libinput_path_add_device() and libinput_path_remove_device()
This allows multiple devices to share a single libinput context. The new function returns the newly added device immediately. Unlike the udev seat where devices may or may not be added - over the lifetime of the seat - a path-based backend knows immediately if device exists or doesn't exist. Returning the device is required by callers that have the event processing separate from adding devices - by the time we have the DEVICE_ADDED event in the queue we may have other events to process first. And the DEVICE_ADDED event won't easily link to the path we gave it anyway, so it's hard to figure out which DEVICE_ADDED event corresponds to the new device. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
6764e7e19a
commit
69dcea11b9
3 changed files with 252 additions and 11 deletions
|
|
@ -726,6 +726,51 @@ libinput_create_from_path(const struct libinput_interface *interface,
|
|||
void *user_data,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* @ingroup base
|
||||
*
|
||||
* Add a device to a libinput context initialized with
|
||||
* libinput_path_create_from_device(). If successful, the device will be
|
||||
* added to the internal list and re-opened on libinput_resume(). The device
|
||||
* can be removed with libinput_path_remove_device().
|
||||
*
|
||||
* If the device was successfully initialized, it is returned in the device
|
||||
* argument. The lifetime of the returned device pointer is limited until
|
||||
* the next linput_dispatch(), use libinput_device_ref() to keep a permanent
|
||||
* reference.
|
||||
*
|
||||
* @param libinput A previously initialized libinput context
|
||||
* @param path Path to an input device
|
||||
* @return The newly initiated device on success, or NULL on failure.
|
||||
*
|
||||
* @note It is an application bug to call this function on a libinput
|
||||
* context initialize with libinput_udev_create_for_seat().
|
||||
*/
|
||||
struct libinput_device *
|
||||
libinput_path_add_device(struct libinput *libinput,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* @ingroup base
|
||||
*
|
||||
* Remove a device from a libinput context initialized with
|
||||
* libinput_path_create_from_device() or added to such a context with
|
||||
* libinput_path_add_device().
|
||||
*
|
||||
* Events already processed from this input device are kept in the queue,
|
||||
* the LIBINPUT_EVENT_DEVICE_REMOVED event marks the end of events for this
|
||||
* device.
|
||||
*
|
||||
* If no matching device exists, this function does nothing.
|
||||
*
|
||||
* @param device A libinput device
|
||||
*
|
||||
* @note It is an application bug to call this function on a libinput
|
||||
* context initialize with libinput_udev_create_for_seat().
|
||||
*/
|
||||
void
|
||||
libinput_path_remove_device(struct libinput_device *device);
|
||||
|
||||
/**
|
||||
* @ingroup base
|
||||
*
|
||||
|
|
|
|||
104
src/path.c
104
src/path.c
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <libudev.h>
|
||||
|
|
@ -35,6 +36,31 @@ static const char default_seat_name[] = "default";
|
|||
int path_input_process_event(struct libinput_event);
|
||||
static void path_seat_destroy(struct libinput_seat *seat);
|
||||
|
||||
static void
|
||||
path_disable_device(struct libinput *libinput,
|
||||
struct evdev_device *device)
|
||||
{
|
||||
struct libinput_seat *seat = device->base.seat;
|
||||
struct evdev_device *dev, *next;
|
||||
|
||||
list_for_each_safe(dev, next,
|
||||
&seat->devices_list, base.link) {
|
||||
if (dev != device)
|
||||
continue;
|
||||
|
||||
evdev_device_remove(device);
|
||||
if (list_empty(&seat->devices_list)) {
|
||||
/* if the seat may be referenced by the
|
||||
client, so make sure it's dropped from
|
||||
the seat list now, to be freed whenever
|
||||
* the device is removed */
|
||||
list_remove(&seat->link);
|
||||
list_init(&seat->link);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
path_input_disable(struct libinput *libinput)
|
||||
{
|
||||
|
|
@ -45,17 +71,8 @@ path_input_disable(struct libinput *libinput)
|
|||
list_for_each_safe(seat, tmp, &input->base.seat_list, base.link) {
|
||||
libinput_seat_ref(&seat->base);
|
||||
list_for_each_safe(device, next,
|
||||
&seat->base.devices_list, base.link) {
|
||||
evdev_device_remove(device);
|
||||
if (list_empty(&seat->base.devices_list)) {
|
||||
/* if the seat may be referenced by the
|
||||
client, so make sure it's dropped from
|
||||
the seat list now, to be freed whenever
|
||||
* the device is removed */
|
||||
list_remove(&seat->base.link);
|
||||
list_init(&seat->base.link);
|
||||
}
|
||||
}
|
||||
&seat->base.devices_list, base.link)
|
||||
path_disable_device(libinput, device);
|
||||
libinput_seat_unref(&seat->base);
|
||||
}
|
||||
}
|
||||
|
|
@ -269,3 +286,68 @@ libinput_create_from_path(const struct libinput_interface *interface,
|
|||
|
||||
return &input->base;
|
||||
}
|
||||
|
||||
LIBINPUT_EXPORT struct libinput_device *
|
||||
libinput_path_add_device(struct libinput *libinput,
|
||||
const char *path)
|
||||
{
|
||||
struct path_input *input = (struct path_input*)libinput;
|
||||
struct path_device *dev;
|
||||
struct libinput_device *device;
|
||||
|
||||
if (libinput->interface_backend != &interface_backend) {
|
||||
log_info("Mismatching backends. This is an application bug.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = zalloc(sizeof *dev);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
dev->path = strdup(path);
|
||||
if (!dev->path) {
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_insert(&input->path_list, &dev->link);
|
||||
|
||||
device = path_device_enable(input, dev->path);
|
||||
|
||||
if (!device) {
|
||||
list_remove(&dev->link);
|
||||
free(dev->path);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
LIBINPUT_EXPORT void
|
||||
libinput_path_remove_device(struct libinput_device *device)
|
||||
{
|
||||
struct libinput *libinput = device->seat->libinput;
|
||||
struct path_input *input = (struct path_input*)libinput;
|
||||
struct libinput_seat *seat;
|
||||
struct evdev_device *evdev = (struct evdev_device*)device;
|
||||
struct path_device *dev;
|
||||
|
||||
if (libinput->interface_backend != &interface_backend) {
|
||||
log_info("Mismatching backends. This is an application bug.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each(dev, &input->path_list, link) {
|
||||
if (strcmp(evdev->devnode, dev->path) == 0) {
|
||||
list_remove(&dev->link);
|
||||
free(dev->path);
|
||||
free(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seat = device->seat;
|
||||
libinput_seat_ref(seat);
|
||||
path_disable_device(libinput, evdev);
|
||||
libinput_seat_unref(seat);
|
||||
}
|
||||
|
|
|
|||
114
test/path.c
114
test/path.c
|
|
@ -196,6 +196,56 @@ START_TEST(path_added_device)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(path_add_device)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct libinput_event *event;
|
||||
struct libinput_device *device;
|
||||
const char *sysname1 = NULL, *sysname2 = NULL;
|
||||
|
||||
libinput_dispatch(li);
|
||||
|
||||
while ((event = libinput_get_event(li))) {
|
||||
enum libinput_event_type type;
|
||||
type = libinput_event_get_type(event);
|
||||
|
||||
if (type == LIBINPUT_EVENT_DEVICE_ADDED) {
|
||||
ck_assert(sysname1 == NULL);
|
||||
device = libinput_event_get_device(event);
|
||||
ck_assert(device != NULL);
|
||||
sysname1 = libinput_device_get_sysname(device);
|
||||
}
|
||||
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
device = libinput_path_add_device(li,
|
||||
libevdev_uinput_get_devnode(dev->uinput));
|
||||
ck_assert(device != NULL);
|
||||
|
||||
libinput_dispatch(li);
|
||||
|
||||
while ((event = libinput_get_event(li))) {
|
||||
enum libinput_event_type type;
|
||||
type = libinput_event_get_type(event);
|
||||
|
||||
if (type == LIBINPUT_EVENT_DEVICE_ADDED) {
|
||||
ck_assert(sysname2 == NULL);
|
||||
device = libinput_event_get_device(event);
|
||||
ck_assert(device != NULL);
|
||||
sysname2 = libinput_device_get_sysname(device);
|
||||
}
|
||||
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
ck_assert_int_eq(strcmp(sysname1, sysname2), 0);
|
||||
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(path_device_sysname)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
|
|
@ -220,6 +270,67 @@ START_TEST(path_device_sysname)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(path_remove_device)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct libinput_event *event;
|
||||
struct libinput_device *device;
|
||||
int remove_event = 0;
|
||||
|
||||
device = libinput_path_add_device(li,
|
||||
libevdev_uinput_get_devnode(dev->uinput));
|
||||
ck_assert(device != NULL);
|
||||
litest_drain_events(li);
|
||||
|
||||
libinput_path_remove_device(device);
|
||||
libinput_dispatch(li);
|
||||
|
||||
while ((event = libinput_get_event(li))) {
|
||||
enum libinput_event_type type;
|
||||
type = libinput_event_get_type(event);
|
||||
|
||||
if (type == LIBINPUT_EVENT_DEVICE_REMOVED)
|
||||
remove_event++;
|
||||
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
ck_assert_int_eq(remove_event, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(path_double_remove_device)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct libinput_event *event;
|
||||
struct libinput_device *device;
|
||||
int remove_event = 0;
|
||||
|
||||
device = libinput_path_add_device(li,
|
||||
libevdev_uinput_get_devnode(dev->uinput));
|
||||
ck_assert(device != NULL);
|
||||
litest_drain_events(li);
|
||||
|
||||
libinput_path_remove_device(device);
|
||||
libinput_path_remove_device(device);
|
||||
libinput_dispatch(li);
|
||||
|
||||
while ((event = libinput_get_event(li))) {
|
||||
enum libinput_event_type type;
|
||||
type = libinput_event_get_type(event);
|
||||
|
||||
if (type == LIBINPUT_EVENT_DEVICE_REMOVED)
|
||||
remove_event++;
|
||||
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
ck_assert_int_eq(remove_event, 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(path_suspend)
|
||||
{
|
||||
struct libinput *li;
|
||||
|
|
@ -347,6 +458,9 @@ int main (int argc, char **argv) {
|
|||
litest_add("path:seat events", path_added_seat, LITEST_ANY, LITEST_ANY);
|
||||
litest_add("path:device events", path_added_device, LITEST_ANY, LITEST_ANY);
|
||||
litest_add("path:device events", path_device_sysname, LITEST_ANY, LITEST_ANY);
|
||||
litest_add("path:device events", path_add_device, LITEST_ANY, LITEST_ANY);
|
||||
litest_add("path:device events", path_remove_device, LITEST_ANY, LITEST_ANY);
|
||||
litest_add("path:device events", path_double_remove_device, LITEST_ANY, LITEST_ANY);
|
||||
|
||||
return litest_run(argc, argv);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue