Add support for LIBINPUT_IGNORE_DEVICE

The recommended way to have libinput ignore specific devices so far was to
remove the ID_INPUT* properties from the device. That may also affect other
pieces of the stack that need access to this device.

For the niche case of a device that should only be ignored by libinput but
otherwise be treated normally by the system, we now support the
LIBINPUT_IGNORE_DEVICE property.

If the property is set to "0", it's equivalent to being unset. This gets
around some technical limitations in udev where unsetting a property is
impossible via a hwdb entry.

https://bugs.freedesktop.org/show_bug.cgi?id=102229

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2017-08-17 01:25:24 +02:00
parent dddcc1f959
commit 00272cfbb1
8 changed files with 196 additions and 0 deletions

View file

@ -32,6 +32,10 @@ Example values are:
devices with the same property value are grouped into the same device group,
the value itself is irrelevant otherwise.
</dd>
<dt>LIBINPUT_IGNORE_DEVICE</dt>
<dd>If set to anything other than "0", the device is ignored by libinput.
See @ref ignoring_devices for more details.
</dd>
<dt>ID_SEAT</dt>
<dd>Assigns the physical @ref seats "seat" for this device. See
libinput_seat_get_physical_name(). Defaults to "seat0".</dd>
@ -105,6 +109,29 @@ ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1"
@endcode
@section ignoring_devices Ignoring specific devices
If a device has the <b>LIBINPUT_IGNORE_DEVICE</b> udev property set to any
value but "0", that device is not initialized by libinput. For a context
created with libinput_udev_create_context(), the device is silently ignored
and never shows up. If the device is added with libinput_path_add_device()
to a context created with libinput_path_create_context(), adding the device
will fail and return NULL (see that function's documentation for more
information).
If the property value is exactly "0", then the property is considered unset
and libinput initializes the device normally.
This property should be used for devices that are correctly detected as
input devices (see @ref udev_device_type) but that should not be used by
libinput. It is recommended that devices that should not be handled as input
devices at all unset the <b>ID_INPUT</b> and related properties instead. The
<b>LIBINPUT_IGNORE_DEVICE</b> property signals that only libinput should
ignore this property but other parts of the stack (if any) should continue
treating this device normally.
@section model_specific_configuration Model-specific configuration
libinput reserves the property prefixes <b>LIBINPUT_MODEL_</b> and

View file

@ -541,6 +541,7 @@ if get_option('tests')
'test/litest-device-generic-singletouch.c',
'test/litest-device-gpio-keys.c',
'test/litest-device-huion-pentablet.c',
'test/litest-device-ignored-mouse.c',
'test/litest-device-keyboard.c',
'test/litest-device-keyboard-all-codes.c',
'test/litest-device-keyboard-razer-blackwidow.c',

View file

@ -3083,6 +3083,17 @@ libevdev_log_func(const struct libevdev *evdev,
log_msg_va(libinput, pri, fmt, args);
}
static bool
udev_device_should_be_ignored(struct udev_device *udev_device)
{
const char *value;
value = udev_device_get_property_value(udev_device,
"LIBINPUT_IGNORE_DEVICE");
return value && !streq(value, "0");
}
struct evdev_device *
evdev_device_create(struct libinput_seat *seat,
struct udev_device *udev_device)
@ -3095,6 +3106,11 @@ evdev_device_create(struct libinput_seat *seat,
const char *devnode = udev_device_get_devnode(udev_device);
const char *sysname = udev_device_get_sysname(udev_device);
if (udev_device_should_be_ignored(udev_device)) {
log_debug(libinput, "%s: device is ignored\n", sysname);
return NULL;
}
/* Use non-blocking mode so that we can loop on read on
* evdev_device_data() until all events on the fd are
* read. mtdev_get() also expects this. */

View file

@ -0,0 +1,73 @@
/*
* Copyright © 2017 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 "litest.h"
#include "litest-int.h"
static void litest_mouse_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_IGNORED_MOUSE);
litest_set_current_device(d);
}
static struct input_id input_id = {
.bustype = 0x3,
.vendor = 0x17ef,
.product = 0x6019,
};
static int events[] = {
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
EV_KEY, BTN_MIDDLE,
EV_REL, REL_X,
EV_REL, REL_Y,
EV_REL, REL_WHEEL,
-1 , -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"mouse_end\"\n"
"KERNEL!=\"event*\", GOTO=\"mouse_end\"\n"
"ENV{ID_INPUT_MOUSE}==\"\", GOTO=\"mouse_end\"\n"
"\n"
"ATTRS{name}==\"litest Ignored Mouse*\",\\\n"
" ENV{LIBINPUT_IGNORE_DEVICE}=\"1\"\n"
"\n"
"LABEL=\"mouse_end\"";
struct litest_test_device litest_ignored_mouse_device = {
.type = LITEST_IGNORED_MOUSE,
.features = LITEST_IGNORED | LITEST_BUTTON | LITEST_RELATIVE,
.shortname = "ignored-mouse",
.setup = litest_mouse_setup,
.interface = NULL,
.name = "Ignored Mouse",
.id = &input_id,
.absinfo = NULL,
.events = events,
.udev_rule = udev_rule,
};

View file

@ -415,6 +415,7 @@ extern struct litest_test_device litest_lid_switch_device;
extern struct litest_test_device litest_lid_switch_surface3_device;
extern struct litest_test_device litest_appletouch_device;
extern struct litest_test_device litest_gpio_keys_device;
extern struct litest_test_device litest_ignored_mouse_device;
struct litest_test_device* devices[] = {
&litest_synaptics_clickpad_device,
@ -481,6 +482,7 @@ struct litest_test_device* devices[] = {
&litest_lid_switch_surface3_device,
&litest_appletouch_device,
&litest_gpio_keys_device,
&litest_ignored_mouse_device,
NULL,
};
@ -610,6 +612,9 @@ litest_add_tcase(const char *suite_name,
added = true;
} else if (required != LITEST_ANY || excluded != LITEST_ANY) {
for (; *dev; dev++) {
if ((*dev)->features & LITEST_IGNORED)
continue;
if (filter_device &&
fnmatch(filter_device, (*dev)->shortname, 0) != 0)
continue;
@ -626,6 +631,9 @@ litest_add_tcase(const char *suite_name,
}
} else {
for (; *dev; dev++) {
if ((*dev)->features & LITEST_IGNORED)
continue;
if (filter_device &&
fnmatch(filter_device, (*dev)->shortname, 0) != 0)
continue;

View file

@ -235,6 +235,7 @@ enum litest_device_type {
LITEST_LID_SWITCH_SURFACE3,
LITEST_APPLETOUCH,
LITEST_GPIO_KEYS,
LITEST_IGNORED_MOUSE,
};
enum litest_device_feature {
@ -267,6 +268,7 @@ enum litest_device_feature {
LITEST_TRACKBALL = 1 << 24,
LITEST_LEDS = 1 << 25,
LITEST_SWITCH = 1 << 26,
LITEST_IGNORED = 1 << 27,
};
/* this is a semi-mt device, so we keep track of the touches that the tests

View file

@ -961,6 +961,26 @@ START_TEST(path_udev_assign_seat)
}
END_TEST
START_TEST(path_ignore_device)
{
struct litest_device *dev;
struct libinput *li;
struct libinput_device *device;
const char *path;
dev = litest_create(LITEST_IGNORED_MOUSE, NULL, NULL, NULL, NULL);
path = libevdev_uinput_get_devnode(dev->uinput);
ck_assert_notnull(path);
li = litest_create_context();
device = libinput_path_add_device(li, path);
ck_assert(device == NULL);
libinput_unref(li);
litest_delete_device(dev);
}
END_TEST
void
litest_setup_tests_path(void)
{
@ -987,4 +1007,6 @@ litest_setup_tests_path(void)
litest_add_for_device("path:device events", path_double_remove_device, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add_no_device("path:seat", path_seat_recycle);
litest_add_for_device("path:udev", path_udev_assign_seat, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add_no_device("path:ignore", path_ignore_device);
}

View file

@ -560,6 +560,51 @@ START_TEST(udev_path_remove_device)
}
END_TEST
START_TEST(udev_ignore_device)
{
struct udev *udev;
struct libinput *li;
struct libinput_device *device;
struct libinput_event *event;
struct litest_device *dev;
const char *devname;
dev = litest_create(LITEST_IGNORED_MOUSE, NULL, NULL, NULL, NULL);
devname = libevdev_get_name(dev->evdev);
udev = udev_new();
ck_assert(udev != NULL);
li = libinput_udev_create_context(&simple_interface, NULL, udev);
ck_assert(li != NULL);
litest_restore_log_handler(li);
ck_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
libinput_dispatch(li);
event = libinput_get_event(li);
ck_assert(event != NULL);
while (event) {
if (libinput_event_get_type(event) ==
LIBINPUT_EVENT_DEVICE_ADDED) {
const char *name;
device = libinput_event_get_device(event);
name = libinput_device_get_name(device);
ck_assert_str_ne(devname, name);
}
libinput_event_destroy(event);
libinput_dispatch(li);
event = libinput_get_event(li);
}
libinput_unref(li);
udev_unref(udev);
litest_delete_device(dev);
}
END_TEST
void
litest_setup_tests_udev(void)
{
@ -579,4 +624,6 @@ litest_setup_tests_udev(void)
litest_add_no_device("udev:path", udev_path_add_device);
litest_add_for_device("udev:path", udev_path_remove_device, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add_no_device("udev:ignore", udev_ignore_device);
}