mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-01-13 18:00:30 +01:00
To quote Bryce Harrington from [1]: "MIT has released software under several slightly different licenses, including the old 'X11 License' or 'MIT License'. Some code under this license was in fact included in X.org's Xserver in the past. However, X.org now prefers the MIT Expat License as the standard (which, confusingly, is also referred to as the 'MIT License'). See http://cgit.freedesktop.org/xorg/xserver/tree/COPYING When Wayland started, it was Kristian Høgsberg's intent to license it compatibly with X.org. "I wanted Wayland to be usable (license-wise) whereever X was usable." But, the text of the older X11 License was taken for Wayland, rather than X11's current standard. This patch corrects this by swapping in the intended text." libinput is a fork of weston and thus inherited the original license intent and the license boilerplate itself. See this thread on wayland-devel here for a discussion: http://lists.freedesktop.org/archives/wayland-devel/2015-May/022301.html [1] http://lists.freedesktop.org/archives/wayland-devel/2015-June/022552.html Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Benjamin Tissoires <benjamin.tissoires@gmail.com> Acked-by: Jonas Ådahl <jadahl@gmail.com>
910 lines
22 KiB
C
910 lines
22 KiB
C
/*
|
|
* Copyright © 2013-2015 Red Hat, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "evdev-mt-touchpad.h"
|
|
|
|
#define CASE_RETURN_STRING(a) case a: return #a
|
|
|
|
#define DEFAULT_TAP_TIMEOUT_PERIOD 180
|
|
#define DEFAULT_DRAG_TIMEOUT_PERIOD 300
|
|
#define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
|
|
|
|
enum tap_event {
|
|
TAP_EVENT_TOUCH = 12,
|
|
TAP_EVENT_MOTION,
|
|
TAP_EVENT_RELEASE,
|
|
TAP_EVENT_BUTTON,
|
|
TAP_EVENT_TIMEOUT,
|
|
};
|
|
|
|
/*****************************************
|
|
* DO NOT EDIT THIS FILE!
|
|
*
|
|
* Look at the state diagram in doc/touchpad-tap-state-machine.svg, or
|
|
* online at
|
|
* https://drive.google.com/file/d/0B1NwWmji69noYTdMcU1kTUZuUVE/edit?usp=sharing
|
|
* (it's a http://draw.io diagram)
|
|
*
|
|
* Any changes in this file must be represented in the diagram.
|
|
*/
|
|
|
|
static inline const char*
|
|
tap_state_to_str(enum tp_tap_state state)
|
|
{
|
|
switch(state) {
|
|
CASE_RETURN_STRING(TAP_STATE_IDLE);
|
|
CASE_RETURN_STRING(TAP_STATE_HOLD);
|
|
CASE_RETURN_STRING(TAP_STATE_TOUCH);
|
|
CASE_RETURN_STRING(TAP_STATE_TAPPED);
|
|
CASE_RETURN_STRING(TAP_STATE_TOUCH_2);
|
|
CASE_RETURN_STRING(TAP_STATE_TOUCH_2_HOLD);
|
|
CASE_RETURN_STRING(TAP_STATE_TOUCH_3);
|
|
CASE_RETURN_STRING(TAP_STATE_TOUCH_3_HOLD);
|
|
CASE_RETURN_STRING(TAP_STATE_DRAGGING);
|
|
CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT);
|
|
CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP);
|
|
CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_TAP);
|
|
CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
|
|
CASE_RETURN_STRING(TAP_STATE_MULTITAP);
|
|
CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN);
|
|
CASE_RETURN_STRING(TAP_STATE_DEAD);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline const char*
|
|
tap_event_to_str(enum tap_event event)
|
|
{
|
|
switch(event) {
|
|
CASE_RETURN_STRING(TAP_EVENT_TOUCH);
|
|
CASE_RETURN_STRING(TAP_EVENT_MOTION);
|
|
CASE_RETURN_STRING(TAP_EVENT_RELEASE);
|
|
CASE_RETURN_STRING(TAP_EVENT_TIMEOUT);
|
|
CASE_RETURN_STRING(TAP_EVENT_BUTTON);
|
|
}
|
|
return NULL;
|
|
}
|
|
#undef CASE_RETURN_STRING
|
|
|
|
static void
|
|
tp_tap_notify(struct tp_dispatch *tp,
|
|
uint64_t time,
|
|
int nfingers,
|
|
enum libinput_button_state state)
|
|
{
|
|
int32_t button;
|
|
|
|
switch (nfingers) {
|
|
case 1: button = BTN_LEFT; break;
|
|
case 2: button = BTN_RIGHT; break;
|
|
case 3: button = BTN_MIDDLE; break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (state == LIBINPUT_BUTTON_STATE_PRESSED)
|
|
tp->tap.buttons_pressed |= (1 << nfingers);
|
|
else
|
|
tp->tap.buttons_pressed &= ~(1 << nfingers);
|
|
|
|
evdev_pointer_notify_button(tp->device,
|
|
time,
|
|
button,
|
|
state);
|
|
}
|
|
|
|
static void
|
|
tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD);
|
|
}
|
|
|
|
static void
|
|
tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAG_TIMEOUT_PERIOD);
|
|
}
|
|
|
|
static void
|
|
tp_tap_clear_timer(struct tp_dispatch *tp)
|
|
{
|
|
libinput_timer_cancel(&tp->tap.timer);
|
|
}
|
|
|
|
static void
|
|
tp_tap_idle_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
struct libinput *libinput = tp_libinput_context(tp);
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_TOUCH;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
log_bug_libinput(libinput,
|
|
"invalid tap event, no fingers are down\n");
|
|
break;
|
|
case TAP_EVENT_TIMEOUT:
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_touch_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_TOUCH_2;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_TAPPED;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_TIMEOUT:
|
|
case TAP_EVENT_MOTION:
|
|
tp->tap.state = TAP_STATE_HOLD;
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_hold_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_TOUCH_2;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_tapped_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
struct libinput *libinput = tp_libinput_context(tp);
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_RELEASE:
|
|
log_bug_libinput(libinput,
|
|
"invalid tap event when fingers are up\n");
|
|
break;
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_touch2_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_TOUCH_3;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_HOLD;
|
|
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
|
|
tp_tap_notify(tp, time, 2, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_notify(tp, 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;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_TOUCH_3;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_HOLD;
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_touch3_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_TOUCH_3_HOLD;
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
|
|
if (t->tap.state == TAP_TOUCH_STATE_TOUCH) {
|
|
tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_notify(tp, time, 3, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
}
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
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:
|
|
tp->tap.state = TAP_STATE_DRAGGING_2;
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_MULTITAP;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_DRAGGING;
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_dragging_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DRAGGING_2;
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_DRAGGING_WAIT;
|
|
tp_tap_set_drag_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
/* noop */
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DRAGGING_OR_TAP;
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
case TAP_EVENT_MOTION:
|
|
break;
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DRAGGING_2;
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_DRAGGING;
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_DRAGGING;
|
|
break;
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
/* noop */
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_multitap_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event, uint64_t time)
|
|
{
|
|
struct libinput *libinput = tp_libinput_context(tp);
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_RELEASE:
|
|
log_bug_libinput(libinput,
|
|
"invalid tap event, no fingers are down\n");
|
|
break;
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_MULTITAP_DOWN;
|
|
tp->tap.multitap_last_time = time;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_set_timer(tp, time);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
log_bug_libinput(libinput,
|
|
"invalid tap event, no fingers are down\n");
|
|
break;
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event,
|
|
uint64_t time)
|
|
{
|
|
switch (event) {
|
|
case TAP_EVENT_RELEASE:
|
|
tp->tap.state = TAP_STATE_MULTITAP;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case TAP_EVENT_TOUCH:
|
|
tp->tap.state = TAP_STATE_DRAGGING_2;
|
|
tp_tap_notify(tp,
|
|
tp->tap.multitap_last_time,
|
|
1,
|
|
LIBINPUT_BUTTON_STATE_RELEASED);
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
tp->tap.state = TAP_STATE_DRAGGING;
|
|
tp_tap_notify(tp,
|
|
tp->tap.multitap_last_time,
|
|
1,
|
|
LIBINPUT_BUTTON_STATE_RELEASED);
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED);
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
case TAP_EVENT_BUTTON:
|
|
tp->tap.state = TAP_STATE_DEAD;
|
|
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
tp_tap_clear_timer(tp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_dead_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event,
|
|
uint64_t time)
|
|
{
|
|
|
|
switch (event) {
|
|
case TAP_EVENT_RELEASE:
|
|
if (tp->nfingers_down == 0)
|
|
tp->tap.state = TAP_STATE_IDLE;
|
|
break;
|
|
case TAP_EVENT_TOUCH:
|
|
case TAP_EVENT_MOTION:
|
|
case TAP_EVENT_TIMEOUT:
|
|
case TAP_EVENT_BUTTON:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_handle_event(struct tp_dispatch *tp,
|
|
struct tp_touch *t,
|
|
enum tap_event event,
|
|
uint64_t time)
|
|
{
|
|
struct libinput *libinput = tp_libinput_context(tp);
|
|
enum tp_tap_state current;
|
|
|
|
current = tp->tap.state;
|
|
|
|
switch(tp->tap.state) {
|
|
case TAP_STATE_IDLE:
|
|
tp_tap_idle_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TOUCH:
|
|
tp_tap_touch_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_HOLD:
|
|
tp_tap_hold_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TAPPED:
|
|
tp_tap_tapped_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TOUCH_2:
|
|
tp_tap_touch2_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TOUCH_2_HOLD:
|
|
tp_tap_touch2_hold_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TOUCH_3:
|
|
tp_tap_touch3_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_TOUCH_3_HOLD:
|
|
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, t, event, time);
|
|
break;
|
|
case TAP_STATE_DRAGGING:
|
|
tp_tap_dragging_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_DRAGGING_WAIT:
|
|
tp_tap_dragging_wait_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_DRAGGING_OR_TAP:
|
|
tp_tap_dragging_tap_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_DRAGGING_2:
|
|
tp_tap_dragging2_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_MULTITAP:
|
|
tp_tap_multitap_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_MULTITAP_DOWN:
|
|
tp_tap_multitap_down_handle_event(tp, t, event, time);
|
|
break;
|
|
case TAP_STATE_DEAD:
|
|
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(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
|
|
tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp,
|
|
struct tp_touch *t)
|
|
{
|
|
struct normalized_coords norm =
|
|
tp_normalize_delta(tp, device_delta(t->point,
|
|
t->tap.initial));
|
|
|
|
return normalized_length(norm) > DEFAULT_TAP_MOVE_THRESHOLD;
|
|
}
|
|
|
|
static bool
|
|
tp_tap_enabled(struct tp_dispatch *tp)
|
|
{
|
|
return tp->tap.enabled && !tp->tap.suspended;
|
|
}
|
|
|
|
int
|
|
tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
struct tp_touch *t;
|
|
int filter_motion = 0;
|
|
|
|
if (!tp_tap_enabled(tp))
|
|
return 0;
|
|
|
|
/* 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 (tp->buttons.is_clickpad &&
|
|
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;
|
|
t->tap.initial = t->point;
|
|
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
|
|
|
|
/* If we think this is a palm, pretend there's a
|
|
* motion event which will prevent tap clicks
|
|
* without requiring extra states in the FSM.
|
|
*/
|
|
if (tp_palm_tap_is_palm(tp, t))
|
|
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
|
|
|
|
} else if (t->state == TOUCH_END) {
|
|
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
|
|
t->tap.state = TAP_TOUCH_STATE_IDLE;
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* In any state where motion exceeding the move threshold would
|
|
* move to the next state, filter that motion until we actually
|
|
* exceed it. This prevents small motion events while we're waiting
|
|
* on a decision if a tap is a tap.
|
|
*/
|
|
switch (tp->tap.state) {
|
|
case TAP_STATE_TOUCH:
|
|
case TAP_STATE_TAPPED:
|
|
case TAP_STATE_DRAGGING_OR_DOUBLETAP:
|
|
case TAP_STATE_DRAGGING_OR_TAP:
|
|
case TAP_STATE_TOUCH_2:
|
|
case TAP_STATE_TOUCH_3:
|
|
case TAP_STATE_MULTITAP_DOWN:
|
|
filter_motion = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
return filter_motion;
|
|
}
|
|
|
|
static void
|
|
tp_tap_handle_timeout(uint64_t time, void *data)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint64_t time)
|
|
{
|
|
bool was_enabled = tp_tap_enabled(tp);
|
|
|
|
tp->tap.suspended = suspended;
|
|
tp->tap.enabled = enabled;
|
|
|
|
if (tp_tap_enabled(tp) == was_enabled)
|
|
return;
|
|
|
|
if (tp_tap_enabled(tp)) {
|
|
/* Must restart in DEAD if fingers are down atm */
|
|
tp->tap.state =
|
|
tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE;
|
|
} else {
|
|
tp_release_all_taps(tp, time);
|
|
}
|
|
}
|
|
|
|
static int
|
|
tp_tap_config_count(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 min(tp->ntouches, 3); /* we only do up to 3 finger tap */
|
|
}
|
|
|
|
static enum libinput_config_status
|
|
tp_tap_config_set_enabled(struct libinput_device *device,
|
|
enum libinput_config_tap_state enabled)
|
|
{
|
|
struct evdev_dispatch *dispatch = ((struct evdev_device *) device)->dispatch;
|
|
struct tp_dispatch *tp = NULL;
|
|
|
|
tp = container_of(dispatch, tp, base);
|
|
tp_tap_enabled_update(tp, tp->tap.suspended,
|
|
(enabled == LIBINPUT_CONFIG_TAP_ENABLED),
|
|
libinput_now(device->seat->libinput));
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
}
|
|
|
|
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_default(struct evdev_device *evdev)
|
|
{
|
|
/**
|
|
* If we don't have a left button we must have tapping enabled by
|
|
* default.
|
|
*/
|
|
if (!libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_LEFT))
|
|
return LIBINPUT_CONFIG_TAP_ENABLED;
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
static enum libinput_config_tap_state
|
|
tp_tap_config_get_default(struct libinput_device *device)
|
|
{
|
|
struct evdev_device *evdev = (struct evdev_device *)device;
|
|
|
|
return tp_tap_default(evdev);
|
|
}
|
|
|
|
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;
|
|
tp->tap.enabled = tp_tap_default(tp->device);
|
|
|
|
libinput_timer_init(&tp->tap.timer,
|
|
tp_libinput_context(tp),
|
|
tp_tap_handle_timeout, tp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
tp_remove_tap(struct tp_dispatch *tp)
|
|
{
|
|
libinput_timer_cancel(&tp->tap.timer);
|
|
}
|
|
|
|
void
|
|
tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i <= 3; i++) {
|
|
if (tp->tap.buttons_pressed & (1 << i))
|
|
tp_tap_notify(tp, now, i, LIBINPUT_BUTTON_STATE_RELEASED);
|
|
}
|
|
|
|
tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE;
|
|
}
|
|
|
|
void
|
|
tp_tap_suspend(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
tp_tap_enabled_update(tp, true, tp->tap.enabled, time);
|
|
}
|
|
|
|
void
|
|
tp_tap_resume(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
tp_tap_enabled_update(tp, false, tp->tap.enabled, time);
|
|
}
|
|
|
|
bool
|
|
tp_tap_dragging(struct tp_dispatch *tp)
|
|
{
|
|
switch (tp->tap.state) {
|
|
case TAP_STATE_DRAGGING:
|
|
case TAP_STATE_DRAGGING_2:
|
|
case TAP_STATE_DRAGGING_WAIT:
|
|
case TAP_STATE_DRAGGING_OR_TAP:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|