From 57a592e2df06e159901e0dd1162a6e99d350294f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 17 Nov 2021 16:24:00 +1000 Subject: [PATCH] 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 --- src/evdev-tablet.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 3b77b47c..25bb57d6 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -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; }