mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-01-07 04:10:11 +01:00
Add a normalized_is_zero helper function, and use it where applicable. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
232 lines
5.9 KiB
C
232 lines
5.9 KiB
C
/*
|
|
* Copyright © 2015 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 "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <limits.h>
|
|
|
|
#include "evdev-mt-touchpad.h"
|
|
|
|
#define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */
|
|
|
|
static struct normalized_coords
|
|
tp_get_touches_delta(struct tp_dispatch *tp, bool average)
|
|
{
|
|
struct tp_touch *t;
|
|
unsigned int i, nchanged = 0;
|
|
struct normalized_coords normalized;
|
|
struct normalized_coords delta = {0.0, 0.0};
|
|
|
|
for (i = 0; i < tp->real_touches; i++) {
|
|
t = &tp->touches[i];
|
|
|
|
if (tp_touch_active(tp, t) && t->dirty) {
|
|
nchanged++;
|
|
normalized = tp_get_delta(t);
|
|
|
|
delta.x += normalized.x;
|
|
delta.y += normalized.y;
|
|
}
|
|
}
|
|
|
|
if (!average || nchanged == 0)
|
|
return delta;
|
|
|
|
delta.x /= nchanged;
|
|
delta.y /= nchanged;
|
|
|
|
return delta;
|
|
}
|
|
|
|
static inline struct normalized_coords
|
|
tp_get_combined_touches_delta(struct tp_dispatch *tp)
|
|
{
|
|
return tp_get_touches_delta(tp, false);
|
|
}
|
|
|
|
static inline struct normalized_coords
|
|
tp_get_average_touches_delta(struct tp_dispatch *tp)
|
|
{
|
|
return tp_get_touches_delta(tp, true);
|
|
}
|
|
|
|
static void
|
|
tp_gesture_start(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
if (tp->gesture.started)
|
|
return;
|
|
|
|
switch (tp->gesture.finger_count) {
|
|
case 2:
|
|
/* NOP */
|
|
break;
|
|
}
|
|
tp->gesture.started = true;
|
|
}
|
|
|
|
static void
|
|
tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
struct normalized_coords delta, unaccel;
|
|
|
|
/* When a clickpad is clicked, combine motion of all active touches */
|
|
if (tp->buttons.is_clickpad && tp->buttons.state)
|
|
delta = tp_get_combined_touches_delta(tp);
|
|
else
|
|
delta = tp_get_average_touches_delta(tp);
|
|
|
|
tp_filter_motion(tp, &delta.x, &delta.y, &unaccel.x, &unaccel.y, time);
|
|
|
|
if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) {
|
|
pointer_notify_motion(&tp->device->base, time,
|
|
&delta, &unaccel);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
struct normalized_coords delta;
|
|
|
|
delta = tp_get_average_touches_delta(tp);
|
|
tp_filter_motion(tp, &delta.x, &delta.y, NULL, NULL, time);
|
|
|
|
if (normalized_is_zero(delta))
|
|
return;
|
|
|
|
tp_gesture_start(tp, time);
|
|
evdev_post_scroll(tp->device,
|
|
time,
|
|
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
|
|
&delta);
|
|
}
|
|
|
|
void
|
|
tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
if (tp->gesture.finger_count == 0)
|
|
return;
|
|
|
|
/* When tap-and-dragging, or a clickpad is clicked force 1fg mode */
|
|
if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) {
|
|
tp_gesture_stop(tp, time);
|
|
tp->gesture.finger_count = 1;
|
|
tp->gesture.finger_count_pending = 0;
|
|
}
|
|
|
|
/* Don't send events when we're unsure in which mode we are */
|
|
if (tp->gesture.finger_count_pending)
|
|
return;
|
|
|
|
switch (tp->gesture.finger_count) {
|
|
case 1:
|
|
tp_gesture_post_pointer_motion(tp, time);
|
|
break;
|
|
case 2:
|
|
tp_gesture_post_twofinger_scroll(tp, time);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
evdev_stop_scroll(tp->device,
|
|
time,
|
|
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
|
|
}
|
|
|
|
void
|
|
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
if (!tp->gesture.started)
|
|
return;
|
|
|
|
switch (tp->gesture.finger_count) {
|
|
case 2:
|
|
tp_gesture_stop_twofinger_scroll(tp, time);
|
|
break;
|
|
}
|
|
tp->gesture.started = false;
|
|
}
|
|
|
|
static void
|
|
tp_gesture_finger_count_switch_timeout(uint64_t now, void *data)
|
|
{
|
|
struct tp_dispatch *tp = data;
|
|
|
|
if (!tp->gesture.finger_count_pending)
|
|
return;
|
|
|
|
tp_gesture_stop(tp, now); /* End current gesture */
|
|
tp->gesture.finger_count = tp->gesture.finger_count_pending;
|
|
tp->gesture.finger_count_pending = 0;
|
|
}
|
|
|
|
void
|
|
tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|
{
|
|
unsigned int active_touches = 0;
|
|
struct tp_touch *t;
|
|
|
|
tp_for_each_touch(tp, t)
|
|
if (tp_touch_active(tp, t))
|
|
active_touches++;
|
|
|
|
if (active_touches != tp->gesture.finger_count) {
|
|
/* If all fingers are lifted immediately end the gesture */
|
|
if (active_touches == 0) {
|
|
tp_gesture_stop(tp, time);
|
|
tp->gesture.finger_count = 0;
|
|
tp->gesture.finger_count_pending = 0;
|
|
/* Immediately switch to new mode to avoid initial latency */
|
|
} else if (!tp->gesture.started) {
|
|
tp->gesture.finger_count = active_touches;
|
|
tp->gesture.finger_count_pending = 0;
|
|
/* Else debounce finger changes */
|
|
} else if (active_touches != tp->gesture.finger_count_pending) {
|
|
tp->gesture.finger_count_pending = active_touches;
|
|
libinput_timer_set(&tp->gesture.finger_count_switch_timer,
|
|
time + DEFAULT_GESTURE_SWITCH_TIMEOUT);
|
|
}
|
|
} else {
|
|
tp->gesture.finger_count_pending = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
tp_init_gesture(struct tp_dispatch *tp)
|
|
{
|
|
libinput_timer_init(&tp->gesture.finger_count_switch_timer,
|
|
tp->device->base.seat->libinput,
|
|
tp_gesture_finger_count_switch_timeout, tp);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
tp_remove_gesture(struct tp_dispatch *tp)
|
|
{
|
|
libinput_timer_cancel(&tp->gesture.finger_count_switch_timer);
|
|
}
|