Use an enum to enable/disable tapping configuration
[platform/upstream/libinput.git] / src / evdev-mt-touchpad-buttons.c
index 65fa21b..fe33d0b 100644 (file)
 
 #include <errno.h>
 #include <limits.h>
-#include <time.h>
 #include <math.h>
 #include <string.h>
 #include <unistd.h>
-#include <linux/input.h>
-#include <sys/timerfd.h>
+#include "linux/input.h"
 
 #include "evdev-mt-touchpad.h"
 
@@ -54,8 +52,10 @@ button_state_to_str(enum button_state state) {
        CASE_RETURN_STRING(BUTTON_STATE_NONE);
        CASE_RETURN_STRING(BUTTON_STATE_AREA);
        CASE_RETURN_STRING(BUTTON_STATE_BOTTOM);
-       CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_NEW);
-       CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_TO_AREA);
+       CASE_RETURN_STRING(BUTTON_STATE_TOP);
+       CASE_RETURN_STRING(BUTTON_STATE_TOP_NEW);
+       CASE_RETURN_STRING(BUTTON_STATE_TOP_TO_IGNORE);
+       CASE_RETURN_STRING(BUTTON_STATE_IGNORE);
        }
        return NULL;
 }
@@ -65,6 +65,9 @@ button_event_to_str(enum button_event event) {
        switch(event) {
        CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R);
        CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L);
+       CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_R);
+       CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_M);
+       CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_L);
        CASE_RETURN_STRING(BUTTON_EVENT_IN_AREA);
        CASE_RETURN_STRING(BUTTON_EVENT_UP);
        CASE_RETURN_STRING(BUTTON_EVENT_PRESS);
@@ -75,54 +78,65 @@ button_event_to_str(enum button_event event) {
 }
 
 static inline bool
-is_inside_button_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_button_area(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       return t->y >= tp->buttons.area.top_edge;
+       return t->y >= tp->buttons.bottom_area.top_edge;
 }
 
 static inline bool
-is_inside_right_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_right_area(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       return is_inside_button_area(tp, t) &&
-              t->x > tp->buttons.area.rightbutton_left_edge;
+       return is_inside_bottom_button_area(tp, t) &&
+              t->x > tp->buttons.bottom_area.rightbutton_left_edge;
 }
 
 static inline bool
-is_inside_left_area(struct tp_dispatch *tp, struct tp_touch *t)
+is_inside_bottom_left_area(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       return is_inside_button_area(tp, t) &&
-              !is_inside_right_area(tp, t);
+       return is_inside_bottom_button_area(tp, t) &&
+              !is_inside_bottom_right_area(tp, t);
 }
 
-static void
-tp_button_set_timer(struct tp_dispatch *tp, uint64_t timeout)
+static inline bool
+is_inside_top_button_area(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       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->buttons.timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
+       return t->y <= tp->buttons.top_area.bottom_edge;
 }
 
-static void
-tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t)
+static inline bool
+is_inside_top_right_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+       return is_inside_top_button_area(tp, t) &&
+              t->x > tp->buttons.top_area.rightbutton_left_edge;
+}
+
+static inline bool
+is_inside_top_left_area(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       t->button.timeout = t->millis + DEFAULT_BUTTON_ENTER_TIMEOUT;
-       tp_button_set_timer(tp, t->button.timeout);
+       return is_inside_top_button_area(tp, t) &&
+              t->x < tp->buttons.top_area.leftbutton_right_edge;
+}
+
+static inline bool
+is_inside_top_middle_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+       return is_inside_top_button_area(tp, t) &&
+              t->x >= tp->buttons.top_area.leftbutton_right_edge &&
+              t->x <= tp->buttons.top_area.rightbutton_left_edge;
 }
 
 static void
-tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
+tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       t->button.timeout = t->millis + DEFAULT_BUTTON_LEAVE_TIMEOUT;
-       tp_button_set_timer(tp, t->button.timeout);
+       libinput_timer_set(&t->button.timer,
+                          t->millis + DEFAULT_BUTTON_ENTER_TIMEOUT);
 }
 
 static void
-tp_button_clear_timer(struct tp_dispatch *tp, struct tp_touch *t)
+tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       t->button.timeout = 0;
+       libinput_timer_set(&t->button.timer,
+                          t->millis + DEFAULT_BUTTON_LEAVE_TIMEOUT);
 }
 
 /*
@@ -133,7 +147,7 @@ static void
 tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
                    enum button_state new_state, enum button_event event)
 {
-       tp_button_clear_timer(tp, t);
+       libinput_timer_cancel(&t->button.timer);
 
        t->button.state = new_state;
        switch (t->button.state) {
@@ -145,14 +159,20 @@ tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
                tp_set_pointer(tp, t);
                break;
        case BUTTON_STATE_BOTTOM:
+               t->button.curr = event;
+               break;
+       case BUTTON_STATE_TOP:
                break;
-       case BUTTON_STATE_BOTTOM_NEW:
+       case BUTTON_STATE_TOP_NEW:
                t->button.curr = event;
                tp_button_set_enter_timer(tp, t);
                break;
-       case BUTTON_STATE_BOTTOM_TO_AREA:
+       case BUTTON_STATE_TOP_TO_IGNORE:
                tp_button_set_leave_timer(tp, t);
                break;
+       case BUTTON_STATE_IGNORE:
+               t->button.curr = 0;
+               break;
        }
 }
 
@@ -164,7 +184,12 @@ tp_button_none_handle_event(struct tp_dispatch *tp,
        switch (event) {
        case BUTTON_EVENT_IN_BOTTOM_R:
        case BUTTON_EVENT_IN_BOTTOM_L:
-               tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, event);
+               tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+               break;
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
+               tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, event);
                break;
        case BUTTON_EVENT_IN_AREA:
                tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
@@ -187,6 +212,9 @@ tp_button_area_handle_event(struct tp_dispatch *tp,
        switch (event) {
        case BUTTON_EVENT_IN_BOTTOM_R:
        case BUTTON_EVENT_IN_BOTTOM_L:
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
        case BUTTON_EVENT_IN_AREA:
                break;
        case BUTTON_EVENT_UP:
@@ -201,18 +229,51 @@ tp_button_area_handle_event(struct tp_dispatch *tp,
 
 static void
 tp_button_bottom_handle_event(struct tp_dispatch *tp,
+                             struct tp_touch *t,
+                             enum button_event event)
+{
+       switch (event) {
+       case BUTTON_EVENT_IN_BOTTOM_R:
+       case BUTTON_EVENT_IN_BOTTOM_L:
+               if (event != t->button.curr)
+                       tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
+                                           event);
+               break;
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
+       case BUTTON_EVENT_IN_AREA:
+               tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+               break;
+       case BUTTON_EVENT_UP:
+               tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+               break;
+       case BUTTON_EVENT_PRESS:
+       case BUTTON_EVENT_RELEASE:
+       case BUTTON_EVENT_TIMEOUT:
+               break;
+       }
+}
+
+static void
+tp_button_top_handle_event(struct tp_dispatch *tp,
                            struct tp_touch *t,
                            enum button_event event)
 {
        switch (event) {
        case BUTTON_EVENT_IN_BOTTOM_R:
        case BUTTON_EVENT_IN_BOTTOM_L:
+               tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event);
+               break;
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
                if (event != t->button.curr)
-                       tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+                       tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
                                            event);
                break;
        case BUTTON_EVENT_IN_AREA:
-               tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_TO_AREA, event);
+               tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event);
                break;
        case BUTTON_EVENT_UP:
                tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
@@ -225,15 +286,20 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
 }
 
 static void
-tp_button_bottom_new_handle_event(struct tp_dispatch *tp,
+tp_button_top_new_handle_event(struct tp_dispatch *tp,
                                struct tp_touch *t,
                                enum button_event event)
 {
        switch(event) {
        case BUTTON_EVENT_IN_BOTTOM_R:
        case BUTTON_EVENT_IN_BOTTOM_L:
+               tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+               break;
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
                if (event != t->button.curr)
-                       tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+                       tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
                                            event);
                break;
        case BUTTON_EVENT_IN_AREA:
@@ -243,31 +309,34 @@ tp_button_bottom_new_handle_event(struct tp_dispatch *tp,
                tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
                break;
        case BUTTON_EVENT_PRESS:
-               tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+               tp_button_set_state(tp, t, BUTTON_STATE_TOP, event);
                break;
        case BUTTON_EVENT_RELEASE:
                break;
        case BUTTON_EVENT_TIMEOUT:
-               tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+               tp_button_set_state(tp, t, BUTTON_STATE_TOP, event);
                break;
        }
 }
 
 static void
-tp_button_bottom_to_area_handle_event(struct tp_dispatch *tp,
+tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
                                      struct tp_touch *t,
                                      enum button_event event)
 {
        switch(event) {
-       case BUTTON_EVENT_IN_BOTTOM_R:
-       case BUTTON_EVENT_IN_BOTTOM_L:
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
                if (event == t->button.curr)
-                       tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
+                       tp_button_set_state(tp, t, BUTTON_STATE_TOP,
                                            event);
                else
-                       tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+                       tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW,
                                            event);
                break;
+       case BUTTON_EVENT_IN_BOTTOM_R:
+       case BUTTON_EVENT_IN_BOTTOM_L:
        case BUTTON_EVENT_IN_AREA:
                break;
        case BUTTON_EVENT_UP:
@@ -277,7 +346,30 @@ tp_button_bottom_to_area_handle_event(struct tp_dispatch *tp,
        case BUTTON_EVENT_RELEASE:
                break;
        case BUTTON_EVENT_TIMEOUT:
-               tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+               tp_button_set_state(tp, t, BUTTON_STATE_IGNORE, event);
+               break;
+       }
+}
+
+static void
+tp_button_ignore_handle_event(struct tp_dispatch *tp,
+                           struct tp_touch *t,
+                           enum button_event event)
+{
+       switch (event) {
+       case BUTTON_EVENT_IN_BOTTOM_R:
+       case BUTTON_EVENT_IN_BOTTOM_L:
+       case BUTTON_EVENT_IN_TOP_R:
+       case BUTTON_EVENT_IN_TOP_M:
+       case BUTTON_EVENT_IN_TOP_L:
+       case BUTTON_EVENT_IN_AREA:
+               break;
+       case BUTTON_EVENT_UP:
+               tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+               break;
+       case BUTTON_EVENT_PRESS:
+       case BUTTON_EVENT_RELEASE:
+       case BUTTON_EVENT_TIMEOUT:
                break;
        }
 }
@@ -288,6 +380,7 @@ tp_button_handle_event(struct tp_dispatch *tp,
                       enum button_event event,
                       uint64_t time)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
        enum button_state current = t->button.state;
 
        switch(t->button.state) {
@@ -300,16 +393,23 @@ tp_button_handle_event(struct tp_dispatch *tp,
        case BUTTON_STATE_BOTTOM:
                tp_button_bottom_handle_event(tp, t, event);
                break;
-       case BUTTON_STATE_BOTTOM_NEW:
-               tp_button_bottom_new_handle_event(tp, t, event);
+       case BUTTON_STATE_TOP:
+               tp_button_top_handle_event(tp, t, event);
+               break;
+       case BUTTON_STATE_TOP_NEW:
+               tp_button_top_new_handle_event(tp, t, event);
+               break;
+       case BUTTON_STATE_TOP_TO_IGNORE:
+               tp_button_top_to_ignore_handle_event(tp, t, event);
                break;
-       case BUTTON_STATE_BOTTOM_TO_AREA:
-               tp_button_bottom_to_area_handle_event(tp, t, event);
+       case BUTTON_STATE_IGNORE:
+               tp_button_ignore_handle_event(tp, t, event);
                break;
        }
 
        if (current != t->button.state)
-               log_debug("button state: from %s, event %s to %s\n",
+               log_debug(libinput,
+                         "button state: from %s, event %s to %s\n",
                          button_state_to_str(current),
                          button_event_to_str(event),
                          button_state_to_str(t->button.state));
@@ -327,10 +427,16 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
                if (t->state == TOUCH_END) {
                        tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time);
                } else if (t->dirty) {
-                       if (is_inside_right_area(tp, t))
+                       if (is_inside_bottom_right_area(tp, t))
                                tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
-                       else if (is_inside_left_area(tp, t))
+                       else if (is_inside_bottom_left_area(tp, t))
                                tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
+                       else if (is_inside_top_right_area(tp, t))
+                               tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_R, time);
+                       else if (is_inside_top_middle_area(tp, t))
+                               tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_M, time);
+                       else if (is_inside_top_left_area(tp, t))
+                               tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_L, time);
                        else
                                tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
                }
@@ -344,16 +450,11 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
 }
 
 static void
-tp_button_handle_timeout(struct tp_dispatch *tp, uint64_t now)
+tp_button_handle_timeout(uint64_t now, void *data)
 {
-       struct tp_touch *t;
+       struct tp_touch *t = data;
 
-       tp_for_each_touch(tp, t) {
-               if (t->button.timeout != 0 && t->button.timeout <= now) {
-                       tp_button_clear_timer(tp, t);
-                       tp_button_handle_event(tp, t, BUTTON_EVENT_TIMEOUT, now);
-               }
-       }
+       tp_button_handle_event(t->tp, t, BUTTON_EVENT_TIMEOUT, now);
 }
 
 int
@@ -361,12 +462,14 @@ tp_process_button(struct tp_dispatch *tp,
                  const struct input_event *e,
                  uint64_t time)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
        uint32_t mask = 1 << (e->code - BTN_LEFT);
 
        /* Ignore other buttons on clickpads */
        if (tp->buttons.is_clickpad && e->code != BTN_LEFT) {
-               log_bug("received %s button event on a clickpad (kernel bug?)\n",
-                       libevdev_event_code_get_name(EV_KEY, e->code));
+               log_bug_kernel(libinput,
+                              "received %s button event on a clickpad\n",
+                              libevdev_event_code_get_name(EV_KEY, e->code));
                return 0;
        }
 
@@ -381,73 +484,86 @@ tp_process_button(struct tp_dispatch *tp,
        return 0;
 }
 
-static void
-tp_button_timeout_handler(void *data)
-{
-       struct tp_dispatch *tp = data;
-       uint64_t expires;
-       int len;
-       struct timespec ts;
-       uint64_t now;
-
-       len = read(tp->buttons.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_button_handle_timeout(tp, now);
-}
-
 int
 tp_init_buttons(struct tp_dispatch *tp,
                struct evdev_device *device)
 {
+       struct libinput *libinput = tp->device->base.seat->libinput;
+       struct tp_touch *t;
        int width, height;
        double diagonal;
+       const struct input_absinfo *absinfo_x, *absinfo_y;
 
        tp->buttons.is_clickpad = libevdev_has_property(device->evdev,
                                                        INPUT_PROP_BUTTONPAD);
+       tp->buttons.has_topbuttons = libevdev_has_property(device->evdev,
+                                                       INPUT_PROP_TOPBUTTONPAD);
 
        if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) ||
            libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) {
                if (tp->buttons.is_clickpad)
-                       log_bug("clickpad advertising right button (kernel bug?)\n");
+                       log_bug_kernel(libinput,
+                                      "%s: clickpad advertising right button\n",
+                                      device->sysname);
        } else {
                if (!tp->buttons.is_clickpad)
-                       log_bug("non clickpad without right button (kernel bug)?\n");
+                       log_bug_kernel(libinput,
+                                      "%s: non clickpad without right button?\n",
+                                      device->sysname);
        }
 
-       width = abs(device->abs.max_x - device->abs.min_x);
-       height = abs(device->abs.max_y - device->abs.min_y);
+       absinfo_x = device->abs.absinfo_x;
+       absinfo_y = device->abs.absinfo_y;
+
+       width = abs(absinfo_x->maximum - absinfo_x->minimum);
+       height = abs(absinfo_y->maximum - absinfo_y->minimum);
        diagonal = sqrt(width*width + height*height);
 
        tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD;
 
-       if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* Apple */
+       if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE)
                tp->buttons.use_clickfinger = true;
 
        if (tp->buttons.is_clickpad && !tp->buttons.use_clickfinger) {
-               tp->buttons.area.top_edge = height * .8 + device->abs.min_y;
-               tp->buttons.area.rightbutton_left_edge = width/2 + device->abs.min_x;
-               tp->buttons.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
-
-               if (tp->buttons.timer_fd == -1)
-                       return -1;
-
-               tp->buttons.source =
-                       libinput_add_fd(tp->device->base.seat->libinput,
-                                       tp->buttons.timer_fd,
-                                       tp_button_timeout_handler,
-                                       tp);
-               if (tp->buttons.source == NULL)
-                       return -1;
+               int xoffset = absinfo_x->minimum,
+                   yoffset = absinfo_y->minimum;
+               int yres = absinfo_y->resolution;
+
+               /* button height: 10mm or 15% of the touchpad height,
+                  whichever is smaller */
+               if (yres > 1 && (height * 0.15/yres) > 10) {
+                       tp->buttons.bottom_area.top_edge =
+                               absinfo_y->maximum - 10 * yres;
+               } else {
+                       tp->buttons.bottom_area.top_edge = height * .85 + yoffset;
+               }
+
+               tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset;
+
+               if (tp->buttons.has_topbuttons) {
+                       /* T440s has the top button line 5mm from the top,
+                          make the buttons 6mm high */
+                       if (yres > 1) {
+                               tp->buttons.top_area.bottom_edge =
+                                       yoffset + 6 * yres;
+                       } else {
+                               tp->buttons.top_area.bottom_edge = height * .08 + yoffset;
+                       }
+                       tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset;
+                       tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset;
+               } else {
+                       tp->buttons.top_area.bottom_edge = INT_MIN;
+               }
        } else {
-               tp->buttons.area.top_edge = INT_MAX;
+               tp->buttons.bottom_area.top_edge = INT_MAX;
+               tp->buttons.top_area.bottom_edge = INT_MIN;
+       }
+
+       tp_for_each_touch(tp, t) {
+               t->button.state = BUTTON_STATE_NONE;
+               libinput_timer_init(&t->button.timer,
+                                   tp->device->base.seat->libinput,
+                                   tp_button_handle_timeout, t);
        }
 
        return 0;
@@ -456,22 +572,17 @@ tp_init_buttons(struct tp_dispatch *tp,
 void
 tp_destroy_buttons(struct tp_dispatch *tp)
 {
-       if (tp->buttons.source) {
-               libinput_remove_source(tp->device->base.seat->libinput,
-                                      tp->buttons.source);
-               tp->buttons.source = NULL;
-       }
-       if (tp->buttons.timer_fd > -1) {
-               close(tp->buttons.timer_fd);
-               tp->buttons.timer_fd = -1;
-       }
+       struct tp_touch *t;
+
+       tp_for_each_touch(tp, t)
+               libinput_timer_cancel(&t->button.timer);
 }
 
 static int
 tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint64_t time)
 {
        uint32_t current, old, button;
-       enum libinput_pointer_button_state state;
+       enum libinput_button_state state;
 
        current = tp->buttons.state;
        old = tp->buttons.old_state;
@@ -488,11 +599,11 @@ tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint64_t time)
                        return 0;
                }
                tp->buttons.active = button;
-               state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+               state = LIBINPUT_BUTTON_STATE_PRESSED;
        } else {
                button = tp->buttons.active;
                tp->buttons.active = 0;
-               state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+               state = LIBINPUT_BUTTON_STATE_RELEASED;
        }
 
        if (button)
@@ -513,13 +624,13 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
        button = BTN_LEFT;
 
        while (current || old) {
-               enum libinput_pointer_button_state state;
+               enum libinput_button_state state;
 
                if ((current & 0x1) ^ (old & 0x1)) {
                        if (!!(current & 0x1))
-                               state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+                               state = LIBINPUT_BUTTON_STATE_PRESSED;
                        else
-                               state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+                               state = LIBINPUT_BUTTON_STATE_RELEASED;
 
                        pointer_notify_button(&tp->device->base,
                                              time,
@@ -539,8 +650,8 @@ static int
 tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time)
 {
        uint32_t current, old, button;
-       enum libinput_pointer_button_state state;
-       enum { AREA = 0x01, LEFT = 0x02, RIGHT = 0x04 };
+       enum libinput_button_state state;
+       enum { AREA = 0x01, LEFT = 0x02, MIDDLE = 0x04, RIGHT = 0x08 };
 
        current = tp->buttons.state;
        old = tp->buttons.old_state;
@@ -558,9 +669,14 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time)
                                button |= AREA;
                                break;
                        case BUTTON_EVENT_IN_BOTTOM_L:
+                       case BUTTON_EVENT_IN_TOP_L:
                                button |= LEFT;
                                break;
+                       case BUTTON_EVENT_IN_TOP_M:
+                               button |= MIDDLE;
+                               break;
                        case BUTTON_EVENT_IN_BOTTOM_R:
+                       case BUTTON_EVENT_IN_TOP_R:
                                button |= RIGHT;
                                break;
                        default:
@@ -568,31 +684,25 @@ tp_post_softbutton_buttons(struct tp_dispatch *tp, uint64_t time)
                        }
                }
 
-               switch (button) {
-               case 0:
+               if (button == 0) {
                        /* No touches, wait for a touch before processing */
                        tp->buttons.click_pending = true;
                        return 0;
-               case RIGHT:
-               case RIGHT | AREA:
-                       /* Some touches in right, no touches in left */
-                       button = BTN_RIGHT;
-                       break;
-               case LEFT | RIGHT:
-               case LEFT | RIGHT | AREA:
-                       /* Some touches in left and some in right */
+               }
+
+               if ((button & MIDDLE) || ((button & LEFT) && (button & RIGHT)))
                        button = BTN_MIDDLE;
-                       break;
-               default:
+               else if (button & RIGHT)
+                       button = BTN_RIGHT;
+               else
                        button = BTN_LEFT;
-               }
 
                tp->buttons.active = button;
-               state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+               state = LIBINPUT_BUTTON_STATE_PRESSED;
        } else {
                button = tp->buttons.active;
                tp->buttons.active = 0;
-               state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+               state = LIBINPUT_BUTTON_STATE_RELEASED;
        }
 
        tp->buttons.click_pending = false;
@@ -623,3 +733,9 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
 {
        return t->button.state == BUTTON_STATE_AREA;
 }
+
+bool
+tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+       return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t);
+}