tablet: handle a BTN_TOOL_PEN on top of BTN_TOOL_RUBBER

The Wacom 524C device triggers a kernel bug in the InRange and Invert
handling. Every time BTN_TOUCH is set/unset the device also sets/unsets
BTN_TOOL_PEN even when we nominally have the eraser in proximity.

The event sequence effectively looks like this:

    # on prox in
    BTN_TOOL_RUBBER 1
    -- SYN_REPORT ---
    # on tip down
    BTN_TOOL_PEN 1
    BTN_TOUCH 1
    -- SYN_REPORT ---
    # on tip up
    BTN_TOUCH 0
    BTN_TOOL_PEN 0
    -- SYN_REPORT ---
    # on prox out
    BTN_TOOL_RUBBER 1
    -- SYN_REPORT ---

To work around this, bias our duplicate tool detection code towards the
eraser - if we have an eraser in-prox already and the pen goes
in-prox, ignore it and continue with the eraser. But if we have a pen
in-prox and the eraser goes in-prox as well, force a prox-out for the
pen and put the eraser in-prox.

Recording originally from
https://github.com/linuxwacom/xf86-input-wacom/issues/186

Fixes #702

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2021-11-17 16:24:00 +10:00 committed by José Expósito
parent 167cebf7de
commit 57a592e2df

View file

@ -1810,20 +1810,35 @@ tablet_update_tool_state(struct tablet_dispatch *tablet,
if (tablet->tool_state == tablet->prev_tool_state)
return false;
/* Kernel tools are supposed to be mutually exclusive, if we have
* two, we force a proximity out for the older tool and handle the
* new tool as separate proximity in event.
/* Kernel tools are supposed to be mutually exclusive, but we may have
* two bits set due to firmware/kernel bugs.
* Two cases that have been seen in the wild:
* - BTN_TOOL_PEN on proximity in, followed by
* BTN_TOOL_RUBBER later, see #259
* -> We force a prox-out of the pen, trigger prox-in for eraser
* - BTN_TOOL_RUBBER on proximity in, but BTN_TOOL_PEN when
* the tip is down, see #702.
* -> We ignore BTN_TOOL_PEN
* In both cases the eraser is what we want, so we bias
* towards that.
*/
if (tablet->tool_state & (tablet->tool_state - 1)) {
/* tool_state has 2 bits set. We set the current tool state
* to zero, thus setting everything up for a prox out on the
* tool. Once that is set up, we change the tool state to be
* the new one we just got so when we re-process this
* function we now get the new tool as prox in.
* Importantly, we basically rely on nothing else happening
* in the meantime.
*/
doubled_up_new_tool_bit = tablet->tool_state ^ tablet->prev_tool_state;
/* The new tool is the pen. Ignore it */
if (doubled_up_new_tool_bit == bit(LIBINPUT_TABLET_TOOL_TYPE_PEN)) {
tablet->tool_state &= ~bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
return false;
}
/* The new tool is some tool other than pen (usually eraser).
* We set the current tool state to zero, thus setting
* everything up for a prox out on the tool. Once that is set
* up, we change the tool state to be the new one we just got.
* When we re-process this function we now get the new tool
* as prox in. Importantly, we basically rely on nothing else
* happening in the meantime.
*/
tablet->tool_state = 0;
}