mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-03-21 14:30:36 +01:00
Merge branch 'edge-motion' into 'main'
Implements Edge motion See merge request libinput/libinput!1438
This commit is contained in:
commit
aae6214e3a
5 changed files with 264 additions and 1 deletions
|
|
@ -436,6 +436,7 @@ src_libinput = src_libfilter + [
|
|||
'src/evdev-mt-touchpad-thumb.c',
|
||||
'src/evdev-mt-touchpad-buttons.c',
|
||||
'src/evdev-mt-touchpad-edge-scroll.c',
|
||||
'src/evdev-mt-touchpad-edge-motion.c',
|
||||
'src/evdev-mt-touchpad-gestures.c',
|
||||
'src/evdev-tablet.c',
|
||||
'src/evdev-tablet-pad.c',
|
||||
|
|
|
|||
222
src/evdev-mt-touchpad-edge-motion.c
Normal file
222
src/evdev-mt-touchpad-edge-motion.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright © 2014-2015 Nobody, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TOUCHPAD EDGE MOTION
|
||||
*
|
||||
* This module implements automatic cursor motion when performing tap-and-drag
|
||||
* operations near the edges of a touchpad. When a user starts dragging content
|
||||
* and reaches the edge of the touchpad, the system automatically continues
|
||||
* moving the cursor in that direction to allow selection/dragging of content
|
||||
* that extends beyond the physical touchpad boundaries.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "evdev-mt-touchpad.h"
|
||||
|
||||
#define EDGE_MOTION_CONFIG_SPEED_MM_S 70.0
|
||||
#define EDGE_MOTION_CONFIG_MIN_INTERVAL_US 8000
|
||||
#define EDGE_MOTION_CONFIG_EDGE_THRESHOLD_MM 5.0
|
||||
|
||||
static void
|
||||
calculate_motion_vector(uint32_t edge, double *dx, double *dy)
|
||||
{
|
||||
*dx = 0.0;
|
||||
*dy = 0.0;
|
||||
|
||||
if (edge & EDGE_LEFT)
|
||||
*dx = -1.0;
|
||||
else if (edge & EDGE_RIGHT)
|
||||
*dx = 1.0;
|
||||
|
||||
if (edge & EDGE_TOP)
|
||||
*dy = -1.0;
|
||||
else if (edge & EDGE_BOTTOM)
|
||||
*dy = 1.0;
|
||||
|
||||
double mag = hypot(*dx, *dy);
|
||||
if (mag > 0) {
|
||||
*dx /= mag;
|
||||
*dy /= mag;
|
||||
}
|
||||
}
|
||||
|
||||
static struct device_float_coords
|
||||
edge_motion_calculate_raw(struct tp_dispatch *tp, double dx, double dy, double dist_mm)
|
||||
{
|
||||
struct device_float_coords raw = {
|
||||
.x = dx * dist_mm * tp->accel.x_scale_coeff,
|
||||
.y = dy * dist_mm * tp->accel.y_scale_coeff,
|
||||
};
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
static void
|
||||
inject_accumulated_motion(struct tp_dispatch *tp, usec_t time)
|
||||
{
|
||||
if (usec_is_zero(tp->edge_motion.last_motion_time)) {
|
||||
tp->edge_motion.last_motion_time = time;
|
||||
return;
|
||||
}
|
||||
|
||||
usec_t time_since_last = usec_sub(time, tp->edge_motion.last_motion_time);
|
||||
double dist_mm =
|
||||
EDGE_MOTION_CONFIG_SPEED_MM_S * usec_to_seconds(time_since_last);
|
||||
|
||||
if (dist_mm < 0.001)
|
||||
return;
|
||||
|
||||
struct device_float_coords raw =
|
||||
edge_motion_calculate_raw(tp,
|
||||
tp->edge_motion.motion_dx,
|
||||
tp->edge_motion.motion_dy,
|
||||
dist_mm);
|
||||
|
||||
struct normalized_coords delta =
|
||||
filter_dispatch(tp->device->pointer.filter, &raw, tp, time);
|
||||
|
||||
pointer_notify_motion(&tp->device->base, time, &delta, &raw);
|
||||
|
||||
tp->edge_motion.last_motion_time = time;
|
||||
tp->edge_motion.continuous_motion_count++;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
detect_touch_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
||||
{
|
||||
uint32_t edge = EDGE_NONE;
|
||||
struct phys_coords mm = { EDGE_MOTION_CONFIG_EDGE_THRESHOLD_MM,
|
||||
EDGE_MOTION_CONFIG_EDGE_THRESHOLD_MM };
|
||||
struct device_coords threshold = evdev_device_mm_to_units(tp->device, &mm);
|
||||
|
||||
if (t->point.x < threshold.x)
|
||||
edge |= EDGE_LEFT;
|
||||
if (t->point.x > tp->device->abs.absinfo_x->maximum - threshold.x)
|
||||
edge |= EDGE_RIGHT;
|
||||
if (t->point.y < threshold.y)
|
||||
edge |= EDGE_TOP;
|
||||
if (t->point.y > tp->device->abs.absinfo_y->maximum - threshold.y)
|
||||
edge |= EDGE_BOTTOM;
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
static void
|
||||
tp_edge_motion_handle_timeout(usec_t now, void *data)
|
||||
{
|
||||
struct tp_dispatch *tp = data;
|
||||
|
||||
if (tp->edge_motion.state != EDGE_MOTION_STATE_EDGE_MOTION)
|
||||
return;
|
||||
|
||||
inject_accumulated_motion(tp, now);
|
||||
libinput_timer_set(
|
||||
&tp->edge_motion.timer,
|
||||
usec_add(now, usec_from_uint64_t(EDGE_MOTION_CONFIG_MIN_INTERVAL_US)));
|
||||
}
|
||||
|
||||
void
|
||||
tp_init_edge_motion(struct tp_dispatch *tp)
|
||||
{
|
||||
tp->edge_motion.state = EDGE_MOTION_STATE_IDLE;
|
||||
tp->edge_motion.last_motion_time = usec_from_uint64_t(0);
|
||||
tp->edge_motion.current_edge = EDGE_NONE;
|
||||
tp->edge_motion.motion_dx = 0.0;
|
||||
tp->edge_motion.motion_dy = 0.0;
|
||||
tp->edge_motion.continuous_motion_count = 0;
|
||||
|
||||
libinput_timer_init(&tp->edge_motion.timer,
|
||||
tp_libinput_context(tp),
|
||||
"edge drag motion",
|
||||
tp_edge_motion_handle_timeout,
|
||||
tp);
|
||||
}
|
||||
|
||||
void
|
||||
tp_remove_edge_motion(struct tp_dispatch *tp)
|
||||
{
|
||||
libinput_timer_cancel(&tp->edge_motion.timer);
|
||||
libinput_timer_destroy(&tp->edge_motion.timer);
|
||||
}
|
||||
|
||||
int
|
||||
tp_edge_motion_handle_drag_state(struct tp_dispatch *tp, usec_t time)
|
||||
{
|
||||
bool drag_active = tp_tap_dragging(tp);
|
||||
|
||||
uint32_t detected_edge = EDGE_NONE;
|
||||
struct tp_touch *t;
|
||||
|
||||
if (drag_active) {
|
||||
tp_for_each_touch(tp, t) {
|
||||
if (t->state != TOUCH_NONE && t->state != TOUCH_HOVERING) {
|
||||
detected_edge = detect_touch_edge(tp, t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum tp_edge_motion_state next_state = EDGE_MOTION_STATE_IDLE;
|
||||
if (drag_active) {
|
||||
next_state = (detected_edge != EDGE_NONE)
|
||||
? EDGE_MOTION_STATE_EDGE_MOTION
|
||||
: EDGE_MOTION_STATE_DRAG_ACTIVE;
|
||||
}
|
||||
|
||||
if (next_state != tp->edge_motion.state) {
|
||||
tp->edge_motion.state = next_state;
|
||||
tp->edge_motion.current_edge = detected_edge;
|
||||
|
||||
if (tp->edge_motion.state != EDGE_MOTION_STATE_EDGE_MOTION)
|
||||
tp->edge_motion.continuous_motion_count = 0;
|
||||
|
||||
switch (tp->edge_motion.state) {
|
||||
case EDGE_MOTION_STATE_IDLE:
|
||||
case EDGE_MOTION_STATE_DRAG_ACTIVE:
|
||||
libinput_timer_cancel(&tp->edge_motion.timer);
|
||||
tp->edge_motion.current_edge = EDGE_NONE;
|
||||
break;
|
||||
|
||||
case EDGE_MOTION_STATE_EDGE_MOTION:
|
||||
calculate_motion_vector(tp->edge_motion.current_edge,
|
||||
&tp->edge_motion.motion_dx,
|
||||
&tp->edge_motion.motion_dy);
|
||||
tp->edge_motion.last_motion_time = time;
|
||||
tp_edge_motion_handle_timeout(time, tp);
|
||||
break;
|
||||
}
|
||||
} else if (tp->edge_motion.state == EDGE_MOTION_STATE_EDGE_MOTION &&
|
||||
detected_edge != tp->edge_motion.current_edge) {
|
||||
tp->edge_motion.current_edge = detected_edge;
|
||||
calculate_motion_vector(tp->edge_motion.current_edge,
|
||||
&tp->edge_motion.motion_dx,
|
||||
&tp->edge_motion.motion_dy);
|
||||
}
|
||||
|
||||
return (tp->edge_motion.state == EDGE_MOTION_STATE_EDGE_MOTION);
|
||||
}
|
||||
|
|
@ -1292,6 +1292,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, usec_t time)
|
|||
}
|
||||
}
|
||||
|
||||
/* Log 1-finger drag state changes */
|
||||
filter_motion |= tp_edge_motion_handle_drag_state(tp, time);
|
||||
|
||||
/**
|
||||
* In any state where motion exceeding the move threshold would
|
||||
* move to the next state, filter that motion until we actually
|
||||
|
|
|
|||
|
|
@ -1960,6 +1960,7 @@ tp_interface_remove(struct evdev_dispatch *dispatch)
|
|||
tp_remove_buttons(tp);
|
||||
tp_remove_sendevents(tp);
|
||||
tp_remove_edge_scroll(tp);
|
||||
tp_remove_edge_motion(tp);
|
||||
tp_remove_gesture(tp);
|
||||
}
|
||||
|
||||
|
|
@ -3799,6 +3800,7 @@ tp_init(struct tp_dispatch *tp, struct evdev_device *device)
|
|||
tp_init_palmdetect(tp, device);
|
||||
tp_init_sendevents(tp, device);
|
||||
tp_init_scroll(tp, device);
|
||||
tp_init_edge_motion(tp);
|
||||
tp_init_gesture(tp);
|
||||
tp_init_thumb(tp);
|
||||
|
||||
|
|
|
|||
|
|
@ -140,11 +140,20 @@ enum tp_tap_touch_state {
|
|||
TAP_TOUCH_STATE_DEAD, /**< exceeded motion/timeout */
|
||||
};
|
||||
|
||||
/* For edge scrolling, so we only care about right and bottom */
|
||||
enum tp_edge_motion_state {
|
||||
EDGE_MOTION_STATE_IDLE,
|
||||
EDGE_MOTION_STATE_DRAG_ACTIVE,
|
||||
EDGE_MOTION_STATE_EDGE_MOTION,
|
||||
};
|
||||
|
||||
/* For edge scrolling, so we only care about right and bottom. For edge motion, we
|
||||
* require all four edges */
|
||||
enum tp_edge {
|
||||
EDGE_NONE = 0,
|
||||
EDGE_RIGHT = bit(0),
|
||||
EDGE_BOTTOM = bit(1),
|
||||
EDGE_LEFT = bit(2),
|
||||
EDGE_TOP = bit(3),
|
||||
};
|
||||
|
||||
enum tp_edge_scroll_touch_state {
|
||||
|
|
@ -421,6 +430,8 @@ struct tp_dispatch {
|
|||
enum libinput_config_scroll_method method;
|
||||
int32_t right_edge; /* in device coordinates */
|
||||
int32_t bottom_edge; /* in device coordinates */
|
||||
int32_t left_edge; /* in device coordinates */
|
||||
int32_t upper_edge; /* in device coordinates */
|
||||
struct {
|
||||
bool h, v;
|
||||
} active;
|
||||
|
|
@ -560,6 +571,21 @@ struct tp_dispatch {
|
|||
struct evdev_device *tablet_device;
|
||||
bool tablet_left_handed_state;
|
||||
} left_handed;
|
||||
|
||||
struct {
|
||||
enum tp_edge_motion_state state;
|
||||
|
||||
usec_t last_motion_time;
|
||||
|
||||
uint32_t current_edge;
|
||||
|
||||
double motion_dx;
|
||||
double motion_dy;
|
||||
|
||||
uint32_t continuous_motion_count;
|
||||
|
||||
struct libinput_timer timer;
|
||||
} edge_motion;
|
||||
};
|
||||
|
||||
static inline struct tp_dispatch *
|
||||
|
|
@ -721,6 +747,15 @@ tp_edge_scroll_touch_active(const struct tp_dispatch *tp, const struct tp_touch
|
|||
uint32_t
|
||||
tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t);
|
||||
|
||||
void
|
||||
tp_init_edge_motion(struct tp_dispatch *tp);
|
||||
|
||||
int
|
||||
tp_edge_motion_handle_drag_state(struct tp_dispatch *tp, usec_t time);
|
||||
|
||||
void
|
||||
tp_remove_edge_motion(struct tp_dispatch *tp);
|
||||
|
||||
void
|
||||
tp_init_gesture(struct tp_dispatch *tp);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue