[PATCH libinput 02/20] touchpad: after a click, lock the finger to its current position

Hans de Goede hdegoede at redhat.com
Tue Apr 15 05:27:59 PDT 2014


From: Peter Hutterer <peter.hutterer at who-t.net>

On clickpads, clicking the pad usually causes some motion events. To avoid
erroneous movements, lock the finger into position on the click and don't
allow for motion events until we move past a given threshold (currently 2% of
the touchpad diagonal).

HdG:
Instead of pinning the lowest touch assuming that that is the one holding
the physical button, simply pin all touches, and unpin as soon as a touch
is moved more then the threshold.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
Signed-off-by: Hans de Goede <hdegoede at redhat.com>
---
 src/evdev-mt-touchpad.c | 79 ++++++++++++++++++++++++-------------------------
 src/evdev-mt-touchpad.h | 12 +++++++-
 2 files changed, 49 insertions(+), 42 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 8021db2..fbe0a7b 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -32,6 +32,7 @@
 #define DEFAULT_MIN_ACCEL_FACTOR 0.16
 #define DEFAULT_MAX_ACCEL_FACTOR 1.0
 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
+#define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* in percent of size */
 
 static inline int
 tp_hysteresis(int in, int center, int margin)
@@ -182,6 +183,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t)
 	t->dirty = true;
 	t->is_pointer = false;
 	t->state = TOUCH_END;
+	t->pinned.is_pinned = false;
 	assert(tp->nfingers_down >= 1);
 	tp->nfingers_down--;
 	tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -346,50 +348,43 @@ tp_process_key(struct tp_dispatch *tp,
 }
 
 static void
-tp_unpin_finger(struct tp_dispatch *tp)
+tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
 {
-	struct tp_touch *t;
-	tp_for_each_touch(tp, t) {
-		if (t->is_pinned) {
-			t->is_pinned = false;
+	unsigned int xdist, ydist;
+	struct tp_touch *tmp;
+
+	if (!t->pinned.is_pinned)
+		return;
+
+	xdist = abs(t->x - t->pinned.center_x);
+	ydist = abs(t->y - t->pinned.center_y);
 
-			if (t->state != TOUCH_END &&
-			    tp->nfingers_down == 1)
-				t->is_pointer = true;
+	if (xdist * xdist + ydist * ydist <
+			tp->buttons.motion_dist * tp->buttons.motion_dist)
+		return;
+
+	t->pinned.is_pinned = false;
+
+	tp_for_each_touch(tp, tmp) {
+		if (tmp->is_pointer)
 			break;
-		}
 	}
+
+	if (t->state != TOUCH_END && !tmp->is_pointer)
+		t->is_pointer = true;
 }
 
 static void
-tp_pin_finger(struct tp_dispatch *tp)
+tp_pin_fingers(struct tp_dispatch *tp)
 {
-	struct tp_touch *t,
-			*pinned = NULL;
+	struct tp_touch *t;
 
 	tp_for_each_touch(tp, t) {
-		if (t->is_pinned) {
-			pinned = t;
-			break;
-		}
-	}
-
-	assert(!pinned);
-
-	pinned = tp_current_touch(tp);
-
-	if (tp->nfingers_down != 1) {
-		tp_for_each_touch(tp, t) {
-			if (t == pinned)
-				continue;
-
-			if (t->y > pinned->y)
-				pinned = t;
-		}
+		t->is_pointer = false;
+		t->pinned.is_pinned = true;
+		t->pinned.center_x = t->x;
+		t->pinned.center_y = t->y;
 	}
-
-	pinned->is_pinned = true;
-	pinned->is_pointer = false;
 }
 
 static void
@@ -409,16 +404,19 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
 
 		tp_motion_hysteresis(tp, t);
 		tp_motion_history_push(t);
+
+		tp_unpin_finger(tp, t);
 	}
 
-	/* We have a physical button down event on a clickpad. For drag and
-	   drop, this means we try to identify which finger pressed the
-	   physical button and "pin" it, i.e. remove pointer-moving
-	   capabilities from it.
+	/*
+	 * We have a physical button down event on a clickpad. To avoid
+	 * spurious pointer moves by the clicking finger we pin all fingers.
+	 * We unpin fingers when they move more then a certain threshold to
+	 * to allow drag and drop.
 	 */
 	if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
 	    !tp->buttons.has_buttons)
-		tp_pin_finger(tp);
+		tp_pin_fingers(tp);
 }
 
 static void
@@ -441,9 +439,6 @@ tp_post_process_state(struct tp_dispatch *tp, uint32_t time)
 
 	tp->buttons.old_state = tp->buttons.state;
 
-	if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
-		tp_unpin_finger(tp);
-
 	tp->queued = TOUCHPAD_EVENT_NONE;
 }
 
@@ -796,6 +791,8 @@ tp_init(struct tp_dispatch *tp,
 	tp->hysteresis.margin_y =
 		diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
 
+	tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD;
+
 	if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) ||
 	    libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT))
 		tp->buttons.has_buttons = true;
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 2bdb329..e5fbd7a 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -77,7 +77,6 @@ struct tp_touch {
 	bool dirty;
 	bool fake;				/* a fake touch */
 	bool is_pointer;			/* the pointer-controlling touch */
-	bool is_pinned;				/* holds the phys. button */
 	int32_t x;
 	int32_t y;
 	uint32_t millis;
@@ -92,6 +91,16 @@ struct tp_touch {
 		int32_t center_x;
 		int32_t center_y;
 	} hysteresis;
+
+	/* A pinned touchpoint is the one that pressed the physical button
+	 * on a clickpad. After the release, it won't move until the center
+	 * moves more than a threshold away from the original coordinates
+	 */
+	struct {
+		bool is_pinned;
+		int32_t center_x;
+		int32_t center_y;
+	} pinned;
 };
 
 struct tp_dispatch {
@@ -122,6 +131,7 @@ struct tp_dispatch {
 		bool has_buttons;		/* true for physical LMR buttons */
 		uint32_t state;
 		uint32_t old_state;
+		uint32_t motion_dist;		/* for pinned touches */
 	} buttons;				/* physical buttons */
 
 	struct {
-- 
1.9.0



More information about the wayland-devel mailing list