[PATCH v4 libinput] touchpad: add a TOUCH_HOVERING state

Hans de Goede hdegoede at redhat.com
Wed Jan 14 00:25:53 PST 2015


Hi,

On 14-01-15 07:21, Peter Hutterer wrote:
> Some touchpads provide touch information while the finger hovers over the
> touchpad, i.e. before BTN_TOUCH. Add a touch state for those touchpads so we
> can ignore the touches until they actually start.
>
> The approach is now: instead of BEGIN we mark a new touch as HOVERING.
> Use the BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP information during
> tp_process_state() to mark any touches that are hovering as down or ended.
>
> i.e. provided BTN_TOUCH is down: if BTN_TOOL_FINGER is down, one hovering
> touch gets marked as down, if DOUBLETAP is down, two touches are marked as
> down, etc.
>
> When ending touches, switch them back into HOVERING if the BTN_TOOL_FINGER
> is still set, otherwise end them properly.
>
> https://bugs.freedesktop.org/show_bug.cgi?id=87197
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
> This time for sure ;)

One tiny nitpick in inline comments, with that fixed (no need to resend)
this is:

Reviewed-by: Hans de Goede <hdegoede at redhat.com>

> Changes to v3:
> - drop the has_ended leftover from tp_end_touch
> - drop the active_real_touches counting from tp_post_process_state
> - when ending touches, hover or end them based on the t->has_ended state
>    (tp_post_process_state)
>
>   src/evdev-mt-touchpad-edge-scroll.c |   1 +
>   src/evdev-mt-touchpad.c             | 135 +++++++++++++++++++++++++++++++-----
>   src/evdev-mt-touchpad.h             |   2 +
>   3 files changed, 121 insertions(+), 17 deletions(-)
>
> diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
> index a4dc093..0e3d648 100644
> --- a/src/evdev-mt-touchpad-edge-scroll.c
> +++ b/src/evdev-mt-touchpad-edge-scroll.c
> @@ -294,6 +294,7 @@ tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
>
>   		switch (t->state) {
>   		case TOUCH_NONE:
> +		case TOUCH_HOVERING:
>   			break;
>   		case TOUCH_BEGIN:
>   			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_TOUCH);
> diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
> index 632ad38..9e6aaa7 100644
> --- a/src/evdev-mt-touchpad.c
> +++ b/src/evdev-mt-touchpad.c
> @@ -178,26 +178,53 @@ tp_fake_finger_set(struct tp_dispatch *tp,
>   }
>
>   static inline void
> -tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
> +tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
>   {
> -	if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE)
> +	if (t->state == TOUCH_BEGIN ||
> +	    t->state == TOUCH_UPDATE ||
> +	    t->state == TOUCH_HOVERING)
>   		return;
>
> +	/* we begin the touch as hovering because until BTN_TOUCH happens we
> +	 * don't know if it's a touch down or not. And BTN_TOUCH may happen
> +	 * after ABS_MT_TRACKING_ID */
>   	tp_motion_history_reset(t);
>   	t->dirty = true;
> +	t->has_ended = false;
> +	t->state = TOUCH_HOVERING;
> +	t->pinned.is_pinned = false;
> +	t->millis = time;
> +	tp->queued |= TOUCHPAD_EVENT_MOTION;
> +}
> +
> +static inline void
> +tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
> +{
> +	t->dirty = true;
>   	t->state = TOUCH_BEGIN;
> -	t->pinned.is_pinned = false;
>   	t->millis = time;
>   	tp->nfingers_down++;
>   	assert(tp->nfingers_down >= 1);
> -	tp->queued |= TOUCHPAD_EVENT_MOTION;
>   }
>
> +/**
> + * End a touch, even if the touch sequence is still active.
> + */
>   static inline void
>   tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
>   {
> -	if (t->state == TOUCH_END || t->state == TOUCH_NONE)
> +	switch (t->state) {
> +	case TOUCH_HOVERING:
> +		t->state = TOUCH_NONE;
> +		/* fallthough */
> +	case TOUCH_NONE:
> +	case TOUCH_END:
>   		return;
> +	case TOUCH_BEGIN:
> +	case TOUCH_UPDATE:
> +		break;
> +
> +	}
>
>   	t->dirty = true;
>   	t->is_pointer = false;
> @@ -210,6 +238,16 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
>   	tp->queued |= TOUCHPAD_EVENT_MOTION;
>   }
>
> +/**
> + * End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received.
> + */
> +static inline void
> +tp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
> +{
> +	t->has_ended = true;
> +	tp_end_touch(tp, t, time);
> +}
> +
>   static double
>   tp_estimate_delta(int x0, int x1, int x2, int x3)
>   {
> @@ -260,9 +298,9 @@ tp_process_absolute(struct tp_dispatch *tp,
>   		break;
>   	case ABS_MT_TRACKING_ID:
>   		if (e->value != -1)
> -			tp_begin_touch(tp, t, time);
> +			tp_new_touch(tp, t, time);
>   		else
> -			tp_end_touch(tp, t, time);
> +			tp_end_sequence(tp, t, time);
>   	}
>   }
>
> @@ -306,13 +344,10 @@ tp_process_fake_touch(struct tp_dispatch *tp,
>   	for (i = start; i < tp->ntouches; i++) {
>   		t = tp_get_touch(tp, i);
>   		if (i < nfake_touches)
> -			tp_begin_touch(tp, t, time);
> +			tp_new_touch(tp, t, time);
>   		else
> -			tp_end_touch(tp, t, time);
> +			tp_end_sequence(tp, t, time);
>   	}
> -
> -	/* On mt the actual touch info may arrive after BTN_TOOL_FOO */
> -	assert(tp->has_mt || tp->nfingers_down == nfake_touches);
>   }
>
>   static void
> @@ -327,6 +362,7 @@ tp_process_key(struct tp_dispatch *tp,
>   			tp_process_button(tp, e, time);
>   			break;
>   		case BTN_TOUCH:
> +		case BTN_TOOL_FINGER:
>   		case BTN_TOOL_DOUBLETAP:
>   		case BTN_TOOL_TRIPLETAP:
>   		case BTN_TOOL_QUADTAP:
> @@ -580,12 +616,69 @@ tp_remove_scroll(struct tp_dispatch *tp)
>   }
>
>   static void
> +tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
> +{
> +	struct tp_touch *t;
> +	unsigned int nfake_touches;
> +	int i;
> +
> +	if (!tp->fake_touches && !tp->nfingers_down)
> +		return;
> +
> +	nfake_touches = tp_fake_finger_count(tp);
> +	if (tp->nfingers_down == nfake_touches &&
> +	    ((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) ||
> +	     (tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp))))
> +		return;
> +
> +	/* if BTN_TOUCH is set and we have less fingers down than fake
> +	 * touches, switch each hovering touch to BEGIN
> +	 * until nfingers_down matches nfake_touches
> +	 */
> +	if (tp_fake_finger_is_touching(tp) &&
> +	    tp->nfingers_down < nfake_touches) {
> +		for (i = 0; i < (int)tp->ntouches; i++) {
> +			t = tp_get_touch(tp, i);
> +
> +			if (t->state == TOUCH_HOVERING) {
> +				tp_begin_touch(tp, t, time);
> +
> +				if (tp->nfingers_down >= nfake_touches)
> +					break;
> +			}
> +		}
> +	}
> +
> +	/* if BTN_TOUCH is unset end all touches, we're hovering now. If we
> +	 * have too many touches also end some of them. This is done in
> +	 * reverse order.
> +	 */
> +	if (tp->nfingers_down > nfake_touches ||
> +	    !tp_fake_finger_is_touching(tp)) {
> +		for (i = tp->ntouches - 1; i >= 0; i--) {
> +			t = tp_get_touch(tp, i);
> +
> +			if (t->state == TOUCH_HOVERING)
> +				continue;
> +
> +			tp_end_touch(tp, t, time);
> +
> +			if (tp_fake_finger_is_touching(tp) &&
> +			    tp->nfingers_down == nfake_touches)
> +				break;
> +		}
> +	}
> +}
> +
> +static void
>   tp_process_state(struct tp_dispatch *tp, uint64_t time)
>   {
>   	struct tp_touch *t;
>   	struct tp_touch *first = tp_get_touch(tp, 0);
>   	unsigned int i;
>
> +	tp_unhover_touches(tp, time);
> +
>   	for (i = 0; i < tp->ntouches; i++) {
>   		t = tp_get_touch(tp, i);
>
> @@ -629,15 +722,22 @@ static void
>   tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
>   {
>   	struct tp_touch *t;
> +	unsigned int i;
> +
> +	for (i = 0; i < tp->ntouches; i++) {
> +		t = tp_get_touch(tp, i);
>
> -	tp_for_each_touch(tp, t) {

Since you now no longer use "i", there is no need for the
above changes, please revert the loop back to using
tp_for_each_touch().

>   		if (!t->dirty)
>   			continue;
>
> -		if (t->state == TOUCH_END)
> -			t->state = TOUCH_NONE;
> -		else if (t->state == TOUCH_BEGIN)
> +		if (t->state == TOUCH_END) {
> +			if (t->has_ended)
> +				t->state = TOUCH_NONE;
> +			else
> +				t->state = TOUCH_HOVERING;
> +		} else if (t->state == TOUCH_BEGIN) {
>   			t->state = TOUCH_UPDATE;
> +		}
>
>   		t->dirty = false;
>   	}
> @@ -818,7 +918,7 @@ tp_clear_state(struct tp_dispatch *tp)
>   	tp_release_all_taps(tp, now);
>
>   	tp_for_each_touch(tp, t) {
> -		tp_end_touch(tp, t, now);
> +		tp_end_sequence(tp, t, now);
>   	}
>
>   	tp_handle_state(tp, now);
> @@ -979,6 +1079,7 @@ tp_init_touch(struct tp_dispatch *tp,
>   	      struct tp_touch *t)
>   {
>   	t->tp = tp;
> +	t->has_ended = true;
>   }
>
>   static int
> diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
> index 1e6152a..7fe152a 100644
> --- a/src/evdev-mt-touchpad.h
> +++ b/src/evdev-mt-touchpad.h
> @@ -53,6 +53,7 @@ enum touchpad_model {
>
>   enum touch_state {
>   	TOUCH_NONE = 0,
> +	TOUCH_HOVERING,
>   	TOUCH_BEGIN,
>   	TOUCH_UPDATE,
>   	TOUCH_END
> @@ -130,6 +131,7 @@ struct tp_motion {
>   struct tp_touch {
>   	struct tp_dispatch *tp;
>   	enum touch_state state;
> +	bool has_ended;				/* TRACKING_ID == -1 */
>   	bool dirty;
>   	bool is_pointer;			/* the pointer-controlling touch */
>   	int32_t x;
>

Regards,

Hans


More information about the wayland-devel mailing list