[PATCH weston 2/8] evdev-touchpad: Add a finite-state machine

Jonas Ådahl jadahl at gmail.com
Thu Sep 27 09:40:40 PDT 2012


The finite-state machine is so far used to implement support for tapping
and dragging.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 src/evdev-touchpad.c |  188 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 188 insertions(+)

diff --git a/src/evdev-touchpad.c b/src/evdev-touchpad.c
index 7a724c8..3ba8f80 100644
--- a/src/evdev-touchpad.c
+++ b/src/evdev-touchpad.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
+#include <stdbool.h>
 #include <linux/input.h>
 
 #include "filter.h"
@@ -34,6 +35,9 @@
 #define DEFAULT_MAX_ACCEL_FACTOR 1.0
 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
 
+#define DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON BTN_LEFT
+#define DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT 100
+
 enum touchpad_model {
 	TOUCHPAD_MODEL_UNKNOWN = 0,
 	TOUCHPAD_MODEL_SYNAPTICS,
@@ -83,6 +87,21 @@ enum touchpad_fingers_state {
 	TOUCHPAD_FINGERS_THREE = (1 << 2)
 };
 
+enum fsm_event {
+	FSM_EVENT_TOUCH,
+	FSM_EVENT_RELEASE,
+	FSM_EVENT_MOTION,
+	FSM_EVENT_TIMEOUT
+};
+
+enum fsm_state {
+	FSM_IDLE,
+	FSM_TOUCH,
+	FSM_TAP,
+	FSM_TAP_2,
+	FSM_DRAG
+};
+
 struct touchpad_dispatch {
 	struct evdev_dispatch base;
 	struct evdev_device *device;
@@ -102,6 +121,12 @@ struct touchpad_dispatch {
 	int reset;
 
 	struct {
+		struct wl_array events;
+		enum fsm_state state;
+		struct wl_event_source *timer_source;
+	} fsm;
+
+	struct {
 		int32_t x;
 		int32_t y;
 	} hw_abs;
@@ -246,6 +271,147 @@ filter_motion(struct touchpad_dispatch *touchpad,
 }
 
 static void
+notify_button_pressed(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+	notify_button(touchpad->device->seat, time,
+		      DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
+		      WL_POINTER_BUTTON_STATE_PRESSED);
+}
+
+static void
+notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+	notify_button(touchpad->device->seat, time,
+		      DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
+		      WL_POINTER_BUTTON_STATE_RELEASED);
+}
+
+static void
+notify_tap(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+	notify_button_pressed(touchpad, time);
+	notify_button_released(touchpad, time);
+}
+
+static void
+process_fsm_events(struct touchpad_dispatch *touchpad, uint32_t time)
+{
+	uint32_t timeout = UINT32_MAX;
+	enum fsm_event *pevent;
+	enum fsm_event event;
+
+	if (touchpad->fsm.events.size == 0)
+		return;
+
+	wl_array_for_each(pevent, &touchpad->fsm.events) {
+		event = *pevent;
+		timeout = 0;
+
+		switch (touchpad->fsm.state) {
+		case FSM_IDLE:
+			switch (event) {
+			case FSM_EVENT_TOUCH:
+				touchpad->fsm.state = FSM_TOUCH;
+				break;
+			default:
+				break;
+			}
+			break;
+		case FSM_TOUCH:
+			switch (event) {
+			case FSM_EVENT_RELEASE:
+				timeout = DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT;
+				touchpad->fsm.state = FSM_TAP;
+				break;
+			default:
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			}
+			break;
+		case FSM_TAP:
+			switch (event) {
+			case FSM_EVENT_TIMEOUT:
+				notify_tap(touchpad, time);
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			case FSM_EVENT_TOUCH:
+				notify_button_pressed(touchpad, time);
+				touchpad->fsm.state = FSM_TAP_2;
+				break;
+			default:
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			}
+			break;
+		case FSM_TAP_2:
+			switch (event) {
+			case FSM_EVENT_MOTION:
+				touchpad->fsm.state = FSM_DRAG;
+				break;
+			case FSM_EVENT_RELEASE:
+				notify_button_released(touchpad, time);
+				notify_tap(touchpad, time);
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			default:
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			}
+			break;
+		case FSM_DRAG:
+			switch (event) {
+			case FSM_EVENT_RELEASE:
+				notify_button_released(touchpad, time);
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			default:
+				touchpad->fsm.state = FSM_IDLE;
+				break;
+			}
+			break;
+		default:
+			weston_log("evdev-touchpad: Unknown state %d",
+				   touchpad->fsm.state);
+			touchpad->fsm.state = FSM_IDLE;
+			break;
+		}
+	}
+
+	if (timeout != UINT32_MAX)
+		wl_event_source_timer_update(touchpad->fsm.timer_source,
+					     timeout);
+
+	wl_array_release(&touchpad->fsm.events);
+	wl_array_init(&touchpad->fsm.events);
+}
+
+static void
+push_fsm_event(struct touchpad_dispatch *touchpad,
+	       enum fsm_event event)
+{
+	enum fsm_event *pevent;
+
+	pevent = wl_array_add(&touchpad->fsm.events, sizeof event);
+	if (pevent)
+		*pevent = event;
+	else
+		touchpad->fsm.state = FSM_IDLE;
+}
+
+static int
+fsm_timout_handler(void *data)
+{
+	struct touchpad_dispatch *touchpad = data;
+
+	if (touchpad->fsm.events.size == 0) {
+		push_fsm_event(touchpad, FSM_EVENT_TIMEOUT);
+		process_fsm_events(touchpad, weston_compositor_get_time());
+	}
+
+	return 1;
+}
+
+static void
 touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
 {
 	int motion_index;
@@ -262,6 +428,8 @@ touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
 
 		touchpad->last_finger_state = touchpad->finger_state;
 
+		process_fsm_events(touchpad, time);
+
 		return;
 	}
 	touchpad->last_finger_state = touchpad->finger_state;
@@ -317,13 +485,18 @@ touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
 	if (!(touchpad->state & TOUCHPAD_STATE_MOVE) &&
 	    ((int)dx || (int)dy)) {
 		touchpad->state |= TOUCHPAD_STATE_MOVE;
+		push_fsm_event(touchpad, FSM_EVENT_MOTION);
 	}
+
+	process_fsm_events(touchpad, time);
 }
 
 static void
 on_touch(struct touchpad_dispatch *touchpad)
 {
 	touchpad->state |= TOUCHPAD_STATE_TOUCH;
+
+	push_fsm_event(touchpad, FSM_EVENT_TOUCH);
 }
 
 static void
@@ -332,6 +505,8 @@ on_release(struct touchpad_dispatch *touchpad)
 
 	touchpad->reset = 1;
 	touchpad->state &= ~(TOUCHPAD_STATE_MOVE | TOUCHPAD_STATE_TOUCH);
+
+	push_fsm_event(touchpad, FSM_EVENT_RELEASE);
 }
 
 static inline void
@@ -453,6 +628,7 @@ touchpad_destroy(struct evdev_dispatch *dispatch)
 		(struct touchpad_dispatch *) dispatch;
 
 	touchpad->filter->interface->destroy(touchpad->filter);
+	wl_event_source_remove(touchpad->fsm.timer_source);
 	free(dispatch);
 }
 
@@ -466,6 +642,7 @@ touchpad_init(struct touchpad_dispatch *touchpad,
 	      struct evdev_device *device)
 {
 	struct weston_motion_filter *accel;
+	struct wl_event_loop *loop;
 
 	struct input_absinfo absinfo;
 	unsigned long abs_bits[NBITS(ABS_MAX)];
@@ -524,6 +701,17 @@ touchpad_init(struct touchpad_dispatch *touchpad,
 	touchpad->last_finger_state = 0;
 	touchpad->finger_state = 0;
 
+	wl_array_init(&touchpad->fsm.events);
+	touchpad->fsm.state = FSM_IDLE;
+
+	loop = wl_display_get_event_loop(device->seat->compositor->wl_display);
+	touchpad->fsm.timer_source =
+		wl_event_loop_add_timer(loop, fsm_timout_handler, touchpad);
+	if (touchpad->fsm.timer_source == NULL) {
+		accel->interface->destroy(accel);
+		return -1;
+	}
+
 	return 0;
 }
 
-- 
1.7.9.5



More information about the wayland-devel mailing list