[PATCH v2] touchpad: add wobbling detection
Konstantin Kharlamov
hi-angel at yandex.ru
Tue Feb 20 17:58:33 UTC 2018
On 20.02.2018 17:31, Konstantin Kharlamov wrote:
> On 20.02.2018 13:44, Konstantin Kharlamov wrote:
>> On 20.02.2018 09:34, Peter Hutterer wrote:
>>> On Sun, Feb 18, 2018 at 11:14:55PM +0300, Konstantin Kharlamov wrote:
>>>> +static inline void
>>>> +tp_detect_wobbling(struct tp_dispatch *tp, int x_diff, uint64_t time)
>>>> +{
>>>> + if (tp->hysteresis.enabled)
>>>> + return;
>>>> +
>>>> + /* Idea: if we got a tuple of *very* quick moves like {Left,
>>>> Right, Left}, or
>>>> + * {Right, Left, Right}, it means touchpad jitters since no
>>>> human supposed to
>>>> + * be able to move like that within thresholds
>>>> + *
>>>> + * Algo: we encode left moves as zeroes, and right as ones. We
>>>> also drop the
>>>> + * array to all zeroes when contraints are not satisfied. Then
>>>> we search for
>>>> + * the pattern {1,0,1}. It can't match {Left, Right, Left}, but
>>>> it does match
>>>> + * {Left, Right, Left, Right}, so it's okay.
>>>> + */
>>>
>>> the interesting bit here will be whether there are touchpads that
>>> wobble but
>>> with more than one event per direction. I suspect there are but this
>>> should
>>> be simple enough that it catches the first RLR movement (bound to
>>> happen at
>>> some point) and enable it then. The main question is whether there is a
>>> noticeable period of wobbling until this happens.
>>> I honestly don't know, we won't know until ppl start shouting at us...
>>
>> My touchpad definitely does; however it does so many events for every
>> touch that I feel like it produces every possible combination within a
>> second or two. The more that the number for the three slots is 2³ = 8,
>> which isn't much.
>>
>>>> + if (time - tp->hysteresis.last_motion_time > ms2us(20) ||
>>>> x_diff == 0) {
>>>> + tp->hysteresis.x_motion_in_threshold = 0;
>>>> + return;
>>>> + }
>>>
>>> empty line here please.
>>>
>>>> + tp->hysteresis.x_motion_in_threshold <<= 1;
>>>> + if (x_diff > 0) { /* right move */
>>>
>>> s/x_diff/dx/, we use that everywhere else for deltas.
>>>
>>>> + tp->hysteresis.x_motion_in_threshold |= 1;
>>>> + static const char r_l_r = 5; /* {Right, Left, Right} */
>>>
>>> declarations at the top of the block please, separated by an empty
>>> line. See
>>> CODING_STYLE in the repository.
>>>
>>>> + if (tp->hysteresis.x_motion_in_threshold & r_l_r) {
>>>
>>> this one will trigger on the first right movement, you need
>>> if ((tp->hysteresis.x_motion_in_threshold & r_l_r) == r_l_r)
>>>
>>> With that change, it seems to work correctly on my non-wobbling touchpad
>>> here. Does it still work for you when you add this?
>>
>> Thank you, nice catch!
>>
>>>> + tp->hysteresis.enabled = true;
>>>> + evdev_log_debug(tp->device, "hysteresis enabled\n");
>>>> + }
>>>> + }
>>>> +}
>>>> +
>>>> static inline void
>>>> tp_motion_hysteresis(struct tp_dispatch *tp,
>>>> struct tp_touch *t)
>>>> @@ -1405,6 +1435,8 @@ tp_process_state(struct tp_dispatch *tp,
>>>> uint64_t time)
>>>> tp_thumb_detect(tp, t, time);
>>>> tp_palm_detect(tp, t, time);
>>>> + tp_detect_wobbling(tp, tp->hysteresis.prev_x - t->point.x,
>>>> time);
>>>> + tp->hysteresis.prev_x = t->point.x;
>>>
>>> the dx calculation should happen within tp_detect_wobbling, just pass
>>> the
>>> touch in and do the subtraction there. Plus that way you can do a
>>> simple:
>>> if (tp->prev.x == 0) dx = 0;
>>> which makes things more obvious.
>>
>> Well, not really can, the comparison for zero here would check
>> coordinate of previous x, whereas I need to check whether the delta
>> between prev. and curr. positions is zero, e.g. if a finger is still
>> and FW filters.
>>
>>>> tp_motion_hysteresis(tp, t);
>>>> tp_motion_history_push(t);
>>>> @@ -2918,6 +2950,7 @@ tp_init_hysteresis(struct tp_dispatch *tp)
>>>> tp->hysteresis.margin.x = res_x/2;
>>>> tp->hysteresis.margin.y = res_y/2;
>>>> tp->hysteresis.enabled = false;
>>>> + tp->hysteresis.x_motion_in_threshold = 0;
>>>
>>> 'threshold' usually refers to a distance in libinput, here it's more a
>>> timeout. But calling it x_motion_history would be best here, IMO.
>>>
>>>> }
>>>> static void
>>>> diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
>>>> index 442f34a3..398a18ed 100644
>>>> --- a/src/evdev-mt-touchpad.h
>>>> +++ b/src/evdev-mt-touchpad.h
>>>> @@ -276,6 +276,8 @@ struct tp_dispatch {
>>>> struct device_coords margin;
>>>> unsigned int other_event_count;
>>>> uint64_t last_motion_time;
>>>> + char x_motion_in_threshold;
>>>> + int prev_x;
>>>
>>> both of these need to be in the struct tp_touch, otherwise 2-finger
>>> scrolling may enable the hysteresis when the fingers move
>>> independently from
>>> each other but you're adding them to the same bitmask.
>>
>> Thanks for you comments!
>>
>> I tried addressing them, and found that the result of the time
>> calculation "time - tp->hysteresis.last_motion_time" whilst my finger
>> is down is constantly increasing, i.e. like "20, 30, 50, 100, 120…".
>> It looks as if "tp->hysteresis.last_motion_time" is not the last
>> motion time, but the last finger-down time.
>>
>> Do you happen to know anything about that?
>
> Although I did find what's the matter. Turns out, the last_motion_time
> have been assigned at tp_maybe_disable_hysteresis() which I did remove.
> But even there it's been guarded by if:
>
> if (tp->queued & TOUCHPAD_EVENT_MOTION)
> tp->hysteresis.last_motion_time = time;
>
> What does that condition mean?
Ok, I see, I was mostly confused by "queued" name. It probably means
"events queued to get processed by libinput". I wrote a code like:
if (tp->queued & TOUCHPAD_EVENT_MOTION) {
tp_detect_wobbling(tp, t, time);
tp->hysteresis.last_motion_time = time;
t->prev_x = t->point.x;
}
It should work like it previously did; what bothers me however —
shouldn't that be specific to a touch, but not touchpad? Couldn't that
get triggered e.g. by 2-finger scrolling? (whatever that is, I dunno,
touching with 2 finger doesn't scroll for me — I do scroll instead using
the right edge of the touchpad).
More information about the wayland-devel
mailing list