quirks: allow tweaking the debounce timeouts

Add `AttrBouncingTimeoutMs` and `AttrBouncingTimeoutSpuriousMs` to allow
quirks to change the debounce timeouts.
The goal is to allow users to workaround defective hardware by
increasing `AttrBouncingTimeoutMs` enough for what they observe.
This commit is contained in:
ThinkChaos 2024-11-25 12:04:11 -05:00
parent e68d80b13b
commit a1710d870c
No known key found for this signature in database
7 changed files with 89 additions and 8 deletions

View file

@ -54,3 +54,27 @@ correspond to the buttons 'pressed' and 'released' states, respectively.
Some devices send events in bursts, erroneously triggering the button Some devices send events in bursts, erroneously triggering the button
debouncing detection. Please :ref:`file a bug <reporting_bugs>` if that debouncing detection. Please :ref:`file a bug <reporting_bugs>` if that
occurs for your device. occurs for your device.
------------------------------------------------------------------------------
Configuration (via Quirks)
------------------------------------------------------------------------------
.. warning:: Quirks are an internal API, they come with no
backwards-compatibility guarantees or support for configuration.
Read the full :ref:`device-quirks` section before using.
Do NOT try to upstream your debouncing timeouts!
It is possible to configure the timeouts used by both debouncing methods with
quirks.
Use ``libinput record`` to figure out the timing of bounced events.
Then increase the "bounce" timeout quirk to be larger than that, but smaller
than the fastest double click you can perform manually.
Example quirk: ::
[My Borked Mouse]
MatchUdevType=mouse
MatchBus=usb
MatchName=*MyBorkedMouse*
ModelBouncingKeys=1
AttrBouncingTimeoutMs=100

View file

@ -142,9 +142,14 @@ ModelTabletModeSwitchUnreliable
Indicates that this tablet mode switch's state cannot be relied upon. Indicates that this tablet mode switch's state cannot be relied upon.
ModelTrackball ModelTrackball
Reserved for trackballs Reserved for trackballs
.. _device-quirks-ModelBouncingKeys:
ModelBouncingKeys ModelBouncingKeys
Indicates that the device may send fake bouncing key events and Indicates that the device may send fake bouncing key events and
timestamps can not be relied upon. timestamps can not be relied upon.
See also :ref:`device-quirks-AttrBouncingTimeoutMs` and
:ref:`device-quirks-AttrBouncingTimeoutSpuriousMs`.
ModelSynapticsSerialTouchpad ModelSynapticsSerialTouchpad
Reserved for touchpads made by Synaptics on the serial bus Reserved for touchpads made by Synaptics on the serial bus
ModelPressurePad ModelPressurePad
@ -202,6 +207,20 @@ AttrTabletSmoothing=1|0
Enables (1) or disables (0) input smoothing for tablet devices. Smoothing is enabled Enables (1) or disables (0) input smoothing for tablet devices. Smoothing is enabled
by default, except on AES devices. by default, except on AES devices.
.. _device-quirks-AttrBouncingTimeoutMs:
AttrBouncingTimeoutMs
Timeout (in milliseconds) for the "bounce" debouncing method.
See :ref:`button_debouncing` for details.
Only applies when :ref:`device-quirks-ModelBouncingKeys` is enabled.
.. _device-quirks-AttrBouncingTimeoutSpuriousMs:
AttrBouncingTimeoutSpuriousMs
Timeout (in milliseconds) for the "spurious" debouncing method.
See :ref:`button_debouncing` for details.
Only applies when :ref:`device-quirks-ModelBouncingKeys` is enabled.
.. _device-quirks-matches: .. _device-quirks-matches:
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View file

@ -126,20 +126,16 @@ static inline void
debounce_set_timer(struct fallback_dispatch *fallback, debounce_set_timer(struct fallback_dispatch *fallback,
uint64_t time) uint64_t time)
{ {
const int DEBOUNCE_TIMEOUT_BOUNCE = ms2us(25);
libinput_timer_set(&fallback->debounce.timer, libinput_timer_set(&fallback->debounce.timer,
time + DEBOUNCE_TIMEOUT_BOUNCE); time + fallback->debounce.timeout_bounce);
} }
static inline void static inline void
debounce_set_timer_short(struct fallback_dispatch *fallback, debounce_set_timer_short(struct fallback_dispatch *fallback,
uint64_t time) uint64_t time)
{ {
const int DEBOUNCE_TIMEOUT_SPURIOUS = ms2us(12);
libinput_timer_set(&fallback->debounce.timer_short, libinput_timer_set(&fallback->debounce.timer_short,
time + DEBOUNCE_TIMEOUT_SPURIOUS); time + fallback->debounce.timeout_spurious);
} }
static inline void static inline void
@ -573,14 +569,28 @@ void
fallback_init_debounce(struct fallback_dispatch *dispatch) fallback_init_debounce(struct fallback_dispatch *dispatch)
{ {
struct evdev_device *device = dispatch->device; struct evdev_device *device = dispatch->device;
struct quirks_context *quirks = evdev_libinput_context(device)->quirks;
struct quirks *q = quirks_fetch_for_device(quirks, device->udev_device);
bool disabled = false;
bool has_spurious_quirk = false;
uint32_t timeout_ms = 25;
uint32_t timeout_spurious_ms = 12;
char timer_name[64]; char timer_name[64];
if (evdev_device_has_model_quirk(device, QUIRK_MODEL_BOUNCING_KEYS)) { quirks_get_bool(q, QUIRK_MODEL_BOUNCING_KEYS, &disabled);
if (disabled) {
dispatch->debounce.state = DEBOUNCE_STATE_DISABLED; dispatch->debounce.state = DEBOUNCE_STATE_DISABLED;
return; goto end;
} }
quirks_get_uint32(q, QUIRK_ATTR_BOUNCING_TIMEOUT_MS, &timeout_ms);
has_spurious_quirk = quirks_get_uint32(q,
QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS,
&timeout_spurious_ms);
dispatch->debounce.state = DEBOUNCE_STATE_IS_UP; dispatch->debounce.state = DEBOUNCE_STATE_IS_UP;
dispatch->debounce.timeout_bounce = ms2us(timeout_ms);
dispatch->debounce.timeout_spurious = ms2us(timeout_spurious_ms);
snprintf(timer_name, snprintf(timer_name,
sizeof(timer_name), sizeof(timer_name),
@ -601,4 +611,10 @@ fallback_init_debounce(struct fallback_dispatch *dispatch)
timer_name, timer_name,
debounce_timeout, debounce_timeout,
device); device);
if (has_spurious_quirk)
debounce_enable_spurious(dispatch);
end:
quirks_unref(q);
} }

View file

@ -145,6 +145,8 @@ struct fallback_dispatch {
uint64_t button_time; uint64_t button_time;
struct libinput_timer timer; struct libinput_timer timer;
struct libinput_timer timer_short; struct libinput_timer timer_short;
uint32_t timeout_bounce;
uint32_t timeout_spurious;
enum debounce_state state; enum debounce_state state;
bool spurious_enabled; bool spurious_enabled;
} debounce; } debounce;

View file

@ -292,6 +292,8 @@ quirk_get_name(enum quirk q)
case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp"; case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp";
case QUIRK_ATTR_EVENT_CODE: return "AttrEventCode"; case QUIRK_ATTR_EVENT_CODE: return "AttrEventCode";
case QUIRK_ATTR_INPUT_PROP: return "AttrInputProp"; case QUIRK_ATTR_INPUT_PROP: return "AttrInputProp";
case QUIRK_ATTR_BOUNCING_TIMEOUT_MS: return "AttrBouncingTimeoutMs";
case QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS: return "AttrBouncingTimeoutSpuriousMs";
default: default:
abort(); abort();
} }
@ -878,6 +880,20 @@ parse_attr(struct quirks_context *ctx,
p->value.tuples.ntuples = nprops; p->value.tuples.ntuples = nprops;
p->type = PT_TUPLES; p->type = PT_TUPLES;
rc = true;
} else if (streq(key, quirk_get_name(QUIRK_ATTR_BOUNCING_TIMEOUT_MS))) {
p->id = QUIRK_ATTR_BOUNCING_TIMEOUT_MS;
if (!safe_atou(value, &v))
goto out;
p->type = PT_UINT;
p->value.u = v;
rc = true;
} else if (streq(key, quirk_get_name(QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS))) {
p->id = QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS;
if (!safe_atou(value, &v))
goto out;
p->type = PT_UINT;
p->value.u = v;
rc = true; rc = true;
} else { } else {
qlog_error(ctx, "Unknown key %s in %s\n", key, s->name); qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);

View file

@ -109,6 +109,8 @@ enum quirk {
QUIRK_ATTR_MSC_TIMESTAMP, QUIRK_ATTR_MSC_TIMESTAMP,
QUIRK_ATTR_EVENT_CODE, QUIRK_ATTR_EVENT_CODE,
QUIRK_ATTR_INPUT_PROP, QUIRK_ATTR_INPUT_PROP,
QUIRK_ATTR_BOUNCING_TIMEOUT_MS,
QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS,
_QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */ _QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */
}; };

View file

@ -899,6 +899,8 @@ tools_list_device_quirks(struct quirks_context *ctx,
case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD: case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:
case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD: case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:
case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:
case QUIRK_ATTR_BOUNCING_TIMEOUT_MS:
case QUIRK_ATTR_BOUNCING_TIMEOUT_SPURIOUS_MS:
quirks_get_uint32(quirks, q, &v); quirks_get_uint32(quirks, q, &v);
snprintf(buf, sizeof(buf), "%s=%u", name, v); snprintf(buf, sizeof(buf), "%s=%u", name, v);
callback(userdata, buf); callback(userdata, buf);