diff --git a/doc/Makefile.am b/doc/Makefile.am
index 31b673b3..a33638da 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,3 +1,5 @@
+EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg
+
if HAVE_DOXYGEN
noinst_DATA = html/index.html
@@ -12,7 +14,7 @@ clean-local:
$(AM_V_at)rm -rf html
doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null)
-EXTRA_DIST = $(builddir)/html/index.html $(doc_src)
+EXTRA_DIST += $(builddir)/html/index.html $(doc_src)
endif
diff --git a/doc/touchpad-softbutton-state-machine.svg b/doc/touchpad-softbutton-state-machine.svg
new file mode 100644
index 00000000..11423435
--- /dev/null
+++ b/doc/touchpad-softbutton-state-machine.svg
@@ -0,0 +1,390 @@
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 471bc1d9..68e02dec 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,6 +14,7 @@ libinput_la_SOURCES = \
evdev-mt-touchpad.c \
evdev-mt-touchpad.h \
evdev-mt-touchpad-tap.c \
+ evdev-mt-touchpad-buttons.c \
evdev-touchpad.c \
filter.c \
filter.h \
diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
new file mode 100644
index 00000000..65fa21be
--- /dev/null
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright © 2014 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "evdev-mt-touchpad.h"
+
+#define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* 2% of size */
+#define DEFAULT_BUTTON_ENTER_TIMEOUT 100 /* ms */
+#define DEFAULT_BUTTON_LEAVE_TIMEOUT 300 /* ms */
+
+/*****************************************
+ * BEFORE YOU EDIT THIS FILE, look at the state diagram in
+ * doc/touchpad-softbutton-state-machine.svg, or online at
+ * https://drive.google.com/file/d/0B1NwWmji69nocUs1cVJTbkdwMFk/edit?usp=sharing
+ * (it's a http://draw.io diagram)
+ *
+ * Any changes in this file must be represented in the diagram.
+ *
+ * The state machine only affects the soft button area code.
+ */
+
+#define CASE_RETURN_STRING(a) case a: return #a;
+
+static inline const char*
+button_state_to_str(enum button_state state) {
+ switch(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);
+ }
+ return NULL;
+}
+
+static inline const char*
+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_AREA);
+ CASE_RETURN_STRING(BUTTON_EVENT_UP);
+ CASE_RETURN_STRING(BUTTON_EVENT_PRESS);
+ CASE_RETURN_STRING(BUTTON_EVENT_RELEASE);
+ CASE_RETURN_STRING(BUTTON_EVENT_TIMEOUT);
+ }
+ return NULL;
+}
+
+static inline bool
+is_inside_button_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return t->y >= tp->buttons.area.top_edge;
+}
+
+static inline bool
+is_inside_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;
+}
+
+static inline bool
+is_inside_left_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return is_inside_button_area(tp, t) &&
+ !is_inside_right_area(tp, t);
+}
+
+static void
+tp_button_set_timer(struct tp_dispatch *tp, uint64_t timeout)
+{
+ 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);
+}
+
+static void
+tp_button_set_enter_timer(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);
+}
+
+static void
+tp_button_set_leave_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);
+}
+
+static void
+tp_button_clear_timer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ t->button.timeout = 0;
+}
+
+/*
+ * tp_button_set_state, change state and implement on-entry behavior
+ * as described in the state machine diagram.
+ */
+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);
+
+ t->button.state = new_state;
+ switch (t->button.state) {
+ case BUTTON_STATE_NONE:
+ t->button.curr = 0;
+ break;
+ case BUTTON_STATE_AREA:
+ t->button.curr = BUTTON_EVENT_IN_AREA;
+ tp_set_pointer(tp, t);
+ break;
+ case BUTTON_STATE_BOTTOM:
+ break;
+ case BUTTON_STATE_BOTTOM_NEW:
+ t->button.curr = event;
+ tp_button_set_enter_timer(tp, t);
+ break;
+ case BUTTON_STATE_BOTTOM_TO_AREA:
+ tp_button_set_leave_timer(tp, t);
+ break;
+ }
+}
+
+static void
+tp_button_none_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_BOTTOM_NEW, event);
+ break;
+ 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_area_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_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;
+ }
+}
+
+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_NEW,
+ event);
+ break;
+ case BUTTON_EVENT_IN_AREA:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_TO_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_bottom_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:
+ if (event != t->button.curr)
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+ event);
+ break;
+ 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:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+ break;
+ case BUTTON_EVENT_RELEASE:
+ break;
+ case BUTTON_EVENT_TIMEOUT:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+ break;
+ }
+}
+
+static void
+tp_button_bottom_to_area_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);
+ else
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+ event);
+ break;
+ 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:
+ break;
+ case BUTTON_EVENT_TIMEOUT:
+ tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+ break;
+ }
+}
+
+static void
+tp_button_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event,
+ uint64_t time)
+{
+ enum button_state current = t->button.state;
+
+ switch(t->button.state) {
+ case BUTTON_STATE_NONE:
+ tp_button_none_handle_event(tp, t, event);
+ break;
+ case BUTTON_STATE_AREA:
+ tp_button_area_handle_event(tp, t, event);
+ break;
+ 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);
+ break;
+ case BUTTON_STATE_BOTTOM_TO_AREA:
+ tp_button_bottom_to_area_handle_event(tp, t, event);
+ break;
+ }
+
+ if (current != t->button.state)
+ log_debug("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));
+}
+
+int
+tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
+{
+ struct tp_touch *t;
+
+ tp_for_each_touch(tp, t) {
+ if (t->state == TOUCH_NONE)
+ continue;
+
+ 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))
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
+ else if (is_inside_left_area(tp, t))
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
+ else
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
+ }
+ if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
+ tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time);
+ if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
+ tp_button_handle_event(tp, t, BUTTON_EVENT_PRESS, time);
+ }
+
+ return 0;
+}
+
+static void
+tp_button_handle_timeout(struct tp_dispatch *tp, uint64_t now)
+{
+ struct tp_touch *t;
+
+ 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);
+ }
+ }
+}
+
+int
+tp_process_button(struct tp_dispatch *tp,
+ const struct input_event *e,
+ uint64_t time)
+{
+ 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));
+ return 0;
+ }
+
+ if (e->value) {
+ tp->buttons.state |= mask;
+ tp->queued |= TOUCHPAD_EVENT_BUTTON_PRESS;
+ } else {
+ tp->buttons.state &= ~mask;
+ tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE;
+ }
+
+ 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)
+{
+ int width, height;
+ double diagonal;
+
+ tp->buttons.is_clickpad = libevdev_has_property(device->evdev,
+ INPUT_PROP_BUTTONPAD);
+
+ 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");
+ } else {
+ if (!tp->buttons.is_clickpad)
+ log_bug("non clickpad without right button (kernel bug)?\n");
+ }
+
+ width = abs(device->abs.max_x - device->abs.min_x);
+ height = abs(device->abs.max_y - device->abs.min_y);
+ diagonal = sqrt(width*width + height*height);
+
+ tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD;
+
+ if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* 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;
+ } else {
+ tp->buttons.area.top_edge = INT_MAX;
+ }
+
+ return 0;
+}
+
+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;
+ }
+}
+
+static int
+tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint64_t time)
+{
+ uint32_t current, old, button;
+ enum libinput_pointer_button_state state;
+
+ current = tp->buttons.state;
+ old = tp->buttons.old_state;
+
+ if (current == old)
+ return 0;
+
+ if (current) {
+ switch (tp->nfingers_down) {
+ case 1: button = BTN_LEFT; break;
+ case 2: button = BTN_RIGHT; break;
+ case 3: button = BTN_MIDDLE; break;
+ default:
+ return 0;
+ }
+ tp->buttons.active = button;
+ state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+ } else {
+ button = tp->buttons.active;
+ tp->buttons.active = 0;
+ state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+ }
+
+ if (button)
+ pointer_notify_button(&tp->device->base,
+ time,
+ button,
+ state);
+ return 1;
+}
+
+static int
+tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
+{
+ uint32_t current, old, button;
+
+ current = tp->buttons.state;
+ old = tp->buttons.old_state;
+ button = BTN_LEFT;
+
+ while (current || old) {
+ enum libinput_pointer_button_state state;
+
+ if ((current & 0x1) ^ (old & 0x1)) {
+ if (!!(current & 0x1))
+ state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+ else
+ state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+
+ pointer_notify_button(&tp->device->base,
+ time,
+ button,
+ state);
+ }
+
+ button++;
+ current >>= 1;
+ old >>= 1;
+ }
+
+ return 0;
+}
+
+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 };
+
+ current = tp->buttons.state;
+ old = tp->buttons.old_state;
+ button = 0;
+
+ if (!tp->buttons.click_pending && current == old)
+ return 0;
+
+ if (current) {
+ struct tp_touch *t;
+
+ tp_for_each_touch(tp, t) {
+ switch (t->button.curr) {
+ case BUTTON_EVENT_IN_AREA:
+ button |= AREA;
+ break;
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ button |= LEFT;
+ break;
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ button |= RIGHT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (button) {
+ case 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 */
+ button = BTN_MIDDLE;
+ break;
+ default:
+ button = BTN_LEFT;
+ }
+
+ tp->buttons.active = button;
+ state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
+ } else {
+ button = tp->buttons.active;
+ tp->buttons.active = 0;
+ state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
+ }
+
+ tp->buttons.click_pending = false;
+
+ if (button)
+ pointer_notify_button(&tp->device->base,
+ time,
+ button,
+ state);
+ return 1;
+}
+
+int
+tp_post_button_events(struct tp_dispatch *tp, uint64_t time)
+{
+ if (tp->buttons.is_clickpad) {
+ if (tp->buttons.use_clickfinger)
+ return tp_post_clickfinger_buttons(tp, time);
+ else
+ return tp_post_softbutton_buttons(tp, time);
+ }
+
+ return tp_post_physical_buttons(tp, time);
+}
+
+int
+tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return t->button.state == BUTTON_STATE_AREA;
+}
diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index 5fa712f6..eee334f2 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -97,7 +97,7 @@ tap_event_to_str(enum tap_event event) {
static void
tp_tap_notify(struct tp_dispatch *tp,
- uint32_t time,
+ uint64_t time,
int nfingers,
enum libinput_pointer_button_state state)
{
@@ -118,9 +118,9 @@ tp_tap_notify(struct tp_dispatch *tp,
}
static void
-tp_tap_set_timer(struct tp_dispatch *tp, uint32_t time)
+tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
{
- uint32_t timeout = time + DEFAULT_TAP_TIMEOUT_PERIOD;
+ uint64_t timeout = time + DEFAULT_TAP_TIMEOUT_PERIOD;
struct itimerspec its;
its.it_interval.tv_sec = 0;
@@ -139,7 +139,7 @@ tp_tap_clear_timer(struct tp_dispatch *tp)
}
static void
-tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -160,7 +160,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t
}
static void
-tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -185,7 +185,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t
}
static void
-tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -206,7 +206,7 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t
}
static void
-tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -230,7 +230,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_
}
static void
-tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -256,7 +256,7 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_
}
static void
-tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -278,7 +278,7 @@ 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, uint32_t time)
+tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -303,7 +303,7 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_
}
static void
-tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -324,7 +324,7 @@ 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, uint32_t time)
+tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
@@ -349,7 +349,7 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, enum tap_event
}
static void
-tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -372,7 +372,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp, enum tap_event event, uint3
}
static void
-tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -395,7 +395,7 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, enum tap_event event,
}
static void
-tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -418,7 +418,7 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, enum tap_event event, uint
}
static void
-tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
switch (event) {
@@ -435,7 +435,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t
}
static void
-tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint32_t time)
+tp_tap_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time)
{
enum tp_tap_state current;
if (!tp->tap.enabled)
@@ -503,7 +503,7 @@ tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp, struct tp_touch *t)
}
int
-tp_tap_handle_state(struct tp_dispatch *tp, uint32_t time)
+tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
int filter_motion = 0;
@@ -554,7 +554,7 @@ tp_tap_timeout_handler(void *data)
uint64_t expires;
int len;
struct timespec ts;
- uint32_t now;
+ uint64_t now;
len = read(touchpad->tap.timer_fd, &expires, sizeof expires);
if (len != sizeof expires)
@@ -564,13 +564,13 @@ tp_tap_timeout_handler(void *data)
log_error("timerfd read error: %s\n", strerror(errno));
clock_gettime(CLOCK_MONOTONIC, &ts);
- now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
tp_tap_handle_timeout(touchpad, now);
}
unsigned int
-tp_tap_handle_timeout(struct tp_dispatch *tp, uint32_t time)
+tp_tap_handle_timeout(struct tp_dispatch *tp, uint64_t time)
{
if (!tp->tap.enabled)
return 0;
@@ -615,6 +615,8 @@ tp_destroy_tap(struct tp_dispatch *tp)
libinput_remove_source(tp->device->base.seat->libinput, tp->tap.source);
tp->tap.source = NULL;
}
- if (tp->tap.timer_fd > -1)
+ if (tp->tap.timer_fd > -1) {
close(tp->tap.timer_fd);
+ tp->tap.timer_fd = -1;
+ }
}
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 6c63cb6c..89cebd55 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -51,7 +51,7 @@ static double
tp_accel_profile(struct motion_filter *filter,
void *data,
double velocity,
- uint32_t time)
+ uint64_t time)
{
struct tp_dispatch *tp =
(struct tp_dispatch *) data;
@@ -80,7 +80,7 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
static void
tp_filter_motion(struct tp_dispatch *tp,
- double *dx, double *dy, uint32_t time)
+ double *dx, double *dy, uint64_t time)
{
struct motion_params motion;
@@ -152,24 +152,14 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
static inline void
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t)
{
- struct tp_touch *tmp = NULL;
-
if (t->state != TOUCH_UPDATE) {
tp_motion_history_reset(t);
t->dirty = true;
t->state = TOUCH_BEGIN;
+ t->pinned.is_pinned = false;
tp->nfingers_down++;
assert(tp->nfingers_down >= 1);
tp->queued |= TOUCHPAD_EVENT_MOTION;
-
- tp_for_each_touch(tp, tmp) {
- if (tmp->is_pointer)
- break;
- }
-
- if (!tmp->is_pointer) {
- t->is_pointer = true;
- }
}
}
@@ -182,6 +172,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t)
t->dirty = true;
t->is_pointer = false;
t->state = TOUCH_END;
+ t->pinned.is_pinned = false;
assert(tp->nfingers_down >= 1);
tp->nfingers_down--;
tp->queued |= TOUCHPAD_EVENT_MOTION;
@@ -215,7 +206,7 @@ tp_get_delta(struct tp_touch *t, double *dx, double *dy)
static void
tp_process_absolute(struct tp_dispatch *tp,
const struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
struct tp_touch *t = tp_current_touch(tp);
@@ -247,7 +238,7 @@ tp_process_absolute(struct tp_dispatch *tp,
static void
tp_process_absolute_st(struct tp_dispatch *tp,
const struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
struct tp_touch *t = tp_current_touch(tp);
@@ -270,7 +261,7 @@ tp_process_absolute_st(struct tp_dispatch *tp,
static void
tp_process_fake_touch(struct tp_dispatch *tp,
const struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
struct tp_touch *t;
unsigned int fake_touches;
@@ -318,22 +309,13 @@ tp_process_fake_touch(struct tp_dispatch *tp,
static void
tp_process_key(struct tp_dispatch *tp,
const struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
- uint32_t mask;
-
switch (e->code) {
case BTN_LEFT:
case BTN_MIDDLE:
case BTN_RIGHT:
- mask = 1 << (e->code - BTN_LEFT);
- if (e->value) {
- tp->buttons.state |= mask;
- tp->queued |= TOUCHPAD_EVENT_BUTTON_PRESS;
- } else {
- tp->buttons.state &= ~mask;
- tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE;
- }
+ tp_process_button(tp, e, time);
break;
case BTN_TOUCH:
case BTN_TOOL_DOUBLETAP:
@@ -346,54 +328,60 @@ tp_process_key(struct tp_dispatch *tp,
}
static void
-tp_unpin_finger(struct tp_dispatch *tp)
+tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ unsigned int xdist, ydist;
+
+ if (!t->pinned.is_pinned)
+ return;
+
+ xdist = abs(t->x - t->pinned.center_x);
+ ydist = abs(t->y - t->pinned.center_y);
+
+ if (xdist * xdist + ydist * ydist >=
+ tp->buttons.motion_dist * tp->buttons.motion_dist) {
+ t->pinned.is_pinned = false;
+ tp_set_pointer(tp, t);
+ }
+}
+
+static void
+tp_pin_fingers(struct tp_dispatch *tp)
{
struct tp_touch *t;
- tp_for_each_touch(tp, t) {
- if (t->is_pinned) {
- t->is_pinned = false;
- if (t->state != TOUCH_END &&
- tp->nfingers_down == 1)
- t->is_pointer = true;
- break;
- }
+ tp_for_each_touch(tp, t) {
+ t->is_pointer = false;
+ t->pinned.is_pinned = true;
+ t->pinned.center_x = t->x;
+ t->pinned.center_y = t->y;
}
}
-static void
-tp_pin_finger(struct tp_dispatch *tp)
+static int
+tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
{
- struct tp_touch *t,
- *pinned = NULL;
+ return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
+ !t->pinned.is_pinned && tp_button_touch_active(tp, t);
+}
- tp_for_each_touch(tp, t) {
- if (t->is_pinned) {
- pinned = t;
- break;
- }
+void
+tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ struct tp_touch *tmp = NULL;
+
+ /* Only set the touch as pointer if we don't have one yet */
+ tp_for_each_touch(tp, tmp) {
+ if (tmp->is_pointer)
+ return;
}
- assert(!pinned);
-
- pinned = tp_current_touch(tp);
-
- if (tp->nfingers_down != 1) {
- tp_for_each_touch(tp, t) {
- if (t == pinned)
- continue;
-
- if (t->y > pinned->y)
- pinned = t;
- }
- }
-
- pinned->is_pinned = true;
- pinned->is_pointer = false;
+ if (tp_touch_active(tp, t))
+ t->is_pointer = true;
}
static void
-tp_process_state(struct tp_dispatch *tp, uint32_t time)
+tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
struct tp_touch *first = tp_get_touch(tp, 0);
@@ -409,20 +397,25 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
tp_motion_hysteresis(tp, t);
tp_motion_history_push(t);
+
+ tp_unpin_finger(tp, t);
}
- /* We have a physical button down event on a clickpad. For drag and
- drop, this means we try to identify which finger pressed the
- physical button and "pin" it, i.e. remove pointer-moving
- capabilities from it.
+ tp_button_handle_state(tp, time);
+
+ /*
+ * We have a physical button down event on a clickpad. To avoid
+ * spurious pointer moves by the clicking finger we pin all fingers.
+ * We unpin fingers when they move more then a certain threshold to
+ * to allow drag and drop.
*/
if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
- !tp->buttons.has_buttons)
- tp_pin_finger(tp);
+ tp->buttons.is_clickpad)
+ tp_pin_fingers(tp);
}
static void
-tp_post_process_state(struct tp_dispatch *tp, uint32_t time)
+tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
@@ -441,14 +434,11 @@ tp_post_process_state(struct tp_dispatch *tp, uint32_t time)
tp->buttons.old_state = tp->buttons.state;
- if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
- tp_unpin_finger(tp);
-
tp->queued = TOUCHPAD_EVENT_NONE;
}
static void
-tp_post_twofinger_scroll(struct tp_dispatch *tp, uint32_t time)
+tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
int nchanged = 0;
@@ -456,7 +446,7 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint32_t time)
double tmpx, tmpy;
tp_for_each_touch(tp, t) {
- if (t->dirty) {
+ if (tp_touch_active(tp, t) && t->dirty) {
nchanged++;
tp_get_delta(t, &tmpx, &tmpy);
@@ -507,14 +497,18 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint32_t time)
}
static int
-tp_post_scroll_events(struct tp_dispatch *tp, uint32_t time)
+tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
{
- /* don't scroll if a clickpad is held down */
- if (!tp->buttons.has_buttons &&
- (tp->buttons.state || tp->buttons.old_state))
- return 0;
+ struct tp_touch *t;
+ int nfingers_down = 0;
- if (tp->nfingers_down != 2) {
+ /* Only count active touches for 2 finger scrolling */
+ tp_for_each_touch(tp, t) {
+ if (tp_touch_active(tp, t))
+ nfingers_down++;
+ }
+
+ if (nfingers_down != 2) {
/* terminate scrolling with a zero scroll event to notify
* caller that it really ended now */
if (tp->scroll.state != SCROLL_STATE_NONE) {
@@ -538,89 +532,8 @@ tp_post_scroll_events(struct tp_dispatch *tp, uint32_t time)
return 0;
}
-static int
-tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint32_t time)
-{
- uint32_t current, old, button;
- enum libinput_pointer_button_state state;
-
- current = tp->buttons.state;
- old = tp->buttons.old_state;
-
- if (current == old)
- return 0;
-
- switch (tp->nfingers_down) {
- case 1: button = BTN_LEFT; break;
- case 2: button = BTN_RIGHT; break;
- case 3: button = BTN_MIDDLE; break;
- default:
- return 0;
- }
-
- if (current)
- state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
- else
- state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
-
- pointer_notify_button(&tp->device->base,
- time,
- button,
- state);
- return 1;
-}
-
-static int
-tp_post_physical_buttons(struct tp_dispatch *tp, uint32_t time)
-{
- uint32_t current, old, button;
-
- current = tp->buttons.state;
- old = tp->buttons.old_state;
- button = BTN_LEFT;
-
- while (current || old) {
- enum libinput_pointer_button_state state;
-
- if ((current & 0x1) ^ (old & 0x1)) {
- if (!!(current & 0x1))
- state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
- else
- state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
-
- pointer_notify_button(&tp->device->base,
- time,
- button,
- state);
- }
-
- button++;
- current >>= 1;
- old >>= 1;
- }
-
- return 0;
-}
-
-static int
-tp_post_button_events(struct tp_dispatch *tp, uint32_t time)
-{
- int rc;
-
- if ((tp->queued &
- (TOUCHPAD_EVENT_BUTTON_PRESS|TOUCHPAD_EVENT_BUTTON_RELEASE)) == 0)
- return 0;
-
- if (tp->buttons.has_buttons)
- rc = tp_post_physical_buttons(tp, time);
- else
- rc = tp_post_clickfinger_buttons(tp, time);
-
- return rc;
-}
-
static void
-tp_post_events(struct tp_dispatch *tp, uint32_t time)
+tp_post_events(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t = tp_current_touch(tp);
double dx, dy;
@@ -661,7 +574,7 @@ static void
tp_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
struct tp_dispatch *tp =
(struct tp_dispatch *)dispatch;
@@ -691,6 +604,7 @@ tp_destroy(struct evdev_dispatch *dispatch)
(struct tp_dispatch*)dispatch;
tp_destroy_tap(tp);
+ tp_destroy_buttons(tp);
if (tp->filter)
tp->filter->interface->destroy(tp->filter);
@@ -703,10 +617,18 @@ static struct evdev_dispatch_interface tp_interface = {
tp_destroy
};
+static void
+tp_init_touch(struct tp_dispatch *tp,
+ struct tp_touch *t)
+{
+ t->button.state = BUTTON_STATE_NONE;
+}
+
static int
tp_init_slots(struct tp_dispatch *tp,
struct evdev_device *device)
{
+ size_t i;
const struct input_absinfo *absinfo;
absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
@@ -715,15 +637,38 @@ tp_init_slots(struct tp_dispatch *tp,
tp->slot = absinfo->value;
tp->has_mt = true;
} else {
- tp->ntouches = 5; /* FIXME: based on DOUBLETAP, etc. */
+ struct map {
+ unsigned int code;
+ int ntouches;
+ } max_touches[] = {
+ { BTN_TOOL_QUINTTAP, 5 },
+ { BTN_TOOL_QUADTAP, 4 },
+ { BTN_TOOL_TRIPLETAP, 3 },
+ { BTN_TOOL_DOUBLETAP, 2 },
+ };
+ struct map *m;
+
tp->slot = 0;
tp->has_mt = false;
+ tp->ntouches = 1;
+
+ ARRAY_FOR_EACH(max_touches, m) {
+ if (libevdev_has_event_code(device->evdev,
+ EV_KEY,
+ m->code)) {
+ tp->ntouches = m->ntouches;
+ break;
+ }
+ }
}
tp->touches = calloc(tp->ntouches,
sizeof(struct tp_touch));
if (!tp->touches)
return -1;
+ for (i = 0; i < tp->ntouches; i++)
+ tp_init_touch(tp, &tp->touches[i]);
+
return 0;
}
@@ -765,6 +710,7 @@ tp_init(struct tp_dispatch *tp,
tp->base.interface = &tp_interface;
tp->device = device;
tp->tap.timer_fd = -1;
+ tp->buttons.timer_fd = -1;
if (tp_init_slots(tp, device) != 0)
return -1;
@@ -778,10 +724,6 @@ tp_init(struct tp_dispatch *tp,
tp->hysteresis.margin_y =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
- if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE) ||
- libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT))
- tp->buttons.has_buttons = true;
-
if (tp_init_scroll(tp) != 0)
return -1;
@@ -791,6 +733,9 @@ tp_init(struct tp_dispatch *tp,
if (tp_init_tap(tp) != 0)
return -1;
+ if (tp_init_buttons(tp, device) != 0)
+ return -1;
+
return 0;
}
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 2bdb3295..41e3ca43 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -46,6 +46,24 @@ enum touch_state {
TOUCH_END
};
+enum button_event {
+ BUTTON_EVENT_IN_BOTTOM_R = 30,
+ BUTTON_EVENT_IN_BOTTOM_L,
+ BUTTON_EVENT_IN_AREA,
+ BUTTON_EVENT_UP,
+ BUTTON_EVENT_PRESS,
+ BUTTON_EVENT_RELEASE,
+ BUTTON_EVENT_TIMEOUT,
+};
+
+enum button_state {
+ BUTTON_STATE_NONE,
+ BUTTON_STATE_AREA,
+ BUTTON_STATE_BOTTOM,
+ BUTTON_STATE_BOTTOM_NEW,
+ BUTTON_STATE_BOTTOM_TO_AREA,
+};
+
enum scroll_state {
SCROLL_STATE_NONE,
SCROLL_STATE_SCROLLING
@@ -77,10 +95,9 @@ struct tp_touch {
bool dirty;
bool fake; /* a fake touch */
bool is_pointer; /* the pointer-controlling touch */
- bool is_pinned; /* holds the phys. button */
int32_t x;
int32_t y;
- uint32_t millis;
+ uint64_t millis;
struct {
struct tp_motion samples[TOUCHPAD_HISTORY_LENGTH];
@@ -92,6 +109,24 @@ struct tp_touch {
int32_t center_x;
int32_t center_y;
} hysteresis;
+
+ /* A pinned touchpoint is the one that pressed the physical button
+ * on a clickpad. After the release, it won't move until the center
+ * moves more than a threshold away from the original coordinates
+ */
+ struct {
+ bool is_pinned;
+ int32_t center_x;
+ int32_t center_y;
+ } pinned;
+
+ /* Software-button state and timeout if applicable */
+ struct {
+ enum button_state state;
+ /* We use button_event here so we can use == on events */
+ enum button_event curr;
+ uint64_t timeout;
+ } button;
};
struct tp_dispatch {
@@ -119,9 +154,28 @@ struct tp_dispatch {
} accel;
struct {
- bool has_buttons; /* true for physical LMR buttons */
+ bool is_clickpad; /* true for clickpads */
+ bool use_clickfinger; /* number of fingers decides button number */
+ bool click_pending;
uint32_t state;
uint32_t old_state;
+ uint32_t motion_dist; /* for pinned touches */
+ unsigned int active; /* currently active button, for release event */
+
+ /* Only used for clickpads. The software button area is always
+ * a horizontal strip across the touchpad. Depending on the
+ * rightbutton_left_edge value, the buttons are split according to the
+ * edge settings.
+ */
+ struct {
+ int32_t top_edge;
+ int32_t rightbutton_left_edge;
+ } area;
+
+ unsigned int timeout; /* current timeout in ms */
+
+ int timer_fd;
+ struct libinput_source *source;
} buttons; /* physical buttons */
struct {
@@ -146,11 +200,14 @@ struct tp_dispatch {
void
tp_get_delta(struct tp_touch *t, double *dx, double *dy);
+void
+tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t);
+
int
-tp_tap_handle_state(struct tp_dispatch *tp, uint32_t time);
+tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
unsigned int
-tp_tap_handle_timeout(struct tp_dispatch *tp, uint32_t time);
+tp_tap_handle_timeout(struct tp_dispatch *tp, uint64_t time);
int
tp_init_tap(struct tp_dispatch *tp);
@@ -158,4 +215,24 @@ tp_init_tap(struct tp_dispatch *tp);
void
tp_destroy_tap(struct tp_dispatch *tp);
+int
+tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device);
+
+void
+tp_destroy_buttons(struct tp_dispatch *tp);
+
+int
+tp_process_button(struct tp_dispatch *tp,
+ const struct input_event *e,
+ uint64_t time);
+
+int
+tp_post_button_events(struct tp_dispatch *tp, uint64_t time);
+
+int
+tp_button_handle_state(struct tp_dispatch *tp, uint64_t time);
+
+int
+tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t);
+
#endif
diff --git a/src/evdev-touchpad.c b/src/evdev-touchpad.c
index 1a484419..18b63530 100644
--- a/src/evdev-touchpad.c
+++ b/src/evdev-touchpad.c
@@ -211,7 +211,7 @@ static double
touchpad_profile(struct motion_filter *filter,
void *data,
double velocity,
- uint32_t time)
+ uint64_t time)
{
struct touchpad_dispatch *touchpad =
(struct touchpad_dispatch *) data;
@@ -273,7 +273,7 @@ touchpad_get_delta(struct touchpad_dispatch *touchpad, double *dx, double *dy)
static void
filter_motion(struct touchpad_dispatch *touchpad,
- double *dx, double *dy, uint32_t time)
+ double *dx, double *dy, uint64_t time)
{
struct motion_params motion;
@@ -287,7 +287,7 @@ filter_motion(struct touchpad_dispatch *touchpad,
}
static void
-notify_button_pressed(struct touchpad_dispatch *touchpad, uint32_t time)
+notify_button_pressed(struct touchpad_dispatch *touchpad, uint64_t time)
{
pointer_notify_button(
&touchpad->device->base,
@@ -297,7 +297,7 @@ notify_button_pressed(struct touchpad_dispatch *touchpad, uint32_t time)
}
static void
-notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
+notify_button_released(struct touchpad_dispatch *touchpad, uint64_t time)
{
pointer_notify_button(
&touchpad->device->base,
@@ -307,16 +307,16 @@ notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
}
static void
-notify_tap(struct touchpad_dispatch *touchpad, uint32_t time)
+notify_tap(struct touchpad_dispatch *touchpad, uint64_t time)
{
notify_button_pressed(touchpad, time);
notify_button_released(touchpad, time);
}
static void
-process_fsm_events(struct touchpad_dispatch *touchpad, uint32_t time)
+process_fsm_events(struct touchpad_dispatch *touchpad, uint64_t time)
{
- uint32_t timeout = UINT32_MAX;
+ uint64_t timeout = UINT64_MAX;
enum fsm_event event;
unsigned int i;
@@ -398,7 +398,7 @@ process_fsm_events(struct touchpad_dispatch *touchpad, uint32_t time)
}
}
- if (timeout != UINT32_MAX) {
+ if (timeout != UINT64_MAX) {
struct itimerspec its;
its.it_interval.tv_sec = 0;
@@ -447,7 +447,7 @@ fsm_timeout_handler(void *data)
uint64_t expires;
int len;
struct timespec ts;
- uint32_t now;
+ uint64_t now;
len = read(touchpad->fsm.timer.fd, &expires, sizeof expires);
if (len != sizeof expires)
@@ -458,7 +458,7 @@ fsm_timeout_handler(void *data)
if (touchpad->fsm.events_count == 0) {
clock_gettime(CLOCK_MONOTONIC, &ts);
- now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
push_fsm_event(touchpad, FSM_EVENT_TIMEOUT);
process_fsm_events(touchpad, now);
@@ -466,7 +466,7 @@ fsm_timeout_handler(void *data)
}
static void
-touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
+touchpad_update_state(struct touchpad_dispatch *touchpad, uint64_t time)
{
int motion_index;
int center_x, center_y;
@@ -618,7 +618,7 @@ static inline void
process_key(struct touchpad_dispatch *touchpad,
struct evdev_device *device,
struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
uint32_t code;
@@ -685,7 +685,7 @@ static void
touchpad_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
struct touchpad_dispatch *touchpad =
(struct touchpad_dispatch *) dispatch;
diff --git a/src/evdev.c b/src/evdev.c
index 57dcca76..08a18fd8 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -110,7 +110,7 @@ evdev_device_transform_y(struct evdev_device *device,
}
static void
-evdev_flush_pending_event(struct evdev_device *device, uint32_t time)
+evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
{
int32_t cx, cy;
li_fixed_t x, y;
@@ -309,7 +309,7 @@ evdev_process_key(struct evdev_device *device, struct input_event *e, int time)
static void
evdev_process_touch(struct evdev_device *device,
struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
switch (e->code) {
case ABS_MT_SLOT:
@@ -358,7 +358,7 @@ evdev_process_absolute_motion(struct evdev_device *device,
static inline void
evdev_process_relative(struct evdev_device *device,
- struct input_event *e, uint32_t time)
+ struct input_event *e, uint64_t time)
{
struct libinput_device *base = &device->base;
@@ -406,7 +406,7 @@ evdev_process_relative(struct evdev_device *device,
static inline void
evdev_process_absolute(struct evdev_device *device,
struct input_event *e,
- uint32_t time)
+ uint64_t time)
{
if (device->is_mt) {
evdev_process_touch(device, e, time);
@@ -441,7 +441,7 @@ static void
fallback_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *event,
- uint32_t time)
+ uint64_t time)
{
int need_frame = 0;
@@ -491,7 +491,7 @@ static inline void
evdev_process_event(struct evdev_device *device, struct input_event *e)
{
struct evdev_dispatch *dispatch = device->dispatch;
- uint32_t time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
+ uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000;
dispatch->interface->process(dispatch, device, e, time);
}
diff --git a/src/evdev.h b/src/evdev.h
index 36daf3c7..bb88074e 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -101,7 +101,7 @@ struct evdev_dispatch_interface {
void (*process)(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct input_event *event,
- uint32_t time);
+ uint64_t time);
/* Destroy an event dispatch handler and free all its resources. */
void (*destroy)(struct evdev_dispatch *dispatch);
diff --git a/src/filter.c b/src/filter.c
index 397e0f63..2b1a6755 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -32,7 +32,7 @@
void
filter_dispatch(struct motion_filter *filter,
struct motion_params *motion,
- void *data, uint32_t time)
+ void *data, uint64_t time)
{
filter->interface->filter(filter, motion, data, time);
}
@@ -48,7 +48,7 @@ filter_dispatch(struct motion_filter *filter,
struct pointer_tracker {
double dx;
double dy;
- uint32_t time;
+ uint64_t time;
int dir;
};
@@ -128,7 +128,7 @@ get_direction(int dx, int dy)
static void
feed_trackers(struct pointer_accelerator *accel,
double dx, double dy,
- uint32_t time)
+ uint64_t time)
{
int i, current;
struct pointer_tracker *trackers = accel->trackers;
@@ -157,7 +157,7 @@ tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
}
static double
-calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time)
+calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
{
int dx;
int dy;
@@ -170,7 +170,7 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time)
}
static double
-calculate_velocity(struct pointer_accelerator *accel, uint32_t time)
+calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
{
struct pointer_tracker *tracker;
double velocity;
@@ -224,14 +224,14 @@ calculate_velocity(struct pointer_accelerator *accel, uint32_t time)
static double
acceleration_profile(struct pointer_accelerator *accel,
- void *data, double velocity, uint32_t time)
+ void *data, double velocity, uint64_t time)
{
return accel->profile(&accel->base, data, velocity, time);
}
static double
calculate_acceleration(struct pointer_accelerator *accel,
- void *data, double velocity, uint32_t time)
+ void *data, double velocity, uint64_t time)
{
double factor;
@@ -273,7 +273,7 @@ apply_softening(struct pointer_accelerator *accel,
static void
accelerator_filter(struct motion_filter *filter,
struct motion_params *motion,
- void *data, uint32_t time)
+ void *data, uint64_t time)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
diff --git a/src/filter.h b/src/filter.h
index 6b2a1d20..0ef3d032 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -34,13 +34,13 @@ struct motion_filter;
void
filter_dispatch(struct motion_filter *filter,
struct motion_params *motion,
- void *data, uint32_t time);
+ void *data, uint64_t time);
struct motion_filter_interface {
void (*filter)(struct motion_filter *filter,
struct motion_params *motion,
- void *data, uint32_t time);
+ void *data, uint64_t time);
void (*destroy)(struct motion_filter *filter);
};
@@ -54,7 +54,7 @@ create_linear_acceleration_filter(double speed);
typedef double (*accel_profile_func_t)(struct motion_filter *filter,
void *data,
double velocity,
- uint32_t time);
+ uint64_t time);
struct motion_filter *
create_pointer_accelator_filter(accel_profile_func_t filter);
diff --git a/src/libinput.h b/src/libinput.h
index 85c7d717..d771e21c 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -38,6 +38,46 @@ extern "C" {
* behind an API.
*/
+/**
+ * @page tpbuttons Touchpad button behavior
+ *
+ * For touchpad devices without physical buttons, libinput enables an
+ * emulated right button area through either of two methods.
+ *
+ * Software button areas
+ * =====================
+ * On most touchpads, the bottom area of the touchpad is split into a a left
+ * and a right-button area. Pressing the touchpad down with a finger in
+ * those areas will generate clicks as shown in the diagram below:
+ *
+ * @code
+ +------------------------+
+ | |
+ | |
+ | LEFT |
+ | |
+ | |
+ +------------------------+
+ | LEFT | RIGHT |
+ +------------------------+
+ * @endcode
+ *
+ * Generally, the touchpad will emulate a right-button click if the finger
+ * was set down in the right button area and did not leave the
+ * right button area before clicking, even if another finger was already
+ * down on the touchpad in another area.
+ * A middle click is generated by clicking the touchpad when one finger is
+ * in the bottom left button area, and one finger is in the botton right
+ * button area.
+ * The exact behavior of the touchpad is implementation-dependent.
+ *
+ * Clickfinger
+ * ===========
+ * On Apple touchpads, no button areas are provided. Instead, use a
+ * two-finger click for a right button click, and a three-finger click for a
+ * middle button click.
+ */
+
/**
* @ingroup fixed_point
*
diff --git a/test/litest-bcm5974.c b/test/litest-bcm5974.c
index 25d59ac2..43605aaf 100644
--- a/test/litest-bcm5974.c
+++ b/test/litest-bcm5974.c
@@ -92,6 +92,7 @@ static int events[] = {
EV_KEY, BTN_TOOL_DOUBLETAP,
EV_KEY, BTN_TOOL_TRIPLETAP,
EV_KEY, BTN_TOOL_QUADTAP,
+ INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
-1, -1
};
diff --git a/test/touchpad.c b/test/touchpad.c
index f4d78391..959978e5 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -70,10 +70,10 @@ START_TEST(touchpad_2fg_no_motion)
litest_drain_events(li);
- litest_touch_down(dev, 0, 50, 50);
- litest_touch_down(dev, 1, 70, 70);
- litest_touch_move_to(dev, 0, 50, 50, 80, 50, 5);
- litest_touch_move_to(dev, 1, 70, 70, 80, 50, 5);
+ litest_touch_down(dev, 0, 20, 20);
+ litest_touch_down(dev, 1, 70, 20);
+ litest_touch_move_to(dev, 0, 20, 20, 80, 80, 5);
+ litest_touch_move_to(dev, 1, 70, 20, 80, 50, 5);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
@@ -217,7 +217,7 @@ END_TEST
START_TEST(touchpad_1fg_clickfinger)
{
- struct litest_device *dev = litest_current_device();
+ struct litest_device *dev = litest_create_device(LITEST_BCM5974);
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
@@ -237,12 +237,14 @@ START_TEST(touchpad_1fg_clickfinger)
LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
assert_button_event(li, BTN_LEFT,
LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+
+ litest_delete_device(dev);
}
END_TEST
START_TEST(touchpad_2fg_clickfinger)
{
- struct litest_device *dev = litest_current_device();
+ struct litest_device *dev = litest_create_device(LITEST_BCM5974);
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct libinput_event_pointer *ptrev;
@@ -264,6 +266,8 @@ START_TEST(touchpad_2fg_clickfinger)
LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
assert_button_event(li, BTN_RIGHT,
LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
+
+ litest_delete_device(dev);
}
END_TEST
@@ -362,8 +366,8 @@ int main(int argc, char **argv) {
litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
- litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_TOUCHPAD, LITEST_ANY);
- litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+ litest_add_no_device("touchpad:clickfinger", touchpad_1fg_clickfinger);
+ litest_add_no_device("touchpad:clickfinger", touchpad_2fg_clickfinger);
litest_add("touchpad:click", touchpad_btn_left, LITEST_TOUCHPAD, LITEST_CLICKPAD);
litest_add("touchpad:click", clickpad_btn_left, LITEST_CLICKPAD, LITEST_ANY);