Commit 981f3a47 authored by Peter Hutterer's avatar Peter Hutterer

quirks: add the ability to disable custom event codes/types

This is a more flexible approach than adding a model flag and the C code to
just call libevdev_disable_event_code(). There's a risk users will think this
is is a configuration API but there are some devices out there (e.g. the
Microsoft Sculpt mouse) that need a more generic solution.

Case in point: the Sculpt mouse insists on holding BTN_SIDE down at all times.
We cannot ship any quirks for that device because we only have the receiver's
generic VID/PID. So a local override is required, but we might as well make
that one generic enough to catch other devices too in the future.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 49b58311
......@@ -177,3 +177,7 @@ AttrTPKComboLayout=below
Indicates the position of the touchpad on an external touchpad+keyboard
combination device. This is a string enum. Don't specify it unless the
touchpad is below.
AttrEventCodeDisable=EV_ABS;BTN_STYLUS;EV_KEY:0x123;
Disables the evdev event type/code tuples on the device. Entries may be
a named event type, or a named event code, or a named event type with a
hexadecimal event code, separated by a single colon.
......@@ -217,7 +217,7 @@ src_libinput_util = [
]
libinput_util = static_library('libinput-util',
src_libinput_util,
dependencies : dep_udev,
dependencies : [dep_udev, dep_libevdev],
include_directories : includes_include)
dep_libinput_util = declare_dependency(link_with : libinput_util)
......
......@@ -1899,6 +1899,7 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
{
struct quirks_context *quirks;
struct quirks *q;
const struct quirk_tuples *t;
char *prop;
/* The Cyborg RAT has a mode button that cycles through event codes.
......@@ -2002,7 +2003,26 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
!streq(prop, "watch")) {
libevdev_disable_event_code(device->evdev, EV_MSC, MSC_TIMESTAMP);
}
if (q && quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE_DISABLE, &t)) {
int type, code;
for (size_t i = 0; i < t->ntuples; i++) {
type = t->tuples[i].first;
code = t->tuples[i].second;
if (code == EVENT_CODE_UNDEFINED)
libevdev_disable_event_type(device->evdev,
type);
else
libevdev_disable_event_code(device->evdev,
type,
code);
}
}
quirks_unref(q);
}
static void
......
......@@ -36,6 +36,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <libevdev/libevdev.h>
#include "libinput-util.h"
#include "libinput-private.h"
......@@ -398,6 +399,125 @@ parse_range_property(const char *prop, int *hi, int *lo)
return true;
}
static bool
parse_evcode_string(const char *s, int *type_out, int *code_out)
{
int type, code;
if (strneq(s, "EV_", 3)) {
type = libevdev_event_type_from_name(s);
if (type == -1)
return false;
code = EVENT_CODE_UNDEFINED;
} else {
struct map {
const char *str;
int type;
} map[] = {
{ "KEY_", EV_KEY },
{ "BTN_", EV_KEY },
{ "ABS_", EV_ABS },
{ "REL_", EV_REL },
{ "SW_", EV_SW },
};
struct map *m;
bool found = false;
ARRAY_FOR_EACH(map, m) {
if (!strneq(s, m->str, strlen(m->str)))
continue;
type = m->type;
code = libevdev_event_code_from_name(type, s);
if (code == -1)
return false;
found = true;
break;
}
if (!found)
return false;
}
*type_out = type;
*code_out = code;
return true;
}
/**
* Parses a string of the format "EV_ABS;KEY_A;BTN_TOOL_DOUBLETAP;ABS_X;"
* where each element must be a named event type OR a named event code OR a
* tuple in the form of EV_KEY:0x123, i.e. a named event type followed by a
* hex event code.
*
* events must point to an existing array of size nevents.
* nevents specifies the size of the array in events and returns the number
* of items, elements exceeding nevents are simply ignored, just make sure
* events is large enough for your use-case.
*
* The results are returned as input events with type and code set, all
* other fields undefined. Where only the event type is specified, the code
* is set to EVENT_CODE_UNDEFINED.
*
* On success, events contains nevents events.
*/
bool
parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents)
{
char **strv = NULL;
bool rc = false;
size_t ncodes = 0;
size_t idx;
struct input_event evs[*nevents];
memset(evs, 0, sizeof evs);
strv = strv_from_string(prop, ";");
if (!strv)
goto out;
for (idx = 0; strv[idx]; idx++)
ncodes++;
/* A randomly chosen max so we avoid crazy quirks */
if (ncodes == 0 || ncodes > 32)
goto out;
ncodes = min(*nevents, ncodes);
for (idx = 0; strv[idx]; idx++) {
char *s = strv[idx];
int type, code;
if (strstr(s, ":") == NULL) {
if (!parse_evcode_string(s, &type, &code))
goto out;
} else {
int consumed;
char stype[13] = {0}; /* EV_FF_STATUS + '\0' */
if (sscanf(s, "%12[A-Z_]:%x%n", stype, &code, &consumed) != 2 ||
strlen(s) != (size_t)consumed ||
(type = libevdev_event_type_from_name(stype)) == -1 ||
code < 0 || code > libevdev_event_type_get_max(type))
goto out;
}
evs[idx].type = type;
evs[idx].code = code;
}
memcpy(events, evs, ncodes * sizeof *events);
*nevents = ncodes;
rc = true;
out:
strv_free(strv);
return rc;
}
/**
* Return the next word in a string pointed to by state before the first
* separator character. Call repeatedly to tokenize a whole string.
......
......@@ -44,6 +44,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/input.h>
#include "libinput.h"
......@@ -426,6 +427,8 @@ int parse_mouse_wheel_click_count_property(const char *prop);
bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
bool parse_calibration_property(const char *prop, float calibration[6]);
bool parse_range_property(const char *prop, int *hi, int *lo);
#define EVENT_CODE_UNDEFINED 0xffff
bool parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
enum tpkbcombo_layout {
TPKBCOMBO_LAYOUT_UNKNOWN,
......
......@@ -57,6 +57,7 @@ enum property_type {
PT_DIMENSION,
PT_RANGE,
PT_DOUBLE,
PT_TUPLES,
};
/**
......@@ -75,9 +76,10 @@ struct property {
uint32_t u;
int32_t i;
char *s;
double d;
struct quirk_dimensions dim;
struct quirk_range range;
double d;
struct quirk_tuples tuples;
} value;
};
......@@ -273,6 +275,7 @@ quirk_get_name(enum quirk q)
case QUIRK_ATTR_USE_VELOCITY_AVERAGING: return "AttrUseVelocityAveraging";
case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: return "AttrThumbSizeThreshold";
case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp";
case QUIRK_ATTR_EVENT_CODE_DISABLE: return "AttrEventCodeDisable";
default:
abort();
}
......@@ -726,6 +729,22 @@ parse_attr(struct quirks_context *ctx,
goto out;
p->type = PT_STRING;
p->value.s = safe_strdup(value);
rc = true;
} else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE))) {
size_t nevents = 32;
struct input_event events[nevents];
p->id = QUIRK_ATTR_EVENT_CODE_DISABLE;
if (!parse_evcode_property(value, events, &nevents) ||
nevents == 0)
goto out;
for (size_t i = 0; i < nevents; i++) {
p->value.tuples.tuples[i].first = events[i].type;
p->value.tuples.tuples[i].second = events[i].code;
}
p->value.tuples.ntuples = nevents;
p->type = PT_TUPLES;
rc = true;
} else {
qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
......@@ -1543,3 +1562,23 @@ quirks_get_range(struct quirks *q,
return true;
}
bool
quirks_get_tuples(struct quirks *q,
enum quirk which,
const struct quirk_tuples **tuples)
{
struct property *p;
if (!q)
return false;
p = quirk_find_prop(q, which);
if (!p)
return false;
assert(p->type == PT_TUPLES);
*tuples = &p->value.tuples;
return true;
}
......@@ -50,6 +50,14 @@ struct quirk_range {
int lower, upper;
};
struct quirk_tuples {
struct {
int first;
int second;
} tuples[32];
size_t ntuples;
};
/**
* Quirks known to libinput
*/
......@@ -102,7 +110,7 @@ enum quirk {
QUIRK_ATTR_USE_VELOCITY_AVERAGING,
QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
QUIRK_ATTR_MSC_TIMESTAMP,
QUIRK_ATTR_EVENT_CODE_DISABLE,
_QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */
};
......@@ -293,3 +301,16 @@ bool
quirks_get_range(struct quirks *q,
enum quirk which,
struct quirk_range *val);
/**
* Get the tuples of the given quirk.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, tuples is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_tuples(struct quirks *q,
enum quirk which,
const struct quirk_tuples **tuples);
......@@ -1013,6 +1013,81 @@ START_TEST(range_prop_parser)
}
END_TEST
START_TEST(evcode_prop_parser)
{
struct parser_test_tuple {
const char *prop;
bool success;
size_t ntuples;
int tuples[20];
} tests[] = {
{ "EV_KEY", true, 1, {EV_KEY, 0xffff} },
{ "EV_ABS;", true, 1, {EV_ABS, 0xffff} },
{ "ABS_X;", true, 1, {EV_ABS, ABS_X} },
{ "SW_TABLET_MODE;", true, 1, {EV_SW, SW_TABLET_MODE} },
{ "EV_SW", true, 1, {EV_SW, 0xffff} },
{ "ABS_Y", true, 1, {EV_ABS, ABS_Y} },
{ "EV_ABS:0x00", true, 1, {EV_ABS, ABS_X} },
{ "EV_ABS:01", true, 1, {EV_ABS, ABS_Y} },
{ "ABS_TILT_X;ABS_TILT_Y;", true, 2,
{ EV_ABS, ABS_TILT_X,
EV_ABS, ABS_TILT_Y} },
{ "BTN_TOOL_DOUBLETAP;EV_KEY;KEY_A", true, 3,
{ EV_KEY, BTN_TOOL_DOUBLETAP,
EV_KEY, 0xffff,
EV_KEY, KEY_A } },
{ "REL_Y;ABS_Z;BTN_STYLUS", true, 3,
{ EV_REL, REL_Y,
EV_ABS, ABS_Z,
EV_KEY, BTN_STYLUS } },
{ "REL_Y;EV_KEY:0x123;BTN_STYLUS", true, 3,
{ EV_REL, REL_Y,
EV_KEY, 0x123,
EV_KEY, BTN_STYLUS } },
{ .prop = "", .success = false },
{ .prop = "EV_FOO", .success = false },
{ .prop = "EV_KEY;EV_FOO", .success = false },
{ .prop = "BTN_STYLUS;EV_FOO", .success = false },
{ .prop = "BTN_UNKNOWN", .success = false },
{ .prop = "BTN_UNKNOWN;EV_KEY", .success = false },
{ .prop = "PR_UNKNOWN", .success = false },
{ .prop = "BTN_STYLUS;PR_UNKNOWN;ABS_X", .success = false },
{ .prop = "EV_REL:0xffff", .success = false },
{ .prop = "EV_REL:0x123.", .success = false },
{ .prop = "EV_REL:ffff", .success = false },
{ .prop = "EV_REL:blah", .success = false },
{ .prop = "KEY_A:0x11", .success = false },
{ .prop = "EV_KEY:0x11 ", .success = false },
{ .prop = "EV_KEY:0x11not", .success = false },
{ .prop = "none", .success = false },
{ .prop = NULL },
};
struct parser_test_tuple *t = tests;
for (int i = 0; tests[i].prop; i++) {
bool success;
size_t nevents = 32;
struct input_event events[nevents];
t = &tests[i];
success = parse_evcode_property(t->prop, events, &nevents);
ck_assert(success == t->success);
if (!success)
continue;
ck_assert_int_eq(nevents, t->ntuples);
for (size_t j = 0; j < nevents; j++) {
int type, code;
type = events[j].type;
code = events[j].code;
ck_assert_int_eq(t->tuples[j * 2], type);
ck_assert_int_eq(t->tuples[j * 2 + 1], code);
}
}
}
END_TEST
START_TEST(time_conversion)
{
ck_assert_int_eq(us(10), 10);
......@@ -1728,6 +1803,7 @@ TEST_COLLECTION(misc)
litest_add_deviceless("misc:parser", reliability_prop_parser);
litest_add_deviceless("misc:parser", calibration_prop_parser);
litest_add_deviceless("misc:parser", range_prop_parser);
litest_add_deviceless("misc:parser", evcode_prop_parser);
litest_add_deviceless("misc:parser", safe_atoi_test);
litest_add_deviceless("misc:parser", safe_atoi_base_16_test);
litest_add_deviceless("misc:parser", safe_atoi_base_8_test);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment