#include <stdbool.h>
#include <stdio.h>
#include <string.h>
-#include <time.h>
#include <unistd.h>
-#include <sys/timerfd.h>
#include "evdev-mt-touchpad.h"
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;
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:
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;
}
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) {
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:
}
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) {
}
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) {
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;
}
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) {
}
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) {
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;
}
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) {
}
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:
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:
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) {
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) {
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) {
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:
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) {
}
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
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);
+ }
}
/**
}
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;
}
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);
}