quirks: allow overriding of AttrEventCode and AttrInputProp

This switches the quirk from AttrEventCodeEnable/Disable to just
AttrEventCode with a +/- prefix for each entry.
This switches the quirk from AttrInputPropEnable/Disable to just
AttrInputProp with a +/- prefix for each entry.

Previously, both event codes and input props would only apply the
last-matching section entry for a device. Furthermore, an earlier Disable entry
would take precedence over a later Enable entry. For example, a set of
sections with these lines *should* enable left, right and middle:

  [first]
  AttrEventCodeEnable=BTN_LEFT;BTN_RIGHT;BTN_MIDDLE

  [second]
  AttrEventCodeDisable=BTN_RIGHT

  [third]
  AttrEventCodeEnable=BTN_LEFT;BTN_RIGHT;

Alas: the first line was effectively ignored (quirks only returned the
last-matching one, i.e. the one from "third"). And due to implementation
details in evdev.c, the Disable attribute was processed after Enable,
i.e. the device was enabled for left + right and then disabled for
right. As a result, the device only had BTN_LEFT enabled.

Fix this by changing the attribute to carry both enable/disable
information and merging the commands together.

Internally, all quirks matching a device are simply ref'd into an array
in the struct quirks. The applied value is simply the last entry in the
array corresponding to our quirk.

For AttrEventCode and AttrInputProp instead do this:
- switch them to a tuple with the code as first entry and a boolean
  enable/disable as second entry
- if the struct quirk already has an entry for either, append the more
  recent one to the existing entry (instead of creating a new entry in
  the array). This way we have all entries that match and in-order of
  precedence - i.e. we can process them left-to-right to end up
  with the right state.

Fixes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/821

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2022-11-15 13:53:43 +10:00
parent bb6ff0ec00
commit a7e4cbc212
29 changed files with 332 additions and 211 deletions

View file

@ -30,7 +30,7 @@ MyVendor OEM::
MatchName=Foo Bar Touchpad
MatchUdevtype=touchpad
MatchDMIModAlias=dmi:*svnMyVendor:pnMyModel:*
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT
The name of the device can be obtained using :ref:`libinput record <libinput-record>`,
the modalias match is a shell-style glob against the value of ``/sys/class/dmi/id/modalias``.

View file

@ -174,19 +174,14 @@ 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
AttrEventCode=+EV_ABS;-BTN_STYLUS;+EV_KEY:0x123;
Enables or disables the evdev event type/code tuples on the device. The prefix
for each entry is either '+' (enable) or '-' (disable). 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.
AttrEventCodeEnable=EV_ABS;BTN_STYLUS;EV_KEY:0x123;
Enables 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.
AttrInputPropDisable=INPUT_PROP_BUTTONPAD;INPUT_PROP_POINTER;
Disables the evdev input property on the device. Entries may be
a named input property or the hexadecimal value of that property.
AttrInputPropEnable=INPUT_PROP_BUTTONPAD;INPUT_PROP_POINTER;
Enables the evdev input property on the device. Entries may be
AttrInputProp=+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;
Enables or disables the evdev input property on the device. The prefix
for each entry is either '+' (enable) or '-' (disable). Entries may be
a named input property or the hexadecimal value of that property.
AttrPointingStickIntegration=internal|external
Indicates the integration of the pointing stick. This is a string enum.

View file

@ -57,6 +57,6 @@ events are sent: ::
The issue can be fixed by adding a quirk to unset the ``REL_WHEEL_HI_RES`` and
``REL_HWHEEL_HI_RES`` event codes: ::
AttrEventCodeDisable=REL_WHEEL_HI_RES;REL_HWHEEL_HI_RES;
AttrEventCode=-REL_WHEEL_HI_RES;-REL_HWHEEL_HI_RES;
Please see :ref:`device-quirks` for details.

View file

@ -4,7 +4,7 @@
MatchUdevType=tablet
MatchBus=usb
MatchVendor=0x08CA
AttrEventCodeDisable=ABS_TILT_X;ABS_TILT_Y;
AttrEventCode=-ABS_TILT_X;-ABS_TILT_Y;
[Aiptek 8000U pressure threshold]
MatchUdevType=tablet

View file

@ -4,4 +4,4 @@ MatchBus=usb
MatchVendor=0x047D
MatchProduct=0x2048
ModelTrackball=1
AttrEventCodeDisable=BTN_MIDDLE
AttrEventCode=-BTN_MIDDLE

View file

@ -10,7 +10,7 @@ MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x46D
MatchProduct=0xC408
AttrEventCodeDisable=BTN_MIDDLE
AttrEventCode=-BTN_MIDDLE
[Logitech K400]
MatchUdevType=mouse

View file

@ -21,7 +21,7 @@ MatchBus=usb
MatchVendor=0x0738
MatchProduct=0x1703
# EV_KEY 0x115, 0x116, 0x117
AttrEventCodeDisable=EV_KEY:0x115;EV_KEY:0x116;EV_KEY:0x117
AttrEventCode=-EV_KEY:0x115;-EV_KEY:0x116;-EV_KEY:0x117
# Like the Madcatz RAT3, but with different codes:
# event8 POINTER_BUTTON +0.488s ??? (280) pressed, seat count: 1
@ -37,4 +37,4 @@ MatchBus=usb
MatchVendor=0x0738
MatchProduct=0x1708
# EV_KEY 0x118, 0x119, 0x11A
AttrEventCodeDisable=EV_KEY:0x118;EV_KEY:0x119;EV_KEY:0x11A
AttrEventCode=-EV_KEY:0x118;-EV_KEY:0x119;-EV_KEY:0x11A

View file

@ -16,4 +16,4 @@ ModelSynapticsSerialTouchpad=1
[Synaptics 0911:5288 Touchpad]
MatchUdevType=touchpad
MatchName=* 0911:5288 Touchpad
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT

View file

@ -18,11 +18,11 @@ MatchUdevType=tablet
MatchBus=usb
MatchVendor=0x56A
MatchProduct=0x4200
AttrEventCodeDisable=ABS_TILT_X;ABS_TILT_Y;
AttrEventCode=-ABS_TILT_X;-ABS_TILT_Y;
[Wacom ISDV4 524c Pen]
MatchUdevType=tablet
MatchBus=usb
MatchVendor=0x2D1F
MatchProduct=0x524C
AttrEventCodeDisable=ABS_TILT_X;ABS_TILT_Y;
AttrEventCode=-ABS_TILT_X;-ABS_TILT_Y;

View file

@ -2,4 +2,4 @@
MatchName=PIXA3854:00 093A:0274 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnFramework:pnLaptop*
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT

View file

@ -41,7 +41,7 @@ MatchUdevType=mouse
MatchBus=bluetooth
MatchVendor=0x05AC
MatchProduct=0x030D
AttrEventCodeDisable=EV_ABS
AttrEventCode=-EV_ABS
# The External Apple "Magic" trackpads, both the 1st and 2nd generations, have
# pretty good built-in spurious touch filtering in the device firmware. Using

View file

@ -16,7 +16,7 @@ AttrPressureRange=24:10
[Asus UX302LA]
MatchName=*ETPS/2 Elantech Touchpad*
MatchDMIModalias=dmi:*svnASUSTeKCOMPUTERINC.:pnUX302LA:*
AttrEventCodeDisable=ABS_MT_PRESSURE;ABS_PRESSURE;
AttrEventCode=-ABS_MT_PRESSURE;-ABS_PRESSURE;
# Asus VivoBook Flip 14 TP412UA tablet switch seems misbehaving, always
# indicating tablet position

View file

@ -1,9 +1,9 @@
[Chuwi Gemibook]
MatchName=HTIX5288:00 093A:1336 Touchpad
MatchUdevType=touchpad
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT
[Chuwi AeroBook Plus]
MatchName=SYNA3602:01 093A:1336 Touchpad
MatchUdevType=touchpad
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT

View file

@ -28,4 +28,4 @@ MatchBus=usb
MatchVendor=0x06A3
MatchProduct=0x0CD5
# EV_KEY 0x118, 0x119, 0x11a
AttrEventCodeDisable=EV_KEY:0x118;EV_KEY:0x119;EV_KEY:0x11a
AttrEventCode=-EV_KEY:0x118;-EV_KEY:0x119;-EV_KEY:0x11a

View file

@ -98,7 +98,7 @@ AttrTrackpointMultiplier=0.3
MatchBus=i2c
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnDellInc.:pnPrecision7?50*
AttrInputPropDisable=INPUT_PROP_BUTTONPAD
AttrInputProp=-INPUT_PROP_BUTTONPAD
# The touch device has the same vid/pid as the totem, the MatchName
# directive is required here

View file

@ -2,4 +2,4 @@
MatchName=HTIX5288:00 093A:0255 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnGPD:*pnG1619-*
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT

View file

@ -5,14 +5,14 @@
[HP Compaq 6910p]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPCompaq6910p*
AttrEventCodeDisable=BTN_TOOL_DOUBLETAP;BTN_TOOL_TRIPLETAP;
AttrEventCode=-BTN_TOOL_DOUBLETAP;-BTN_TOOL_TRIPLETAP;
# Claims to have double/tripletap but doesn't actually send it
# https://bugzilla.redhat.com/show_bug.cgi?id=1351285 and
[HP Compaq 8510w]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPCompaq8510w*
AttrEventCodeDisable=BTN_TOOL_DOUBLETAP;BTN_TOOL_TRIPLETAP;
AttrEventCode=-BTN_TOOL_DOUBLETAP;-BTN_TOOL_TRIPLETAP;
[HP Pavilion dmi4]
MatchName=*SynPS/2 Synaptics TouchPad
@ -24,7 +24,7 @@ ModelHPPavilionDM4Touchpad=1
[HP Stream 11]
MatchName=SYN1EDE:00 06CB:7442*
MatchDMIModalias=dmi:*svnHewlett-Packard:pnHPStreamNotebookPC11*
AttrInputPropEnable=INPUT_PROP_BUTTONPAD
AttrInputProp=+INPUT_PROP_BUTTONPAD
# The HP stream x360's embedded-controller filters out events form its builtin
# keyboard when in tablet-mode itself; and it has a capacitive home-button

View file

@ -4,4 +4,4 @@
MatchName=ELAN2604:00 04F3:3114 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnHUAWEI:*pvrM1010*
AttrEventCodeDisable=BTN_RIGHT
AttrEventCode=-BTN_RIGHT

View file

@ -41,14 +41,14 @@ AttrTrackpointMultiplier=0.4
[Lenovo T480s Touchpad]
MatchName=Elan Touchpad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT480s*
AttrInputPropEnable=INPUT_PROP_BUTTONPAD
AttrInputProp=+INPUT_PROP_BUTTONPAD
# Touchpad is a clickpad but INPUT_PROP_BUTTONPAD is not set, see
# https://gitlab.freedesktop.org/libinput/libinput/issues/177
[Lenovo T490s Touchpad]
MatchName=Elan Touchpad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT490s*
AttrInputPropEnable=INPUT_PROP_BUTTONPAD
AttrInputProp=+INPUT_PROP_BUTTONPAD
[Lenovo T490s Trackpoint]
MatchName=*TPPS/2 IBM TrackPoint
@ -60,7 +60,7 @@ AttrTrackpointMultiplier=0.4
[Lenovo L380 Touchpad]
MatchName=Elan Touchpad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadL380*
AttrInputPropEnable=INPUT_PROP_BUTTONPAD
AttrInputProp=+INPUT_PROP_BUTTONPAD
[Lenovo X200 Trackpoint]
MatchName=*TPPS/2 IBM TrackPoint
@ -147,7 +147,7 @@ AttrTrackpointMultiplier=1.25
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCE37
AttrEventCodeDisable=ABS_MT_PRESSURE;ABS_PRESSURE;
AttrEventCode=-ABS_MT_PRESSURE;-ABS_PRESSURE;
[Lenovo Yoga C930 Tablet]
MatchBus=i2c
@ -160,7 +160,7 @@ AttrTabletSmoothing=1
[Lenovo Carbon X1 6th gen]
MatchName=Synaptics TM3288-011
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkPadX1Carbon6th:*
AttrEventCodeDisable=ABS_MT_TOOL_TYPE
AttrEventCode=-ABS_MT_TOOL_TYPE
ModelLenovoX1Gen6Touchpad=1
[Lenovo X41 Tablet]
@ -272,7 +272,7 @@ AttrKeyboardIntegration=internal
MatchBus=i2c
MatchVendor=0x27C6
MatchProduct=0x01E8
AttrEventCodeDisable=ABS_MT_PRESSURE;ABS_PRESSURE;
AttrEventCode=-ABS_MT_PRESSURE;-ABS_PRESSURE;
# Duet 7i tablet switch activated by folding keyboard cover, or removing it.
# We must not disable volume rocker 'keyboard'.

View file

@ -2,5 +2,5 @@
MatchName=*Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnStarLabs:*
AttrEventCodeDisable=BTN_RIGHT
AttrInputPropEnable=INPUT_PROP_BUTTONPAD
AttrEventCode=-BTN_RIGHT
AttrInputProp=+INPUT_PROP_BUTTONPAD

View file

@ -2231,8 +2231,6 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
struct quirks_context *quirks;
struct quirks *q;
const struct quirk_tuples *t;
const uint32_t *props = NULL;
size_t nprops = 0;
char *prop;
/* Touchpad claims to have 4 slots but only ever sends 2
@ -2251,7 +2249,7 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
libevdev_disable_event_code(device->evdev, EV_MSC, MSC_TIMESTAMP);
}
if (quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE_ENABLE, &t)) {
if (quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE, &t)) {
for (size_t i = 0; i < t->ntuples; i++) {
const struct input_absinfo absinfo = {
.minimum = 0,
@ -2260,37 +2258,27 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
int type = t->tuples[i].first;
int code = t->tuples[i].second;
bool enable = t->tuples[i].third;
if (code == EVENT_CODE_UNDEFINED)
if (code == EVENT_CODE_UNDEFINED) {
if (enable)
libevdev_enable_event_type(device->evdev, type);
else
libevdev_disable_event_type(device->evdev, type);
} else {
if (enable)
libevdev_enable_event_code(device->evdev,
type,
code,
type == EV_ABS ? &absinfo : NULL);
evdev_log_debug(device,
"quirks: enabling %s %s (%#x %#x)\n",
libevdev_event_type_get_name(type),
libevdev_event_code_get_name(type, code),
type,
code);
}
}
if (quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE_DISABLE, &t)) {
for (size_t i = 0; i < t->ntuples; i++) {
int type = t->tuples[i].first;
int 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);
}
evdev_log_debug(device,
"quirks: disabling %s %s (%#x %#x)\n",
"quirks: %s %s %s (%#x %#x)\n",
enable ? "enabling" : "disabling",
libevdev_event_type_get_name(type),
libevdev_event_code_get_name(type, code),
type,
@ -2298,38 +2286,29 @@ evdev_pre_configure_model_quirks(struct evdev_device *device)
}
}
if (quirks_get_uint32_array(q,
QUIRK_ATTR_INPUT_PROP_ENABLE,
&props,
&nprops)) {
for (size_t idx = 0; idx < nprops; idx++) {
unsigned int p = props[idx];
libevdev_enable_property(device->evdev, p);
evdev_log_debug(device,
"quirks: enabling %s (%#x)\n",
libevdev_property_get_name(p),
p);
}
}
if (quirks_get_tuples(q, QUIRK_ATTR_INPUT_PROP, &t)) {
for (size_t idx = 0; idx < t->ntuples; idx++) {
unsigned int p = t->tuples[idx].first;
bool enable = t->tuples[idx].second;
if (quirks_get_uint32_array(q,
QUIRK_ATTR_INPUT_PROP_DISABLE,
&props,
&nprops)) {
#if HAVE_LIBEVDEV_DISABLE_PROPERTY
for (size_t idx = 0; idx < nprops; idx++) {
unsigned int p = props[idx];
libevdev_disable_property(device->evdev, p);
evdev_log_debug(device,
"quirks: disabling %s (%#x)\n",
libevdev_property_get_name(p),
p);
if (enable) {
libevdev_enable_property(device->evdev, p);
}
else {
#if HAVE_LIBEVDEV_DISABLE_PROPERTY
libevdev_disable_property(device->evdev, p);
#else
evdev_log_error(device,
"quirks: a quirk for this device requires newer libevdev than installed\n");
#endif
}
evdev_log_debug(device,
"quirks: %s %s (%#x)\n",
enable ? "enabling" : "disabling",
libevdev_property_get_name(p),
p);
}
}
quirks_unref(q);
}

View file

@ -175,6 +175,10 @@ struct quirks {
/* These are not ref'd, just a collection of pointers */
struct property **properties;
size_t nproperties;
/* Special properties for AttrEventCode and AttrInputCode, these are
* owned by us, not the section */
struct list floating_properties;
};
/**
@ -282,10 +286,8 @@ quirk_get_name(enum quirk q)
case QUIRK_ATTR_TABLET_SMOOTHING: return "AttrTabletSmoothing";
case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: return "AttrThumbSizeThreshold";
case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp";
case QUIRK_ATTR_EVENT_CODE_DISABLE: return "AttrEventCodeDisable";
case QUIRK_ATTR_EVENT_CODE_ENABLE: return "AttrEventCodeEnable";
case QUIRK_ATTR_INPUT_PROP_DISABLE: return "AttrInputPropDisable";
case QUIRK_ATTR_INPUT_PROP_ENABLE: return "AttrInputPropEnable";
case QUIRK_ATTR_EVENT_CODE: return "AttrEventCode";
case QUIRK_ATTR_INPUT_PROP: return "AttrInputProp";
default:
abort();
}
@ -817,14 +819,11 @@ parse_attr(struct quirks_context *ctx,
p->type = PT_STRING;
p->value.s = safe_strdup(value);
rc = true;
} else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE)) ||
streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_ENABLE))) {
} else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE))) {
struct input_event events[32];
size_t nevents = ARRAY_LENGTH(events);
if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE)))
p->id = QUIRK_ATTR_EVENT_CODE_DISABLE;
else
p->id = QUIRK_ATTR_EVENT_CODE_ENABLE;
p->id = QUIRK_ATTR_EVENT_CODE;
if (!parse_evcode_property(value, events, &nevents) ||
nevents == 0)
@ -833,27 +832,29 @@ parse_attr(struct quirks_context *ctx,
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.tuples[i].third = events[i].value;
}
p->value.tuples.ntuples = nevents;
p->type = PT_TUPLES;
rc = true;
} else if (streq(key, quirk_get_name(QUIRK_ATTR_INPUT_PROP_DISABLE)) ||
streq(key, quirk_get_name(QUIRK_ATTR_INPUT_PROP_ENABLE))) {
unsigned int props[INPUT_PROP_CNT];
} else if (streq(key, quirk_get_name(QUIRK_ATTR_INPUT_PROP))) {
struct input_prop props[INPUT_PROP_CNT];
size_t nprops = ARRAY_LENGTH(props);
if (streq(key, quirk_get_name(QUIRK_ATTR_INPUT_PROP_DISABLE)))
p->id = QUIRK_ATTR_INPUT_PROP_DISABLE;
else
p->id = QUIRK_ATTR_INPUT_PROP_ENABLE;
p->id = QUIRK_ATTR_INPUT_PROP;
if (!parse_input_prop_property(value, props, &nprops) ||
nprops == 0)
goto out;
memcpy(p->value.array.data.u, props, nprops * sizeof(unsigned int));
p->value.array.nelements = nprops;
p->type = PT_UINT_ARRAY;
for (size_t i = 0; i < nprops; i++) {
p->value.tuples.tuples[i].first = props[i].prop;
p->value.tuples.tuples[i].second = props[i].enabled;
}
p->value.tuples.ntuples = nprops;
p->type = PT_TUPLES;
rc = true;
} else {
@ -1201,6 +1202,7 @@ quirks_new(void)
q->refcount = 1;
q->nproperties = 0;
list_init(&q->link);
list_init(&q->floating_properties);
return q;
}
@ -1219,6 +1221,13 @@ quirks_unref(struct quirks *q)
property_unref(q->properties[i]);
}
/* Floating properties are owned by our quirks context, need to be
* cleaned up here */
struct property *p;
list_for_each_safe(p, &q->floating_properties, link) {
property_cleanup(p);
}
list_remove(&q->link);
free(q->properties);
free(q);
@ -1378,6 +1387,41 @@ match_free(struct match *m)
free(m);
}
static void
quirk_merge_event_codes(struct quirks_context *ctx,
struct quirks *q,
const struct property *property)
{
for (size_t i = 0; i < q->nproperties; i++) {
struct property *p = q->properties[i];
if (p->id != property->id)
continue;
/* We have a duplicated property, merge in with ours */
size_t offset = p->value.tuples.ntuples;
size_t max = ARRAY_LENGTH(p->value.tuples.tuples);
for (size_t j = 0; j < property->value.tuples.ntuples; j++) {
if (offset + j >= max)
break;
p->value.tuples.tuples[offset + j] = property->value.tuples.tuples[j];
p->value.tuples.ntuples++;
}
return;
}
/* First time we add AttrEventCode: create a new property.
* Unlike the other properties, this one isn't part of a section, it belongs
* to the quirks */
struct property *newprop = property_new();
newprop->id = property->id;
newprop->type = property->type;
newprop->value.tuples = property->value.tuples;
/* Caller responsible for pre-allocating space */
q->properties[q->nproperties++] = property_ref(newprop);
list_append(&q->floating_properties, &newprop->link);
}
static void
quirk_apply_section(struct quirks_context *ctx,
struct quirks *q,
@ -1401,6 +1445,25 @@ quirk_apply_section(struct quirks_context *ctx,
qlog_debug(ctx, "property added: %s from %s\n",
quirk_get_name(p->id), s->name);
/* All quirks but AttrEventCode and AttrInputProp
* simply overwrite each other, so we can just append the
* matching property and, later when checking the quirk, pick
* the last one in the array.
*
* The event codes/input props are special because they're lists
* that may *partially* override each other, e.g. a section may
* enable BTN_LEFT and BTN_RIGHT but a later section may disable
* only BTN_RIGHT. This should result in BTN_LEFT force-enabled
* and BTN_RIGHT force-disabled.
*
* To hack around this, those are the only ones where only ever
* have one struct property in the list (not owned by a section)
* and we simply merge any extra sections onto that.
*/
if (p->id == QUIRK_ATTR_EVENT_CODE ||
p->id == QUIRK_ATTR_INPUT_PROP)
quirk_merge_event_codes(ctx, q, p);
else
q->properties[q->nproperties++] = property_ref(p);
}
}

View file

@ -54,6 +54,7 @@ struct quirk_tuples {
struct {
int first;
int second;
int third;
} tuples[32];
size_t ntuples;
};
@ -104,10 +105,8 @@ enum quirk {
QUIRK_ATTR_TABLET_SMOOTHING,
QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
QUIRK_ATTR_MSC_TIMESTAMP,
QUIRK_ATTR_EVENT_CODE_DISABLE,
QUIRK_ATTR_EVENT_CODE_ENABLE,
QUIRK_ATTR_INPUT_PROP_DISABLE,
QUIRK_ATTR_INPUT_PROP_ENABLE,
QUIRK_ATTR_EVENT_CODE,
QUIRK_ATTR_INPUT_PROP,
_QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */
};

View file

@ -347,10 +347,10 @@ parse_evcode_string(const char *s, int *type_out, int *code_out)
}
/**
* 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.
* Parses a string of the format "+EV_ABS;+KEY_A;-BTN_TOOL_DOUBLETAP;-ABS_X;"
* where each element must be + or - (enable/disable) followed by 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
@ -361,7 +361,8 @@ parse_evcode_string(const char *s, int *type_out, int *code_out)
* other fields undefined. Where only the event type is specified, the code
* is set to EVENT_CODE_UNDEFINED.
*
* On success, events contains nevents events.
* On success, events contains nevents events with each event's value set to 1
* or 0 depending on the + or - prefix.
*/
bool
parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents)
@ -380,6 +381,16 @@ parse_evcode_property(const char *prop, struct input_event *events, size_t *neve
ncodes = min(*nevents, ncodes);
for (size_t idx = 0; strv[idx]; idx++) {
char *s = strv[idx];
bool enable;
switch (*s) {
case '+': enable = true; break;
case '-': enable = false; break;
default:
goto out;
}
s++;
int type, code;
@ -399,6 +410,7 @@ parse_evcode_property(const char *prop, struct input_event *events, size_t *neve
evs[idx].type = type;
evs[idx].code = code;
evs[idx].value = enable;
}
memcpy(events, evs, ncodes * sizeof *events);
@ -411,9 +423,9 @@ out:
}
/**
* Parses a string of the format "INPUT_PROP_BUTTONPAD;INPUT_PROP_POINTER;0x123;"
* Parses a string of the format "+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;+0x123;"
* where each element must be a named input prop OR a hexcode in the form
* 0x1234
* 0x1234. The prefix for each element must be either '+' (enable) or '-' (disable).
*
* props must point to an existing array of size nprops.
* nprops specifies the size of the array in props and returns the number
@ -423,10 +435,10 @@ out:
* On success, props contains nprops elements.
*/
bool
parse_input_prop_property(const char *prop, unsigned int *props_out, size_t *nprops)
parse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops)
{
bool rc = false;
unsigned int props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */
struct input_prop props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */
size_t count;
char **strv = strv_from_string(prop, ";", &count);
@ -437,6 +449,16 @@ parse_input_prop_property(const char *prop, unsigned int *props_out, size_t *npr
for (size_t idx = 0; strv[idx]; idx++) {
char *s = strv[idx];
unsigned int prop;
bool enable;
switch (*s) {
case '+': enable = true; break;
case '-': enable = false; break;
default:
goto out;
}
s++;
if (safe_atou_base(s, &prop, 16)) {
if (prop > INPUT_PROP_MAX)
@ -447,7 +469,8 @@ parse_input_prop_property(const char *prop, unsigned int *props_out, size_t *npr
goto out;
prop = (unsigned int)val;
}
props[idx] = prop;
props[idx].prop = prop;
props[idx].enabled = enable;
}
memcpy(props_out, props, count * sizeof *props);

View file

@ -30,6 +30,11 @@
#include <stddef.h>
#include <stdint.h>
struct input_prop {
unsigned int prop;
bool enabled;
};
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
int parse_mouse_wheel_click_count_property(const char *prop);
@ -39,7 +44,7 @@ bool parse_range_property(const char *prop, int *hi, int *lo);
bool parse_boolean_property(const char *prop, bool *b);
#define EVENT_CODE_UNDEFINED 0xffff
bool parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
bool parse_input_prop_property(const char *prop, unsigned int *props_out, size_t *nprops);
bool parse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops);
enum tpkbcombo_layout {
TPKBCOMBO_LAYOUT_UNKNOWN,

View file

@ -199,22 +199,46 @@ static int events[] = {
};
static const char quirk_file[] =
"[litest Quirked Keyboard enable rel]\n"
"[litest Quirked Keyboard enable buttons]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrEventCodeEnable=BTN_RIGHT;EV_KEY:0x110\n" /* BTN_LEFT */
"AttrEventCode=+BTN_RIGHT;+BTN_MIDDLE;+EV_KEY:0x110\n" /* BTN_LEFT */
"\n"
"[litest Quirked Keyboard disable buttons]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrEventCode=-BTN_MIDDLE;-BTN_RIGHT\n"
"\n"
"[litest Quirked Keyboard re-enable buttons]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrEventCode=+BTN_RIGHT\n"
"\n"
"[litest Quirked keyboard disable F1-F3]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrEventCodeDisable=KEY_F1;EV_KEY:0x3c;KEY_F3\n"
"AttrEventCode=-KEY_F1;-EV_KEY:0x3c;-KEY_F3\n"
#if HAVE_LIBEVDEV_DISABLE_PROPERTY
"\n"
"[litest Quirked keyboard enable buttonpad]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputPropEnable=INPUT_PROP_BUTTONPAD\n"
"AttrInputProp=+INPUT_PROP_BUTTONPAD\n"
"\n"
"[litest Quirked keyboard disable pointingstick]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputPropDisable=INPUT_PROP_POINTING_STICK\n"
"AttrInputProp=-INPUT_PROP_POINTING_STICK\n"
"\n"
"[litest Quirked keyboard enable direct]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputProp=+INPUT_PROP_DIRECT\n"
"\n"
"[litest Quirked keyboard disable direct]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputProp=-INPUT_PROP_DIRECT\n"
"\n"
"[litest Quirked keyboard disable semi-mt]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputProp=-INPUT_PROP_SEMI_MT\n"
"\n"
"[litest Quirked keyboard enable semi-mt]\n"
"MatchName=litest Quirked Keyboard\n"
"AttrInputProp=+INPUT_PROP_SEMI_MT\n"
#endif
;

View file

@ -1477,7 +1477,11 @@ START_TEST(device_quirks)
enable_btn_left = false;
#if HAVE_LIBEVDEV_DISABLE_PROPERTY
bool disable_pointingstick = false,
enable_buttonpad = false;
enable_buttonpad = false,
enable_direct = false,
disable_direct = false,
enable_semi_mt = false,
disable_semi_mt = false;
#endif
li = litest_create_context();
@ -1490,6 +1494,8 @@ START_TEST(device_quirks)
BTN_LEFT));
ck_assert(libinput_device_pointer_has_button(dev->libinput_device,
BTN_RIGHT));
ck_assert(!libinput_device_pointer_has_button(device,
BTN_MIDDLE));
ck_assert(!libinput_device_keyboard_has_key(dev->libinput_device,
KEY_F1));
ck_assert(!libinput_device_keyboard_has_key(dev->libinput_device,
@ -1510,6 +1516,22 @@ START_TEST(device_quirks)
enable_buttonpad = true;
if (strstr(*message, "disabling INPUT_PROP_POINTING_STICK"))
disable_pointingstick = true;
if (strstr(*message, "enabling INPUT_PROP_DIRECT")) {
ck_assert(!disable_direct);
enable_direct = true;
}
if (strstr(*message, "disabling INPUT_PROP_DIRECT")) {
ck_assert(enable_direct);
disable_direct = true;
}
if (strstr(*message, "enabling INPUT_PROP_SEMI_MT")) {
ck_assert(disable_semi_mt);
enable_semi_mt = true;
}
if (strstr(*message, "disabling INPUT_PROP_SEMI_MT")) {
ck_assert(!enable_semi_mt);
disable_semi_mt = true;
}
#endif
free(*message);
message++;
@ -1520,6 +1542,10 @@ START_TEST(device_quirks)
#if HAVE_LIBEVDEV_DISABLE_PROPERTY
ck_assert(enable_buttonpad);
ck_assert(disable_pointingstick);
ck_assert(enable_direct);
ck_assert(disable_direct);
ck_assert(enable_semi_mt);
ck_assert(disable_semi_mt);
#endif
litest_disable_log_handler(li);

View file

@ -560,47 +560,50 @@ START_TEST(evcode_prop_parser)
struct parser_test_tuple {
const char *prop;
bool success;
size_t ntuples;
int tuples[20];
size_t nevents;
struct input_event events[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 } },
{ "+EV_KEY", true, 1, {{ .type = EV_KEY, .code = 0xffff, .value = 1 }} },
{ "-EV_ABS;", true, 1, {{ .type = EV_ABS, .code = 0xffff, .value = 0 }} },
{ "+ABS_X;", true, 1, {{ .type = EV_ABS, .code = ABS_X, .value = 1 }} },
{ "-SW_TABLET_MODE;", true, 1, {{ .type = EV_SW, .code = SW_TABLET_MODE, .value = 0 }} },
{ "+EV_SW", true, 1, {{ .type = EV_SW, .code = 0xffff, .value = 1 }} },
{ "-ABS_Y", true, 1, {{ .type = EV_ABS, .code = ABS_Y, .value = 0 }} },
{ "+EV_ABS:0x00", true, 1, {{ .type = EV_ABS, .code = ABS_X, .value = 1 }} },
{ "-EV_ABS:01", true, 1, {{ .type = EV_ABS, .code = ABS_Y, .value = 0 }} },
{ "+ABS_TILT_X;-ABS_TILT_Y;", true, 2,
{{ .type = EV_ABS, .code = ABS_TILT_X, .value = 1 },
{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0}} },
{ "+BTN_TOOL_DOUBLETAP;+EV_KEY;-KEY_A", true, 3,
{{ .type = EV_KEY, .code = BTN_TOOL_DOUBLETAP, .value = 1 } ,
{ .type = EV_KEY, .code = 0xffff, .value = 1 },
{ .type = EV_KEY, .code = KEY_A, .value = 0 }} },
{ "+REL_Y;-ABS_Z;+BTN_STYLUS", true, 3,
{{ .type = EV_REL, .code = REL_Y, .value = 1},
{ .type = EV_ABS, .code = ABS_Z, .value = 0},
{ .type = EV_KEY, .code = BTN_STYLUS, .value = 1 }} },
{ "-REL_Y;+EV_KEY:0x123;-BTN_STYLUS", true, 3,
{{ .type = EV_REL, .code = REL_Y, .value = 0 },
{ .type = EV_KEY, .code = 0x123, .value = 1 },
{ .type = EV_KEY, .code = BTN_STYLUS, .value = 0 }} },
{ .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 = "+", .success = false },
{ .prop = "-", .success = false },
{ .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 },
};
@ -617,14 +620,14 @@ START_TEST(evcode_prop_parser)
if (!success)
continue;
ck_assert_int_eq(nevents, t->ntuples);
ck_assert_int_eq(nevents, t->nevents);
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);
unsigned int type = events[j].type;
unsigned int code = events[j].code;
int value = events[j].value;
ck_assert_int_eq(t->events[j].type, type);
ck_assert_int_eq(t->events[j].code, code);
ck_assert_int_eq(t->events[j].value, value);
}
}
}
@ -636,16 +639,16 @@ START_TEST(input_prop_parser)
const char *prop;
bool success;
size_t nvals;
uint32_t values[20];
struct input_prop values[20];
} tests[] = {
{ "INPUT_PROP_BUTTONPAD", true, 1, {INPUT_PROP_BUTTONPAD}},
{ "INPUT_PROP_BUTTONPAD;INPUT_PROP_POINTER", true, 2,
{ INPUT_PROP_BUTTONPAD,
INPUT_PROP_POINTER }},
{ "INPUT_PROP_BUTTONPAD;0x00;0x03", true, 3,
{ INPUT_PROP_BUTTONPAD,
INPUT_PROP_POINTER,
INPUT_PROP_SEMI_MT }},
{ "+INPUT_PROP_BUTTONPAD", true, 1, {{ INPUT_PROP_BUTTONPAD, true }}},
{ "+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER", true, 2,
{ { INPUT_PROP_BUTTONPAD, true },
{ INPUT_PROP_POINTER, false }}},
{ "+INPUT_PROP_BUTTONPAD;-0x00;+0x03", true, 3,
{ { INPUT_PROP_BUTTONPAD, true },
{ INPUT_PROP_POINTER, false },
{ INPUT_PROP_SEMI_MT, true }}},
{ .prop = "", .success = false },
{ .prop = "0xff", .success = false },
{ .prop = "INPUT_PROP", .success = false },
@ -659,7 +662,7 @@ START_TEST(input_prop_parser)
for (int i = 0; tests[i].prop; i++) {
bool success;
uint32_t props[32];
struct input_prop props[32];
size_t nprops = ARRAY_LENGTH(props);
t = &tests[i];
@ -670,7 +673,8 @@ START_TEST(input_prop_parser)
ck_assert_int_eq(nprops, t->nvals);
for (size_t j = 0; j < t->nvals; j++) {
ck_assert_int_eq(t->values[j], props[j]);
ck_assert_int_eq(t->values[j].prop, props[j].prop);
ck_assert_int_eq(t->values[j].enabled, props[j].enabled);
}
}
}

View file

@ -632,11 +632,13 @@ sprintf_event_codes(char *buf, size_t sz, struct quirks *quirks, enum quirk q)
off += printed;
for (size_t i = 0; off < sz && i < t->ntuples; i++) {
const char *name = libevdev_event_code_get_name(
t->tuples[i].first,
t->tuples[i].second);
unsigned int type = t->tuples[i].first;
unsigned int code = t->tuples[i].second;
bool enable = t->tuples[i].third;
printed = snprintf(buf + off, sz - off, "%s;", name);
const char *name = libevdev_event_code_get_name(type, code);
printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
assert(printed != -1);
off += printed;
}
@ -645,21 +647,24 @@ sprintf_event_codes(char *buf, size_t sz, struct quirks *quirks, enum quirk q)
static void
sprintf_input_props(char *buf, size_t sz, struct quirks *quirks, enum quirk q)
{
const uint32_t *properties;
size_t nprops = 0;
const struct quirk_tuples *t;
size_t off = 0;
int printed;
const char *name;
quirks_get_uint32_array(quirks, q, &properties, &nprops);
quirks_get_tuples(quirks, q, &t);
name = quirk_get_name(q);
printed = snprintf(buf, sz, "%s=", name);
assert(printed != -1);
off += printed;
for (size_t i = 0; off < sz && i < nprops; i++) {
const char *name = libevdev_property_get_name(properties[i]);
printed = snprintf(buf + off, sz - off, "%s;", name);
for (size_t i = 0; off < sz && i < t->ntuples; i++) {
unsigned int prop = t->tuples[i].first;
bool enable = t->tuples[i].second;
const char *name = libevdev_property_get_name(prop);
printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
assert(printed != -1);
off += printed;
}
@ -747,13 +752,11 @@ tools_list_device_quirks(struct quirks_context *ctx,
snprintf(buf, sizeof(buf), "%s=%d", name, b);
callback(userdata, buf);
break;
case QUIRK_ATTR_EVENT_CODE_DISABLE:
case QUIRK_ATTR_EVENT_CODE_ENABLE:
case QUIRK_ATTR_EVENT_CODE:
sprintf_event_codes(buf, sizeof(buf), quirks, q);
callback(userdata, buf);
break;
case QUIRK_ATTR_INPUT_PROP_DISABLE:
case QUIRK_ATTR_INPUT_PROP_ENABLE:
case QUIRK_ATTR_INPUT_PROP:
sprintf_input_props(buf, sizeof(buf), quirks, q);
callback(userdata, buf);
break;