filter: add a filter-private.h header file
[platform/upstream/libinput.git] / src / evdev-mt-touchpad-tap.c
index eee334f..8f055fc 100644 (file)
@@ -30,9 +30,7 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
-#include <time.h>
 #include <unistd.h>
-#include <sys/timerfd.h>
 
 #include "evdev-mt-touchpad.h"
 
@@ -97,12 +95,16 @@ tap_event_to_str(enum tap_event event) {
 
 static void
 tp_tap_notify(struct tp_dispatch *tp,
+             struct tp_touch *t,
              uint64_t time,
              int nfingers,
-             enum libinput_pointer_button_state state)
+             enum libinput_button_state state)
 {
        int32_t button;
 
+       if (t && t->tap.state == TAP_TOUCH_STATE_DEAD)
+               return;
+
        switch (nfingers) {
        case 1: button = BTN_LEFT; break;
        case 2: button = BTN_RIGHT; break;
@@ -111,36 +113,30 @@ tp_tap_notify(struct tp_dispatch *tp,
                return;
        }
 
-       pointer_notify_button(&tp->device->base,
-                             time,
-                             button,
-                             state);
+       evdev_pointer_notify_button(tp->device,
+                                   time,
+                                   button,
+                                   state);
 }
 
 static void
 tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
 {
-       uint64_t timeout = time + DEFAULT_TAP_TIMEOUT_PERIOD;
-       struct itimerspec its;
-
-       its.it_interval.tv_sec = 0;
-       its.it_interval.tv_nsec = 0;
-       its.it_value.tv_sec = timeout / 1000;
-       its.it_value.tv_nsec = (timeout % 1000) * 1000 * 1000;
-       timerfd_settime(tp->tap.timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
-
-       tp->tap.timeout = timeout;
+       libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD);
 }
 
 static void
 tp_tap_clear_timer(struct tp_dispatch *tp)
 {
-       tp->tap.timeout = 0;
+       libinput_timer_cancel(&tp->tap.timer);
 }
 
 static void
-tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_idle_handle_event(struct tp_dispatch *tp,
+                        struct tp_touch *t,
+                        enum tap_event event, uint64_t time)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
 
        switch (event) {
        case TAP_EVENT_TOUCH:
@@ -149,7 +145,8 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
                break;
        case TAP_EVENT_RELEASE:
        case TAP_EVENT_MOTION:
-               log_info("invalid event, no fingers are down\n");
+               log_bug_libinput(libinput,
+                                "invalid event, no fingers are down\n");
                break;
        case TAP_EVENT_TIMEOUT:
                break;
@@ -160,7 +157,9 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
 }
 
 static void
-tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_touch_handle_event(struct tp_dispatch *tp,
+                         struct tp_touch *t,
+                         enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -170,7 +169,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
                break;
        case TAP_EVENT_RELEASE:
                tp->tap.state = TAP_STATE_TAPPED;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
                tp_tap_set_timer(tp, time);
                break;
        case TAP_EVENT_TIMEOUT:
@@ -185,7 +184,9 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
 }
 
 static void
-tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_hold_handle_event(struct tp_dispatch *tp,
+                        struct tp_touch *t,
+                        enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -206,31 +207,36 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
 }
 
 static void
-tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_tapped_handle_event(struct tp_dispatch *tp,
+                          struct tp_touch *t,
+                          enum tap_event event, uint64_t time)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
 
        switch (event) {
        case TAP_EVENT_MOTION:
        case TAP_EVENT_RELEASE:
-               log_info("invalid event when fingers are up\n");
+               log_bug_libinput(libinput,
+                                "invalid event when fingers are up\n");
                break;
        case TAP_EVENT_TOUCH:
                tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
-               tp_tap_clear_timer(tp);
                break;
        case TAP_EVENT_TIMEOUT:
                tp->tap.state = TAP_STATE_IDLE;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        }
 }
 
 static void
-tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_touch2_handle_event(struct tp_dispatch *tp,
+                          struct tp_touch *t,
+                          enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -240,12 +246,13 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_
                break;
        case TAP_EVENT_RELEASE:
                tp->tap.state = TAP_STATE_HOLD;
-               tp_tap_notify(tp, time, 2, LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
-               tp_tap_notify(tp, time, 2, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 2, LIBINPUT_BUTTON_STATE_PRESSED);
+               tp_tap_notify(tp, t, time, 2, LIBINPUT_BUTTON_STATE_RELEASED);
                tp_tap_clear_timer(tp);
                break;
        case TAP_EVENT_MOTION:
                tp_tap_clear_timer(tp);
+               /* fallthrough */
        case TAP_EVENT_TIMEOUT:
                tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
                break;
@@ -256,7 +263,9 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_
 }
 
 static void
-tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
+                               struct tp_touch *t,
+                               enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -278,7 +287,9 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, ui
 }
 
 static void
-tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_touch3_handle_event(struct tp_dispatch *tp,
+                          struct tp_touch *t,
+                          enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -293,8 +304,8 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_
                break;
        case TAP_EVENT_RELEASE:
                tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
-               tp_tap_notify(tp, time, 3, LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
-               tp_tap_notify(tp, time, 3, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 3, LIBINPUT_BUTTON_STATE_PRESSED);
+               tp_tap_notify(tp, t, time, 3, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
@@ -303,7 +314,9 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_
 }
 
 static void
-tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
+                               struct tp_touch *t,
+                               enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -324,7 +337,9 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, ui
 }
 
 static void
-tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
+                                         struct tp_touch *t,
+                                         enum tap_event event, uint64_t time)
 {
        switch (event) {
        case TAP_EVENT_TOUCH:
@@ -332,9 +347,9 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event
                break;
        case TAP_EVENT_RELEASE:
                tp->tap.state = TAP_STATE_IDLE;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                tp_tap_clear_timer(tp);
                break;
        case TAP_EVENT_MOTION:
@@ -343,13 +358,15 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        }
 }
 
 static void
-tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_dragging_handle_event(struct tp_dispatch *tp,
+                            struct tp_touch *t,
+                            enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -366,13 +383,15 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint6
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        }
 }
 
 static void
-tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
+                                 struct tp_touch *t,
+                                 enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -385,17 +404,19 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event,
                break;
        case TAP_EVENT_TIMEOUT:
                tp->tap.state = TAP_STATE_IDLE;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        }
 }
 
 static void
-tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
+                             struct tp_touch *t,
+                             enum tap_event event, uint64_t time)
 {
 
        switch (event) {
@@ -404,7 +425,7 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint
                break;
        case TAP_EVENT_TOUCH:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        case TAP_EVENT_MOTION:
        case TAP_EVENT_TIMEOUT:
@@ -412,13 +433,16 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint
                break;
        case TAP_EVENT_BUTTON:
                tp->tap.state = TAP_STATE_DEAD;
-               tp_tap_notify(tp, time, 1, LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+               tp_tap_notify(tp, t, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
                break;
        }
 }
 
 static void
-tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_dead_handle_event(struct tp_dispatch *tp,
+                        struct tp_touch *t,
+                        enum tap_event event,
+                        uint64_t time)
 {
 
        switch (event) {
@@ -435,60 +459,69 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t
 }
 
 static void
-tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
+tp_tap_handle_event(struct tp_dispatch *tp,
+                   struct tp_touch *t,
+                   enum tap_event event,
+                   uint64_t time)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
        enum tp_tap_state current;
-       if (!tp->tap.enabled)
-               return;
 
        current = tp->tap.state;
 
        switch(tp->tap.state) {
        case TAP_STATE_IDLE:
-               tp_tap_idle_handle_event(tp, event, time);
+               if (!tp->tap.enabled)
+                       break;
+
+               tp_tap_idle_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TOUCH:
-               tp_tap_touch_handle_event(tp, event, time);
+               tp_tap_touch_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_HOLD:
-               tp_tap_hold_handle_event(tp, event, time);
+               tp_tap_hold_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TAPPED:
-               tp_tap_tapped_handle_event(tp, event, time);
+               tp_tap_tapped_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TOUCH_2:
-               tp_tap_touch2_handle_event(tp, event, time);
+               tp_tap_touch2_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TOUCH_2_HOLD:
-               tp_tap_touch2_hold_handle_event(tp, event, time);
+               tp_tap_touch2_hold_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TOUCH_3:
-               tp_tap_touch3_handle_event(tp, event, time);
+               tp_tap_touch3_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_TOUCH_3_HOLD:
-               tp_tap_touch3_hold_handle_event(tp, event, time);
+               tp_tap_touch3_hold_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_DRAGGING_OR_DOUBLETAP:
-               tp_tap_dragging_or_doubletap_handle_event(tp, event, time);
+               tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_DRAGGING:
-               tp_tap_dragging_handle_event(tp, event, time);
+               tp_tap_dragging_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_DRAGGING_WAIT:
-               tp_tap_dragging_wait_handle_event(tp, event, time);
+               tp_tap_dragging_wait_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_DRAGGING_2:
-               tp_tap_dragging2_handle_event(tp, event, time);
+               tp_tap_dragging2_handle_event(tp, t, event, time);
                break;
        case TAP_STATE_DEAD:
-               tp_tap_dead_handle_event(tp, event, time);
+               tp_tap_dead_handle_event(tp, t, event, time);
                break;
        }
 
        if (tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_DEAD)
                tp_tap_clear_timer(tp);
 
-       log_debug("%s → %s → %s\n", tap_state_to_str(current), tap_event_to_str(event), tap_state_to_str(tp->tap.state));
+       log_debug(libinput,
+                 "tap state: %s → %s → %s\n",
+                 tap_state_to_str(current),
+                 tap_event_to_str(event),
+                 tap_state_to_str(tp->tap.state));
 }
 
 static bool
@@ -508,20 +541,38 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
        struct tp_touch *t;
        int filter_motion = 0;
 
-       if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
-               tp_tap_handle_event(tp, TAP_EVENT_BUTTON, time);
+       /* Handle queued button pressed events from clickpads. For touchpads
+        * with separate physical buttons, ignore button pressed events so they
+        * don't interfere with tapping. */
+       if (tp->buttons.is_clickpad && tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
+               tp_tap_handle_event(tp, NULL, TAP_EVENT_BUTTON, time);
 
        tp_for_each_touch(tp, t) {
                if (!t->dirty || t->state == TOUCH_NONE)
                        continue;
 
-               if (t->state == TOUCH_BEGIN)
-                       tp_tap_handle_event(tp, TAP_EVENT_TOUCH, time);
-               else if (t->state == TOUCH_END)
-                       tp_tap_handle_event(tp, TAP_EVENT_RELEASE, time);
-               else if (tp->tap.state != TAP_STATE_IDLE &&
-                        tp_tap_exceeds_motion_threshold(tp, t))
-                       tp_tap_handle_event(tp, TAP_EVENT_MOTION, time);
+               if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
+                       t->tap.state = TAP_TOUCH_STATE_DEAD;
+
+               if (t->state == TOUCH_BEGIN) {
+                       t->tap.state = TAP_TOUCH_STATE_TOUCH;
+                       tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
+               } else if (t->state == TOUCH_END) {
+                       tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
+                       t->tap.state = TAP_TOUCH_STATE_DEAD;
+               } else if (tp->tap.state != TAP_STATE_IDLE &&
+                        tp_tap_exceeds_motion_threshold(tp, t)) {
+                       struct tp_touch *tmp;
+
+                       /* Any touch exceeding the threshold turns all
+                        * touches into DEAD */
+                       tp_for_each_touch(tp, tmp) {
+                               if (tmp->tap.state == TAP_TOUCH_STATE_TOUCH)
+                                       tmp->tap.state = TAP_TOUCH_STATE_DEAD;
+                       }
+
+                       tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
+               }
        }
 
        /**
@@ -548,62 +599,91 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 }
 
 static void
-tp_tap_timeout_handler(void *data)
+tp_tap_handle_timeout(uint64_t time, void *data)
 {
-       struct tp_dispatch *touchpad = data;
-       uint64_t expires;
-       int len;
-       struct timespec ts;
-       uint64_t now;
-
-       len = read(touchpad->tap.timer_fd, &expires, sizeof expires);
-       if (len != sizeof expires)
-               /* This will only happen if the application made the fd
-                * non-blocking, but this function should only be called
-                * upon the timeout, so lets continue anyway. */
-               log_error("timerfd read error: %s\n", strerror(errno));
-
-       clock_gettime(CLOCK_MONOTONIC, &ts);
-       now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
-
-       tp_tap_handle_timeout(touchpad, now);
+       struct tp_dispatch *tp = data;
+       struct tp_touch *t;
+
+       tp_tap_handle_event(tp, NULL, TAP_EVENT_TIMEOUT, time);
+
+       tp_for_each_touch(tp, t) {
+               if (t->state == TOUCH_NONE ||
+                   t->tap.state == TAP_TOUCH_STATE_IDLE)
+                       continue;
+
+               t->tap.state = TAP_TOUCH_STATE_DEAD;
+       }
 }
 
-unsigned int
-tp_tap_handle_timeout(struct tp_dispatch *tp, uint64_t time)
+static int
+tp_tap_config_count(struct libinput_device *device)
 {
-       if (!tp->tap.enabled)
-               return 0;
+       struct evdev_dispatch *dispatch;
+       struct tp_dispatch *tp = NULL;
 
-       if (tp->tap.timeout && tp->tap.timeout <= time) {
-               tp_tap_clear_timer(tp);
-               tp_tap_handle_event(tp, TAP_EVENT_TIMEOUT, time);
-       }
+       dispatch = ((struct evdev_device *) device)->dispatch;
+       tp = container_of(dispatch, tp, base);
 
-       return tp->tap.timeout;
+       return min(tp->ntouches, 3); /* we only do up to 3 finger tap */
 }
 
-int
-tp_init_tap(struct tp_dispatch *tp)
+static enum libinput_config_status
+tp_tap_config_set_enabled(struct libinput_device *device,
+                         enum libinput_config_tap_state enabled)
 {
-       tp->tap.state = TAP_STATE_IDLE;
-       tp->tap.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+       struct evdev_dispatch *dispatch;
+       struct tp_dispatch *tp = NULL;
 
-       if (tp->tap.timer_fd == -1)
-               return -1;
+       dispatch = ((struct evdev_device *) device)->dispatch;
+       tp = container_of(dispatch, tp, base);
 
-       tp->tap.source =
-               libinput_add_fd(tp->device->base.seat->libinput,
-                               tp->tap.timer_fd,
-                               tp_tap_timeout_handler,
-                               tp);
+       tp->tap.enabled = (enabled == LIBINPUT_CONFIG_TAP_ENABLED);
 
-       if (tp->tap.source == NULL) {
-               close(tp->tap.timer_fd);
-               return -1;
-       }
+       return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
 
-       tp->tap.enabled = 1; /* FIXME */
+static enum libinput_config_tap_state
+tp_tap_config_is_enabled(struct libinput_device *device)
+{
+       struct evdev_dispatch *dispatch;
+       struct tp_dispatch *tp = NULL;
+
+       dispatch = ((struct evdev_device *) device)->dispatch;
+       tp = container_of(dispatch, tp, base);
+
+       return tp->tap.enabled ? LIBINPUT_CONFIG_TAP_ENABLED :
+                                LIBINPUT_CONFIG_TAP_DISABLED;
+}
+
+static enum libinput_config_tap_state
+tp_tap_config_get_default(struct libinput_device *device)
+{
+       /**
+        * Tapping is disabled by default for two reasons:
+        * * if you don't know that tapping is a thing (or enabled by
+        *   default), you get spurious mouse events that make the desktop
+        *   feel buggy.
+        * * if you do know what tapping is and you want it, you
+        *   usually know where to enable it, or at least you can search for
+        *   it.
+        */
+       return LIBINPUT_CONFIG_TAP_DISABLED;
+}
+
+int
+tp_init_tap(struct tp_dispatch *tp)
+{
+       tp->tap.config.count = tp_tap_config_count;
+       tp->tap.config.set_enabled = tp_tap_config_set_enabled;
+       tp->tap.config.get_enabled = tp_tap_config_is_enabled;
+       tp->tap.config.get_default = tp_tap_config_get_default;
+       tp->device->base.config.tap = &tp->tap.config;
+
+       tp->tap.state = TAP_STATE_IDLE;
+
+       libinput_timer_init(&tp->tap.timer,
+                           tp->device->base.seat->libinput,
+                           tp_tap_handle_timeout, tp);
 
        return 0;
 }
@@ -611,12 +691,11 @@ tp_init_tap(struct tp_dispatch *tp)
 void
 tp_destroy_tap(struct tp_dispatch *tp)
 {
-       if (tp->tap.source) {
-               libinput_remove_source(tp->device->base.seat->libinput, tp->tap.source);
-               tp->tap.source = NULL;
-       }
-       if (tp->tap.timer_fd > -1) {
-               close(tp->tap.timer_fd);
-               tp->tap.timer_fd = -1;
-       }
+       libinput_timer_cancel(&tp->tap.timer);
+}
+
+void
+tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
+{
+       tp_tap_handle_timeout(now, tp);
 }