[PATCH libinput] Add right click drag

Corentin Marciau corentin at marciau.fr
Tue Aug 22 22:07:15 UTC 2017


Hi,

For what it's worth, here is a patch of the touchpad tap state machine 
to handle right click drag.

Since it's first time I change something in libinput, I have no idea if 
I did things correctly or even if this work is relevant at all. At 
least, as demanded in evdev-mt-touchpad-tap.c, I updated 
touchpad-tap-state-machine.svg accordingly ...

@Peter Hutterer, thanks a lot for your help !

Enclosed:
- patch
- touchpad-tap-state-machine.svg
- strange file from draw.io

For now, I just fixed the two tests that were broken by my change. I'll 
probably try to add some additional ones for this feature, but it's not 
done yet.

Cheers,
Corentin

PS: If anyone wonder who in the world would want to right click and 
drag, I use the Firefox plugin FireGestures

PPS: Yes, I use Firefox. I have a big fat CPU

PPPS: Yes, I do use a mouse. Call me a noob

-------------- next part --------------
From a5d88078a30500fa33bb9285c1921110d82824ba Mon Sep 17 00:00:00 2001
From: Corentin Marciau <corentin at marciau.fr>
Date: Mon, 21 Aug 2017 02:25:26 +0200
Subject: [PATCH libinput 1/2] [DEV] Add right click drag

---
 src/evdev-mt-touchpad-tap.c | 253 +++++++++++++++++++++++++++++++++++++++++---
 src/evdev-mt-touchpad.h     |   5 +
 2 files changed, 245 insertions(+), 13 deletions(-)

diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index f01487f..c015a8a 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -74,10 +74,15 @@ tap_state_to_str(enum tp_tap_state state)
 	CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT);
 	CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP);
 	CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_TAP);
-	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
+	CASE_RETURN_STRING(TAP_STATE_DRAGGING_TOUCH);
 	CASE_RETURN_STRING(TAP_STATE_MULTITAP);
 	CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN);
 	CASE_RETURN_STRING(TAP_STATE_DEAD);
+	CASE_RETURN_STRING(TAP_STATE_TAPPED_2);
+	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2_OR_DOUBLETAP);
+	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2_OR_TAP);
+	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2_WAIT);
+	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
 	}
 	return NULL;
 }
@@ -358,11 +363,17 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
 			      tp->tap.saved_press_time,
 			      2,
 			      LIBINPUT_BUTTON_STATE_PRESSED);
-		tp_tap_notify(tp,
-			      tp->tap.saved_release_time,
-			      2,
-			      LIBINPUT_BUTTON_STATE_RELEASED);
-		tp->tap.state = TAP_STATE_IDLE;
+		if (tp->tap.drag_enabled) {
+			tp->tap.state = TAP_STATE_TAPPED_2;
+			tp->tap.saved_release_time = time;
+			tp_tap_set_timer(tp, time);
+		} else {
+			tp->tap.state = TAP_STATE_IDLE;
+			tp_tap_notify(tp,
+				      tp->tap.saved_release_time,
+				      2,
+				      LIBINPUT_BUTTON_STATE_RELEASED);
+		}
 		break;
 	case TAP_EVENT_MOTION:
 	case TAP_EVENT_TIMEOUT:
@@ -442,7 +453,7 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
 {
 	switch (event) {
 	case TAP_EVENT_TOUCH:
-		tp->tap.state = TAP_STATE_DRAGGING_2;
+		tp->tap.state = TAP_STATE_DRAGGING_TOUCH;
 		break;
 	case TAP_EVENT_RELEASE:
 		tp->tap.state = TAP_STATE_MULTITAP;
@@ -476,7 +487,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
 
 	switch (event) {
 	case TAP_EVENT_TOUCH:
-		tp->tap.state = TAP_STATE_DRAGGING_2;
+		tp->tap.state = TAP_STATE_DRAGGING_TOUCH;
 		break;
 	case TAP_EVENT_RELEASE:
 		if (tp->tap.drag_lock_enabled) {
@@ -538,7 +549,7 @@ tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
 
 	switch (event) {
 	case TAP_EVENT_TOUCH:
-		tp->tap.state = TAP_STATE_DRAGGING_2;
+		tp->tap.state = TAP_STATE_DRAGGING_TOUCH;
 		tp_tap_clear_timer(tp);
 		break;
 	case TAP_EVENT_RELEASE:
@@ -644,7 +655,7 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
 		tp->tap.saved_release_time = time;
 		break;
 	case TAP_EVENT_TOUCH:
-		tp->tap.state = TAP_STATE_DRAGGING_2;
+		tp->tap.state = TAP_STATE_DRAGGING_TOUCH;
 		tp_tap_clear_timer(tp);
 		break;
 	case TAP_EVENT_MOTION:
@@ -671,7 +682,6 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
 			 enum tap_event event,
 			 uint64_t time)
 {
-
 	switch (event) {
 	case TAP_EVENT_RELEASE:
 		if (tp->nfingers_down == 0)
@@ -688,6 +698,202 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
 }
 
 static void
+tp_tap_tapped2_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum tap_event event,
+			    uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+		evdev_log_bug_libinput(tp->device,
+				 "invalid tap event when fingers are up\n");
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_DRAGGING_2_OR_DOUBLETAP;
+		tp->tap.saved_press_time = time;
+		tp_tap_set_timer(tp, time);
+		break;
+	case TAP_EVENT_MOTION:
+		evdev_log_bug_libinput(tp->device,
+				 "invalid tap event, no fingers are down\n");
+		break;
+	case TAP_EVENT_TIMEOUT:
+		tp->tap.state = TAP_STATE_IDLE;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+		break;
+	}
+}
+
+static void
+tp_tap_dragging2_or_tapped2tapped_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum tap_event event,
+			    uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+		tp->tap.state = TAP_STATE_IDLE;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		tp_tap_notify(tp,
+			      tp->tap.saved_press_time,
+			      1,
+			      LIBINPUT_BUTTON_STATE_PRESSED);
+		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_TOUCH_2;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		tp->tap.saved_press_time = time;
+		tp_tap_set_timer(tp, time);
+		break;
+	case TAP_EVENT_MOTION:
+	case TAP_EVENT_TIMEOUT:
+		tp->tap.state = TAP_STATE_DRAGGING_2;
+		break;
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+		break;
+	}
+}
+
+static void
+tp_tap_dragging2_tap_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum tap_event event,
+			    uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+		tp->tap.state = TAP_STATE_IDLE;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_MOTION:
+	case TAP_EVENT_TIMEOUT:
+		tp->tap.state = TAP_STATE_DRAGGING_2;
+		break;
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+		break;
+	}
+}
+
+static void
+tp_tap_dragging2_wait_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum tap_event event,
+			    uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+	case TAP_EVENT_MOTION:
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_DRAGGING_2_OR_TAP;
+		tp->tap.saved_press_time = time;
+		tp_tap_set_timer(tp, time);
+		break;
+	case TAP_EVENT_TIMEOUT:
+		tp->tap.state = TAP_STATE_IDLE;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+		break;
+	}
+}
+
+static void
+tp_tap_dragging2_therealone_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum tap_event event,
+			    uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+		if (tp->tap.drag_lock_enabled) {
+			tp->tap.state = TAP_STATE_DRAGGING_2_WAIT;
+			tp->tap.saved_release_time = time;
+			tp_tap_set_drag_timer(tp, time);
+		} else {
+			tp->tap.state = TAP_STATE_IDLE;
+			tp_tap_notify(tp,
+				      time,
+				      2,
+				      LIBINPUT_BUTTON_STATE_RELEASED);
+		}
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_MOTION:
+	case TAP_EVENT_TIMEOUT:
+		break;
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_DEAD;
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      2,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+		break;
+	}
+}
+
+static void
 tp_tap_handle_event(struct tp_dispatch *tp,
 		    struct tp_touch *t,
 		    enum tap_event event,
@@ -737,7 +943,7 @@ tp_tap_handle_event(struct tp_dispatch *tp,
 	case TAP_STATE_DRAGGING_OR_TAP:
 		tp_tap_dragging_tap_handle_event(tp, t, event, time);
 		break;
-	case TAP_STATE_DRAGGING_2:
+	case TAP_STATE_DRAGGING_TOUCH:
 		tp_tap_dragging2_handle_event(tp, t, event, time);
 		break;
 	case TAP_STATE_MULTITAP:
@@ -749,6 +955,21 @@ tp_tap_handle_event(struct tp_dispatch *tp,
 	case TAP_STATE_DEAD:
 		tp_tap_dead_handle_event(tp, t, event, time);
 		break;
+	case TAP_STATE_TAPPED_2:
+		tp_tap_tapped2_handle_event(tp, t, event, time);
+		break;
+	case TAP_STATE_DRAGGING_2_OR_DOUBLETAP:
+		tp_tap_dragging2_or_tapped2tapped_handle_event(tp, t, event, time);
+		break;
+	case TAP_STATE_DRAGGING_2_OR_TAP:
+		tp_tap_dragging2_tap_handle_event(tp, t, event, time);
+		break;
+	case TAP_STATE_DRAGGING_2_WAIT:
+		tp_tap_dragging2_wait_handle_event(tp, t, event, time);
+		break;
+	case TAP_STATE_DRAGGING_2:
+		tp_tap_dragging2_therealone_handle_event(tp, t, event, time);
+		break;
 	}
 
 	if (tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_DEAD)
@@ -877,6 +1098,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 	case TAP_STATE_TOUCH_2:
 	case TAP_STATE_TOUCH_3:
 	case TAP_STATE_MULTITAP_DOWN:
+	case TAP_STATE_TAPPED_2:
+	case TAP_STATE_DRAGGING_2_OR_DOUBLETAP:
+	case TAP_STATE_DRAGGING_2_OR_TAP:
 		filter_motion = 1;
 		break;
 
@@ -1176,9 +1400,12 @@ tp_tap_dragging(const struct tp_dispatch *tp)
 {
 	switch (tp->tap.state) {
 	case TAP_STATE_DRAGGING:
-	case TAP_STATE_DRAGGING_2:
+	case TAP_STATE_DRAGGING_TOUCH:
 	case TAP_STATE_DRAGGING_WAIT:
 	case TAP_STATE_DRAGGING_OR_TAP:
+	case TAP_STATE_DRAGGING_2_OR_TAP:
+	case TAP_STATE_DRAGGING_2_WAIT:
+	case TAP_STATE_DRAGGING_2:
 		return true;
 	default:
 		return false;
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 664514d..f6fe600 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -100,6 +100,11 @@ enum tp_tap_state {
 	TAP_STATE_DRAGGING_OR_TAP,
 	TAP_STATE_DRAGGING,
 	TAP_STATE_DRAGGING_WAIT,
+	TAP_STATE_DRAGGING_TOUCH,
+	TAP_STATE_TAPPED_2,
+	TAP_STATE_DRAGGING_2_OR_DOUBLETAP,
+	TAP_STATE_DRAGGING_2_OR_TAP,
+	TAP_STATE_DRAGGING_2_WAIT,
 	TAP_STATE_DRAGGING_2,
 	TAP_STATE_MULTITAP,
 	TAP_STATE_MULTITAP_DOWN,
-- 
2.11.0


From 981005cf5ade719c99b4dc2f117b15d0d4ba71f2 Mon Sep 17 00:00:00 2001
From: Corentin Marciau <corentin at marciau.fr>
Date: Tue, 22 Aug 2017 23:25:31 +0200
Subject: [PATCH libinput 2/2] [TEST] Fix 2fg tap touchpad tests

---
 test/test-touchpad-tap.c | 44 ++++++++++----------------------------------
 1 file changed, 10 insertions(+), 34 deletions(-)

diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c
index bdfab81..974d1b1 100644
--- a/test/test-touchpad-tap.c
+++ b/test/test-touchpad-tap.c
@@ -975,9 +975,6 @@ START_TEST(touchpad_2fg_tap)
 	struct libinput *li = dev->libinput;
 	enum libinput_config_tap_button_map map = _i; /* ranged test */
 	unsigned int button = 0;
-	struct libinput_event *ev;
-	struct libinput_event_pointer *ptrev;
-	uint64_t ptime, rtime;
 
 	litest_enable_tap(dev->libinput_device);
 	litest_set_tap_map(dev->libinput_device, map);
@@ -1002,20 +999,11 @@ START_TEST(touchpad_2fg_tap)
 
 	libinput_dispatch(li);
 
-	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-	ptime = libinput_event_pointer_get_time_usec(ptrev);
-	libinput_event_destroy(ev);
-	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-	rtime = libinput_event_pointer_get_time_usec(ptrev);
-	libinput_event_destroy(ev);
-
-	ck_assert_int_lt(ptime, rtime);
+	litest_assert_button_event(li, button,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li, button,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1027,9 +1015,6 @@ START_TEST(touchpad_2fg_tap_inverted)
 	struct libinput *li = dev->libinput;
 	enum libinput_config_tap_button_map map = _i; /* ranged test */
 	unsigned int button = 0;
-	struct libinput_event *ev;
-	struct libinput_event_pointer *ptrev;
-	uint64_t ptime, rtime;
 
 	litest_enable_tap(dev->libinput_device);
 	litest_set_tap_map(dev->libinput_device, map);
@@ -1054,20 +1039,11 @@ START_TEST(touchpad_2fg_tap_inverted)
 
 	libinput_dispatch(li);
 
-	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-	ptime = libinput_event_pointer_get_time_usec(ptrev);
-	libinput_event_destroy(ev);
-	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-	rtime = libinput_event_pointer_get_time_usec(ptrev);
-	libinput_event_destroy(ev);
-
-	ck_assert_int_lt(ptime, rtime);
+	litest_assert_button_event(li, button,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li, button,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
-- 
2.11.0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: touchpad-tap-state-machine.svg
Type: image/svg+xml
Size: 159606 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/wayland-devel/attachments/20170823/3e77f861/attachment-0001.svg>
-------------- next part --------------
<mxfile type="device" userAgent="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" version="7.1.10" editor="www.draw.io"><diagram id="48b441b8-a022-e7a4-0242-ebb41f7c39df" name="Page-1">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</diagram></mxfile>


More information about the wayland-devel mailing list