mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-03-23 10:50:36 +01:00
Implements Edge motion
Hook into the tap-and-drag state machine to emit pointer events when the finger reaches the touchpad edge. This allows continued motion when dragging beyond the physical boundary. Signed-off-by: suraj <nagisetty.suraj@gmail.com>
This commit is contained in:
parent
db62bf7ab1
commit
48c86a275b
4 changed files with 262 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',
|
||||
|
|
|
|||
246
src/evdev-mt-touchpad-edge-motion.c
Normal file
246
src/evdev-mt-touchpad-edge-motion.c
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright © 2014-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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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"
|
||||
|
||||
void
|
||||
tp_edge_motion_init(struct tp_dispatch *tp);
|
||||
|
||||
enum edge_motion_state { STATE_IDLE, STATE_DRAG_ACTIVE, STATE_EDGE_MOTION };
|
||||
|
||||
struct edge_motion_fsm {
|
||||
enum edge_motion_state current_state;
|
||||
usec_t last_motion_time;
|
||||
uint32_t current_edge;
|
||||
double motion_dx;
|
||||
double motion_dy;
|
||||
uint32_t continuous_motion_count;
|
||||
struct tp_dispatch *tp;
|
||||
struct libinput_timer timer;
|
||||
};
|
||||
|
||||
static struct edge_motion_fsm fsm = {
|
||||
.current_state = STATE_IDLE,
|
||||
.tp = NULL,
|
||||
};
|
||||
|
||||
#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 = sqrt((*dx) * (*dx) + (*dy) * (*dy));
|
||||
if (mag > 0) {
|
||||
*dx /= mag;
|
||||
*dy /= mag;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
inject_accumulated_motion(struct tp_dispatch *tp, usec_t time)
|
||||
{
|
||||
if (usec_is_zero(fsm.last_motion_time)) {
|
||||
fsm.last_motion_time = time;
|
||||
return;
|
||||
}
|
||||
|
||||
usec_t time_since_last = usec_sub(time, fsm.last_motion_time);
|
||||
double dist_mm =
|
||||
EDGE_MOTION_CONFIG_SPEED_MM_S * ((double)usec_as_uint64_t(time_since_last) / 1000000.0);
|
||||
|
||||
if (dist_mm < 0.001)
|
||||
return;
|
||||
|
||||
struct device_float_coords raw = {
|
||||
.x = fsm.motion_dx * dist_mm * tp->accel.x_scale_coeff,
|
||||
.y = fsm.motion_dy * dist_mm * tp->accel.y_scale_coeff
|
||||
};
|
||||
|
||||
struct normalized_coords delta =
|
||||
filter_dispatch(tp->device->pointer.filter, &raw, tp, time);
|
||||
|
||||
pointer_notify_motion(&tp->device->base, time, &delta, &raw);
|
||||
|
||||
fsm.last_motion_time = time;
|
||||
fsm.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 edge_motion_fsm *fsm_ptr = data;
|
||||
|
||||
if (fsm_ptr->current_state != STATE_EDGE_MOTION)
|
||||
return;
|
||||
|
||||
inject_accumulated_motion(fsm_ptr->tp, now);
|
||||
libinput_timer_set(&fsm_ptr->timer, usec_add(now, usec_from_uint64_t(EDGE_MOTION_CONFIG_MIN_INTERVAL_US)));
|
||||
}
|
||||
|
||||
void
|
||||
tp_edge_motion_init(struct tp_dispatch *tp)
|
||||
{
|
||||
if (fsm.tp)
|
||||
return;
|
||||
|
||||
memset(&fsm, 0, sizeof(fsm));
|
||||
fsm.current_state = STATE_IDLE;
|
||||
fsm.tp = tp;
|
||||
|
||||
libinput_timer_init(&fsm.timer,
|
||||
tp_libinput_context(tp),
|
||||
"edge drag motion",
|
||||
tp_edge_motion_handle_timeout,
|
||||
&fsm);
|
||||
}
|
||||
|
||||
void
|
||||
tp_edge_motion_cleanup(void)
|
||||
{
|
||||
if (fsm.tp)
|
||||
libinput_timer_destroy(&fsm.timer);
|
||||
|
||||
memset(&fsm, 0, sizeof(fsm));
|
||||
fsm.current_state = STATE_IDLE;
|
||||
}
|
||||
|
||||
int
|
||||
tp_edge_motion_handle_drag_state(struct tp_dispatch *tp, usec_t time)
|
||||
{
|
||||
if (!fsm.tp)
|
||||
tp_edge_motion_init(tp);
|
||||
|
||||
bool drag_active = false;
|
||||
|
||||
switch (tp->tap.state) {
|
||||
case TAP_STATE_1FGTAP_DRAGGING:
|
||||
case TAP_STATE_1FGTAP_DRAGGING_2:
|
||||
case TAP_STATE_1FGTAP_DRAGGING_WAIT:
|
||||
case TAP_STATE_1FGTAP_DRAGGING_OR_TAP:
|
||||
case TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP:
|
||||
drag_active = true;
|
||||
break;
|
||||
default:
|
||||
drag_active = false;
|
||||
break;
|
||||
}
|
||||
|
||||
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 edge_motion_state next_state = STATE_IDLE;
|
||||
if (drag_active) {
|
||||
next_state = (detected_edge != EDGE_NONE) ? STATE_EDGE_MOTION
|
||||
: STATE_DRAG_ACTIVE;
|
||||
}
|
||||
|
||||
if (next_state != fsm.current_state) {
|
||||
fsm.current_state = next_state;
|
||||
fsm.current_edge = detected_edge;
|
||||
|
||||
if (fsm.current_state != STATE_EDGE_MOTION)
|
||||
fsm.continuous_motion_count = 0;
|
||||
|
||||
switch (fsm.current_state) {
|
||||
case STATE_IDLE:
|
||||
case STATE_DRAG_ACTIVE:
|
||||
libinput_timer_cancel(&fsm.timer);
|
||||
break;
|
||||
|
||||
case STATE_EDGE_MOTION:
|
||||
calculate_motion_vector(fsm.current_edge,
|
||||
&fsm.motion_dx,
|
||||
&fsm.motion_dy);
|
||||
fsm.last_motion_time = time;
|
||||
tp_edge_motion_handle_timeout(time, &fsm);
|
||||
break;
|
||||
}
|
||||
} else if (fsm.current_state == STATE_EDGE_MOTION &&
|
||||
detected_edge != fsm.current_edge) {
|
||||
fsm.current_edge = detected_edge;
|
||||
calculate_motion_vector(fsm.current_edge,
|
||||
&fsm.motion_dx,
|
||||
&fsm.motion_dy);
|
||||
}
|
||||
|
||||
return (fsm.current_state == 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
|
||||
|
|
@ -1594,6 +1597,7 @@ void
|
|||
tp_remove_tap(struct tp_dispatch *tp)
|
||||
{
|
||||
libinput_timer_cancel(&tp->tap.timer);
|
||||
tp_edge_motion_cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -140,11 +140,13 @@ enum tp_tap_touch_state {
|
|||
TAP_TOUCH_STATE_DEAD, /**< exceeded motion/timeout */
|
||||
};
|
||||
|
||||
/* For edge scrolling, so we only care about right and bottom */
|
||||
/* 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 +423,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;
|
||||
|
|
@ -721,6 +725,12 @@ 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);
|
||||
|
||||
int
|
||||
tp_edge_motion_handle_drag_state(struct tp_dispatch *tp, usec_t time);
|
||||
|
||||
void
|
||||
tp_edge_motion_cleanup(void);
|
||||
|
||||
void
|
||||
tp_init_gesture(struct tp_dispatch *tp);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue