evdev-touchpad: Add a finite-state machine
authorJonas Ådahl <jadahl@gmail.com>
Thu, 27 Sep 2012 16:40:40 +0000 (18:40 +0200)
committerJonas Ådahl <jadahl@gmail.com>
Sun, 10 Nov 2013 16:51:31 +0000 (17:51 +0100)
The finite-state machine is so far used to implement support for tapping
and dragging.

Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
src/evdev-touchpad.c

index 32a22cb..9c028de 100644 (file)
@@ -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;
 }