2014-02-06 15:05:36 +10:00
|
|
|
/*
|
2015-05-28 08:23:59 +10:00
|
|
|
* Copyright © 2014-2015 Red Hat, Inc.
|
2014-02-06 15:05:36 +10:00
|
|
|
*
|
2015-06-11 12:09:18 +10:00
|
|
|
* 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:
|
2014-02-06 15:05:36 +10:00
|
|
|
*
|
2015-06-11 12:09:18 +10:00
|
|
|
* 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.
|
2014-02-06 15:05:36 +10:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
#include <assert.h>
|
2014-02-06 18:57:10 +10:00
|
|
|
#include <math.h>
|
2014-02-06 15:32:32 +10:00
|
|
|
#include <stdbool.h>
|
2014-07-15 14:01:00 +10:00
|
|
|
#include <limits.h>
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
#if HAVE_LIBWACOM
|
|
|
|
|
#include <libwacom/libwacom.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
#include "quirks.h"
|
2014-02-07 13:39:27 +10:00
|
|
|
#include "evdev-mt-touchpad.h"
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2015-07-27 17:51:52 +08:00
|
|
|
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300)
|
2018-04-27 14:40:57 +10:00
|
|
|
#define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40)
|
2015-07-27 17:51:52 +08:00
|
|
|
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200)
|
|
|
|
|
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500)
|
2015-04-09 14:23:16 +10:00
|
|
|
#define FAKE_FINGER_OVERFLOW (1 << 7)
|
2017-06-21 12:29:52 +10:00
|
|
|
#define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
enum notify {
|
|
|
|
|
DONT_NOTIFY,
|
|
|
|
|
DO_NOTIFY,
|
|
|
|
|
};
|
|
|
|
|
|
2017-06-21 11:16:27 +10:00
|
|
|
static inline struct tp_history_point*
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_motion_history_offset(struct tp_touch *t, int offset)
|
|
|
|
|
{
|
|
|
|
|
int offset_index =
|
|
|
|
|
(t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
|
|
|
|
|
TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
|
|
|
|
|
|
return &t->history.samples[offset_index];
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-24 16:43:24 +01:00
|
|
|
struct normalized_coords
|
2014-02-06 19:43:48 +10:00
|
|
|
tp_filter_motion(struct tp_dispatch *tp,
|
2018-01-10 11:48:58 +10:00
|
|
|
const struct device_float_coords *unaccelerated,
|
2014-12-04 11:44:09 +08:00
|
|
|
uint64_t time)
|
2014-02-06 19:43:48 +10:00
|
|
|
{
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
struct device_float_coords raw;
|
2018-01-10 11:48:58 +10:00
|
|
|
const struct normalized_coords zero = { 0.0, 0.0 };
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
if (device_float_is_zero(*unaccelerated))
|
|
|
|
|
return zero;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
/* Convert to device units with x/y in the same resolution */
|
|
|
|
|
raw = tp_scale_to_xaxis(tp, *unaccelerated);
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
|
2015-03-24 16:43:24 +01:00
|
|
|
return filter_dispatch(tp->device->pointer.filter,
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
&raw, tp, time);
|
2014-02-06 19:43:48 +10:00
|
|
|
}
|
|
|
|
|
|
2015-08-06 14:59:38 +10:00
|
|
|
struct normalized_coords
|
|
|
|
|
tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
|
2018-01-10 11:48:58 +10:00
|
|
|
const struct device_float_coords *unaccelerated,
|
2015-08-06 14:59:38 +10:00
|
|
|
uint64_t time)
|
|
|
|
|
{
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
struct device_float_coords raw;
|
2018-01-10 11:48:58 +10:00
|
|
|
const struct normalized_coords zero = { 0.0, 0.0 };
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
if (device_float_is_zero(*unaccelerated))
|
|
|
|
|
return zero;
|
2015-08-06 14:59:38 +10:00
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
/* Convert to device units with x/y in the same resolution */
|
|
|
|
|
raw = tp_scale_to_xaxis(tp, *unaccelerated);
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
|
2015-08-06 14:59:38 +10:00
|
|
|
return filter_dispatch_constant(tp->device->pointer.filter,
|
filter: change the filter functions to take raw device coordinates
We used to normalize all deltas to equivalents of a 1000dpi mouse before
passing it into the acceleration functions. This has a bunch of drawbacks, not
least that we already have to un-normalize back into device units for a few
devices already (trackpoints, tablet, low-dpi mice).
Switch the filter code over to use device units, relying on the dpi set
earlier during filter creation to convert to normalized. To make things easy,
the output of the filter code is still normalized data, i.e. data ready to be
handed to the libinput caller.
No effective functional changes. For touchpads, we still send normalized
coordinates (for now, anyway). For the various filter methods, we either drop
the places where we unnormalized before or we normalize where needed.
Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor
of 1.0 before - now we don't anymore. This was only the case if a low-dpi
mouse had more than 1000dpi (never true) or a trackpoint had a const accel
lower than 1.0 (yeah, whatever).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2016-12-15 08:36:22 +10:00
|
|
|
&raw, tp, time);
|
2015-08-06 14:59:38 +10:00
|
|
|
}
|
|
|
|
|
|
2017-06-21 12:29:52 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
const struct tp_history_point *last;
|
|
|
|
|
struct device_coords delta;
|
|
|
|
|
struct phys_coords mm;
|
|
|
|
|
double distance;
|
|
|
|
|
double speed;
|
|
|
|
|
|
2018-02-27 16:18:20 +10:00
|
|
|
/* Don't do this on single-touch or semi-mt devices */
|
|
|
|
|
if (!tp->has_mt || tp->semi_mt)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-10-02 10:40:47 +10:00
|
|
|
if (t->state != TOUCH_UPDATE)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-06-21 12:29:52 +10:00
|
|
|
/* This doesn't kick in until we have at least 4 events in the
|
|
|
|
|
* motion history. As a side-effect, this automatically handles the
|
|
|
|
|
* 2fg scroll where a finger is down and moving fast before the
|
|
|
|
|
* other finger comes down for the scroll.
|
|
|
|
|
*
|
|
|
|
|
* We do *not* reset the speed to 0 here though. The motion history
|
|
|
|
|
* is reset whenever a new finger is down, so we'd be resetting the
|
|
|
|
|
* speed and failing.
|
|
|
|
|
*/
|
|
|
|
|
if (t->history.count < 4)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* TODO: we probably need a speed history here so we can average
|
|
|
|
|
* across a few events */
|
|
|
|
|
last = tp_motion_history_offset(t, 1);
|
|
|
|
|
delta.x = abs(t->point.x - last->point.x);
|
|
|
|
|
delta.y = abs(t->point.y - last->point.y);
|
|
|
|
|
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
|
|
|
|
|
|
|
|
|
|
distance = length_in_mm(mm);
|
|
|
|
|
speed = distance/(t->time - last->time); /* mm/us */
|
|
|
|
|
speed *= 1000000; /* mm/s */
|
|
|
|
|
|
|
|
|
|
t->speed.last_speed = speed;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_history_push(struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
|
|
|
|
|
|
if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
|
|
|
|
|
t->history.count++;
|
|
|
|
|
|
2017-06-21 11:16:27 +10:00
|
|
|
t->history.samples[motion_index].point = t->point;
|
|
|
|
|
t->history.samples[motion_index].time = t->time;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->history.index = motion_index;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-21 20:16:41 +03:00
|
|
|
/* Idea: if we got a tuple of *very* quick moves like {Left, Right,
|
|
|
|
|
* Left}, or {Right, Left, Right}, it means touchpad jitters since no
|
|
|
|
|
* human can move like that within thresholds.
|
|
|
|
|
*
|
|
|
|
|
* We encode left moves as zeroes, and right as ones. We also drop
|
|
|
|
|
* the array to all zeroes when contraints are not satisfied. Then we
|
|
|
|
|
* search for the pattern {1,0,1}. It can't match {Left, Right, Left},
|
|
|
|
|
* but it does match {Left, Right, Left, Right}, so it's okay.
|
|
|
|
|
*
|
|
|
|
|
* This only looks at x changes, y changes are ignored.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
tp_detect_wobbling(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
int dx, dy;
|
|
|
|
|
uint64_t dtime;
|
2018-06-12 18:53:51 +03:00
|
|
|
const struct device_coords* prev_point;
|
2018-02-21 20:16:41 +03:00
|
|
|
|
2018-05-03 19:14:14 +10:00
|
|
|
if (tp->nfingers_down != 1 ||
|
|
|
|
|
tp->nfingers_down != tp->old_nfingers_down)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-06-12 18:53:51 +03:00
|
|
|
if (tp->hysteresis.enabled || t->history.count == 0)
|
2018-02-21 20:16:41 +03:00
|
|
|
return;
|
|
|
|
|
|
2018-04-24 15:35:46 +10:00
|
|
|
if (!(tp->queued & TOUCHPAD_EVENT_MOTION)) {
|
|
|
|
|
t->hysteresis.x_motion_history = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 18:53:51 +03:00
|
|
|
prev_point = &tp_motion_history_offset(t, 0)->point;
|
|
|
|
|
dx = prev_point->x - t->point.x;
|
|
|
|
|
dy = prev_point->y - t->point.y;
|
2018-02-21 20:16:41 +03:00
|
|
|
dtime = time - tp->hysteresis.last_motion_time;
|
|
|
|
|
|
|
|
|
|
tp->hysteresis.last_motion_time = time;
|
|
|
|
|
|
2018-04-24 10:34:21 +10:00
|
|
|
if ((dx == 0 && dy != 0) || dtime > ms2us(40)) {
|
2018-02-21 20:16:41 +03:00
|
|
|
t->hysteresis.x_motion_history = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-12 14:32:39 +10:00
|
|
|
t->hysteresis.x_motion_history >>= 1;
|
2018-02-21 20:16:41 +03:00
|
|
|
if (dx > 0) { /* right move */
|
|
|
|
|
static const char r_l_r = 0x5; /* {Right, Left, Right} */
|
|
|
|
|
|
2018-03-12 14:32:39 +10:00
|
|
|
t->hysteresis.x_motion_history |= (1 << 2);
|
2018-02-21 20:16:41 +03:00
|
|
|
if (t->hysteresis.x_motion_history == r_l_r) {
|
|
|
|
|
tp->hysteresis.enabled = true;
|
2018-04-26 11:47:54 +10:00
|
|
|
evdev_log_debug(tp->device,
|
|
|
|
|
"hysteresis enabled. "
|
2018-08-10 20:39:49 +10:00
|
|
|
"See %stouchpad-jitter.html for details\n",
|
2018-04-26 11:47:54 +10:00
|
|
|
HTTP_DOC_LINK);
|
2018-02-21 20:16:41 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 18:57:10 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_hysteresis(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
2017-10-26 13:27:07 +10:00
|
|
|
if (!tp->hysteresis.enabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-03-01 17:03:28 +08:00
|
|
|
if (t->history.count > 0)
|
|
|
|
|
t->point = evdev_hysteresis(&t->point,
|
|
|
|
|
&t->hysteresis.center,
|
|
|
|
|
&tp->hysteresis.margin);
|
|
|
|
|
|
|
|
|
|
t->hysteresis.center = t->point;
|
2014-02-06 18:57:10 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_history_reset(struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
t->history.count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct tp_touch *
|
|
|
|
|
tp_current_touch(struct tp_dispatch *tp)
|
|
|
|
|
{
|
2014-06-18 14:22:24 +02:00
|
|
|
return &tp->touches[min(tp->slot, tp->ntouches - 1)];
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-14 15:12:22 +10:00
|
|
|
static inline struct tp_touch *
|
|
|
|
|
tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
|
|
|
|
|
{
|
|
|
|
|
assert(slot < tp->ntouches);
|
|
|
|
|
return &tp->touches[slot];
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-12 10:31:12 +10:00
|
|
|
static inline unsigned int
|
|
|
|
|
tp_fake_finger_count(struct tp_dispatch *tp)
|
|
|
|
|
{
|
2019-06-15 14:04:38 -07:00
|
|
|
unsigned int fake_touches =
|
|
|
|
|
tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1);
|
|
|
|
|
|
2016-01-07 15:49:02 +10:00
|
|
|
/* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any
|
|
|
|
|
* time */
|
2019-06-15 14:04:38 -07:00
|
|
|
if (fake_touches & (fake_touches - 1))
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_bug_kernel(tp->device,
|
|
|
|
|
"Invalid fake finger state %#x\n",
|
|
|
|
|
tp->fake_touches);
|
2016-01-07 15:49:02 +10:00
|
|
|
|
2015-04-09 14:23:16 +10:00
|
|
|
if (tp->fake_touches & FAKE_FINGER_OVERFLOW)
|
|
|
|
|
return FAKE_FINGER_OVERFLOW;
|
|
|
|
|
else /* don't count BTN_TOUCH */
|
|
|
|
|
return ffs(tp->fake_touches >> 1);
|
2014-12-12 10:31:12 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_fake_finger_is_touching(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
return tp->fake_touches & 0x1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
tp_fake_finger_set(struct tp_dispatch *tp,
|
|
|
|
|
unsigned int code,
|
|
|
|
|
bool is_press)
|
|
|
|
|
{
|
|
|
|
|
unsigned int shift;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
case BTN_TOUCH:
|
2015-04-09 14:23:16 +10:00
|
|
|
if (!is_press)
|
|
|
|
|
tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
|
2014-12-12 10:31:12 +10:00
|
|
|
shift = 0;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_TOOL_FINGER:
|
|
|
|
|
shift = 1;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_TOOL_DOUBLETAP:
|
|
|
|
|
case BTN_TOOL_TRIPLETAP:
|
|
|
|
|
case BTN_TOOL_QUADTAP:
|
|
|
|
|
shift = code - BTN_TOOL_DOUBLETAP + 2;
|
|
|
|
|
break;
|
2015-04-09 14:23:16 +10:00
|
|
|
/* when QUINTTAP is released we're either switching to 6 fingers
|
|
|
|
|
(flag stays in place until BTN_TOUCH is released) or
|
|
|
|
|
one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */
|
|
|
|
|
case BTN_TOOL_QUINTTAP:
|
|
|
|
|
if (is_press)
|
|
|
|
|
tp->fake_touches |= FAKE_FINGER_OVERFLOW;
|
|
|
|
|
return;
|
2014-12-12 10:31:12 +10:00
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-09 14:23:16 +10:00
|
|
|
if (is_press) {
|
|
|
|
|
tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
|
2014-12-12 10:31:12 +10:00
|
|
|
tp->fake_touches |= 1 << shift;
|
2015-04-09 14:23:16 +10:00
|
|
|
|
|
|
|
|
} else {
|
2014-12-12 10:31:12 +10:00
|
|
|
tp->fake_touches &= ~(0x1 << shift);
|
2015-04-09 14:23:16 +10:00
|
|
|
}
|
2014-12-12 10:31:12 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2014-12-11 16:39:04 +10:00
|
|
|
if (t->state == TOUCH_BEGIN ||
|
|
|
|
|
t->state == TOUCH_UPDATE ||
|
|
|
|
|
t->state == TOUCH_HOVERING)
|
2014-07-18 11:06:36 +02:00
|
|
|
return;
|
|
|
|
|
|
2018-10-15 14:45:06 +10:00
|
|
|
/* Bug #161: touch ends in the same event frame where it restarts
|
|
|
|
|
again. That's a kernel bug, so let's complain. */
|
|
|
|
|
if (t->state == TOUCH_MAYBE_END) {
|
|
|
|
|
evdev_log_bug_kernel(tp->device,
|
|
|
|
|
"touch %d ended and began in in same frame.\n",
|
|
|
|
|
t->index);
|
|
|
|
|
tp->nfingers_down++;
|
|
|
|
|
t->state = TOUCH_UPDATE;
|
|
|
|
|
t->has_ended = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
/* we begin the touch as hovering because until BTN_TOUCH happens we
|
|
|
|
|
* don't know if it's a touch down or not. And BTN_TOUCH may happen
|
|
|
|
|
* after ABS_MT_TRACKING_ID */
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
t->dirty = true;
|
2014-12-11 16:39:04 +10:00
|
|
|
t->has_ended = false;
|
2017-01-11 16:12:47 +10:00
|
|
|
t->was_down = false;
|
2018-02-21 15:08:31 +10:00
|
|
|
t->palm.state = PALM_NONE;
|
2014-12-11 16:39:04 +10:00
|
|
|
t->state = TOUCH_HOVERING;
|
2014-07-18 11:06:36 +02:00
|
|
|
t->pinned.is_pinned = false;
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2017-06-21 12:29:52 +10:00
|
|
|
t->speed.last_speed = 0;
|
|
|
|
|
t->speed.exceeded_count = 0;
|
2018-02-21 20:16:41 +03:00
|
|
|
t->hysteresis.x_motion_history = 0;
|
2014-12-11 16:39:04 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
t->state = TOUCH_BEGIN;
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2020-04-04 12:56:21 -05:00
|
|
|
t->initial_time = time;
|
2017-01-11 16:12:47 +10:00
|
|
|
t->was_down = true;
|
2014-07-18 11:06:36 +02:00
|
|
|
tp->nfingers_down++;
|
2015-06-24 10:57:07 +10:00
|
|
|
t->palm.time = time;
|
2015-05-27 18:25:49 +10:00
|
|
|
t->tap.is_thumb = false;
|
2017-11-03 10:59:36 +10:00
|
|
|
t->tap.is_palm = false;
|
2018-10-02 10:40:47 +10:00
|
|
|
t->speed.exceeded_count = 0;
|
2014-07-18 11:06:36 +02:00
|
|
|
assert(tp->nfingers_down >= 1);
|
2017-10-26 13:56:44 +10:00
|
|
|
tp->hysteresis.last_motion_time = time;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
/**
|
2018-02-28 12:51:27 +10:00
|
|
|
* Schedule a touch to be ended, based on either the events or some
|
|
|
|
|
* attributes of the touch (size, pressure). In some cases we need to
|
|
|
|
|
* resurrect a touch that has ended, so this doesn't actually end the touch
|
|
|
|
|
* yet. All the TOUCH_MAYBE_END touches get properly ended once the device
|
|
|
|
|
* state has been processed once and we know how many zombie touches we
|
|
|
|
|
* need.
|
2014-12-11 16:39:04 +10:00
|
|
|
*/
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2014-12-11 16:39:04 +10:00
|
|
|
switch (t->state) {
|
|
|
|
|
case TOUCH_NONE:
|
2018-02-28 12:51:27 +10:00
|
|
|
case TOUCH_MAYBE_END:
|
|
|
|
|
return;
|
2014-12-11 16:39:04 +10:00
|
|
|
case TOUCH_END:
|
2018-02-28 12:51:27 +10:00
|
|
|
evdev_log_bug_libinput(tp->device,
|
|
|
|
|
"touch %d: already in TOUCH_END\n",
|
|
|
|
|
t->index);
|
2014-02-06 15:32:32 +10:00
|
|
|
return;
|
2018-03-12 10:33:21 +10:00
|
|
|
case TOUCH_HOVERING:
|
2014-12-11 16:39:04 +10:00
|
|
|
case TOUCH_BEGIN:
|
|
|
|
|
case TOUCH_UPDATE:
|
|
|
|
|
break;
|
2018-02-28 12:51:27 +10:00
|
|
|
}
|
|
|
|
|
|
2018-03-12 10:33:21 +10:00
|
|
|
if (t->state != TOUCH_HOVERING) {
|
|
|
|
|
assert(tp->nfingers_down >= 1);
|
|
|
|
|
tp->nfingers_down--;
|
|
|
|
|
t->state = TOUCH_MAYBE_END;
|
|
|
|
|
} else {
|
|
|
|
|
t->state = TOUCH_NONE;
|
|
|
|
|
}
|
2018-02-28 12:51:27 +10:00
|
|
|
|
2018-03-12 10:33:21 +10:00
|
|
|
t->dirty = true;
|
2018-02-28 12:51:27 +10:00
|
|
|
}
|
2014-12-11 16:39:04 +10:00
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
/**
|
|
|
|
|
* Inverse to tp_maybe_end_touch(), restores a touch back to its previous
|
|
|
|
|
* state.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
tp_recover_ended_touch(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
t->state = TOUCH_UPDATE;
|
|
|
|
|
tp->nfingers_down++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* End a touch, even if the touch sequence is still active.
|
|
|
|
|
* Use tp_maybe_end_touch() instead.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (t->state != TOUCH_MAYBE_END) {
|
|
|
|
|
evdev_log_bug_libinput(tp->device,
|
|
|
|
|
"touch %d should be MAYBE_END, is %d\n",
|
|
|
|
|
t->index,
|
|
|
|
|
t->state);
|
|
|
|
|
return;
|
2014-12-11 16:39:04 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
|
|
|
|
t->dirty = true;
|
2015-04-20 16:09:31 +10:00
|
|
|
t->palm.state = PALM_NONE;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_END;
|
2014-04-15 14:27:59 +02:00
|
|
|
t->pinned.is_pinned = false;
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2015-05-21 16:32:42 +10:00
|
|
|
t->palm.time = 0;
|
2018-10-02 10:40:47 +10:00
|
|
|
t->speed.exceeded_count = 0;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
/**
|
|
|
|
|
* End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
tp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
t->has_ended = true;
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(tp, t, time);
|
2014-12-11 16:39:04 +10:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 15:15:01 +10:00
|
|
|
static void
|
|
|
|
|
tp_stop_actions(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
|
|
|
|
tp_gesture_cancel(tp, time);
|
|
|
|
|
tp_tap_suspend(tp, time);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
struct device_coords
|
2015-03-11 09:58:11 +10:00
|
|
|
tp_get_delta(struct tp_touch *t)
|
2014-02-06 15:05:36 +10:00
|
|
|
{
|
2018-01-10 11:48:58 +10:00
|
|
|
struct device_coords delta;
|
|
|
|
|
const struct device_coords zero = { 0.0, 0.0 };
|
2015-03-11 09:58:11 +10:00
|
|
|
|
2016-11-21 08:39:47 +10:00
|
|
|
if (t->history.count <= 1)
|
2015-03-24 13:14:18 +01:00
|
|
|
return zero;
|
|
|
|
|
|
2017-06-21 11:16:27 +10:00
|
|
|
delta.x = tp_motion_history_offset(t, 0)->point.x -
|
|
|
|
|
tp_motion_history_offset(t, 1)->point.x;
|
|
|
|
|
delta.y = tp_motion_history_offset(t, 0)->point.y -
|
|
|
|
|
tp_motion_history_offset(t, 1)->point.y;
|
2015-03-24 13:14:18 +01:00
|
|
|
|
2018-01-10 11:48:58 +10:00
|
|
|
return delta;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
static inline int32_t
|
|
|
|
|
rotated(struct tp_dispatch *tp, unsigned int code, int value)
|
|
|
|
|
{
|
|
|
|
|
const struct input_absinfo *absinfo;
|
|
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
if (!tp->left_handed.rotate)
|
2019-04-30 14:52:28 +10:00
|
|
|
return value;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
case ABS_X:
|
|
|
|
|
case ABS_MT_POSITION_X:
|
|
|
|
|
absinfo = tp->device->abs.absinfo_x;
|
|
|
|
|
break;
|
|
|
|
|
case ABS_Y:
|
|
|
|
|
case ABS_MT_POSITION_Y:
|
|
|
|
|
absinfo = tp->device->abs.absinfo_y;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
return absinfo->maximum - (value - absinfo->minimum);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:05:36 +10:00
|
|
|
static void
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_process_absolute(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 15:05:36 +10:00
|
|
|
{
|
2014-02-06 15:32:32 +10:00
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
|
|
|
|
|
switch(e->code) {
|
|
|
|
|
case ABS_MT_POSITION_X:
|
2016-11-28 14:58:18 +10:00
|
|
|
evdev_device_check_abs_axis_range(tp->device,
|
|
|
|
|
e->code,
|
|
|
|
|
e->value);
|
2019-04-30 14:52:28 +10:00
|
|
|
t->point.x = rotated(tp, e->code, e->value);
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->dirty = true;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_MT_POSITION_Y:
|
2016-11-28 14:58:18 +10:00
|
|
|
evdev_device_check_abs_axis_range(tp->device,
|
|
|
|
|
e->code,
|
|
|
|
|
e->value);
|
2019-04-30 14:52:28 +10:00
|
|
|
t->point.y = rotated(tp, e->code, e->value);
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->dirty = true;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_MT_SLOT:
|
|
|
|
|
tp->slot = e->value;
|
|
|
|
|
break;
|
|
|
|
|
case ABS_MT_TRACKING_ID:
|
2020-01-09 10:10:31 +10:00
|
|
|
if (e->value != -1) {
|
|
|
|
|
tp->nactive_slots += 1;
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_new_touch(tp, t, time);
|
2020-01-09 10:10:31 +10:00
|
|
|
} else {
|
|
|
|
|
assert(tp->nactive_slots >= 1);
|
|
|
|
|
tp->nactive_slots -= 1;
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_end_sequence(tp, t, time);
|
2020-01-09 10:10:31 +10:00
|
|
|
}
|
2015-05-27 18:29:26 +10:00
|
|
|
break;
|
2015-05-27 18:25:49 +10:00
|
|
|
case ABS_MT_PRESSURE:
|
|
|
|
|
t->pressure = e->value;
|
2017-06-21 11:39:08 +10:00
|
|
|
t->time = time;
|
2015-07-22 12:24:47 +10:00
|
|
|
t->dirty = true;
|
2016-03-07 16:05:25 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
|
2015-05-27 18:25:49 +10:00
|
|
|
break;
|
2017-04-03 13:58:42 +10:00
|
|
|
case ABS_MT_TOOL_TYPE:
|
|
|
|
|
t->is_tool_palm = e->value == MT_TOOL_PALM;
|
2017-06-21 11:39:08 +10:00
|
|
|
t->time = time;
|
2017-04-03 13:58:42 +10:00
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
|
|
|
|
|
break;
|
2017-03-22 16:16:21 +10:00
|
|
|
case ABS_MT_TOUCH_MAJOR:
|
|
|
|
|
t->major = e->value;
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
|
|
|
|
|
break;
|
|
|
|
|
case ABS_MT_TOUCH_MINOR:
|
|
|
|
|
t->minor = e->value;
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
|
|
|
|
|
break;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-14 14:18:27 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_absolute_st(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-14 14:18:27 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
|
|
|
|
|
switch(e->code) {
|
|
|
|
|
case ABS_X:
|
2016-11-28 14:58:18 +10:00
|
|
|
evdev_device_check_abs_axis_range(tp->device,
|
|
|
|
|
e->code,
|
|
|
|
|
e->value);
|
2019-04-30 14:52:28 +10:00
|
|
|
t->point.x = rotated(tp, e->code, e->value);
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2014-02-14 14:18:27 +10:00
|
|
|
t->dirty = true;
|
2014-03-25 11:30:42 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-14 14:18:27 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_Y:
|
2016-11-28 14:58:18 +10:00
|
|
|
evdev_device_check_abs_axis_range(tp->device,
|
|
|
|
|
e->code,
|
|
|
|
|
e->value);
|
2019-04-30 14:52:28 +10:00
|
|
|
t->point.y = rotated(tp, e->code, e->value);
|
2017-06-21 11:02:48 +10:00
|
|
|
t->time = time;
|
2014-02-14 14:18:27 +10:00
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
|
|
|
|
break;
|
2017-01-10 15:58:21 +10:00
|
|
|
case ABS_PRESSURE:
|
|
|
|
|
t->pressure = e->value;
|
2017-06-21 11:39:08 +10:00
|
|
|
t->time = time;
|
2017-01-10 15:58:21 +10:00
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
|
|
|
|
|
break;
|
2014-02-14 14:18:27 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 09:30:03 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_restore_synaptics_touches(struct tp_dispatch *tp,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int nfake_touches;
|
|
|
|
|
|
|
|
|
|
nfake_touches = tp_fake_finger_count(tp);
|
|
|
|
|
if (nfake_touches < 3)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tp->nfingers_down >= nfake_touches ||
|
2018-08-02 16:58:58 +10:00
|
|
|
(tp->nfingers_down == tp->num_slots && nfake_touches == tp->num_slots))
|
2015-07-17 09:30:03 +10:00
|
|
|
return;
|
|
|
|
|
|
2020-02-27 14:46:10 +10:00
|
|
|
/* Synaptics devices may end touch 2 on transition to/fro
|
|
|
|
|
* BTN_TOOL_TRIPLETAP and start it again on the next frame with
|
|
|
|
|
* different coordinates (bz#91352, gitlab#434). We search the
|
|
|
|
|
* touches we have, if there is one that has just ended despite us
|
|
|
|
|
* being on tripletap, we move it back to update.
|
|
|
|
|
*
|
|
|
|
|
* Note: we only handle the transition from 2 to 3 touches, not the
|
|
|
|
|
* other way round (see gitlab#434)
|
2015-07-17 09:30:03 +10:00
|
|
|
*/
|
|
|
|
|
for (i = 0; i < tp->num_slots; i++) {
|
|
|
|
|
struct tp_touch *t = tp_get_touch(tp, i);
|
|
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
if (t->state != TOUCH_MAYBE_END)
|
2015-07-17 09:30:03 +10:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* new touch, move it through begin to update immediately */
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_recover_ended_touch(tp, t);
|
2015-07-17 09:30:03 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 15:12:22 +10:00
|
|
|
static void
|
2015-04-08 17:00:50 +10:00
|
|
|
tp_process_fake_touches(struct tp_dispatch *tp,
|
|
|
|
|
uint64_t time)
|
2014-02-14 15:12:22 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
unsigned int nfake_touches;
|
2014-07-18 11:06:38 +02:00
|
|
|
unsigned int i, start;
|
2014-02-14 15:12:22 +10:00
|
|
|
|
2014-12-12 10:31:12 +10:00
|
|
|
nfake_touches = tp_fake_finger_count(tp);
|
2015-04-09 14:23:16 +10:00
|
|
|
if (nfake_touches == FAKE_FINGER_OVERFLOW)
|
|
|
|
|
return;
|
2014-02-14 15:12:22 +10:00
|
|
|
|
2015-07-17 09:30:03 +10:00
|
|
|
if (tp->device->model_flags &
|
|
|
|
|
EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
|
|
|
|
|
tp_restore_synaptics_touches(tp, time);
|
|
|
|
|
|
2020-02-27 14:48:28 +10:00
|
|
|
/* ALPS serial touchpads always set 3 slots in the kernel, even
|
2020-01-09 10:10:31 +10:00
|
|
|
* where they support less than that. So we get BTN_TOOL_TRIPLETAP
|
|
|
|
|
* but never slot 2 because our slot count is wrong.
|
|
|
|
|
* This also means that the third touch falls through the cracks and
|
|
|
|
|
* is ignored.
|
|
|
|
|
*
|
2020-02-27 14:48:28 +10:00
|
|
|
* See https://gitlab.freedesktop.org/libinput/libinput/issues/408
|
|
|
|
|
*
|
2020-01-09 10:10:31 +10:00
|
|
|
* All touchpad devices have at least one slot so we only do this
|
|
|
|
|
* for 2 touches or higher.
|
2020-02-14 16:20:32 +10:00
|
|
|
*
|
|
|
|
|
* There's an bug in libevdev < 1.9.0 affecting slots after a
|
|
|
|
|
* SYN_DROPPED. Where a user release one or more touches during
|
|
|
|
|
* SYN_DROPPED and places new ones on the touchpad, we may end up
|
|
|
|
|
* with fake touches but no active slots.
|
|
|
|
|
* So let's check for nactive_slots > 0 to make sure we don't lose
|
|
|
|
|
* all fingers. That's a workaround only, this must be fixed in
|
|
|
|
|
* libevdev.
|
|
|
|
|
*
|
|
|
|
|
* For a long explanation of what happens, see
|
|
|
|
|
* https://gitlab.freedesktop.org/libevdev/libevdev/merge_requests/19
|
2020-01-09 10:10:31 +10:00
|
|
|
*/
|
2020-02-27 14:48:28 +10:00
|
|
|
if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD &&
|
|
|
|
|
nfake_touches > 1 && tp->has_mt &&
|
2020-02-14 16:20:32 +10:00
|
|
|
tp->nactive_slots > 0 &&
|
2020-01-09 10:10:31 +10:00
|
|
|
nfake_touches > tp->nactive_slots &&
|
|
|
|
|
tp->nactive_slots < tp->num_slots) {
|
|
|
|
|
evdev_log_bug_kernel(tp->device,
|
|
|
|
|
"Wrong slot count (%d), reducing to %d\n",
|
|
|
|
|
tp->num_slots,
|
|
|
|
|
tp->nactive_slots);
|
|
|
|
|
/* This should be safe since we fill the slots from the
|
|
|
|
|
* first one so hiding the excessive slots shouldn't matter.
|
|
|
|
|
* There are sequences where we could accidentally lose an
|
|
|
|
|
* actual touch point but that requires specially crafted
|
|
|
|
|
* sequences and let's deal with that when it happens.
|
|
|
|
|
*/
|
|
|
|
|
tp->num_slots = tp->nactive_slots;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-04-09 15:27:34 +10:00
|
|
|
start = tp->has_mt ? tp->num_slots : 0;
|
2014-07-18 11:06:38 +02:00
|
|
|
for (i = start; i < tp->ntouches; i++) {
|
2014-02-14 15:12:22 +10:00
|
|
|
t = tp_get_touch(tp, i);
|
2014-07-18 11:06:38 +02:00
|
|
|
if (i < nfake_touches)
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_new_touch(tp, t, time);
|
2014-07-18 11:06:38 +02:00
|
|
|
else
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_end_sequence(tp, t, time);
|
2014-02-14 15:12:22 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-28 15:38:00 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_trackpoint_button(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_dispatch *dispatch;
|
|
|
|
|
struct input_event event;
|
2018-01-15 16:16:37 -08:00
|
|
|
struct input_event syn_report = {
|
|
|
|
|
.input_event_sec = 0,
|
|
|
|
|
.input_event_usec = 0,
|
|
|
|
|
.type = EV_SYN,
|
|
|
|
|
.code = SYN_REPORT,
|
|
|
|
|
.value = 0
|
|
|
|
|
};
|
2015-01-28 15:38:00 +10:00
|
|
|
|
2015-07-23 14:27:59 +10:00
|
|
|
if (!tp->buttons.trackpoint)
|
2015-01-28 15:38:00 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dispatch = tp->buttons.trackpoint->dispatch;
|
|
|
|
|
|
|
|
|
|
event = *e;
|
2018-01-15 16:16:37 -08:00
|
|
|
syn_report.input_event_sec = e->input_event_sec;
|
|
|
|
|
syn_report.input_event_usec = e->input_event_usec;
|
2015-01-28 15:38:00 +10:00
|
|
|
|
|
|
|
|
switch (event.code) {
|
|
|
|
|
case BTN_0:
|
|
|
|
|
event.code = BTN_LEFT;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_1:
|
|
|
|
|
event.code = BTN_RIGHT;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_2:
|
|
|
|
|
event.code = BTN_MIDDLE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch->interface->process(dispatch,
|
|
|
|
|
tp->buttons.trackpoint,
|
|
|
|
|
&event, time);
|
2017-11-13 13:29:00 +10:00
|
|
|
dispatch->interface->process(dispatch,
|
|
|
|
|
tp->buttons.trackpoint,
|
|
|
|
|
&syn_report, time);
|
2015-01-28 15:38:00 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_key(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2020-06-02 10:52:11 +10:00
|
|
|
/* ignore kernel key repeat */
|
|
|
|
|
if (e->value == 2)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
switch (e->code) {
|
|
|
|
|
case BTN_LEFT:
|
|
|
|
|
case BTN_MIDDLE:
|
|
|
|
|
case BTN_RIGHT:
|
2014-03-27 13:09:06 +10:00
|
|
|
tp_process_button(tp, e, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
2014-02-14 14:18:27 +10:00
|
|
|
case BTN_TOUCH:
|
2014-12-11 16:39:04 +10:00
|
|
|
case BTN_TOOL_FINGER:
|
2014-02-14 15:12:22 +10:00
|
|
|
case BTN_TOOL_DOUBLETAP:
|
|
|
|
|
case BTN_TOOL_TRIPLETAP:
|
|
|
|
|
case BTN_TOOL_QUADTAP:
|
2015-04-09 14:23:16 +10:00
|
|
|
case BTN_TOOL_QUINTTAP:
|
2015-04-08 17:00:50 +10:00
|
|
|
tp_fake_finger_set(tp, e->code, !!e->value);
|
2014-02-14 14:18:27 +10:00
|
|
|
break;
|
2015-01-28 15:38:00 +10:00
|
|
|
case BTN_0:
|
|
|
|
|
case BTN_1:
|
|
|
|
|
case BTN_2:
|
|
|
|
|
tp_process_trackpoint_button(tp, e, time);
|
|
|
|
|
break;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
touchpad: add timestamp-based jump detection
On Dell i2c touchpads, the controller appears to go to sleep after about 1s of
inactivity on the touchpad. The wakeup takes a while so on the next touch, we
may see a pointer jump, specifially on the third event (i.e. touch down,
event, event+jump). The MSC_TIMESTAMP value carries a hint for what's
happening here, the event sequence for a touchpad with scanout intervals
7300µs is:
...
MSC_TIMESTAMP 0
SYN_REPORT
...
MSC_TIMESTAMP 7300
SYN_REPORT +2ms
...
MSC_TIMESTAMP 123456
SYN_REPORT +7ms
...
MSC_TIMESTAMP 123456+7300
SYN_REPORT +8ms
Note how the SYN_REPORT timestamps don't reflect the MSC_TIMESTAMPS.
This patch adds a quirk activate MSC_TIMESTAMP watching. When we do so, we
monitor for a 0 MSC_TIMESTAMP. Let's assume that the first event after that is
the interval, then check the third event. If that third event's timestamp is too
large rewrite the touches' motion history to reflect the correct timestamps,
i.e. instead of the SYN_REPORT timestamps the motion history now uses
"third-event SYN_REPORT timestamps minus MSC_TIMESTAMP values".
The pointer accel filter code uses absolute timestamps (#123) so we have to
restart the pointer acceleration filter when we detect this jump. This allows
us to reset the 0 time for the filter to the previous event's MSC_TIMESTAMP
time, so that our new large delta has the correct time delta too. This
calculates the acceleration correctly for that window.
The result is that the pointer is still delayed by the wake-up window (not
fixable in libinput) but at least it ends up where it should've.
There are a few side-effects: thumb, gesture, and hysteresis all still use the
unmodified SYN_REPORT time. There is a potential for false detection of either
of these now, but we'll have to fix those as they come up.
Fixes #36
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-21 15:43:38 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_msc(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (e->code != MSC_TIMESTAMP)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tp->quirks.msc_timestamp.now = e->value;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_TIMESTAMP;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-17 14:24:20 +10:00
|
|
|
static void
|
2016-12-13 09:54:30 +10:00
|
|
|
tp_unpin_finger(const struct tp_dispatch *tp, struct tp_touch *t)
|
2014-02-17 14:24:20 +10:00
|
|
|
{
|
2016-12-13 10:01:02 +10:00
|
|
|
struct phys_coords mm;
|
|
|
|
|
struct device_coords delta;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
if (!t->pinned.is_pinned)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-12-13 10:01:02 +10:00
|
|
|
delta.x = abs(t->point.x - t->pinned.center.x);
|
|
|
|
|
delta.y = abs(t->point.y - t->pinned.center.y);
|
|
|
|
|
|
|
|
|
|
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2015-07-20 11:24:17 +10:00
|
|
|
/* 1.5mm movement -> unpin */
|
2016-12-13 10:01:02 +10:00
|
|
|
if (hypot(mm.x, mm.y) >= 1.5) {
|
2014-04-04 17:22:02 +02:00
|
|
|
t->pinned.is_pinned = false;
|
2015-01-14 10:17:06 +01:00
|
|
|
return;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_pin_fingers(struct tp_dispatch *tp)
|
2014-02-17 14:24:20 +10:00
|
|
|
{
|
2014-04-15 14:27:59 +02:00
|
|
|
struct tp_touch *t;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-04-15 14:27:59 +02:00
|
|
|
t->pinned.is_pinned = true;
|
2015-03-11 09:24:52 +10:00
|
|
|
t->pinned.center = t->point;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
bool
|
2016-01-07 13:04:58 +10:00
|
|
|
tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
|
2014-04-04 17:22:02 +02:00
|
|
|
{
|
|
|
|
|
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
|
2015-04-20 16:09:31 +10:00
|
|
|
t->palm.state == PALM_NONE &&
|
2014-11-24 12:16:06 +01:00
|
|
|
!t->pinned.is_pinned &&
|
2019-06-18 10:16:33 +10:00
|
|
|
!tp_thumb_ignored(tp, t) &&
|
2014-11-24 12:16:06 +01:00
|
|
|
tp_button_touch_active(tp, t) &&
|
|
|
|
|
tp_edge_scroll_touch_active(tp, t);
|
2014-04-04 17:22:02 +02:00
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2019-06-18 10:21:13 +10:00
|
|
|
bool
|
|
|
|
|
tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
|
|
|
|
|
t->palm.state == PALM_NONE &&
|
|
|
|
|
!t->pinned.is_pinned &&
|
2019-06-21 13:19:22 +10:00
|
|
|
!tp_thumb_ignored_for_gesture(tp, t) &&
|
2019-06-18 10:21:13 +10:00
|
|
|
tp_button_touch_active(tp, t) &&
|
|
|
|
|
tp_edge_scroll_touch_active(tp, t);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-02 18:07:20 +08:00
|
|
|
static inline bool
|
|
|
|
|
tp_palm_was_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return t->palm.first.x < tp->palm.left_edge ||
|
|
|
|
|
t->palm.first.x > tp->palm.right_edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_palm_was_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return t->palm.first.y < tp->palm.upper_edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_palm_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return t->point.x < tp->palm.left_edge ||
|
|
|
|
|
t->point.x > tp->palm.right_edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_palm_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return t->point.y < tp->palm.upper_edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_palm_in_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return tp_palm_in_side_edge(tp, t) || tp_palm_in_top_edge(tp, t);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-15 15:21:08 +10:00
|
|
|
bool
|
2016-01-07 13:04:58 +10:00
|
|
|
tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t)
|
2015-04-15 15:21:08 +10:00
|
|
|
{
|
|
|
|
|
if (t->state != TOUCH_BEGIN)
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-07-02 18:07:20 +08:00
|
|
|
if (!tp_palm_in_edge(tp, t))
|
2015-04-15 15:21:08 +10:00
|
|
|
return false;
|
|
|
|
|
|
2018-02-20 13:27:10 +10:00
|
|
|
evdev_log_debug(tp->device,
|
|
|
|
|
"palm: touch %d: palm-tap detected\n",
|
|
|
|
|
t->index);
|
2017-05-09 15:07:20 +10:00
|
|
|
return true;
|
2015-04-15 15:21:08 +10:00
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
static bool
|
|
|
|
|
tp_palm_detect_dwt_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2015-05-21 13:30:24 +10:00
|
|
|
{
|
2015-07-09 08:14:35 +10:00
|
|
|
if (tp->dwt.dwt_enabled &&
|
|
|
|
|
tp->dwt.keyboard_active &&
|
2015-05-21 16:32:42 +10:00
|
|
|
t->state == TOUCH_BEGIN) {
|
2015-05-21 13:30:24 +10:00
|
|
|
t->palm.state = PALM_TYPING;
|
|
|
|
|
t->palm.first = t->point;
|
2016-07-19 10:05:37 +10:00
|
|
|
return true;
|
2015-05-21 16:32:42 +10:00
|
|
|
} else if (!tp->dwt.keyboard_active &&
|
|
|
|
|
t->state == TOUCH_UPDATE &&
|
2015-07-09 09:32:40 +10:00
|
|
|
t->palm.state == PALM_TYPING) {
|
2015-05-21 16:32:42 +10:00
|
|
|
/* If a touch has started before the first or after the last
|
|
|
|
|
key press, release it on timeout. Benefit: a palm rested
|
|
|
|
|
while typing on the touchpad will be ignored, but a touch
|
|
|
|
|
started once we stop typing will be able to control the
|
|
|
|
|
pointer (alas not tap, etc.).
|
|
|
|
|
*/
|
|
|
|
|
if (t->palm.time == 0 ||
|
|
|
|
|
t->palm.time > tp->dwt.keyboard_last_press_time) {
|
|
|
|
|
t->palm.state = PALM_NONE;
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d released, timeout after typing\n",
|
|
|
|
|
t->index);
|
2015-05-21 16:32:42 +10:00
|
|
|
}
|
2015-05-21 13:30:24 +10:00
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
return false;
|
2015-05-21 13:30:24 +10:00
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
static bool
|
|
|
|
|
tp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2015-06-23 15:36:05 +10:00
|
|
|
{
|
2015-06-24 11:41:47 +10:00
|
|
|
if (!tp->palm.monitor_trackpoint)
|
2016-07-19 10:05:37 +10:00
|
|
|
return false;
|
2015-06-24 11:41:47 +10:00
|
|
|
|
2015-06-23 15:36:05 +10:00
|
|
|
if (t->palm.state == PALM_NONE &&
|
|
|
|
|
t->state == TOUCH_BEGIN &&
|
|
|
|
|
tp->palm.trackpoint_active) {
|
|
|
|
|
t->palm.state = PALM_TRACKPOINT;
|
2016-07-19 10:05:37 +10:00
|
|
|
return true;
|
2015-06-23 15:36:05 +10:00
|
|
|
} else if (t->palm.state == PALM_TRACKPOINT &&
|
|
|
|
|
t->state == TOUCH_UPDATE &&
|
|
|
|
|
!tp->palm.trackpoint_active) {
|
|
|
|
|
|
|
|
|
|
if (t->palm.time == 0 ||
|
|
|
|
|
t->palm.time > tp->palm.trackpoint_last_event_time) {
|
|
|
|
|
t->palm.state = PALM_NONE;
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d released, timeout after trackpoint\n", t->index);
|
2015-06-23 15:36:05 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
return false;
|
2015-06-23 15:36:05 +10:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 13:58:42 +10:00
|
|
|
static bool
|
|
|
|
|
tp_palm_detect_tool_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (!tp->palm.use_mt_tool)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (t->palm.state != PALM_NONE &&
|
|
|
|
|
t->palm.state != PALM_TOOL_PALM)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (t->palm.state == PALM_NONE &&
|
|
|
|
|
t->is_tool_palm)
|
|
|
|
|
t->palm.state = PALM_TOOL_PALM;
|
|
|
|
|
else if (t->palm.state == PALM_TOOL_PALM &&
|
|
|
|
|
!t->is_tool_palm)
|
|
|
|
|
t->palm.state = PALM_NONE;
|
|
|
|
|
|
|
|
|
|
return t->palm.state == PALM_TOOL_PALM;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 09:07:48 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2014-07-10 17:34:08 +10:00
|
|
|
{
|
2015-07-27 17:51:52 +08:00
|
|
|
const int PALM_TIMEOUT = ms2us(200);
|
2017-07-02 18:07:20 +08:00
|
|
|
int directions = 0;
|
2015-03-25 15:05:19 +01:00
|
|
|
struct device_float_coords delta;
|
|
|
|
|
int dirs;
|
2014-07-14 16:06:51 +10:00
|
|
|
|
2017-07-02 18:07:20 +08:00
|
|
|
if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) {
|
|
|
|
|
if (tp_palm_was_in_side_edge(tp, t))
|
|
|
|
|
directions = NE|E|SE|SW|W|NW;
|
|
|
|
|
else if (tp_palm_was_in_top_edge(tp, t))
|
|
|
|
|
directions = S|SE|SW;
|
|
|
|
|
|
|
|
|
|
if (directions) {
|
|
|
|
|
delta = device_delta(t->point, t->palm.first);
|
|
|
|
|
dirs = phys_get_direction(tp_phys_delta(tp, delta));
|
|
|
|
|
if ((dirs & directions) && !(dirs & ~directions))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-06-15 09:07:48 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-13 16:41:43 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *other;
|
|
|
|
|
|
|
|
|
|
if (tp->nfingers_down < 2)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* If we have at least one other active non-palm touch make this
|
|
|
|
|
* touch non-palm too. This avoids palm detection during two-finger
|
|
|
|
|
* scrolling.
|
|
|
|
|
*
|
|
|
|
|
* Note: if both touches start in the palm zone within the same
|
|
|
|
|
* frame the second touch will still be PALM_NONE and thus detected
|
|
|
|
|
* here as non-palm touch. This is too niche to worry about for now.
|
|
|
|
|
*/
|
|
|
|
|
tp_for_each_touch(tp, other) {
|
|
|
|
|
if (other == t)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (tp_touch_active(tp, other) &&
|
|
|
|
|
other->palm.state == PALM_NONE) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-23 14:54:00 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_palm_detect_touch_size_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (!tp->palm.use_size)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* If a finger size is large enough for palm, we stick with that and
|
|
|
|
|
* force the user to release and reset the finger */
|
|
|
|
|
if (t->palm.state != PALM_NONE && t->palm.state != PALM_TOUCH_SIZE)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (t->major > tp->palm.size_threshold ||
|
|
|
|
|
t->minor > tp->palm.size_threshold) {
|
2017-10-19 12:14:47 +10:00
|
|
|
if (t->palm.state != PALM_TOUCH_SIZE)
|
|
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d size exceeded\n",
|
|
|
|
|
t->index);
|
2017-03-23 14:54:00 +10:00
|
|
|
t->palm.state = PALM_TOUCH_SIZE;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_palm_detect_edge(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2016-06-15 09:07:48 +10:00
|
|
|
{
|
2015-04-20 16:09:31 +10:00
|
|
|
if (t->palm.state == PALM_EDGE) {
|
2016-06-13 16:41:43 +10:00
|
|
|
if (tp_palm_detect_multifinger(tp, t, time)) {
|
|
|
|
|
t->palm.state = PALM_NONE;
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d released, multiple fingers\n",
|
|
|
|
|
t->index);
|
2016-06-13 16:41:43 +10:00
|
|
|
|
|
|
|
|
/* If labelled a touch as palm, we unlabel as palm when
|
|
|
|
|
we move out of the palm edge zone within the timeout, provided
|
|
|
|
|
the direction is within 45 degrees of the horizontal.
|
|
|
|
|
*/
|
|
|
|
|
} else if (tp_palm_detect_move_out_of_edge(tp, t, time)) {
|
2016-06-15 09:07:48 +10:00
|
|
|
t->palm.state = PALM_NONE;
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d released, out of edge zone\n",
|
|
|
|
|
t->index);
|
2014-07-14 16:06:51 +10:00
|
|
|
}
|
2017-03-23 13:46:51 +10:00
|
|
|
return false;
|
2016-06-13 16:41:43 +10:00
|
|
|
} else if (tp_palm_detect_multifinger(tp, t, time)) {
|
2017-03-23 13:46:51 +10:00
|
|
|
return false;
|
2014-07-14 16:06:51 +10:00
|
|
|
}
|
2014-07-10 17:34:08 +10:00
|
|
|
|
|
|
|
|
/* palm must start in exclusion zone, it's ok to move into
|
|
|
|
|
the zone without being a palm */
|
2017-07-02 18:07:20 +08:00
|
|
|
if (t->state != TOUCH_BEGIN || !tp_palm_in_edge(tp, t))
|
2017-03-23 13:46:51 +10:00
|
|
|
return false;
|
2014-07-10 17:34:08 +10:00
|
|
|
|
|
|
|
|
/* don't detect palm in software button areas, it's
|
|
|
|
|
likely that legitimate touches start in the area
|
|
|
|
|
covered by the exclusion zone */
|
|
|
|
|
if (tp->buttons.is_clickpad &&
|
|
|
|
|
tp_button_is_inside_softbutton_area(tp, t))
|
2017-03-23 13:46:51 +10:00
|
|
|
return false;
|
2014-07-10 17:34:08 +10:00
|
|
|
|
2015-06-16 15:39:48 +10:00
|
|
|
if (tp_touch_get_edge(tp, t) & EDGE_RIGHT)
|
2017-03-23 13:46:51 +10:00
|
|
|
return false;
|
2015-06-16 15:39:48 +10:00
|
|
|
|
2015-04-20 16:09:31 +10:00
|
|
|
t->palm.state = PALM_EDGE;
|
2014-07-14 16:06:51 +10:00
|
|
|
t->palm.time = time;
|
2015-03-11 09:24:52 +10:00
|
|
|
t->palm.first = t->point;
|
2015-05-22 15:14:04 +10:00
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-28 13:09:22 +10:00
|
|
|
static bool
|
|
|
|
|
tp_palm_detect_pressure_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (!tp->palm.use_pressure)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (t->palm.state != PALM_NONE &&
|
|
|
|
|
t->palm.state != PALM_PRESSURE)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (t->pressure > tp->palm.pressure_threshold)
|
|
|
|
|
t->palm.state = PALM_PRESSURE;
|
|
|
|
|
|
|
|
|
|
return t->palm.state == PALM_PRESSURE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-19 14:19:53 +10:00
|
|
|
static bool
|
|
|
|
|
tp_palm_detect_arbitration_triggered(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2018-09-19 12:02:51 +10:00
|
|
|
if (tp->arbitration.state == ARBITRATION_NOT_ACTIVE)
|
2018-02-19 14:19:53 +10:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
t->palm.state = PALM_ARBITRATION;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
static void
|
|
|
|
|
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
2017-03-23 13:55:32 +10:00
|
|
|
const char *palm_state;
|
|
|
|
|
enum touch_palm_state oldstate = t->palm.state;
|
2017-03-23 13:46:51 +10:00
|
|
|
|
2017-06-28 13:09:22 +10:00
|
|
|
if (tp_palm_detect_pressure_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2018-02-19 14:19:53 +10:00
|
|
|
if (tp_palm_detect_arbitration_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
if (tp_palm_detect_dwt_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (tp_palm_detect_trackpoint_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-04-03 13:58:42 +10:00
|
|
|
if (tp_palm_detect_tool_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-03-23 14:54:00 +10:00
|
|
|
if (tp_palm_detect_touch_size_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
if (tp_palm_detect_edge(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-06-28 13:09:22 +10:00
|
|
|
/* Pressure is highest priority because it cannot be released and
|
|
|
|
|
* overrides all other checks. So we check once before anything else
|
|
|
|
|
* in case pressure triggers on a non-palm touch. And again after
|
|
|
|
|
* everything in case one of the others released but we have a
|
|
|
|
|
* pressure trigger now.
|
|
|
|
|
*/
|
|
|
|
|
if (tp_palm_detect_pressure_triggered(tp, t, time))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2017-03-23 13:46:51 +10:00
|
|
|
return;
|
2015-05-22 15:14:04 +10:00
|
|
|
out:
|
2017-06-28 13:09:22 +10:00
|
|
|
|
2017-03-23 13:55:32 +10:00
|
|
|
if (oldstate == t->palm.state)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (t->palm.state) {
|
|
|
|
|
case PALM_EDGE:
|
|
|
|
|
palm_state = "edge";
|
|
|
|
|
break;
|
|
|
|
|
case PALM_TYPING:
|
|
|
|
|
palm_state = "typing";
|
|
|
|
|
break;
|
|
|
|
|
case PALM_TRACKPOINT:
|
|
|
|
|
palm_state = "trackpoint";
|
|
|
|
|
break;
|
2017-04-03 13:58:42 +10:00
|
|
|
case PALM_TOOL_PALM:
|
|
|
|
|
palm_state = "tool-palm";
|
|
|
|
|
break;
|
2017-06-28 13:09:22 +10:00
|
|
|
case PALM_PRESSURE:
|
|
|
|
|
palm_state = "pressure";
|
|
|
|
|
break;
|
2017-03-23 14:54:00 +10:00
|
|
|
case PALM_TOUCH_SIZE:
|
|
|
|
|
palm_state = "touch size";
|
|
|
|
|
break;
|
2018-02-19 14:19:53 +10:00
|
|
|
case PALM_ARBITRATION:
|
|
|
|
|
palm_state = "arbitration";
|
|
|
|
|
break;
|
2017-03-23 13:55:32 +10:00
|
|
|
case PALM_NONE:
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"palm: touch %d, palm detected (%s)\n",
|
|
|
|
|
t->index,
|
2017-03-23 13:55:32 +10:00
|
|
|
palm_state);
|
2014-07-10 17:34:08 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
static void
|
2017-01-10 15:58:21 +10:00
|
|
|
tp_unhover_pressure(struct tp_dispatch *tp, uint64_t time)
|
2015-05-06 19:41:25 -04:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
2017-01-10 15:58:21 +10:00
|
|
|
int i;
|
|
|
|
|
unsigned int nfake_touches;
|
|
|
|
|
unsigned int real_fingers_down = 0;
|
2015-05-06 19:41:25 -04:00
|
|
|
|
2017-01-10 15:58:21 +10:00
|
|
|
nfake_touches = tp_fake_finger_count(tp);
|
|
|
|
|
if (nfake_touches == FAKE_FINGER_OVERFLOW)
|
|
|
|
|
nfake_touches = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < (int)tp->num_slots; i++) {
|
2015-05-06 19:41:25 -04:00
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
2017-03-29 10:36:30 +10:00
|
|
|
if (t->state == TOUCH_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
|
2017-01-10 15:58:21 +10:00
|
|
|
if (t->dirty) {
|
|
|
|
|
if (t->state == TOUCH_HOVERING) {
|
|
|
|
|
if (t->pressure >= tp->pressure.high) {
|
2017-03-01 13:42:09 +10:00
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"pressure: begin touch %d\n",
|
|
|
|
|
t->index);
|
2017-01-10 15:58:21 +10:00
|
|
|
/* avoid jumps when landing a finger */
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
|
|
|
|
}
|
2018-02-28 11:38:12 +10:00
|
|
|
/* don't unhover for pressure if we have too many
|
touchpad: only keep low-pressure fingers alive for 2+-slot touchpads
Regression introduced by 3979b9e16a5ed141506d95f80ddfd7b94651dcfa, bug 105258.
With that commit, we only ended real touches when we had less than nslots fake
fingers down. i.e. tripletap on a 2 slot touchpad would not end the
first/second touch even if the pressure goes below the threshold. e.g. Lenovo
x270 needs this, see https://bugs.freedesktop.org/attachment.cgi?id=137672, it
dips below the pressure threshold for the first slot and ends the second slot
in the same frame as the third finger is detected. Fun times.
Anyway, this breaks semi-mt touchpads, another fine category of devices,
because some of those can detect hovering fingers at low pressure, see bug
105535. Because semi-mt devices are generally garbage, we treat them as
single-touch devices instead. So whenever two fingers are down, we treat both
as above the pressure threshold, even when they're physicall hovering.
Fix this by making the x270 fix conditional on at least 2 slots.
https://bugs.freedesktop.org/show_bug.cgi?id=105535
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-03-21 10:24:12 +10:00
|
|
|
* fake fingers down, see comment below. Except
|
|
|
|
|
* for single-finger touches where the real touch
|
|
|
|
|
* decides for the rest.
|
|
|
|
|
*/
|
|
|
|
|
} else if (nfake_touches <= tp->num_slots ||
|
|
|
|
|
tp->num_slots == 1) {
|
2017-03-01 13:42:09 +10:00
|
|
|
if (t->pressure < tp->pressure.low) {
|
|
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"pressure: end touch %d\n",
|
|
|
|
|
t->index);
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(tp, t, time);
|
2017-03-01 13:42:09 +10:00
|
|
|
}
|
2017-01-10 15:58:21 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (t->state == TOUCH_BEGIN ||
|
|
|
|
|
t->state == TOUCH_UPDATE)
|
|
|
|
|
real_fingers_down++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nfake_touches <= tp->num_slots ||
|
|
|
|
|
tp->nfingers_down == 0)
|
|
|
|
|
return;
|
2016-01-18 17:04:39 +10:00
|
|
|
|
2017-01-10 15:58:21 +10:00
|
|
|
/* if we have more fake fingers down than slots, we assume
|
|
|
|
|
* _all_ fingers have enough pressure, even if some of the slotted
|
|
|
|
|
* ones don't. Anything else gets insane quickly.
|
|
|
|
|
*/
|
2018-02-20 13:27:57 +10:00
|
|
|
if (real_fingers_down > 0) {
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (t->state == TOUCH_HOVERING) {
|
|
|
|
|
/* avoid jumps when landing a finger */
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
2017-01-10 15:58:21 +10:00
|
|
|
|
2018-02-20 13:27:57 +10:00
|
|
|
if (tp->nfingers_down >= nfake_touches)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-01-10 15:58:21 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tp->nfingers_down > nfake_touches ||
|
|
|
|
|
real_fingers_down == 0) {
|
|
|
|
|
for (i = tp->ntouches - 1; i >= 0; i--) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
|
|
|
|
if (t->state == TOUCH_HOVERING ||
|
2018-02-28 12:51:27 +10:00
|
|
|
t->state == TOUCH_NONE ||
|
|
|
|
|
t->state == TOUCH_MAYBE_END)
|
2017-01-10 15:58:21 +10:00
|
|
|
continue;
|
|
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(tp, t, time);
|
2017-01-10 15:58:21 +10:00
|
|
|
|
|
|
|
|
if (real_fingers_down > 0 &&
|
|
|
|
|
tp->nfingers_down == nfake_touches)
|
|
|
|
|
break;
|
2015-05-06 19:41:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-22 16:16:21 +10:00
|
|
|
static void
|
|
|
|
|
tp_unhover_size(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
int low = tp->touch_size.low,
|
|
|
|
|
high = tp->touch_size.high;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* We require 5 slots for size handling, so we don't need to care
|
|
|
|
|
* about fake touches here */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < (int)tp->num_slots; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
|
|
|
|
if (t->state == TOUCH_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (t->state == TOUCH_HOVERING) {
|
|
|
|
|
if ((t->major > high && t->minor > low) ||
|
|
|
|
|
(t->major > low && t->minor > high)) {
|
|
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"touch-size: begin touch %d\n",
|
|
|
|
|
t->index);
|
2017-03-22 16:16:21 +10:00
|
|
|
/* avoid jumps when landing a finger */
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (t->major < low || t->minor < low) {
|
|
|
|
|
evdev_log_debug(tp->device,
|
2018-02-20 13:27:10 +10:00
|
|
|
"touch-size: end touch %d\n",
|
|
|
|
|
t->index);
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(tp, t, time);
|
2017-03-22 16:16:21 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 19:41:25 -04:00
|
|
|
static void
|
|
|
|
|
tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
|
2014-12-11 16:39:04 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
unsigned int nfake_touches;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!tp->fake_touches && !tp->nfingers_down)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
nfake_touches = tp_fake_finger_count(tp);
|
2015-04-09 14:23:16 +10:00
|
|
|
if (nfake_touches == FAKE_FINGER_OVERFLOW)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
if (tp->nfingers_down == nfake_touches &&
|
|
|
|
|
((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) ||
|
|
|
|
|
(tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp))))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* if BTN_TOUCH is set and we have less fingers down than fake
|
|
|
|
|
* touches, switch each hovering touch to BEGIN
|
|
|
|
|
* until nfingers_down matches nfake_touches
|
|
|
|
|
*/
|
|
|
|
|
if (tp_fake_finger_is_touching(tp) &&
|
|
|
|
|
tp->nfingers_down < nfake_touches) {
|
2017-05-02 22:54:03 -03:00
|
|
|
tp_for_each_touch(tp, t) {
|
2014-12-11 16:39:04 +10:00
|
|
|
if (t->state == TOUCH_HOVERING) {
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
|
|
|
|
|
|
|
|
|
if (tp->nfingers_down >= nfake_touches)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if BTN_TOUCH is unset end all touches, we're hovering now. If we
|
|
|
|
|
* have too many touches also end some of them. This is done in
|
|
|
|
|
* reverse order.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->nfingers_down > nfake_touches ||
|
|
|
|
|
!tp_fake_finger_is_touching(tp)) {
|
|
|
|
|
for (i = tp->ntouches - 1; i >= 0; i--) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
2015-04-09 15:27:33 +10:00
|
|
|
if (t->state == TOUCH_HOVERING ||
|
|
|
|
|
t->state == TOUCH_NONE)
|
2014-12-11 16:39:04 +10:00
|
|
|
continue;
|
|
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_maybe_end_touch(tp, t, time);
|
2014-12-11 16:39:04 +10:00
|
|
|
|
|
|
|
|
if (tp_fake_finger_is_touching(tp) &&
|
|
|
|
|
tp->nfingers_down == nfake_touches)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 19:41:25 -04:00
|
|
|
static void
|
|
|
|
|
tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
2017-01-10 15:58:21 +10:00
|
|
|
if (tp->pressure.use_pressure)
|
|
|
|
|
tp_unhover_pressure(tp, time);
|
2017-03-22 16:16:21 +10:00
|
|
|
else if (tp->touch_size.use_touch_size)
|
|
|
|
|
tp_unhover_size(tp, time);
|
2015-05-06 19:41:25 -04:00
|
|
|
else
|
|
|
|
|
tp_unhover_fake_touches(tp, time);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 16:40:50 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_position_fake_touches(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
struct tp_touch *topmost = NULL;
|
|
|
|
|
unsigned int start, i;
|
|
|
|
|
|
2015-12-02 10:36:30 +10:00
|
|
|
if (tp_fake_finger_count(tp) <= tp->num_slots ||
|
|
|
|
|
tp->nfingers_down == 0)
|
2015-07-17 16:40:50 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We have at least one fake touch down. Find the top-most real
|
|
|
|
|
* touch and copy its coordinates over to to all fake touches.
|
|
|
|
|
* This is more reliable than just taking the first touch.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < tp->num_slots; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
if (t->state == TOUCH_END ||
|
|
|
|
|
t->state == TOUCH_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (topmost == NULL || t->point.y < topmost->point.y)
|
|
|
|
|
topmost = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!topmost) {
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_bug_libinput(tp->device,
|
|
|
|
|
"Unable to find topmost touch\n");
|
2015-07-17 16:40:50 +10:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start = tp->has_mt ? tp->num_slots : 1;
|
|
|
|
|
for (i = start; i < tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
if (t->state == TOUCH_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
t->point = topmost->point;
|
2017-01-10 15:58:21 +10:00
|
|
|
t->pressure = topmost->pressure;
|
2015-07-17 16:40:50 +10:00
|
|
|
if (!t->dirty)
|
|
|
|
|
t->dirty = topmost->dirty;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-20 13:10:29 +10:00
|
|
|
static inline bool
|
2016-03-17 09:35:19 +10:00
|
|
|
tp_need_motion_history_reset(struct tp_dispatch *tp)
|
2015-07-20 13:10:29 +10:00
|
|
|
{
|
2016-03-07 16:05:25 +10:00
|
|
|
bool rc = false;
|
|
|
|
|
|
2016-08-29 10:47:08 +10:00
|
|
|
/* Changing the numbers of fingers can cause a jump in the
|
|
|
|
|
* coordinates, always reset the motion history for all touches when
|
|
|
|
|
* that happens.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->nfingers_down != tp->old_nfingers_down)
|
2015-07-20 13:10:29 +10:00
|
|
|
return true;
|
|
|
|
|
|
2016-03-07 16:05:25 +10:00
|
|
|
/* Quirk: if we had multiple events without x/y axis
|
|
|
|
|
information, the next x/y event is going to be a jump. So we
|
|
|
|
|
reset that touch to non-dirty effectively swallowing that event
|
|
|
|
|
and restarting with the next event again.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->device->model_flags & EVDEV_MODEL_LENOVO_T450_TOUCHPAD) {
|
|
|
|
|
if (tp->queued & TOUCHPAD_EVENT_MOTION) {
|
|
|
|
|
if (tp->quirks.nonmotion_event_count > 10) {
|
2016-03-29 13:34:00 +10:00
|
|
|
tp->queued &= ~TOUCHPAD_EVENT_MOTION;
|
2016-03-07 16:05:25 +10:00
|
|
|
rc = true;
|
|
|
|
|
}
|
|
|
|
|
tp->quirks.nonmotion_event_count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((tp->queued & (TOUCHPAD_EVENT_OTHERAXIS|TOUCHPAD_EVENT_MOTION)) ==
|
|
|
|
|
TOUCHPAD_EVENT_OTHERAXIS)
|
|
|
|
|
tp->quirks.nonmotion_event_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
2015-07-20 13:10:29 +10:00
|
|
|
}
|
|
|
|
|
|
2014-09-16 15:13:00 +10:00
|
|
|
static bool
|
2018-08-27 12:35:27 +10:00
|
|
|
tp_detect_jumps(const struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
uint64_t time)
|
2014-09-16 15:13:00 +10:00
|
|
|
{
|
2017-06-21 11:16:27 +10:00
|
|
|
struct device_coords delta;
|
2016-12-13 10:01:02 +10:00
|
|
|
struct phys_coords mm;
|
2017-06-21 11:16:27 +10:00
|
|
|
struct tp_history_point *last;
|
2018-08-27 12:35:27 +10:00
|
|
|
double abs_distance, rel_distance;
|
2018-08-13 15:25:48 +10:00
|
|
|
bool is_jump = false;
|
2018-08-27 12:35:27 +10:00
|
|
|
uint64_t tdelta;
|
|
|
|
|
/* Reference interval from the touchpad the various thresholds
|
|
|
|
|
* were measured from */
|
|
|
|
|
unsigned int reference_interval = ms2us(12);
|
2014-09-16 15:13:00 +10:00
|
|
|
|
2016-05-18 12:19:30 +10:00
|
|
|
/* We haven't seen pointer jumps on Wacom tablets yet, so exclude
|
|
|
|
|
* those.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
|
|
|
|
|
return false;
|
|
|
|
|
|
2018-08-13 15:25:48 +10:00
|
|
|
if (t->history.count == 0) {
|
|
|
|
|
t->jumps.last_delta_mm = 0.0;
|
2014-09-16 15:13:00 +10:00
|
|
|
return false;
|
2018-08-13 15:25:48 +10:00
|
|
|
}
|
2014-09-16 15:13:00 +10:00
|
|
|
|
|
|
|
|
/* called before tp_motion_history_push, so offset 0 is the most
|
|
|
|
|
* recent coordinate */
|
|
|
|
|
last = tp_motion_history_offset(t, 0);
|
2018-08-27 12:35:27 +10:00
|
|
|
tdelta = time - last->time;
|
|
|
|
|
|
|
|
|
|
/* For test devices we always force the time delta to 12, at least
|
|
|
|
|
until the test suite actually does proper intervals. */
|
|
|
|
|
if (tp->device->model_flags & EVDEV_MODEL_TEST_DEVICE)
|
|
|
|
|
reference_interval = tdelta;
|
|
|
|
|
|
|
|
|
|
/* If the last frame is more than 25ms ago, we have irregular
|
|
|
|
|
* frames, who knows what's a pointer jump here and what's
|
|
|
|
|
* legitimate movement.... */
|
|
|
|
|
if (tdelta > 2 * reference_interval || tdelta == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* We historically expected ~12ms frame intervals, so the numbers
|
|
|
|
|
below are normalized to that (and that's also where the
|
|
|
|
|
measured data came from) */
|
2017-06-21 11:16:27 +10:00
|
|
|
delta.x = abs(t->point.x - last->point.x);
|
|
|
|
|
delta.y = abs(t->point.y - last->point.y);
|
2016-12-13 10:01:02 +10:00
|
|
|
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
|
2018-08-27 12:35:27 +10:00
|
|
|
abs_distance = hypot(mm.x, mm.y) * reference_interval/tdelta;
|
|
|
|
|
rel_distance = abs_distance - t->jumps.last_delta_mm;
|
2018-08-13 15:25:48 +10:00
|
|
|
|
|
|
|
|
/* Cursor jump if:
|
|
|
|
|
* - current single-event delta is >20mm, or
|
2018-08-27 12:35:27 +10:00
|
|
|
* - we increased the delta by over 7mm within a 12ms frame.
|
|
|
|
|
* (12ms simply because that's what I measured)
|
2018-08-13 15:25:48 +10:00
|
|
|
*/
|
2018-08-27 12:35:27 +10:00
|
|
|
is_jump = abs_distance > 20.0 || rel_distance > 7;
|
|
|
|
|
t->jumps.last_delta_mm = abs_distance;
|
2018-08-13 15:25:48 +10:00
|
|
|
|
|
|
|
|
return is_jump;
|
2014-09-16 15:13:00 +10:00
|
|
|
}
|
|
|
|
|
|
touchpad: add timestamp-based jump detection
On Dell i2c touchpads, the controller appears to go to sleep after about 1s of
inactivity on the touchpad. The wakeup takes a while so on the next touch, we
may see a pointer jump, specifially on the third event (i.e. touch down,
event, event+jump). The MSC_TIMESTAMP value carries a hint for what's
happening here, the event sequence for a touchpad with scanout intervals
7300µs is:
...
MSC_TIMESTAMP 0
SYN_REPORT
...
MSC_TIMESTAMP 7300
SYN_REPORT +2ms
...
MSC_TIMESTAMP 123456
SYN_REPORT +7ms
...
MSC_TIMESTAMP 123456+7300
SYN_REPORT +8ms
Note how the SYN_REPORT timestamps don't reflect the MSC_TIMESTAMPS.
This patch adds a quirk activate MSC_TIMESTAMP watching. When we do so, we
monitor for a 0 MSC_TIMESTAMP. Let's assume that the first event after that is
the interval, then check the third event. If that third event's timestamp is too
large rewrite the touches' motion history to reflect the correct timestamps,
i.e. instead of the SYN_REPORT timestamps the motion history now uses
"third-event SYN_REPORT timestamps minus MSC_TIMESTAMP values".
The pointer accel filter code uses absolute timestamps (#123) so we have to
restart the pointer acceleration filter when we detect this jump. This allows
us to reset the 0 time for the filter to the previous event's MSC_TIMESTAMP
time, so that our new large delta has the correct time delta too. This
calculates the acceleration correctly for that window.
The result is that the pointer is still delayed by the wake-up window (not
fixable in libinput) but at least it ends up where it should've.
There are a few side-effects: thumb, gesture, and hysteresis all still use the
unmodified SYN_REPORT time. There is a potential for false detection of either
of these now, but we'll have to fix those as they come up.
Fixes #36
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-21 15:43:38 +10:00
|
|
|
/**
|
|
|
|
|
* Rewrite the motion history so that previous points' timestamps are the
|
|
|
|
|
* current point's timestamp minus whatever MSC_TIMESTAMP gives us.
|
|
|
|
|
*
|
|
|
|
|
* This must be called before tp_motion_history_push()
|
|
|
|
|
*
|
|
|
|
|
* @param t The touch point
|
|
|
|
|
* @param jumping_interval The large time interval in µs
|
|
|
|
|
* @param normal_interval Normal hw interval in µs
|
|
|
|
|
* @param time Current time in µs
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
tp_motion_history_fix_last(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
unsigned int jumping_interval,
|
|
|
|
|
unsigned int normal_interval,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (t->state != TOUCH_UPDATE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We know the coordinates are correct because the touchpad should
|
|
|
|
|
* get that bit right. But the timestamps we got from the kernel are
|
|
|
|
|
* messed up, so we go back in the history and fix them.
|
|
|
|
|
*
|
|
|
|
|
* This way the next delta is huge but it's over a large time, so
|
|
|
|
|
* the pointer accel code should do the right thing.
|
|
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < (int)t->history.count; i++) {
|
|
|
|
|
struct tp_history_point *p;
|
|
|
|
|
|
|
|
|
|
p = tp_motion_history_offset(t, i);
|
|
|
|
|
p->time = time - jumping_interval - normal_interval * i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct msc_timestamp *m = &tp->quirks.msc_timestamp;
|
|
|
|
|
|
|
|
|
|
/* Pointer jump detection based on MSC_TIMESTAMP.
|
|
|
|
|
|
|
|
|
|
MSC_TIMESTAMP gets reset after a kernel timeout (1s) and on some
|
|
|
|
|
devices (Dell XPS) the i2c controller sleeps after a timeout. On
|
|
|
|
|
wakeup, some events are swallowed, triggering a cursor jump. The
|
|
|
|
|
event sequence after a sleep is always:
|
|
|
|
|
|
|
|
|
|
initial finger down:
|
|
|
|
|
ABS_X/Y x/y
|
|
|
|
|
MSC_TIMESTAMP 0
|
|
|
|
|
SYN_REPORT +2500ms
|
|
|
|
|
second event:
|
|
|
|
|
ABS_X/Y x+n/y+n # normal movement
|
|
|
|
|
MSC_TIMESTAMP 7300 # the hw interval
|
|
|
|
|
SYN_REPORT +2ms
|
|
|
|
|
third event:
|
|
|
|
|
ABS_X/Y x+lots/y+lots # pointer jump!
|
|
|
|
|
MSC_TIMESTAMP 123456 # well above the hw interval
|
|
|
|
|
SYN_REPORT +2ms
|
|
|
|
|
fourth event:
|
|
|
|
|
ABS_X/Y x+lots+n/y+lots+n # all normal again
|
|
|
|
|
MSC_TIMESTAMP 123456 + 7300
|
|
|
|
|
SYN_REPORT +8ms
|
|
|
|
|
|
|
|
|
|
Our approach is to detect the 0 timestamp, check the interval on
|
|
|
|
|
the next event and then calculate the movement for one fictious
|
|
|
|
|
event instead, swallowing all other movements. So if the time
|
|
|
|
|
delta is equivalent to 10 events and the movement is x, we
|
|
|
|
|
instead pretend there was movement of x/10.
|
|
|
|
|
*/
|
|
|
|
|
if (m->now == 0) {
|
|
|
|
|
m->state = JUMP_STATE_EXPECT_FIRST;
|
|
|
|
|
m->interval = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(m->state) {
|
|
|
|
|
case JUMP_STATE_EXPECT_FIRST:
|
|
|
|
|
if (m->now > ms2us(20)) {
|
|
|
|
|
m->state = JUMP_STATE_IGNORE;
|
|
|
|
|
} else {
|
|
|
|
|
m->state = JUMP_STATE_EXPECT_DELAY;
|
|
|
|
|
m->interval = m->now;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JUMP_STATE_EXPECT_DELAY:
|
|
|
|
|
if (m->now > m->interval * 2) {
|
|
|
|
|
uint32_t tdelta; /* µs */
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
|
|
|
|
/* The current time is > 2 times the interval so we
|
|
|
|
|
* have a jump. Fix the motion history */
|
|
|
|
|
tdelta = m->now - m->interval;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
tp_motion_history_fix_last(tp,
|
|
|
|
|
t,
|
|
|
|
|
tdelta,
|
|
|
|
|
m->interval,
|
|
|
|
|
time);
|
|
|
|
|
}
|
|
|
|
|
m->state = JUMP_STATE_IGNORE;
|
|
|
|
|
|
|
|
|
|
/* We need to restart the acceleration filter to forget its history.
|
|
|
|
|
* The current point becomes the first point in the history there
|
|
|
|
|
* (including timestamp) and that accelerates correctly.
|
|
|
|
|
* This has a potential to be incorrect but since we only ever see
|
|
|
|
|
* those jumps over the first three events it doesn't matter.
|
|
|
|
|
*/
|
|
|
|
|
filter_restart(tp->device->pointer.filter, tp, time - tdelta);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JUMP_STATE_IGNORE:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
static void
|
|
|
|
|
tp_pre_process_state(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
touchpad: add timestamp-based jump detection
On Dell i2c touchpads, the controller appears to go to sleep after about 1s of
inactivity on the touchpad. The wakeup takes a while so on the next touch, we
may see a pointer jump, specifially on the third event (i.e. touch down,
event, event+jump). The MSC_TIMESTAMP value carries a hint for what's
happening here, the event sequence for a touchpad with scanout intervals
7300µs is:
...
MSC_TIMESTAMP 0
SYN_REPORT
...
MSC_TIMESTAMP 7300
SYN_REPORT +2ms
...
MSC_TIMESTAMP 123456
SYN_REPORT +7ms
...
MSC_TIMESTAMP 123456+7300
SYN_REPORT +8ms
Note how the SYN_REPORT timestamps don't reflect the MSC_TIMESTAMPS.
This patch adds a quirk activate MSC_TIMESTAMP watching. When we do so, we
monitor for a 0 MSC_TIMESTAMP. Let's assume that the first event after that is
the interval, then check the third event. If that third event's timestamp is too
large rewrite the touches' motion history to reflect the correct timestamps,
i.e. instead of the SYN_REPORT timestamps the motion history now uses
"third-event SYN_REPORT timestamps minus MSC_TIMESTAMP values".
The pointer accel filter code uses absolute timestamps (#123) so we have to
restart the pointer acceleration filter when we detect this jump. This allows
us to reset the 0 time for the filter to the previous event's MSC_TIMESTAMP
time, so that our new large delta has the correct time delta too. This
calculates the acceleration correctly for that window.
The result is that the pointer is still delayed by the wake-up window (not
fixable in libinput) but at least it ends up where it should've.
There are a few side-effects: thumb, gesture, and hysteresis all still use the
unmodified SYN_REPORT time. There is a potential for false detection of either
of these now, but we'll have to fix those as they come up.
Fixes #36
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-21 15:43:38 +10:00
|
|
|
if (tp->queued & TOUCHPAD_EVENT_TIMESTAMP)
|
|
|
|
|
tp_process_msc_timestamp(tp, time);
|
|
|
|
|
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_process_fake_touches(tp, time);
|
|
|
|
|
tp_unhover_touches(tp, time);
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (t->state == TOUCH_MAYBE_END)
|
|
|
|
|
tp_end_touch(tp, t, time);
|
2018-06-13 14:38:20 +03:00
|
|
|
|
|
|
|
|
/* Ignore motion when pressure/touch size fell below the
|
|
|
|
|
* threshold, thus ending the touch */
|
|
|
|
|
if (t->state == TOUCH_END && t->history.count > 0)
|
|
|
|
|
t->point = tp_motion_history_offset(t, 0)->point;
|
2018-02-28 12:51:27 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_process_state(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
2015-06-10 09:54:06 +10:00
|
|
|
bool restart_filter = false;
|
2015-07-20 13:10:29 +10:00
|
|
|
bool want_motion_reset;
|
2017-06-21 12:29:52 +10:00
|
|
|
bool have_new_touch = false;
|
|
|
|
|
unsigned int speed_exceeded_count = 0;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2015-07-17 16:40:50 +10:00
|
|
|
tp_position_fake_touches(tp);
|
2014-12-11 16:39:04 +10:00
|
|
|
|
2016-03-17 09:35:19 +10:00
|
|
|
want_motion_reset = tp_need_motion_history_reset(tp);
|
2014-12-11 16:39:04 +10:00
|
|
|
|
2017-05-02 22:54:03 -03:00
|
|
|
tp_for_each_touch(tp, t) {
|
2018-04-05 11:18:40 +10:00
|
|
|
if (t->state == TOUCH_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-07-20 13:10:29 +10:00
|
|
|
if (want_motion_reset) {
|
2014-07-21 15:25:47 +02:00
|
|
|
tp_motion_history_reset(t);
|
2015-07-20 13:10:29 +10:00
|
|
|
t->quirks.reset_motion_history = true;
|
|
|
|
|
} else if (t->quirks.reset_motion_history) {
|
2014-07-21 15:25:47 +02:00
|
|
|
tp_motion_history_reset(t);
|
2015-07-20 13:10:29 +10:00
|
|
|
t->quirks.reset_motion_history = false;
|
2014-07-10 17:17:53 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2017-06-21 12:29:52 +10:00
|
|
|
if (!t->dirty) {
|
|
|
|
|
/* A non-dirty touch must be below the speed limit */
|
|
|
|
|
if (t->speed.exceeded_count > 0)
|
|
|
|
|
t->speed.exceeded_count--;
|
|
|
|
|
|
|
|
|
|
speed_exceeded_count = max(speed_exceeded_count,
|
|
|
|
|
t->speed.exceeded_count);
|
2014-07-18 11:06:37 +02:00
|
|
|
continue;
|
2017-06-21 12:29:52 +10:00
|
|
|
}
|
2014-07-18 11:06:37 +02:00
|
|
|
|
2018-08-27 12:35:27 +10:00
|
|
|
if (tp_detect_jumps(tp, t, time)) {
|
2016-06-10 10:30:24 +10:00
|
|
|
if (!tp->semi_mt)
|
2020-03-21 21:03:32 +10:00
|
|
|
evdev_log_bug_kernel_ratelimit(tp->device,
|
|
|
|
|
&tp->jump.warning,
|
|
|
|
|
"Touch jump detected and discarded.\n"
|
|
|
|
|
"See %stouchpad-jumping-cursors.html for details\n",
|
|
|
|
|
HTTP_DOC_LINK);
|
2014-09-16 15:13:00 +10:00
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-20 10:56:17 +10:00
|
|
|
tp_thumb_update_touch(tp, t, time);
|
2014-07-14 16:06:51 +10:00
|
|
|
tp_palm_detect(tp, t, time);
|
2018-02-21 20:16:41 +03:00
|
|
|
tp_detect_wobbling(tp, t, time);
|
2014-02-06 18:57:10 +10:00
|
|
|
tp_motion_hysteresis(tp, t);
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_motion_history_push(t);
|
2014-04-15 14:27:59 +02:00
|
|
|
|
2017-06-21 12:29:52 +10:00
|
|
|
/* Touch speed handling: if we'are above the threshold,
|
|
|
|
|
* count each event that we're over the threshold up to 10
|
|
|
|
|
* events. Count down when we are below the speed.
|
|
|
|
|
*
|
|
|
|
|
* Take the touch with the highest speed excess, if it is
|
|
|
|
|
* above a certain threshold (5, see below), assume a
|
|
|
|
|
* dropped finger is a thumb.
|
|
|
|
|
*
|
|
|
|
|
* Yes, this relies on the touchpad to keep sending us
|
|
|
|
|
* events even if the finger doesn't move, otherwise we
|
|
|
|
|
* never count down. Let's see how far we get with that.
|
|
|
|
|
*/
|
|
|
|
|
if (t->speed.last_speed > THUMB_IGNORE_SPEED_THRESHOLD) {
|
2019-07-10 12:10:42 +10:00
|
|
|
if (t->speed.exceeded_count < 15)
|
2017-06-21 12:29:52 +10:00
|
|
|
t->speed.exceeded_count++;
|
|
|
|
|
} else if (t->speed.exceeded_count > 0) {
|
|
|
|
|
t->speed.exceeded_count--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
speed_exceeded_count = max(speed_exceeded_count,
|
|
|
|
|
t->speed.exceeded_count);
|
|
|
|
|
|
|
|
|
|
tp_calculate_motion_speed(tp, t);
|
|
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_unpin_finger(tp, t);
|
2015-06-10 09:54:06 +10:00
|
|
|
|
2017-06-21 12:29:52 +10:00
|
|
|
if (t->state == TOUCH_BEGIN) {
|
|
|
|
|
have_new_touch = true;
|
2015-06-10 09:54:06 +10:00
|
|
|
restart_filter = true;
|
2017-06-21 12:29:52 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2019-06-21 13:19:22 +10:00
|
|
|
if (tp->thumb.detect_thumbs &&
|
|
|
|
|
have_new_touch &&
|
|
|
|
|
tp->nfingers_down >= 2)
|
2019-06-20 10:56:17 +10:00
|
|
|
tp_thumb_update_multifinger(tp);
|
2017-06-21 12:29:52 +10:00
|
|
|
|
2015-06-10 09:54:06 +10:00
|
|
|
if (restart_filter)
|
|
|
|
|
filter_restart(tp->device->pointer.filter, tp, time);
|
|
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
tp_button_handle_state(tp, time);
|
2015-02-16 10:16:30 +01:00
|
|
|
tp_edge_scroll_handle_state(tp, time);
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
/*
|
|
|
|
|
* We have a physical button down event on a clickpad. To avoid
|
|
|
|
|
* spurious pointer moves by the clicking finger we pin all fingers.
|
|
|
|
|
* We unpin fingers when they move more then a certain threshold to
|
|
|
|
|
* to allow drag and drop.
|
2014-02-17 14:24:20 +10:00
|
|
|
*/
|
|
|
|
|
if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
|
2014-04-07 13:44:07 +02:00
|
|
|
tp->buttons.is_clickpad)
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_pin_fingers(tp);
|
2015-02-16 13:30:24 +01:00
|
|
|
|
|
|
|
|
tp_gesture_handle_state(tp, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-12-11 16:39:04 +10:00
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
if (t->state == TOUCH_END) {
|
|
|
|
|
if (t->has_ended)
|
|
|
|
|
t->state = TOUCH_NONE;
|
|
|
|
|
else
|
|
|
|
|
t->state = TOUCH_HOVERING;
|
|
|
|
|
} else if (t->state == TOUCH_BEGIN) {
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_UPDATE;
|
2014-12-11 16:39:04 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
|
|
|
|
t->dirty = false;
|
|
|
|
|
}
|
2014-02-07 13:48:06 +10:00
|
|
|
|
2014-07-21 15:25:47 +02:00
|
|
|
tp->old_nfingers_down = tp->nfingers_down;
|
2014-02-07 13:59:38 +10:00
|
|
|
tp->buttons.old_state = tp->buttons.state;
|
|
|
|
|
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued = TOUCHPAD_EVENT_NONE;
|
2016-07-21 14:03:28 +10:00
|
|
|
|
2019-06-21 13:19:22 +10:00
|
|
|
if (tp->nfingers_down == 0)
|
|
|
|
|
tp_thumb_reset(tp);
|
|
|
|
|
|
2016-07-21 14:03:28 +10:00
|
|
|
tp_tap_post_process_state(tp);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-09 12:47:09 +01:00
|
|
|
static void
|
|
|
|
|
tp_post_events(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
int filter_motion = 0;
|
|
|
|
|
|
|
|
|
|
/* Only post (top) button events while suspended */
|
2016-07-04 15:41:37 +10:00
|
|
|
if (tp->device->is_suspended) {
|
2014-12-09 12:47:09 +01:00
|
|
|
tp_post_button_events(tp, time);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filter_motion |= tp_tap_handle_state(tp, time);
|
|
|
|
|
filter_motion |= tp_post_button_events(tp, time);
|
|
|
|
|
|
2015-04-20 15:51:20 +10:00
|
|
|
if (filter_motion ||
|
2015-06-24 11:22:29 +10:00
|
|
|
tp->palm.trackpoint_active ||
|
2015-05-25 10:07:51 +10:00
|
|
|
tp->dwt.keyboard_active) {
|
2015-02-16 10:16:30 +01:00
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
2015-05-22 10:09:22 +10:00
|
|
|
tp_gesture_cancel(tp, time);
|
2014-12-09 12:47:09 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-16 10:16:30 +01:00
|
|
|
if (tp_edge_scroll_post_events(tp, time) != 0)
|
|
|
|
|
return;
|
2014-12-09 12:47:09 +01:00
|
|
|
|
2015-02-16 13:30:24 +01:00
|
|
|
tp_gesture_post_events(tp, time);
|
2014-12-09 12:47:09 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
static void
|
|
|
|
|
tp_apply_rotation(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
|
|
|
|
|
|
|
|
|
|
if (tp->left_handed.want_rotate == tp->left_handed.rotate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tp->nfingers_down)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tp->left_handed.rotate = tp->left_handed.want_rotate;
|
|
|
|
|
|
|
|
|
|
evdev_log_debug(device,
|
|
|
|
|
"touchpad-rotation: rotation is %s\n",
|
|
|
|
|
tp->left_handed.rotate ? "on" : "off");
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:05:49 +10:00
|
|
|
static void
|
|
|
|
|
tp_handle_state(struct tp_dispatch *tp,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2018-02-28 12:51:27 +10:00
|
|
|
tp_pre_process_state(tp, time);
|
2014-09-01 17:05:49 +10:00
|
|
|
tp_process_state(tp, time);
|
|
|
|
|
tp_post_events(tp, time);
|
|
|
|
|
tp_post_process_state(tp, time);
|
2016-06-30 15:49:40 +10:00
|
|
|
|
|
|
|
|
tp_clickpad_middlebutton_apply_config(tp->device);
|
2019-05-03 15:56:54 +10:00
|
|
|
tp_apply_rotation(tp->device);
|
2014-09-01 17:05:49 +10:00
|
|
|
}
|
|
|
|
|
|
2017-04-26 14:40:11 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_debug_touch_state(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
char buf[1024] = {0};
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (i >= tp->nfingers_down)
|
|
|
|
|
break;
|
|
|
|
|
sprintf(&buf[strlen(buf)],
|
|
|
|
|
"slot %zd: %04d/%04d p%03d %s |",
|
|
|
|
|
i++,
|
|
|
|
|
t->point.x,
|
|
|
|
|
t->point.y,
|
|
|
|
|
t->pressure,
|
|
|
|
|
tp_touch_active(tp, t) ? "" : "inactive");
|
|
|
|
|
}
|
2018-10-15 13:47:29 +10:00
|
|
|
if (buf[0] != '\0')
|
|
|
|
|
evdev_log_debug(device, "touch state: %s\n", buf);
|
2017-04-26 14:40:11 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_process(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct input_event *e,
|
|
|
|
|
uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
2014-02-06 15:32:32 +10:00
|
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
|
case EV_ABS:
|
2014-02-14 14:18:27 +10:00
|
|
|
if (tp->has_mt)
|
|
|
|
|
tp_process_absolute(tp, e, time);
|
|
|
|
|
else
|
|
|
|
|
tp_process_absolute_st(tp, e, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case EV_KEY:
|
|
|
|
|
tp_process_key(tp, e, time);
|
|
|
|
|
break;
|
touchpad: add timestamp-based jump detection
On Dell i2c touchpads, the controller appears to go to sleep after about 1s of
inactivity on the touchpad. The wakeup takes a while so on the next touch, we
may see a pointer jump, specifially on the third event (i.e. touch down,
event, event+jump). The MSC_TIMESTAMP value carries a hint for what's
happening here, the event sequence for a touchpad with scanout intervals
7300µs is:
...
MSC_TIMESTAMP 0
SYN_REPORT
...
MSC_TIMESTAMP 7300
SYN_REPORT +2ms
...
MSC_TIMESTAMP 123456
SYN_REPORT +7ms
...
MSC_TIMESTAMP 123456+7300
SYN_REPORT +8ms
Note how the SYN_REPORT timestamps don't reflect the MSC_TIMESTAMPS.
This patch adds a quirk activate MSC_TIMESTAMP watching. When we do so, we
monitor for a 0 MSC_TIMESTAMP. Let's assume that the first event after that is
the interval, then check the third event. If that third event's timestamp is too
large rewrite the touches' motion history to reflect the correct timestamps,
i.e. instead of the SYN_REPORT timestamps the motion history now uses
"third-event SYN_REPORT timestamps minus MSC_TIMESTAMP values".
The pointer accel filter code uses absolute timestamps (#123) so we have to
restart the pointer acceleration filter when we detect this jump. This allows
us to reset the 0 time for the filter to the previous event's MSC_TIMESTAMP
time, so that our new large delta has the correct time delta too. This
calculates the acceleration correctly for that window.
The result is that the pointer is still delayed by the wake-up window (not
fixable in libinput) but at least it ends up where it should've.
There are a few side-effects: thumb, gesture, and hysteresis all still use the
unmodified SYN_REPORT time. There is a potential for false detection of either
of these now, but we'll have to fix those as they come up.
Fixes #36
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-21 15:43:38 +10:00
|
|
|
case EV_MSC:
|
|
|
|
|
tp_process_msc(tp, e, time);
|
|
|
|
|
break;
|
2014-02-06 15:32:32 +10:00
|
|
|
case EV_SYN:
|
2014-09-01 17:05:49 +10:00
|
|
|
tp_handle_state(tp, time);
|
2017-04-26 14:40:11 +10:00
|
|
|
#if 0
|
|
|
|
|
tp_debug_touch_state(tp, device);
|
|
|
|
|
#endif
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
static void
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove_sendevents(struct tp_dispatch *tp)
|
2014-09-28 13:21:08 +02:00
|
|
|
{
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
2017-10-23 14:34:47 +10:00
|
|
|
|
2015-06-24 11:22:29 +10:00
|
|
|
libinput_timer_cancel(&tp->palm.trackpoint_timer);
|
2015-05-25 10:07:51 +10:00
|
|
|
libinput_timer_cancel(&tp->dwt.keyboard_timer);
|
2014-09-28 13:21:08 +02:00
|
|
|
|
2015-06-24 11:41:47 +10:00
|
|
|
if (tp->buttons.trackpoint &&
|
|
|
|
|
tp->palm.monitor_trackpoint)
|
2014-09-28 13:21:08 +02:00
|
|
|
libinput_device_remove_event_listener(
|
2015-06-24 11:22:29 +10:00
|
|
|
&tp->palm.trackpoint_listener);
|
2015-04-20 15:51:20 +10:00
|
|
|
|
2018-05-28 11:46:59 +10:00
|
|
|
list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) {
|
|
|
|
|
libinput_device_remove_event_listener(&kbd->listener);
|
2017-10-23 14:34:47 +10:00
|
|
|
}
|
2017-01-20 16:54:15 +11:00
|
|
|
|
|
|
|
|
if (tp->lid_switch.lid_switch)
|
|
|
|
|
libinput_device_remove_event_listener(
|
2017-09-01 17:08:17 +10:00
|
|
|
&tp->lid_switch.listener);
|
2017-09-01 17:30:08 +10:00
|
|
|
|
|
|
|
|
if (tp->tablet_mode_switch.tablet_mode_switch)
|
|
|
|
|
libinput_device_remove_event_listener(
|
|
|
|
|
&tp->tablet_mode_switch.listener);
|
2014-09-28 13:21:08 +02:00
|
|
|
}
|
|
|
|
|
|
2014-12-05 12:50:39 +01:00
|
|
|
static void
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_remove(struct evdev_dispatch *dispatch)
|
2014-12-05 12:50:39 +01:00
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
2014-12-05 12:50:39 +01:00
|
|
|
|
2018-06-29 13:43:15 +10:00
|
|
|
libinput_timer_cancel(&tp->arbitration.arbitration_timer);
|
|
|
|
|
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove_tap(tp);
|
|
|
|
|
tp_remove_buttons(tp);
|
|
|
|
|
tp_remove_sendevents(tp);
|
2015-02-16 10:16:30 +01:00
|
|
|
tp_remove_edge_scroll(tp);
|
2015-02-16 13:30:24 +01:00
|
|
|
tp_remove_gesture(tp);
|
2014-12-05 12:50:39 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_destroy(struct evdev_dispatch *dispatch)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2018-02-19 14:00:42 +10:00
|
|
|
libinput_timer_destroy(&tp->arbitration.arbitration_timer);
|
2017-07-03 11:28:18 +10:00
|
|
|
libinput_timer_destroy(&tp->palm.trackpoint_timer);
|
|
|
|
|
libinput_timer_destroy(&tp->dwt.keyboard_timer);
|
|
|
|
|
libinput_timer_destroy(&tp->tap.timer);
|
|
|
|
|
libinput_timer_destroy(&tp->gesture.finger_count_switch_timer);
|
2014-02-06 15:32:32 +10:00
|
|
|
free(tp->touches);
|
|
|
|
|
free(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-30 14:48:42 +10:00
|
|
|
static void
|
|
|
|
|
tp_release_fake_touches(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
tp->fake_touches = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static void
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(struct tp_dispatch *tp)
|
2014-09-01 17:17:18 +10:00
|
|
|
{
|
2015-05-22 15:16:31 +10:00
|
|
|
uint64_t now = libinput_now(tp_libinput_context(tp));
|
2014-09-01 17:17:18 +10:00
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
|
|
|
|
/* Unroll the touchpad state.
|
|
|
|
|
* Release buttons first. If tp is a clickpad, the button event
|
|
|
|
|
* must come before the touch up. If it isn't, the order doesn't
|
|
|
|
|
* matter anyway
|
|
|
|
|
*
|
|
|
|
|
* Then cancel all timeouts on the taps, triggering the last set
|
|
|
|
|
* of events.
|
|
|
|
|
*
|
|
|
|
|
* Then lift all touches so the touchpad is in a neutral state.
|
|
|
|
|
*
|
2019-06-21 13:19:22 +10:00
|
|
|
* Then reset thumb state.
|
|
|
|
|
*
|
2014-09-01 17:17:18 +10:00
|
|
|
*/
|
|
|
|
|
tp_release_all_buttons(tp, now);
|
|
|
|
|
tp_release_all_taps(tp, now);
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_end_sequence(tp, t, now);
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
2015-04-30 14:48:42 +10:00
|
|
|
tp_release_fake_touches(tp);
|
2014-09-01 17:17:18 +10:00
|
|
|
|
2019-06-21 13:19:22 +10:00
|
|
|
tp_thumb_reset(tp);
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
tp_handle_state(tp, now);
|
2014-09-16 16:22:40 +02:00
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
|
2014-09-16 16:22:40 +02:00
|
|
|
static void
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
enum suspend_trigger trigger)
|
2014-09-16 16:22:40 +02:00
|
|
|
{
|
2018-05-16 09:40:50 +10:00
|
|
|
if (tp->suspend_reason & trigger)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tp->suspend_reason != 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(tp);
|
2014-09-16 16:22:41 +02:00
|
|
|
|
|
|
|
|
/* On devices with top softwarebuttons we don't actually suspend the
|
|
|
|
|
* device, to keep the "trackpoint" buttons working. tp_post_events()
|
|
|
|
|
* will only send events for the trackpoint while suspended.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->buttons.has_topbuttons) {
|
|
|
|
|
evdev_notify_suspended_device(device);
|
2014-09-17 15:35:30 +02:00
|
|
|
/* Enlarge topbutton area while suspended */
|
2016-04-13 08:55:28 +02:00
|
|
|
tp_init_top_softbuttons(tp, device, 3.0);
|
2014-09-16 16:22:41 +02:00
|
|
|
} else {
|
|
|
|
|
evdev_device_suspend(device);
|
|
|
|
|
}
|
2018-05-16 09:40:50 +10:00
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
tp->suspend_reason |= trigger;
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
2015-05-19 15:57:11 +10:00
|
|
|
static void
|
|
|
|
|
tp_interface_suspend(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
2015-05-19 15:57:11 +10:00
|
|
|
|
|
|
|
|
tp_clear_state(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-06 11:46:12 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_sync_touch(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
int slot)
|
|
|
|
|
{
|
|
|
|
|
struct libevdev *evdev = device->evdev;
|
2020-02-18 16:48:09 +10:00
|
|
|
int tracking_id;
|
2017-07-06 11:46:12 +10:00
|
|
|
|
|
|
|
|
if (!libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_POSITION_X,
|
|
|
|
|
&t->point.x))
|
|
|
|
|
t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X);
|
|
|
|
|
if (!libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_POSITION_Y,
|
|
|
|
|
&t->point.y))
|
|
|
|
|
t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
|
|
|
|
|
|
|
|
|
|
if (!libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_PRESSURE,
|
|
|
|
|
&t->pressure))
|
|
|
|
|
t->pressure = libevdev_get_event_value(evdev,
|
|
|
|
|
EV_ABS,
|
|
|
|
|
ABS_PRESSURE);
|
2017-03-22 16:16:21 +10:00
|
|
|
|
|
|
|
|
libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_TOUCH_MAJOR,
|
|
|
|
|
&t->major);
|
|
|
|
|
libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_TOUCH_MINOR,
|
|
|
|
|
&t->minor);
|
2020-02-18 16:48:09 +10:00
|
|
|
|
|
|
|
|
if (libevdev_fetch_slot_value(evdev,
|
|
|
|
|
slot,
|
|
|
|
|
ABS_MT_TRACKING_ID,
|
|
|
|
|
&tracking_id) &&
|
|
|
|
|
tracking_id != -1)
|
|
|
|
|
tp->nactive_slots++;
|
2017-07-06 11:46:12 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_sync_slots(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
/* Always sync the first touch so we get ABS_X/Y synced on
|
|
|
|
|
* single-touch touchpads */
|
|
|
|
|
tp_sync_touch(tp, device, &tp->touches[0], 0);
|
|
|
|
|
for (unsigned int i = 1; i < tp->num_slots; i++)
|
|
|
|
|
tp_sync_touch(tp, device, &tp->touches[i], i);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static void
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
enum suspend_trigger trigger)
|
2014-09-01 17:17:18 +10:00
|
|
|
{
|
2018-05-16 09:40:50 +10:00
|
|
|
tp->suspend_reason &= ~trigger;
|
|
|
|
|
if (tp->suspend_reason != 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-09-16 16:22:41 +02:00
|
|
|
if (tp->buttons.has_topbuttons) {
|
|
|
|
|
/* tap state-machine is offline while suspended, reset state */
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(tp);
|
2014-09-17 15:35:30 +02:00
|
|
|
/* restore original topbutton area size */
|
2015-01-14 14:54:43 +01:00
|
|
|
tp_init_top_softbuttons(tp, device, 1.0);
|
2014-09-16 16:22:41 +02:00
|
|
|
evdev_notify_resumed_device(device);
|
|
|
|
|
} else {
|
|
|
|
|
evdev_device_resume(device);
|
|
|
|
|
}
|
2017-07-06 11:46:12 +10:00
|
|
|
|
|
|
|
|
tp_sync_slots(tp, device);
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
static void
|
|
|
|
|
tp_trackpoint_timeout(uint64_t now, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
2018-04-27 14:40:57 +10:00
|
|
|
if (tp->palm.trackpoint_active) {
|
|
|
|
|
tp_tap_resume(tp, now);
|
|
|
|
|
tp->palm.trackpoint_active = false;
|
|
|
|
|
}
|
2016-09-07 08:18:33 +10:00
|
|
|
tp->palm.trackpoint_event_count = 0;
|
2014-09-28 13:21:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
|
|
|
|
/* Buttons do not count as trackpad activity, as people may use
|
|
|
|
|
the trackpoint buttons in combination with the touchpad. */
|
|
|
|
|
if (event->type == LIBINPUT_EVENT_POINTER_BUTTON)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-09-07 08:18:33 +10:00
|
|
|
tp->palm.trackpoint_last_event_time = time;
|
|
|
|
|
tp->palm.trackpoint_event_count++;
|
|
|
|
|
|
2018-04-27 14:40:57 +10:00
|
|
|
|
2016-09-07 08:18:33 +10:00
|
|
|
/* Require at least three events before enabling palm detection */
|
2018-04-27 14:40:57 +10:00
|
|
|
if (tp->palm.trackpoint_event_count < 3) {
|
|
|
|
|
libinput_timer_set(&tp->palm.trackpoint_timer,
|
|
|
|
|
time + DEFAULT_TRACKPOINT_EVENT_TIMEOUT);
|
2016-09-07 08:18:33 +10:00
|
|
|
return;
|
2018-04-27 14:40:57 +10:00
|
|
|
}
|
2016-09-07 08:18:33 +10:00
|
|
|
|
2015-06-24 11:22:29 +10:00
|
|
|
if (!tp->palm.trackpoint_active) {
|
2017-04-03 15:15:01 +10:00
|
|
|
tp_stop_actions(tp, time);
|
2015-06-24 11:22:29 +10:00
|
|
|
tp->palm.trackpoint_active = true;
|
2014-09-28 13:21:08 +02:00
|
|
|
}
|
|
|
|
|
|
2015-06-24 11:22:29 +10:00
|
|
|
libinput_timer_set(&tp->palm.trackpoint_timer,
|
2014-09-28 13:21:08 +02:00
|
|
|
time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
static void
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_keyboard_timeout(uint64_t now, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
2016-02-08 11:48:51 +10:00
|
|
|
if (tp->dwt.dwt_enabled &&
|
|
|
|
|
long_any_bit_set(tp->dwt.key_mask,
|
2016-02-04 08:31:25 +10:00
|
|
|
ARRAY_LENGTH(tp->dwt.key_mask))) {
|
|
|
|
|
libinput_timer_set(&tp->dwt.keyboard_timer,
|
|
|
|
|
now + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2);
|
|
|
|
|
tp->dwt.keyboard_last_press_time = now;
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device, "palm: keyboard timeout refresh\n");
|
2016-02-04 08:31:25 +10:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_tap_resume(tp, now);
|
2015-05-22 15:14:04 +10:00
|
|
|
|
2015-05-25 10:07:51 +10:00
|
|
|
tp->dwt.keyboard_active = false;
|
2015-05-22 15:14:04 +10:00
|
|
|
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(tp->device, "palm: keyboard timeout\n");
|
2015-04-20 15:51:20 +10:00
|
|
|
}
|
|
|
|
|
|
2015-05-25 08:48:25 +10:00
|
|
|
static inline bool
|
2016-08-04 10:43:25 +10:00
|
|
|
tp_key_is_modifier(unsigned int keycode)
|
2015-05-25 08:48:25 +10:00
|
|
|
{
|
|
|
|
|
switch (keycode) {
|
|
|
|
|
/* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */
|
|
|
|
|
case KEY_LEFTCTRL:
|
|
|
|
|
case KEY_RIGHTCTRL:
|
|
|
|
|
case KEY_LEFTALT:
|
|
|
|
|
case KEY_RIGHTALT:
|
|
|
|
|
case KEY_LEFTSHIFT:
|
|
|
|
|
case KEY_RIGHTSHIFT:
|
|
|
|
|
case KEY_FN:
|
|
|
|
|
case KEY_CAPSLOCK:
|
|
|
|
|
case KEY_TAB:
|
|
|
|
|
case KEY_COMPOSE:
|
|
|
|
|
case KEY_RIGHTMETA:
|
|
|
|
|
case KEY_LEFTMETA:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
2016-08-04 10:43:25 +10:00
|
|
|
return false;
|
2015-05-25 08:48:25 +10:00
|
|
|
}
|
2016-08-04 10:43:25 +10:00
|
|
|
}
|
2015-05-25 08:48:25 +10:00
|
|
|
|
2016-08-04 10:43:25 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_key_ignore_for_dwt(unsigned int keycode)
|
|
|
|
|
{
|
2015-05-25 08:48:25 +10:00
|
|
|
/* Ignore keys not part of the "typewriter set", i.e. F-keys,
|
|
|
|
|
* multimedia keys, numpad, etc.
|
|
|
|
|
*/
|
|
|
|
|
|
2016-08-04 10:43:25 +10:00
|
|
|
if (tp_key_is_modifier(keycode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return keycode >= KEY_F1;
|
2015-04-20 15:51:20 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
struct libinput_event_keyboard *kbdev;
|
2015-04-20 16:20:00 +10:00
|
|
|
unsigned int timeout;
|
2016-02-04 08:31:25 +10:00
|
|
|
unsigned int key;
|
2016-08-04 10:43:25 +10:00
|
|
|
bool is_modifier;
|
2015-04-20 15:51:20 +10:00
|
|
|
|
|
|
|
|
if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
kbdev = libinput_event_get_keyboard_event(event);
|
2016-02-04 08:31:25 +10:00
|
|
|
key = libinput_event_keyboard_get_key(kbdev);
|
2015-04-20 16:20:00 +10:00
|
|
|
|
|
|
|
|
/* Only trigger the timer on key down. */
|
|
|
|
|
if (libinput_event_keyboard_get_key_state(kbdev) !=
|
2016-02-04 08:31:25 +10:00
|
|
|
LIBINPUT_KEY_STATE_PRESSED) {
|
|
|
|
|
long_clear_bit(tp->dwt.key_mask, key);
|
2016-08-04 10:43:25 +10:00
|
|
|
long_clear_bit(tp->dwt.mod_mask, key);
|
2015-04-20 16:20:00 +10:00
|
|
|
return;
|
2016-02-04 08:31:25 +10:00
|
|
|
}
|
2015-04-20 16:20:00 +10:00
|
|
|
|
2016-02-08 11:48:51 +10:00
|
|
|
if (!tp->dwt.dwt_enabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-08-04 10:43:25 +10:00
|
|
|
if (tp_key_ignore_for_dwt(key))
|
|
|
|
|
return;
|
|
|
|
|
|
2015-04-20 15:51:20 +10:00
|
|
|
/* modifier keys don't trigger disable-while-typing so things like
|
|
|
|
|
* ctrl+zoom or ctrl+click are possible */
|
2016-08-04 10:43:25 +10:00
|
|
|
is_modifier = tp_key_is_modifier(key);
|
|
|
|
|
if (is_modifier) {
|
|
|
|
|
long_set_bit(tp->dwt.mod_mask, key);
|
2015-05-25 08:48:25 +10:00
|
|
|
return;
|
2016-08-04 10:43:25 +10:00
|
|
|
}
|
2015-04-20 15:51:20 +10:00
|
|
|
|
2015-05-25 10:07:51 +10:00
|
|
|
if (!tp->dwt.keyboard_active) {
|
2016-08-04 10:43:25 +10:00
|
|
|
/* This is the first non-modifier key press. Check if the
|
|
|
|
|
* modifier mask is set. If any modifier is down we don't
|
|
|
|
|
* trigger dwt because it's likely to be combination like
|
|
|
|
|
* Ctrl+S or similar */
|
|
|
|
|
|
|
|
|
|
if (long_any_bit_set(tp->dwt.mod_mask,
|
|
|
|
|
ARRAY_LENGTH(tp->dwt.mod_mask)))
|
|
|
|
|
return;
|
|
|
|
|
|
2017-04-03 15:15:01 +10:00
|
|
|
tp_stop_actions(tp, time);
|
2015-05-25 10:07:51 +10:00
|
|
|
tp->dwt.keyboard_active = true;
|
2015-04-20 16:20:00 +10:00
|
|
|
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
|
|
|
|
|
} else {
|
|
|
|
|
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
|
2015-04-20 15:51:20 +10:00
|
|
|
}
|
|
|
|
|
|
2015-05-21 16:32:42 +10:00
|
|
|
tp->dwt.keyboard_last_press_time = time;
|
2016-02-04 08:31:25 +10:00
|
|
|
long_set_bit(tp->dwt.key_mask, key);
|
2015-05-25 10:07:51 +10:00
|
|
|
libinput_timer_set(&tp->dwt.keyboard_timer,
|
2015-04-20 16:20:00 +10:00
|
|
|
time + timeout);
|
2015-04-20 15:51:20 +10:00
|
|
|
}
|
|
|
|
|
|
2015-05-22 16:07:10 +10:00
|
|
|
static bool
|
|
|
|
|
tp_want_dwt(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *keyboard)
|
|
|
|
|
{
|
2015-12-13 22:27:55 -08:00
|
|
|
unsigned int vendor_tp = evdev_device_get_id_vendor(touchpad);
|
|
|
|
|
unsigned int vendor_kbd = evdev_device_get_id_vendor(keyboard);
|
2016-12-19 15:36:34 +10:00
|
|
|
unsigned int product_tp = evdev_device_get_id_product(touchpad);
|
|
|
|
|
unsigned int product_kbd = evdev_device_get_id_product(keyboard);
|
2015-05-22 16:07:10 +10:00
|
|
|
|
2016-12-19 15:36:34 +10:00
|
|
|
/* External touchpads with the same vid/pid as the keyboard are
|
|
|
|
|
considered a happy couple */
|
|
|
|
|
if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
|
|
|
|
|
return vendor_tp == vendor_kbd && product_tp == product_kbd;
|
2017-06-02 13:32:46 +10:00
|
|
|
else if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD)
|
|
|
|
|
return true;
|
2016-12-19 15:36:34 +10:00
|
|
|
|
2017-06-02 13:32:46 +10:00
|
|
|
/* keyboard is not tagged as internal keyboard and it's not part of
|
|
|
|
|
* a combo */
|
|
|
|
|
return false;
|
2015-05-22 16:07:10 +10:00
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:20:38 +10:00
|
|
|
static void
|
|
|
|
|
tp_dwt_pair_keyboard(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *keyboard)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
|
|
|
|
size_t count = 0;
|
2016-02-04 11:20:38 +10:00
|
|
|
|
2016-01-04 10:00:03 +10:00
|
|
|
if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-02-04 11:20:38 +10:00
|
|
|
if (!tp_want_dwt(touchpad, keyboard))
|
|
|
|
|
return;
|
|
|
|
|
|
2018-05-28 11:46:59 +10:00
|
|
|
list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) {
|
|
|
|
|
count++;
|
|
|
|
|
if (count > 3) {
|
|
|
|
|
evdev_log_info(touchpad,
|
|
|
|
|
"too many internal keyboards for dwt\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-10-23 14:34:47 +10:00
|
|
|
}
|
|
|
|
|
|
2018-05-28 11:46:59 +10:00
|
|
|
kbd = zalloc(sizeof(*kbd));
|
|
|
|
|
kbd->device = keyboard;
|
|
|
|
|
libinput_device_add_event_listener(&keyboard->base,
|
|
|
|
|
&kbd->listener,
|
|
|
|
|
tp_keyboard_event, tp);
|
|
|
|
|
list_insert(&tp->dwt.paired_keyboard_list, &kbd->link);
|
|
|
|
|
evdev_log_debug(touchpad,
|
|
|
|
|
"palm: dwt activated with %s<->%s\n",
|
|
|
|
|
touchpad->devname,
|
|
|
|
|
keyboard->devname);
|
2016-02-04 11:20:38 +10:00
|
|
|
}
|
|
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
static void
|
2016-01-04 09:58:36 +10:00
|
|
|
tp_pair_trackpoint(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *trackpoint)
|
2014-09-03 15:45:57 +10:00
|
|
|
{
|
2016-01-04 09:58:36 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
|
|
|
|
|
unsigned int bus_tp = libevdev_get_id_bustype(touchpad->evdev),
|
|
|
|
|
bus_trp = libevdev_get_id_bustype(trackpoint->evdev);
|
2015-05-22 16:07:10 +10:00
|
|
|
bool tp_is_internal, trp_is_internal;
|
2015-04-14 17:04:45 +10:00
|
|
|
|
2016-01-04 10:00:03 +10:00
|
|
|
if ((trackpoint->tags & EVDEV_TAG_TRACKPOINT) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-04-14 17:04:45 +10:00
|
|
|
tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH;
|
|
|
|
|
trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
|
2014-09-03 15:45:57 +10:00
|
|
|
|
2014-09-16 16:22:38 +02:00
|
|
|
if (tp->buttons.trackpoint == NULL &&
|
2015-04-14 17:04:45 +10:00
|
|
|
tp_is_internal && trp_is_internal) {
|
2014-09-16 16:22:39 +02:00
|
|
|
/* Don't send any pending releases to the new trackpoint */
|
|
|
|
|
tp->buttons.active_is_topbutton = false;
|
2016-01-04 09:58:36 +10:00
|
|
|
tp->buttons.trackpoint = trackpoint;
|
2015-06-24 11:41:47 +10:00
|
|
|
if (tp->palm.monitor_trackpoint)
|
2016-01-04 09:58:36 +10:00
|
|
|
libinput_device_add_event_listener(&trackpoint->base,
|
2015-06-24 11:41:47 +10:00
|
|
|
&tp->palm.trackpoint_listener,
|
|
|
|
|
tp_trackpoint_event, tp);
|
2014-09-16 16:22:39 +02:00
|
|
|
}
|
2016-01-04 09:58:36 +10:00
|
|
|
}
|
|
|
|
|
|
2017-01-20 16:54:15 +11:00
|
|
|
static void
|
2018-05-16 10:03:35 +10:00
|
|
|
tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
|
2017-01-20 16:54:15 +11:00
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
struct libinput_event_switch *swev;
|
|
|
|
|
|
2017-01-24 12:16:08 +10:00
|
|
|
if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
|
|
|
|
|
return;
|
2017-01-20 16:54:15 +11:00
|
|
|
|
2017-01-24 12:16:08 +10:00
|
|
|
swev = libinput_event_get_switch_event(event);
|
2018-05-16 10:03:35 +10:00
|
|
|
if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_LID)
|
|
|
|
|
return;
|
2017-09-01 17:30:08 +10:00
|
|
|
|
2018-05-16 10:03:35 +10:00
|
|
|
switch (libinput_event_switch_get_switch_state(swev)) {
|
|
|
|
|
case LIBINPUT_SWITCH_STATE_OFF:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, tp->device, SUSPEND_LID);
|
2018-05-16 10:03:35 +10:00
|
|
|
evdev_log_debug(tp->device, "lid: resume touchpad\n");
|
2017-09-01 17:30:08 +10:00
|
|
|
break;
|
2018-05-16 10:03:35 +10:00
|
|
|
case LIBINPUT_SWITCH_STATE_ON:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, tp->device, SUSPEND_LID);
|
2018-05-16 10:03:35 +10:00
|
|
|
evdev_log_debug(tp->device, "lid: suspending touchpad\n");
|
2017-09-01 17:30:08 +10:00
|
|
|
break;
|
|
|
|
|
}
|
2018-05-16 10:03:35 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_tablet_mode_switch_event(uint64_t time,
|
|
|
|
|
struct libinput_event *event,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
struct libinput_event_switch *swev;
|
|
|
|
|
|
|
|
|
|
if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
swev = libinput_event_get_switch_event(event);
|
|
|
|
|
if (libinput_event_switch_get_switch(swev) !=
|
|
|
|
|
LIBINPUT_SWITCH_TABLET_MODE)
|
|
|
|
|
return;
|
2017-09-01 17:30:08 +10:00
|
|
|
|
2017-01-24 12:16:08 +10:00
|
|
|
switch (libinput_event_switch_get_switch_state(swev)) {
|
|
|
|
|
case LIBINPUT_SWITCH_STATE_OFF:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, tp->device, SUSPEND_TABLET_MODE);
|
2018-05-16 10:03:35 +10:00
|
|
|
evdev_log_debug(tp->device, "tablet-mode: resume touchpad\n");
|
2017-01-24 12:16:08 +10:00
|
|
|
break;
|
|
|
|
|
case LIBINPUT_SWITCH_STATE_ON:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, tp->device, SUSPEND_TABLET_MODE);
|
2018-05-16 10:03:35 +10:00
|
|
|
evdev_log_debug(tp->device, "tablet-mode: suspending touchpad\n");
|
2017-01-24 12:16:08 +10:00
|
|
|
break;
|
2017-01-20 16:54:15 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_pair_lid_switch(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *lid_switch)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
|
|
|
|
|
|
|
|
|
|
if ((lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-06-08 15:15:24 +10:00
|
|
|
if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-01-20 16:54:15 +11:00
|
|
|
if (tp->lid_switch.lid_switch == NULL) {
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(touchpad,
|
2019-05-03 14:12:44 +10:00
|
|
|
"lid: activated for %s<->%s\n",
|
2017-02-13 14:17:52 +10:00
|
|
|
touchpad->devname,
|
|
|
|
|
lid_switch->devname);
|
2017-01-20 16:54:15 +11:00
|
|
|
|
|
|
|
|
libinput_device_add_event_listener(&lid_switch->base,
|
2017-09-01 17:08:17 +10:00
|
|
|
&tp->lid_switch.listener,
|
2018-05-16 10:03:35 +10:00
|
|
|
tp_lid_switch_event, tp);
|
2017-01-20 16:54:15 +11:00
|
|
|
tp->lid_switch.lid_switch = lid_switch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 17:30:08 +10:00
|
|
|
static void
|
|
|
|
|
tp_pair_tablet_mode_switch(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *tablet_mode_switch)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
|
|
|
|
|
|
|
|
|
|
if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-09-20 13:19:36 +10:00
|
|
|
if (tp->tablet_mode_switch.tablet_mode_switch)
|
|
|
|
|
return;
|
2017-09-01 17:30:08 +10:00
|
|
|
|
2018-06-08 15:15:24 +10:00
|
|
|
if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-01-03 02:22:21 +01:00
|
|
|
if (evdev_device_has_model_quirk(touchpad,
|
|
|
|
|
QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
|
|
|
|
|
return;
|
|
|
|
|
|
2017-09-20 13:19:36 +10:00
|
|
|
evdev_log_debug(touchpad,
|
2019-05-03 14:12:44 +10:00
|
|
|
"tablet-mode: activated for %s<->%s\n",
|
2017-09-20 13:19:36 +10:00
|
|
|
touchpad->devname,
|
|
|
|
|
tablet_mode_switch->devname);
|
2017-09-01 17:30:08 +10:00
|
|
|
|
2017-09-20 13:19:36 +10:00
|
|
|
libinput_device_add_event_listener(&tablet_mode_switch->base,
|
|
|
|
|
&tp->tablet_mode_switch.listener,
|
2018-05-16 10:03:35 +10:00
|
|
|
tp_tablet_mode_switch_event, tp);
|
2017-09-20 13:19:36 +10:00
|
|
|
tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch;
|
|
|
|
|
|
|
|
|
|
if (evdev_device_switch_get_state(tablet_mode_switch,
|
|
|
|
|
LIBINPUT_SWITCH_TABLET_MODE)
|
|
|
|
|
== LIBINPUT_SWITCH_STATE_ON) {
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, touchpad, SUSPEND_TABLET_MODE);
|
2017-09-01 17:30:08 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
static void
|
|
|
|
|
tp_change_rotation(struct evdev_device *device, enum notify notify)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
|
|
|
|
|
struct evdev_device *tablet_device = tp->left_handed.tablet_device;
|
|
|
|
|
bool tablet_is_left, touchpad_is_left;
|
|
|
|
|
|
|
|
|
|
if (!tp->left_handed.must_rotate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
touchpad_is_left = device->left_handed.enabled;
|
|
|
|
|
tablet_is_left = tp->left_handed.tablet_left_handed_state;
|
|
|
|
|
|
|
|
|
|
tp->left_handed.want_rotate = touchpad_is_left || tablet_is_left;
|
|
|
|
|
|
|
|
|
|
tp_apply_rotation(device);
|
|
|
|
|
|
|
|
|
|
if (notify == DO_NOTIFY && tablet_device) {
|
|
|
|
|
struct evdev_dispatch *dispatch = tablet_device->dispatch;
|
|
|
|
|
|
|
|
|
|
if (dispatch->interface->left_handed_toggle)
|
|
|
|
|
dispatch->interface->left_handed_toggle(dispatch,
|
|
|
|
|
tablet_device,
|
|
|
|
|
tp->left_handed.want_rotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_pair_tablet(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *tablet)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
|
|
|
|
|
|
|
|
|
|
if (!tp->left_handed.must_rotate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((tablet->seat_caps & EVDEV_DEVICE_TABLET) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (libinput_device_get_device_group(&touchpad->base) !=
|
|
|
|
|
libinput_device_get_device_group(&tablet->base))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tp->left_handed.tablet_device = tablet;
|
|
|
|
|
|
|
|
|
|
evdev_log_debug(touchpad,
|
|
|
|
|
"touchpad-rotation: %s will rotate %s\n",
|
|
|
|
|
touchpad->devname,
|
|
|
|
|
tablet->devname);
|
|
|
|
|
|
|
|
|
|
if (libinput_device_config_left_handed_get(&tablet->base)) {
|
|
|
|
|
tp->left_handed.want_rotate = true;
|
|
|
|
|
tp->left_handed.tablet_left_handed_state = true;
|
|
|
|
|
tp_change_rotation(touchpad, DONT_NOTIFY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 09:58:36 +10:00
|
|
|
static void
|
|
|
|
|
tp_interface_device_added(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *added_device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
|
2014-09-16 16:22:38 +02:00
|
|
|
|
2016-01-04 09:58:36 +10:00
|
|
|
tp_pair_trackpoint(device, added_device);
|
2016-01-04 10:00:03 +10:00
|
|
|
tp_dwt_pair_keyboard(device, added_device);
|
2017-01-20 16:54:15 +11:00
|
|
|
tp_pair_lid_switch(device, added_device);
|
2017-09-01 17:30:08 +10:00
|
|
|
tp_pair_tablet_mode_switch(device, added_device);
|
2019-05-03 15:56:54 +10:00
|
|
|
tp_pair_tablet(device, added_device);
|
2015-04-20 15:51:20 +10:00
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
if (tp->sendevents.current_mode !=
|
|
|
|
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE)
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE);
|
2014-09-03 15:45:57 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_device_removed(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *removed_device)
|
2014-09-03 15:45:57 +10:00
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd, *tmp;
|
2014-09-03 15:45:57 +10:00
|
|
|
|
2014-09-16 16:22:39 +02:00
|
|
|
if (removed_device == tp->buttons.trackpoint) {
|
|
|
|
|
/* Clear any pending releases for the trackpoint */
|
|
|
|
|
if (tp->buttons.active && tp->buttons.active_is_topbutton) {
|
|
|
|
|
tp->buttons.active = 0;
|
|
|
|
|
tp->buttons.active_is_topbutton = false;
|
|
|
|
|
}
|
2015-06-24 11:41:47 +10:00
|
|
|
if (tp->palm.monitor_trackpoint)
|
|
|
|
|
libinput_device_remove_event_listener(
|
|
|
|
|
&tp->palm.trackpoint_listener);
|
2014-09-16 16:22:38 +02:00
|
|
|
tp->buttons.trackpoint = NULL;
|
2014-09-16 16:22:39 +02:00
|
|
|
}
|
2014-09-16 16:22:38 +02:00
|
|
|
|
2018-05-28 11:46:59 +10:00
|
|
|
list_for_each_safe(kbd, tmp, &tp->dwt.paired_keyboard_list, link) {
|
2017-10-23 14:34:47 +10:00
|
|
|
if (kbd->device == removed_device) {
|
2018-05-28 11:46:59 +10:00
|
|
|
evdev_paired_keyboard_destroy(kbd);
|
2017-10-23 14:34:47 +10:00
|
|
|
tp->dwt.keyboard_active = false;
|
|
|
|
|
}
|
2015-04-20 15:51:20 +10:00
|
|
|
}
|
|
|
|
|
|
2017-05-05 12:48:35 +10:00
|
|
|
if (removed_device == tp->lid_switch.lid_switch) {
|
|
|
|
|
libinput_device_remove_event_listener(
|
2017-09-01 17:08:17 +10:00
|
|
|
&tp->lid_switch.listener);
|
2017-05-05 12:48:35 +10:00
|
|
|
tp->lid_switch.lid_switch = NULL;
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, device, SUSPEND_LID);
|
2017-05-05 12:48:35 +10:00
|
|
|
}
|
|
|
|
|
|
2017-09-01 17:30:08 +10:00
|
|
|
if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) {
|
|
|
|
|
libinput_device_remove_event_listener(
|
|
|
|
|
&tp->tablet_mode_switch.listener);
|
|
|
|
|
tp->tablet_mode_switch.tablet_mode_switch = NULL;
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, device, SUSPEND_TABLET_MODE);
|
2017-09-01 17:30:08 +10:00
|
|
|
}
|
|
|
|
|
|
2018-05-16 09:40:50 +10:00
|
|
|
if (tp->sendevents.current_mode ==
|
|
|
|
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) {
|
|
|
|
|
struct libinput_device *dev;
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
|
|
list_for_each(dev, &device->base.seat->devices_list, link) {
|
|
|
|
|
struct evdev_device *d = evdev_device(dev);
|
|
|
|
|
if (d != removed_device &&
|
|
|
|
|
(d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found)
|
|
|
|
|
tp_resume(tp, device, SUSPEND_EXTERNAL_MOUSE);
|
|
|
|
|
}
|
2019-05-03 15:56:54 +10:00
|
|
|
|
|
|
|
|
if (removed_device == tp->left_handed.tablet_device) {
|
|
|
|
|
tp->left_handed.tablet_device = NULL;
|
|
|
|
|
tp->left_handed.tablet_left_handed_state = false;
|
|
|
|
|
|
|
|
|
|
/* Slight awkwardness: removing the tablet causes the
|
|
|
|
|
* touchpad to rotate back to normal if only the tablet was
|
|
|
|
|
* set to left-handed. Niche case, nothing to worry about
|
|
|
|
|
*/
|
|
|
|
|
tp_change_rotation(device, DO_NOTIFY);
|
|
|
|
|
}
|
2014-09-03 15:45:57 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-30 12:05:35 +10:00
|
|
|
static inline void
|
|
|
|
|
evdev_tag_touchpad_internal(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
|
|
|
|
|
device->tags &= ~EVDEV_TAG_EXTERNAL_TOUCHPAD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
evdev_tag_touchpad_external(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
device->tags |= EVDEV_TAG_EXTERNAL_TOUCHPAD;
|
|
|
|
|
device->tags &= ~EVDEV_TAG_INTERNAL_TOUCHPAD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-02 17:22:41 -04:00
|
|
|
evdev_tag_touchpad(struct evdev_device *device,
|
|
|
|
|
struct udev_device *udev_device)
|
2014-09-03 15:45:57 +10:00
|
|
|
{
|
2016-06-30 12:05:35 +10:00
|
|
|
int bustype, vendor;
|
2016-07-06 07:59:31 +10:00
|
|
|
const char *prop;
|
|
|
|
|
|
|
|
|
|
prop = udev_device_get_property_value(udev_device,
|
|
|
|
|
"ID_INPUT_TOUCHPAD_INTEGRATION");
|
|
|
|
|
if (prop) {
|
|
|
|
|
if (streq(prop, "internal")) {
|
|
|
|
|
evdev_tag_touchpad_internal(device);
|
|
|
|
|
return;
|
|
|
|
|
} else if (streq(prop, "external")) {
|
|
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_info(device,
|
|
|
|
|
"tagged with unknown value %s\n",
|
|
|
|
|
prop);
|
2016-07-06 07:59:31 +10:00
|
|
|
}
|
|
|
|
|
}
|
2014-09-03 15:45:57 +10:00
|
|
|
|
2020-05-12 13:46:04 +10:00
|
|
|
/* The hwdb is the authority on integration, these heuristics are
|
|
|
|
|
* the fallback only (they precede the hwdb too).
|
|
|
|
|
*
|
|
|
|
|
* Simple approach: USB is unknown, with the exception
|
|
|
|
|
* of Apple where internal touchpads are connected over USB and it
|
|
|
|
|
* doesn't have external USB touchpads anyway.
|
|
|
|
|
*
|
|
|
|
|
* Bluetooth touchpads are considered external, anything else is
|
|
|
|
|
* internal.
|
2014-09-03 15:45:57 +10:00
|
|
|
*/
|
|
|
|
|
bustype = libevdev_get_id_bustype(device->evdev);
|
2016-06-30 12:05:35 +10:00
|
|
|
vendor = libevdev_get_id_vendor(device->evdev);
|
|
|
|
|
|
|
|
|
|
switch (bustype) {
|
|
|
|
|
case BUS_USB:
|
2018-09-20 10:01:49 +10:00
|
|
|
if (evdev_device_has_model_quirk(device,
|
|
|
|
|
QUIRK_MODEL_APPLE_TOUCHPAD))
|
2016-06-30 12:05:35 +10:00
|
|
|
evdev_tag_touchpad_internal(device);
|
|
|
|
|
break;
|
|
|
|
|
case BUS_BLUETOOTH:
|
|
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
evdev_tag_touchpad_internal(device);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (vendor) {
|
|
|
|
|
/* Logitech does not have internal touchpads */
|
|
|
|
|
case VENDOR_ID_LOGITECH:
|
|
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wacom makes touchpads, but not internal ones */
|
|
|
|
|
if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
|
|
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
|
|
|
|
|
if ((device->tags &
|
|
|
|
|
(EVDEV_TAG_EXTERNAL_TOUCHPAD|EVDEV_TAG_INTERNAL_TOUCHPAD)) == 0) {
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_bug_libinput(device,
|
|
|
|
|
"Internal or external? Please file a bug.\n");
|
2016-06-30 12:05:35 +10:00
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
}
|
2014-09-03 15:45:57 +10:00
|
|
|
}
|
|
|
|
|
|
2018-02-19 14:00:42 +10:00
|
|
|
static void
|
|
|
|
|
tp_arbitration_timeout(uint64_t now, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
if (tp->arbitration.state != ARBITRATION_NOT_ACTIVE)
|
|
|
|
|
tp->arbitration.state = ARBITRATION_NOT_ACTIVE;
|
2018-02-19 14:00:42 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-28 11:28:34 +10:00
|
|
|
static void
|
|
|
|
|
tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2018-09-19 12:02:51 +10:00
|
|
|
enum evdev_arbitration_state which,
|
2018-09-19 12:02:51 +10:00
|
|
|
const struct phys_rect *rect,
|
2018-02-19 14:41:50 +10:00
|
|
|
uint64_t time)
|
2016-06-28 11:28:34 +10:00
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
2016-06-28 11:28:34 +10:00
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
if (which == tp->arbitration.state)
|
2016-06-28 11:28:34 +10:00
|
|
|
return;
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
switch (which) {
|
|
|
|
|
case ARBITRATION_IGNORE_ALL:
|
2018-09-19 12:02:51 +10:00
|
|
|
case ARBITRATION_IGNORE_RECT:
|
2018-02-19 14:00:42 +10:00
|
|
|
libinput_timer_cancel(&tp->arbitration.arbitration_timer);
|
2016-06-28 11:28:34 +10:00
|
|
|
tp_clear_state(tp);
|
2018-09-19 12:02:51 +10:00
|
|
|
tp->arbitration.state = which;
|
|
|
|
|
break;
|
|
|
|
|
case ARBITRATION_NOT_ACTIVE:
|
2018-02-19 14:00:42 +10:00
|
|
|
/* if in-kernel arbitration is in use and there is a touch
|
|
|
|
|
* and a pen in proximity, lifting the pen out of proximity
|
2018-09-28 10:38:43 +10:00
|
|
|
* causes a touch begin for the touch. On a hand-lift the
|
2018-02-19 14:00:42 +10:00
|
|
|
* proximity out precedes the touch up by a few ms, so we
|
|
|
|
|
* get what looks like a tap. Fix this by delaying
|
|
|
|
|
* arbitration by just a little bit so that any touch in
|
|
|
|
|
* event is caught as palm touch. */
|
|
|
|
|
libinput_timer_set(&tp->arbitration.arbitration_timer,
|
|
|
|
|
time + ms2us(90));
|
2018-09-19 12:02:51 +10:00
|
|
|
break;
|
2018-02-19 14:00:42 +10:00
|
|
|
}
|
2016-06-28 11:28:34 +10:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
/* Called when the tablet toggles to left-handed */
|
|
|
|
|
static void
|
|
|
|
|
touchpad_left_handed_toggled(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
bool left_handed_enabled)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = tp_dispatch(dispatch);
|
|
|
|
|
|
|
|
|
|
if (!tp->left_handed.tablet_device)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
evdev_log_debug(device,
|
|
|
|
|
"touchpad-rotation: tablet is %s\n",
|
|
|
|
|
left_handed_enabled ? "left-handed" : "right-handed");
|
|
|
|
|
|
|
|
|
|
/* Our left-handed config is independent even though rotation is
|
|
|
|
|
* locked. So we rotate when either device is left-handed. But it
|
|
|
|
|
* can only be actually changed when the device is in a neutral
|
|
|
|
|
* state, hence the want_rotate.
|
|
|
|
|
*/
|
|
|
|
|
tp->left_handed.tablet_left_handed_state = left_handed_enabled;
|
|
|
|
|
tp_change_rotation(device, DONT_NOTIFY);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static struct evdev_dispatch_interface tp_interface = {
|
2017-09-19 13:28:58 +10:00
|
|
|
.process = tp_interface_process,
|
|
|
|
|
.suspend = tp_interface_suspend,
|
|
|
|
|
.remove = tp_interface_remove,
|
|
|
|
|
.destroy = tp_interface_destroy,
|
|
|
|
|
.device_added = tp_interface_device_added,
|
|
|
|
|
.device_removed = tp_interface_device_removed,
|
|
|
|
|
.device_suspended = tp_interface_device_removed, /* treat as remove */
|
|
|
|
|
.device_resumed = tp_interface_device_added, /* treat as add */
|
|
|
|
|
.post_added = NULL,
|
2018-09-19 12:02:51 +10:00
|
|
|
.touch_arbitration_toggle = tp_interface_toggle_touch,
|
|
|
|
|
.touch_arbitration_update_rect = NULL,
|
2017-09-19 13:45:22 +10:00
|
|
|
.get_switch_state = NULL,
|
2019-05-03 15:56:54 +10:00
|
|
|
.left_handed_toggle = touchpad_left_handed_toggled,
|
2014-02-06 15:05:36 +10:00
|
|
|
};
|
|
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
static void
|
|
|
|
|
tp_init_touch(struct tp_dispatch *tp,
|
2018-02-20 13:27:10 +10:00
|
|
|
struct tp_touch *t,
|
|
|
|
|
unsigned int index)
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
{
|
2014-06-06 17:01:06 +02:00
|
|
|
t->tp = tp;
|
2014-12-11 16:39:04 +10:00
|
|
|
t->has_ended = true;
|
2018-02-20 13:27:10 +10:00
|
|
|
t->index = index;
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
}
|
|
|
|
|
|
2017-01-23 09:55:03 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_disable_abs_mt(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct libevdev *evdev = device->evdev;
|
|
|
|
|
unsigned int code;
|
|
|
|
|
|
|
|
|
|
for (code = ABS_MT_SLOT; code <= ABS_MAX; code++)
|
|
|
|
|
libevdev_disable_event_code(evdev, EV_ABS, code);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
static bool
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_init_slots(struct tp_dispatch *tp,
|
2014-02-06 15:05:36 +10:00
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-14 14:18:27 +10:00
|
|
|
const struct input_absinfo *absinfo;
|
2014-07-18 11:06:38 +02:00
|
|
|
struct map {
|
|
|
|
|
unsigned int code;
|
|
|
|
|
int ntouches;
|
|
|
|
|
} max_touches[] = {
|
|
|
|
|
{ BTN_TOOL_QUINTTAP, 5 },
|
|
|
|
|
{ BTN_TOOL_QUADTAP, 4 },
|
|
|
|
|
{ BTN_TOOL_TRIPLETAP, 3 },
|
|
|
|
|
{ BTN_TOOL_DOUBLETAP, 2 },
|
|
|
|
|
};
|
|
|
|
|
struct map *m;
|
|
|
|
|
unsigned int i, n_btn_tool_touches = 1;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-02-14 14:18:27 +10:00
|
|
|
absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
|
|
|
|
|
if (absinfo) {
|
2015-04-09 15:27:34 +10:00
|
|
|
tp->num_slots = absinfo->maximum + 1;
|
2014-02-14 14:18:27 +10:00
|
|
|
tp->slot = absinfo->value;
|
|
|
|
|
tp->has_mt = true;
|
|
|
|
|
} else {
|
2015-04-09 15:27:34 +10:00
|
|
|
tp->num_slots = 1;
|
2014-02-14 14:18:27 +10:00
|
|
|
tp->slot = 0;
|
|
|
|
|
tp->has_mt = false;
|
2014-07-18 11:06:38 +02:00
|
|
|
}
|
2014-04-15 14:27:58 +02:00
|
|
|
|
2014-07-21 15:25:47 +02:00
|
|
|
tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT);
|
|
|
|
|
|
2016-01-19 09:05:31 +10:00
|
|
|
/* Semi-mt devices are not reliable for true multitouch data, so we
|
|
|
|
|
* simply pretend they're single touch touchpads with BTN_TOOL bits.
|
|
|
|
|
* Synaptics:
|
|
|
|
|
* Terrible resolution when two fingers are down,
|
2015-07-30 11:54:38 +10:00
|
|
|
* causing scroll jumps. The single-touch emulation ABS_X/Y is
|
|
|
|
|
* accurate but the ABS_MT_POSITION touchpoints report the bounding
|
2016-01-19 09:05:31 +10:00
|
|
|
* box and that causes jumps. See https://bugzilla.redhat.com/1235175
|
|
|
|
|
* Elantech:
|
|
|
|
|
* On three-finger taps/clicks, one slot doesn't get a coordinate
|
|
|
|
|
* assigned. See https://bugs.freedesktop.org/show_bug.cgi?id=93583
|
|
|
|
|
* Alps:
|
|
|
|
|
* If three fingers are set down in the same frame, one slot has the
|
|
|
|
|
* coordinates 0/0 and may not get updated for several frames.
|
|
|
|
|
* See https://bugzilla.redhat.com/show_bug.cgi?id=1295073
|
2016-11-02 09:40:42 +10:00
|
|
|
*
|
|
|
|
|
* The HP Pavilion DM4 touchpad has random jumps in slots, including
|
|
|
|
|
* for single-finger movement. See fdo bug 91135
|
2015-07-30 11:54:38 +10:00
|
|
|
*/
|
2016-11-02 09:40:42 +10:00
|
|
|
if (tp->semi_mt ||
|
2018-09-20 10:01:49 +10:00
|
|
|
evdev_device_has_model_quirk(tp->device,
|
|
|
|
|
QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD)) {
|
2015-07-30 11:54:38 +10:00
|
|
|
tp->num_slots = 1;
|
|
|
|
|
tp->slot = 0;
|
|
|
|
|
tp->has_mt = false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-23 09:55:03 +10:00
|
|
|
if (!tp->has_mt)
|
|
|
|
|
tp_disable_abs_mt(device);
|
|
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
ARRAY_FOR_EACH(max_touches, m) {
|
|
|
|
|
if (libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_KEY,
|
|
|
|
|
m->code)) {
|
|
|
|
|
n_btn_tool_touches = m->ntouches;
|
|
|
|
|
break;
|
2014-04-15 14:27:58 +02:00
|
|
|
}
|
2014-02-14 14:18:27 +10:00
|
|
|
}
|
2014-07-18 11:06:38 +02:00
|
|
|
|
2015-04-09 15:27:34 +10:00
|
|
|
tp->ntouches = max(tp->num_slots, n_btn_tool_touches);
|
2017-07-07 11:18:40 +10:00
|
|
|
tp->touches = zalloc(tp->ntouches * sizeof(struct tp_touch));
|
2014-02-06 15:32:32 +10:00
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
for (i = 0; i < tp->ntouches; i++)
|
2018-02-20 13:27:10 +10:00
|
|
|
tp_init_touch(tp, &tp->touches[i], i);
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
|
2017-07-06 11:46:12 +10:00
|
|
|
tp_sync_slots(tp, device);
|
2015-05-08 13:30:13 +10:00
|
|
|
|
2016-12-05 13:39:42 +10:00
|
|
|
/* Some touchpads don't reset BTN_TOOL_FINGER on touch up and only
|
|
|
|
|
* change to/from it when BTN_TOOL_DOUBLETAP is set. This causes us
|
|
|
|
|
* to ignore the first touches events until a two-finger gesture is
|
|
|
|
|
* performed.
|
|
|
|
|
*/
|
|
|
|
|
if (libevdev_get_event_value(device->evdev, EV_KEY, BTN_TOOL_FINGER))
|
|
|
|
|
tp_fake_finger_set(tp, BTN_TOOL_FINGER, 1);
|
|
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
return true;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2018-05-21 12:09:20 +10:00
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_accel_config_set_profile(struct libinput_device *libinput_device,
|
2020-05-22 13:24:34 -04:00
|
|
|
enum libinput_config_accel_profile profile);
|
2015-08-27 13:13:47 +10:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
static bool
|
2020-05-22 13:24:34 -04:00
|
|
|
tp_init_accel(struct tp_dispatch *tp, enum libinput_config_accel_profile which)
|
2014-05-24 16:53:45 +02:00
|
|
|
{
|
2015-08-27 13:13:47 +10:00
|
|
|
struct evdev_device *device = tp->device;
|
2014-05-24 16:53:45 +02:00
|
|
|
int res_x, res_y;
|
2015-07-28 15:06:13 +10:00
|
|
|
struct motion_filter *filter;
|
2018-08-17 15:12:58 +02:00
|
|
|
int dpi = device->dpi;
|
|
|
|
|
bool use_v_avg = device->use_velocity_averaging;
|
2014-05-24 16:53:45 +02:00
|
|
|
|
2015-03-16 13:49:52 +10:00
|
|
|
res_x = tp->device->abs.absinfo_x->resolution;
|
|
|
|
|
res_y = tp->device->abs.absinfo_y->resolution;
|
2014-05-24 16:53:45 +02:00
|
|
|
|
2014-07-01 14:53:18 +02:00
|
|
|
/*
|
|
|
|
|
* Not all touchpads report the same amount of units/mm (resolution).
|
2014-11-28 10:08:02 +10:00
|
|
|
* Normalize motion events to the default mouse DPI as base
|
|
|
|
|
* (unaccelerated) speed. This also evens out any differences in x
|
|
|
|
|
* and y resolution, so that a circle on the
|
2014-07-08 13:43:45 +10:00
|
|
|
* touchpad does not turn into an elipse on the screen.
|
2014-07-01 14:53:18 +02:00
|
|
|
*/
|
2015-06-30 20:56:03 +10:00
|
|
|
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
|
|
|
|
|
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
|
2018-01-10 11:48:58 +10:00
|
|
|
tp->accel.xy_scale_coeff = 1.0 * res_x/res_y;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2020-05-22 13:24:34 -04:00
|
|
|
if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
|
|
|
|
|
filter = create_pointer_accelerator_filter_touchpad_flat(dpi);
|
|
|
|
|
else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) ||
|
|
|
|
|
tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81)
|
2018-08-17 15:12:58 +02:00
|
|
|
filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg);
|
2017-07-02 16:35:36 +02:00
|
|
|
else if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH)
|
2018-08-17 15:12:58 +02:00
|
|
|
filter = create_pointer_accelerator_filter_touchpad(dpi,
|
|
|
|
|
ms2us(50),
|
|
|
|
|
ms2us(10),
|
|
|
|
|
use_v_avg);
|
2015-07-16 15:59:01 +10:00
|
|
|
else
|
2018-08-17 15:12:58 +02:00
|
|
|
filter = create_pointer_accelerator_filter_touchpad(dpi, 0, 0, use_v_avg);
|
2015-04-23 14:32:40 -04:00
|
|
|
|
2015-07-28 15:06:13 +10:00
|
|
|
if (!filter)
|
2016-07-12 11:03:03 +10:00
|
|
|
return false;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
evdev_device_init_pointer_acceleration(tp->device, filter);
|
2015-08-27 13:13:47 +10:00
|
|
|
|
2018-05-21 12:09:20 +10:00
|
|
|
device->pointer.config.set_profile = tp_accel_config_set_profile;
|
2015-08-27 13:13:47 +10:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
return true;
|
2014-02-06 19:43:48 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2020-05-22 13:24:34 -04:00
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_accel_config_set_speed(struct libinput_device *device, double speed)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *dev = evdev_device(device);
|
|
|
|
|
|
|
|
|
|
if (!filter_set_speed(dev->pointer.filter, speed))
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_INVALID;
|
|
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_accel_config_set_profile(struct libinput_device *libinput_device,
|
|
|
|
|
enum libinput_config_accel_profile profile)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *device = evdev_device(libinput_device);
|
|
|
|
|
struct tp_dispatch *tp = tp_dispatch(device->dispatch);
|
|
|
|
|
struct motion_filter *filter;
|
|
|
|
|
double speed;
|
|
|
|
|
|
|
|
|
|
filter = device->pointer.filter;
|
|
|
|
|
if (filter_get_type(filter) == profile)
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
speed = filter_get_speed(filter);
|
|
|
|
|
device->pointer.filter = NULL;
|
|
|
|
|
|
|
|
|
|
if (tp_init_accel(tp, profile)) {
|
|
|
|
|
tp_accel_config_set_speed(libinput_device, speed);
|
|
|
|
|
filter_destroy(filter);
|
|
|
|
|
} else {
|
|
|
|
|
device->pointer.filter = filter;
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-04 10:43:39 +01:00
|
|
|
static uint32_t
|
2015-07-14 10:27:46 +10:00
|
|
|
tp_scroll_get_methods(struct tp_dispatch *tp)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
2015-07-03 14:33:41 +10:00
|
|
|
uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2016-11-02 09:40:42 +10:00
|
|
|
/* Any movement with more than one finger has random cursor
|
|
|
|
|
* jumps. Don't allow for 2fg scrolling on this device, see
|
|
|
|
|
* fdo bug 91135 */
|
2018-09-20 10:01:49 +10:00
|
|
|
if (evdev_device_has_model_quirk(tp->device,
|
|
|
|
|
QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD))
|
2016-11-02 09:40:42 +10:00
|
|
|
return LIBINPUT_CONFIG_SCROLL_EDGE;
|
|
|
|
|
|
2014-11-04 10:43:39 +01:00
|
|
|
if (tp->ntouches >= 2)
|
2014-11-19 12:16:28 +10:00
|
|
|
methods |= LIBINPUT_CONFIG_SCROLL_2FG;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
return methods;
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 10:27:46 +10:00
|
|
|
static uint32_t
|
|
|
|
|
tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2015-07-14 10:27:46 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp_scroll_get_methods(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-04 10:43:39 +01:00
|
|
|
static enum libinput_config_status
|
2014-11-19 12:16:28 +10:00
|
|
|
tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
|
|
|
|
|
enum libinput_config_scroll_method method)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-11-04 10:43:39 +01:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
2015-05-22 15:16:31 +10:00
|
|
|
uint64_t time = libinput_now(tp_libinput_context(tp));
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
if (method == tp->scroll.method)
|
2014-11-04 10:43:39 +01:00
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
|
2015-02-16 10:16:30 +01:00
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
2015-02-16 13:30:24 +01:00
|
|
|
tp_gesture_stop_twofinger_scroll(tp, time);
|
2015-02-16 10:16:30 +01:00
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->scroll.method = method;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_config_scroll_method_get_method(struct libinput_device *device)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-11-04 10:43:39 +01:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
return tp->scroll.method;
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_get_default_method(struct tp_dispatch *tp)
|
|
|
|
|
{
|
2015-07-14 10:27:46 +10:00
|
|
|
uint32_t methods;
|
|
|
|
|
enum libinput_config_scroll_method method;
|
|
|
|
|
|
|
|
|
|
methods = tp_scroll_get_methods(tp);
|
|
|
|
|
|
|
|
|
|
if (methods & LIBINPUT_CONFIG_SCROLL_2FG)
|
|
|
|
|
method = LIBINPUT_CONFIG_SCROLL_2FG;
|
2014-11-24 12:16:06 +01:00
|
|
|
else
|
2015-07-14 10:27:46 +10:00
|
|
|
method = LIBINPUT_CONFIG_SCROLL_EDGE;
|
|
|
|
|
|
|
|
|
|
if ((methods & method) == 0)
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_bug_libinput(tp->device,
|
|
|
|
|
"invalid default scroll method %d\n",
|
|
|
|
|
method);
|
2015-07-14 10:27:46 +10:00
|
|
|
return method;
|
2014-11-24 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_config_scroll_method_get_default_method(struct libinput_device *device)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-11-24 12:16:06 +01:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp_scroll_get_default_method(tp);
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2014-11-18 11:01:10 +10:00
|
|
|
tp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
|
2014-09-18 15:51:53 +10:00
|
|
|
{
|
2016-07-13 07:55:13 +10:00
|
|
|
tp_edge_scroll_init(tp, device);
|
2014-09-18 15:51:53 +10:00
|
|
|
|
2014-11-18 11:01:10 +10:00
|
|
|
evdev_init_natural_scroll(device);
|
2014-09-18 15:51:53 +10:00
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods;
|
|
|
|
|
tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method;
|
|
|
|
|
tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method;
|
|
|
|
|
tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method;
|
2014-11-24 12:16:06 +01:00
|
|
|
tp->scroll.method = tp_scroll_get_default_method(tp);
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->device->base.config.scroll_method = &tp->scroll.config_method;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2015-08-05 10:55:39 +10:00
|
|
|
/* In mm for touchpads with valid resolution, see tp_init_accel() */
|
|
|
|
|
tp->device->scroll.threshold = 0.0;
|
|
|
|
|
tp->device->scroll.direction_lock_threshold = 5.0;
|
2014-09-18 15:51:53 +10:00
|
|
|
}
|
|
|
|
|
|
2015-07-09 08:14:35 +10:00
|
|
|
static int
|
|
|
|
|
tp_dwt_config_is_available(struct libinput_device *device)
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_dwt_config_set(struct libinput_device *device,
|
|
|
|
|
enum libinput_config_dwt_state enable)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2015-07-09 08:14:35 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
switch(enable) {
|
|
|
|
|
case LIBINPUT_CONFIG_DWT_ENABLED:
|
|
|
|
|
case LIBINPUT_CONFIG_DWT_DISABLED:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp->dwt.dwt_enabled = (enable == LIBINPUT_CONFIG_DWT_ENABLED);
|
|
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_dwt_state
|
|
|
|
|
tp_dwt_config_get(struct libinput_device *device)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2015-07-09 08:14:35 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp->dwt.dwt_enabled ?
|
|
|
|
|
LIBINPUT_CONFIG_DWT_ENABLED :
|
|
|
|
|
LIBINPUT_CONFIG_DWT_DISABLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
tp_dwt_default_enabled(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_dwt_state
|
|
|
|
|
tp_dwt_config_get_default(struct libinput_device *device)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2015-07-09 08:14:35 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp_dwt_default_enabled(tp) ?
|
|
|
|
|
LIBINPUT_CONFIG_DWT_ENABLED :
|
|
|
|
|
LIBINPUT_CONFIG_DWT_DISABLED;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-10 10:30:38 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_is_tpkb_combo_below(struct evdev_device *device)
|
|
|
|
|
{
|
2018-05-24 11:56:24 +10:00
|
|
|
struct quirks_context *quirks;
|
|
|
|
|
struct quirks *q;
|
|
|
|
|
char *prop;
|
2017-02-10 10:30:38 +10:00
|
|
|
enum tpkbcombo_layout layout = TPKBCOMBO_LAYOUT_UNKNOWN;
|
2018-05-24 11:56:24 +10:00
|
|
|
int rc = false;
|
2017-02-10 10:30:38 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks = evdev_libinput_context(device)->quirks;
|
|
|
|
|
q = quirks_fetch_for_device(quirks, device->udev_device);
|
|
|
|
|
if (!q)
|
2017-02-10 10:30:38 +10:00
|
|
|
return false;
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
if (quirks_get_string(q, QUIRK_ATTR_TPKBCOMBO_LAYOUT, &prop)) {
|
|
|
|
|
rc = parse_tpkbcombo_layout_poperty(prop, &layout) &&
|
|
|
|
|
layout == TPKBCOMBO_LAYOUT_BELOW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quirks_unref(q);
|
|
|
|
|
|
|
|
|
|
return rc;
|
2017-02-10 10:30:38 +10:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 12:17:38 +10:00
|
|
|
static inline bool
|
|
|
|
|
tp_is_tablet(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
return device->tags & EVDEV_TAG_TABLET_TOUCHPAD;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2015-07-09 08:14:35 +10:00
|
|
|
tp_init_dwt(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2017-02-10 10:39:25 +10:00
|
|
|
if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
|
|
|
|
|
!tp_is_tpkb_combo_below(device))
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-07-09 08:14:35 +10:00
|
|
|
|
|
|
|
|
tp->dwt.config.is_available = tp_dwt_config_is_available;
|
|
|
|
|
tp->dwt.config.set_enabled = tp_dwt_config_set;
|
|
|
|
|
tp->dwt.config.get_enabled = tp_dwt_config_get;
|
|
|
|
|
tp->dwt.config.get_default_enabled = tp_dwt_config_get_default;
|
|
|
|
|
tp->dwt.dwt_enabled = tp_dwt_default_enabled(tp);
|
|
|
|
|
device->base.config.dwt = &tp->dwt.config;
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-07-09 08:14:35 +10:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 13:31:50 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_init_palmdetect_edge(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
2014-07-10 17:34:08 +10:00
|
|
|
{
|
2016-07-15 11:31:07 +10:00
|
|
|
double width, height;
|
|
|
|
|
struct phys_coords mm = { 0.0, 0.0 };
|
|
|
|
|
struct device_coords edges;
|
2014-07-10 17:34:08 +10:00
|
|
|
|
2017-03-23 14:54:00 +10:00
|
|
|
if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
|
|
|
|
|
!tp_is_tpkb_combo_below(device))
|
|
|
|
|
return;
|
|
|
|
|
|
2016-07-15 11:31:07 +10:00
|
|
|
evdev_device_get_size(device, &width, &height);
|
|
|
|
|
|
2017-03-23 14:54:00 +10:00
|
|
|
/* Enable edge palm detection on touchpads >= 70 mm. Anything
|
|
|
|
|
smaller probably won't need it, until we find out it does */
|
2016-07-15 11:31:07 +10:00
|
|
|
if (width < 70.0)
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2014-07-15 14:01:00 +10:00
|
|
|
|
2017-10-25 14:18:17 +10:00
|
|
|
/* palm edges are 8% of the width on each side */
|
2017-10-23 15:30:41 +10:00
|
|
|
mm.x = min(8, width * 0.08);
|
2016-07-15 11:31:07 +10:00
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->palm.left_edge = edges.x;
|
|
|
|
|
|
2017-10-23 15:30:41 +10:00
|
|
|
mm.x = width - min(8, width * 0.08);
|
2016-07-15 11:31:07 +10:00
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->palm.right_edge = edges.x;
|
2017-07-02 18:07:20 +08:00
|
|
|
|
2018-03-13 09:33:57 +10:00
|
|
|
if (!tp->buttons.has_topbuttons && height > 55) {
|
2017-07-02 18:07:20 +08:00
|
|
|
/* top edge is 5% of the height */
|
|
|
|
|
mm.y = height * 0.05;
|
|
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->palm.upper_edge = edges.y;
|
|
|
|
|
}
|
2017-04-03 13:31:50 +10:00
|
|
|
}
|
|
|
|
|
|
2017-06-28 13:09:22 +10:00
|
|
|
static int
|
|
|
|
|
tp_read_palm_pressure_prop(struct tp_dispatch *tp,
|
|
|
|
|
const struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
const int default_palm_threshold = 130;
|
2018-05-24 11:56:24 +10:00
|
|
|
uint32_t threshold = default_palm_threshold;
|
|
|
|
|
struct quirks_context *quirks;
|
|
|
|
|
struct quirks *q;
|
2017-06-28 13:09:22 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks = evdev_libinput_context(device)->quirks;
|
|
|
|
|
q = quirks_fetch_for_device(quirks, device->udev_device);
|
|
|
|
|
if (!q)
|
|
|
|
|
return threshold;
|
2017-06-28 13:09:22 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks_get_uint32(q, QUIRK_ATTR_PALM_PRESSURE_THRESHOLD, &threshold);
|
|
|
|
|
quirks_unref(q);
|
2017-06-28 13:09:22 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
return threshold;
|
2017-06-28 13:09:22 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
tp_init_palmdetect_pressure(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
|
|
|
|
|
tp->palm.use_pressure = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp->palm.pressure_threshold = tp_read_palm_pressure_prop(tp, device);
|
|
|
|
|
tp->palm.use_pressure = true;
|
|
|
|
|
|
|
|
|
|
evdev_log_debug(device,
|
|
|
|
|
"palm: pressure threshold is %d\n",
|
|
|
|
|
tp->palm.pressure_threshold);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-23 14:54:00 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_init_palmdetect_size(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2018-05-24 11:56:24 +10:00
|
|
|
struct quirks_context *quirks;
|
|
|
|
|
struct quirks *q;
|
|
|
|
|
uint32_t threshold;
|
2017-03-23 14:54:00 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks = evdev_libinput_context(device)->quirks;
|
|
|
|
|
q = quirks_fetch_for_device(quirks, device->udev_device);
|
|
|
|
|
if (!q)
|
2017-03-23 14:54:00 +10:00
|
|
|
return;
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
if (quirks_get_uint32(q, QUIRK_ATTR_PALM_SIZE_THRESHOLD, &threshold)) {
|
|
|
|
|
if (threshold == 0) {
|
|
|
|
|
evdev_log_bug_client(device,
|
|
|
|
|
"palm: ignoring invalid threshold %d\n",
|
|
|
|
|
threshold);
|
|
|
|
|
} else {
|
|
|
|
|
tp->palm.use_size = true;
|
|
|
|
|
tp->palm.size_threshold = threshold;
|
|
|
|
|
}
|
2017-03-23 14:54:00 +10:00
|
|
|
}
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks_unref(q);
|
2017-03-23 14:54:00 +10:00
|
|
|
}
|
|
|
|
|
|
2018-02-19 14:00:42 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_init_palmdetect_arbitration(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
char timer_name[64];
|
|
|
|
|
|
|
|
|
|
snprintf(timer_name,
|
|
|
|
|
sizeof(timer_name),
|
|
|
|
|
"%s arbitration",
|
|
|
|
|
evdev_device_get_sysname(device));
|
|
|
|
|
libinput_timer_init(&tp->arbitration.arbitration_timer,
|
|
|
|
|
tp_libinput_context(tp),
|
|
|
|
|
timer_name,
|
|
|
|
|
tp_arbitration_timeout, tp);
|
2018-09-19 12:02:51 +10:00
|
|
|
tp->arbitration.state = ARBITRATION_NOT_ACTIVE;
|
2018-02-19 14:00:42 +10:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 13:31:50 +10:00
|
|
|
static void
|
|
|
|
|
tp_init_palmdetect(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
tp->palm.right_edge = INT_MAX;
|
|
|
|
|
tp->palm.left_edge = INT_MIN;
|
2017-07-02 18:07:20 +08:00
|
|
|
tp->palm.upper_edge = INT_MIN;
|
2017-04-03 13:31:50 +10:00
|
|
|
|
2018-02-19 14:00:42 +10:00
|
|
|
tp_init_palmdetect_arbitration(tp, device);
|
|
|
|
|
|
2017-04-03 13:31:50 +10:00
|
|
|
if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
|
2018-02-09 12:17:38 +10:00
|
|
|
!tp_is_tpkb_combo_below(device) &&
|
|
|
|
|
!tp_is_tablet(device))
|
2017-04-03 13:31:50 +10:00
|
|
|
return;
|
2014-07-10 17:34:08 +10:00
|
|
|
|
2018-02-09 12:17:38 +10:00
|
|
|
if (!tp_is_tablet(device))
|
|
|
|
|
tp->palm.monitor_trackpoint = true;
|
2017-04-03 13:31:50 +10:00
|
|
|
|
2017-04-03 13:58:42 +10:00
|
|
|
if (libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_ABS,
|
|
|
|
|
ABS_MT_TOOL_TYPE))
|
|
|
|
|
tp->palm.use_mt_tool = true;
|
|
|
|
|
|
2018-02-09 12:17:38 +10:00
|
|
|
if (!tp_is_tablet(device))
|
|
|
|
|
tp_init_palmdetect_edge(tp, device);
|
2017-06-28 13:09:22 +10:00
|
|
|
tp_init_palmdetect_pressure(tp, device);
|
2017-03-23 14:54:00 +10:00
|
|
|
tp_init_palmdetect_size(tp, device);
|
2014-07-10 17:34:08 +10:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2014-09-28 13:21:08 +02:00
|
|
|
tp_init_sendevents(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2017-07-03 11:28:18 +10:00
|
|
|
char timer_name[64];
|
|
|
|
|
|
|
|
|
|
snprintf(timer_name,
|
|
|
|
|
sizeof(timer_name),
|
|
|
|
|
"%s trackpoint",
|
|
|
|
|
evdev_device_get_sysname(device));
|
2015-06-24 11:22:29 +10:00
|
|
|
libinput_timer_init(&tp->palm.trackpoint_timer,
|
2015-05-22 15:16:31 +10:00
|
|
|
tp_libinput_context(tp),
|
2017-07-03 11:28:18 +10:00
|
|
|
timer_name,
|
2014-09-28 13:21:08 +02:00
|
|
|
tp_trackpoint_timeout, tp);
|
2015-04-20 15:51:20 +10:00
|
|
|
|
2017-07-03 11:28:18 +10:00
|
|
|
snprintf(timer_name,
|
|
|
|
|
sizeof(timer_name),
|
|
|
|
|
"%s keyboard",
|
|
|
|
|
evdev_device_get_sysname(device));
|
2015-05-25 10:07:51 +10:00
|
|
|
libinput_timer_init(&tp->dwt.keyboard_timer,
|
2015-05-22 15:16:31 +10:00
|
|
|
tp_libinput_context(tp),
|
2017-07-03 11:28:18 +10:00
|
|
|
timer_name,
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_keyboard_timeout, tp);
|
2014-09-28 13:21:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:16:49 +10:00
|
|
|
static bool
|
|
|
|
|
tp_pass_sanity_check(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
2015-05-28 13:41:58 +10:00
|
|
|
{
|
|
|
|
|
struct libevdev *evdev = device->evdev;
|
|
|
|
|
|
|
|
|
|
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER))
|
|
|
|
|
goto error;
|
|
|
|
|
|
2016-07-19 10:16:49 +10:00
|
|
|
return true;
|
2015-05-28 13:41:58 +10:00
|
|
|
|
|
|
|
|
error:
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_bug_kernel(device,
|
|
|
|
|
"device failed touchpad sanity checks\n");
|
2016-07-19 10:16:49 +10:00
|
|
|
return false;
|
2015-05-28 13:41:58 +10:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2015-06-30 14:26:11 +10:00
|
|
|
tp_init_default_resolution(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
const int touchpad_width_mm = 69, /* 1 under palm detection */
|
|
|
|
|
touchpad_height_mm = 50;
|
|
|
|
|
int xres, yres;
|
|
|
|
|
|
2016-07-04 15:41:37 +10:00
|
|
|
if (!device->abs.is_fake_resolution)
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-06-30 14:26:11 +10:00
|
|
|
|
|
|
|
|
/* we only get here if
|
|
|
|
|
* - the touchpad provides no resolution
|
2019-02-13 14:31:30 +10:00
|
|
|
* - the udev hwdb didn't override the resolution
|
2015-06-30 14:26:11 +10:00
|
|
|
* - no ATTR_SIZE_HINT is set
|
|
|
|
|
*
|
|
|
|
|
* The majority of touchpads that triggers all these conditions
|
|
|
|
|
* are old ones, so let's assume a small touchpad size and assume
|
|
|
|
|
* that.
|
|
|
|
|
*/
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_info(device,
|
|
|
|
|
"no resolution or size hints, assuming a size of %dx%dmm\n",
|
|
|
|
|
touchpad_width_mm,
|
|
|
|
|
touchpad_height_mm);
|
2015-06-30 14:26:11 +10:00
|
|
|
|
|
|
|
|
xres = device->abs.dimensions.x/touchpad_width_mm;
|
|
|
|
|
yres = device->abs.dimensions.y/touchpad_height_mm;
|
|
|
|
|
libevdev_set_abs_resolution(device->evdev, ABS_X, xres);
|
|
|
|
|
libevdev_set_abs_resolution(device->evdev, ABS_Y, yres);
|
|
|
|
|
libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_X, xres);
|
|
|
|
|
libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_Y, yres);
|
2016-07-04 15:41:37 +10:00
|
|
|
device->abs.is_fake_resolution = false;
|
2015-06-30 14:26:11 +10:00
|
|
|
}
|
|
|
|
|
|
2016-01-29 16:25:31 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_init_hysteresis(struct tp_dispatch *tp)
|
|
|
|
|
{
|
touchpad: use the fuzz value (if any) for the hysteresis margin
We currently used 0.5mm on touchpads as hysteresis value. This causes pointer
movement delays, it is likely too high. Reduce it to a kernel-set fuzz value
(if any) and see how we go with that. On many touchpads, the fuzz is 8 which
would be closer to 0.2mm on e.g. a T440.
Note that the does some defuzzing anyway, but the response of that function is
nonlinear, e.g. for a fuzz of 8, the physical deltas map to:
phys 0..3 → delta 0
phys 4..7 → delta 1
phys 8..15 → delta 4, 5, 6, 7
phys 16..N → delta 16..N
In other words, we never see some logical deltas 2 and 3. While this shouldn't
matter given the average touchpad resolution, reducing the hysteresis margin
is likely to provide some better response. We never see values 8-15 either
which could be the cause of some pointer jumps we've been seeing.
see https://bugs.freedesktop.org/show_bug.cgi?id=105303
Devices with a fuzz of 0 have the hysteresis margin reduced to 0.25mm (from
0.5mm).
https://bugs.freedesktop.org/show_bug.cgi?id=105108
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-02-21 13:41:31 +10:00
|
|
|
int xmargin, ymargin;
|
|
|
|
|
const struct input_absinfo *ax = tp->device->abs.absinfo_x,
|
|
|
|
|
*ay = tp->device->abs.absinfo_y;
|
2016-01-29 16:25:31 +10:00
|
|
|
|
touchpad: use the fuzz value (if any) for the hysteresis margin
We currently used 0.5mm on touchpads as hysteresis value. This causes pointer
movement delays, it is likely too high. Reduce it to a kernel-set fuzz value
(if any) and see how we go with that. On many touchpads, the fuzz is 8 which
would be closer to 0.2mm on e.g. a T440.
Note that the does some defuzzing anyway, but the response of that function is
nonlinear, e.g. for a fuzz of 8, the physical deltas map to:
phys 0..3 → delta 0
phys 4..7 → delta 1
phys 8..15 → delta 4, 5, 6, 7
phys 16..N → delta 16..N
In other words, we never see some logical deltas 2 and 3. While this shouldn't
matter given the average touchpad resolution, reducing the hysteresis margin
is likely to provide some better response. We never see values 8-15 either
which could be the cause of some pointer jumps we've been seeing.
see https://bugs.freedesktop.org/show_bug.cgi?id=105303
Devices with a fuzz of 0 have the hysteresis margin reduced to 0.25mm (from
0.5mm).
https://bugs.freedesktop.org/show_bug.cgi?id=105108
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-02-21 13:41:31 +10:00
|
|
|
if (ax->fuzz)
|
|
|
|
|
xmargin = ax->fuzz;
|
|
|
|
|
else
|
|
|
|
|
xmargin = ax->resolution/4;
|
|
|
|
|
|
|
|
|
|
if (ay->fuzz)
|
|
|
|
|
ymargin = ay->fuzz;
|
|
|
|
|
else
|
|
|
|
|
ymargin = ay->resolution/4;
|
|
|
|
|
|
|
|
|
|
tp->hysteresis.margin.x = xmargin;
|
|
|
|
|
tp->hysteresis.margin.y = ymargin;
|
2018-02-23 11:44:23 +10:00
|
|
|
tp->hysteresis.enabled = (ax->fuzz || ay->fuzz);
|
|
|
|
|
if (tp->hysteresis.enabled)
|
2018-04-26 11:47:54 +10:00
|
|
|
evdev_log_debug(tp->device,
|
|
|
|
|
"hysteresis enabled. "
|
2018-08-10 20:39:49 +10:00
|
|
|
"See %stouchpad-jitter.html for details\n",
|
2018-04-26 11:47:54 +10:00
|
|
|
HTTP_DOC_LINK);
|
2016-01-29 16:25:31 +10:00
|
|
|
}
|
|
|
|
|
|
2017-01-10 15:58:21 +10:00
|
|
|
static void
|
|
|
|
|
tp_init_pressure(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
const struct input_absinfo *abs;
|
2018-02-20 13:00:24 +10:00
|
|
|
unsigned int code;
|
2018-05-24 11:56:24 +10:00
|
|
|
struct quirks_context *quirks;
|
|
|
|
|
struct quirks *q;
|
|
|
|
|
struct quirk_range r;
|
2017-04-19 13:45:34 +10:00
|
|
|
int hi, lo;
|
2017-01-10 15:58:21 +10:00
|
|
|
|
2018-02-20 13:00:24 +10:00
|
|
|
code = tp->has_mt ? ABS_MT_PRESSURE : ABS_PRESSURE;
|
2017-01-10 15:58:21 +10:00
|
|
|
if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) {
|
|
|
|
|
tp->pressure.use_pressure = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abs = libevdev_get_abs_info(device->evdev, code);
|
|
|
|
|
assert(abs);
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks = evdev_libinput_context(device)->quirks;
|
|
|
|
|
q = quirks_fetch_for_device(quirks, device->udev_device);
|
|
|
|
|
if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
|
|
|
|
|
hi = r.upper;
|
|
|
|
|
lo = r.lower;
|
2017-01-10 15:58:21 +10:00
|
|
|
|
2017-04-19 13:45:34 +10:00
|
|
|
if (hi == 0 && lo == 0) {
|
|
|
|
|
evdev_log_info(device,
|
|
|
|
|
"pressure-based touch detection disabled\n");
|
2018-05-24 11:56:24 +10:00
|
|
|
goto out;
|
2017-04-19 13:45:34 +10:00
|
|
|
}
|
2017-03-06 14:31:40 +10:00
|
|
|
} else {
|
2017-04-19 13:45:34 +10:00
|
|
|
unsigned int range = abs->maximum - abs->minimum;
|
|
|
|
|
|
2017-03-06 14:31:40 +10:00
|
|
|
/* Approximately the synaptics defaults */
|
2017-04-19 13:45:34 +10:00
|
|
|
hi = abs->minimum + 0.12 * range;
|
|
|
|
|
lo = abs->minimum + 0.10 * range;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
|
2017-04-19 13:45:34 +10:00
|
|
|
if (hi > abs->maximum || hi < abs->minimum ||
|
|
|
|
|
lo > abs->maximum || lo < abs->minimum) {
|
|
|
|
|
evdev_log_bug_libinput(device,
|
|
|
|
|
"discarding out-of-bounds pressure range %d:%d\n",
|
|
|
|
|
hi, lo);
|
2018-05-24 11:56:24 +10:00
|
|
|
goto out;
|
2017-03-06 14:31:40 +10:00
|
|
|
}
|
2017-01-10 15:58:21 +10:00
|
|
|
|
2017-04-19 13:45:34 +10:00
|
|
|
tp->pressure.use_pressure = true;
|
|
|
|
|
tp->pressure.high = hi;
|
|
|
|
|
tp->pressure.low = lo;
|
|
|
|
|
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_debug(device,
|
2018-02-28 11:02:41 +10:00
|
|
|
"using pressure-based touch detection (%d:%d)\n",
|
|
|
|
|
lo,
|
|
|
|
|
hi);
|
2018-05-24 11:56:24 +10:00
|
|
|
out:
|
|
|
|
|
quirks_unref(q);
|
2017-01-10 15:58:21 +10:00
|
|
|
}
|
|
|
|
|
|
2017-03-22 16:16:21 +10:00
|
|
|
static bool
|
|
|
|
|
tp_init_touch_size(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2018-05-24 11:56:24 +10:00
|
|
|
struct quirks_context *quirks;
|
|
|
|
|
struct quirks *q;
|
|
|
|
|
struct quirk_range r;
|
2017-03-22 16:16:21 +10:00
|
|
|
int lo, hi;
|
2018-05-24 11:56:24 +10:00
|
|
|
int rc = false;
|
2017-03-22 16:16:21 +10:00
|
|
|
|
|
|
|
|
if (!libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_ABS,
|
|
|
|
|
ABS_MT_TOUCH_MAJOR)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
quirks = evdev_libinput_context(device)->quirks;
|
|
|
|
|
q = quirks_fetch_for_device(quirks, device->udev_device);
|
|
|
|
|
if (q && quirks_get_range(q, QUIRK_ATTR_TOUCH_SIZE_RANGE, &r)) {
|
|
|
|
|
hi = r.upper;
|
|
|
|
|
lo = r.lower;
|
|
|
|
|
} else {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2018-02-07 15:01:31 +10:00
|
|
|
|
2017-03-22 16:16:21 +10:00
|
|
|
if (libevdev_get_num_slots(device->evdev) < 5) {
|
|
|
|
|
evdev_log_bug_libinput(device,
|
|
|
|
|
"Expected 5+ slots for touch size detection\n");
|
2018-05-24 11:56:24 +10:00
|
|
|
goto out;
|
2017-03-22 16:16:21 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hi == 0 && lo == 0) {
|
|
|
|
|
evdev_log_info(device,
|
|
|
|
|
"touch size based touch detection disabled\n");
|
2018-05-24 11:56:24 +10:00
|
|
|
goto out;
|
2017-03-22 16:16:21 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Thresholds apply for both major or minor */
|
|
|
|
|
tp->touch_size.low = lo;
|
|
|
|
|
tp->touch_size.high = hi;
|
|
|
|
|
tp->touch_size.use_touch_size = true;
|
|
|
|
|
|
2018-04-05 11:05:14 +10:00
|
|
|
evdev_log_debug(device,
|
|
|
|
|
"using size-based touch detection (%d:%d)\n",
|
|
|
|
|
hi, lo);
|
2017-03-22 16:16:21 +10:00
|
|
|
|
2018-05-24 11:56:24 +10:00
|
|
|
rc = true;
|
|
|
|
|
out:
|
|
|
|
|
quirks_unref(q);
|
|
|
|
|
return rc;
|
2017-03-22 16:16:21 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static int
|
|
|
|
|
tp_init(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2017-03-22 16:16:21 +10:00
|
|
|
bool use_touch_size = false;
|
|
|
|
|
|
2017-01-30 18:01:09 +10:00
|
|
|
tp->base.dispatch_type = DISPATCH_TOUCHPAD;
|
2014-02-06 15:32:32 +10:00
|
|
|
tp->base.interface = &tp_interface;
|
|
|
|
|
tp->device = device;
|
2018-05-28 11:46:59 +10:00
|
|
|
list_init(&tp->dwt.paired_keyboard_list);
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2016-07-19 10:16:49 +10:00
|
|
|
if (!tp_pass_sanity_check(tp, device))
|
|
|
|
|
return false;
|
2015-05-28 13:41:58 +10:00
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
tp_init_default_resolution(tp, device);
|
2015-06-30 14:26:11 +10:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
if (!tp_init_slots(tp, device))
|
|
|
|
|
return false;
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2016-11-28 14:58:18 +10:00
|
|
|
evdev_device_init_abs_range_warnings(device);
|
2018-02-07 15:01:31 +10:00
|
|
|
use_touch_size = tp_init_touch_size(tp, device);
|
2017-03-22 16:16:21 +10:00
|
|
|
|
|
|
|
|
if (!use_touch_size)
|
|
|
|
|
tp_init_pressure(tp, device);
|
2015-05-06 19:41:25 -04:00
|
|
|
|
2020-03-21 21:03:32 +10:00
|
|
|
/* 5 warnings per 2 hours should be enough */
|
|
|
|
|
ratelimit_init(&tp->jump.warning, s2us(2 * 60 * 60), 5);
|
|
|
|
|
|
2016-12-15 12:45:38 +10:00
|
|
|
/* Set the dpi to that of the x axis, because that's what we normalize
|
|
|
|
|
to when needed*/
|
|
|
|
|
device->dpi = device->abs.absinfo_x->resolution * 25.4;
|
|
|
|
|
|
2016-01-29 16:25:31 +10:00
|
|
|
tp_init_hysteresis(tp);
|
2014-02-06 18:57:10 +10:00
|
|
|
|
2020-05-22 13:24:34 -04:00
|
|
|
if (!tp_init_accel(tp, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE))
|
2016-07-12 11:03:03 +10:00
|
|
|
return false;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
tp_init_tap(tp);
|
|
|
|
|
tp_init_buttons(tp, device);
|
|
|
|
|
tp_init_dwt(tp, device);
|
|
|
|
|
tp_init_palmdetect(tp, device);
|
|
|
|
|
tp_init_sendevents(tp, device);
|
|
|
|
|
tp_init_scroll(tp, device);
|
|
|
|
|
tp_init_gesture(tp);
|
|
|
|
|
tp_init_thumb(tp);
|
2015-05-27 18:25:49 +10:00
|
|
|
|
2014-07-26 21:34:47 +02:00
|
|
|
device->seat_caps |= EVDEV_DEVICE_POINTER;
|
2015-07-30 10:48:39 +10:00
|
|
|
if (tp->gesture.enabled)
|
|
|
|
|
device->seat_caps |= EVDEV_DEVICE_GESTURE;
|
2014-07-26 21:34:47 +02:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
return true;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static uint32_t
|
|
|
|
|
tp_sendevents_get_modes(struct libinput_device *device)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-10-30 15:36:52 +10:00
|
|
|
uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
|
2014-09-03 15:45:57 +10:00
|
|
|
|
|
|
|
|
if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD)
|
|
|
|
|
modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
|
|
|
|
|
|
|
|
|
|
return modes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_suspend_conditional(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *dev;
|
|
|
|
|
|
|
|
|
|
list_for_each(dev, &device->base.seat->devices_list, link) {
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *d = evdev_device(dev);
|
2014-09-03 15:45:57 +10:00
|
|
|
if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) {
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE);
|
|
|
|
|
break;
|
2014-09-03 15:45:57 +10:00
|
|
|
}
|
|
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_sendevents_set_mode(struct libinput_device *device,
|
|
|
|
|
enum libinput_config_send_events_mode mode)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-09-01 17:17:18 +10:00
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
2014-10-30 15:36:52 +10:00
|
|
|
/* DISABLED overrides any DISABLED_ON_ */
|
|
|
|
|
if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) &&
|
|
|
|
|
(mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE))
|
|
|
|
|
mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
if (mode == tp->sendevents.current_mode)
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
switch(mode) {
|
|
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, evdev, SUSPEND_SENDEVENTS);
|
|
|
|
|
tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE);
|
2014-09-01 17:17:18 +10:00
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_suspend(tp, evdev, SUSPEND_SENDEVENTS);
|
|
|
|
|
tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE);
|
2014-09-01 17:17:18 +10:00
|
|
|
break;
|
2014-09-03 15:45:57 +10:00
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
|
|
|
|
|
tp_suspend_conditional(tp, evdev);
|
2018-05-16 09:40:50 +10:00
|
|
|
tp_resume(tp, evdev, SUSPEND_SENDEVENTS);
|
2014-09-03 15:45:57 +10:00
|
|
|
break;
|
2014-09-01 17:17:18 +10:00
|
|
|
default:
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp->sendevents.current_mode = mode;
|
|
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_send_events_mode
|
|
|
|
|
tp_sendevents_get_mode(struct libinput_device *device)
|
|
|
|
|
{
|
2017-01-30 19:48:33 +10:00
|
|
|
struct evdev_device *evdev = evdev_device(device);
|
2014-09-01 17:17:18 +10:00
|
|
|
struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return dispatch->sendevents.current_mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_send_events_mode
|
|
|
|
|
tp_sendevents_get_default_mode(struct libinput_device *device)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-23 14:35:42 +10:00
|
|
|
static void
|
|
|
|
|
tp_change_to_left_handed(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
|
|
|
|
|
|
2015-01-06 21:20:22 -05:00
|
|
|
if (device->left_handed.want_enabled == device->left_handed.enabled)
|
2014-09-23 14:35:42 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* tapping and clickfinger aren't affected by left-handed config,
|
|
|
|
|
* so checking physical buttons is enough */
|
|
|
|
|
|
2015-01-06 21:20:22 -05:00
|
|
|
device->left_handed.enabled = device->left_handed.want_enabled;
|
2019-05-03 15:56:54 +10:00
|
|
|
tp_change_rotation(device, DO_NOTIFY);
|
2014-09-23 14:35:42 +10:00
|
|
|
}
|
|
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
static bool
|
2019-05-03 15:56:54 +10:00
|
|
|
tp_requires_rotation(struct tp_dispatch *tp, struct evdev_device *device)
|
2019-04-30 14:52:28 +10:00
|
|
|
{
|
|
|
|
|
bool rotate = false;
|
|
|
|
|
#if HAVE_LIBWACOM
|
2019-05-27 18:21:09 +10:00
|
|
|
struct libinput *li = tp_libinput_context(tp);
|
2019-05-03 13:52:09 +10:00
|
|
|
WacomDeviceDatabase *db = NULL;
|
2019-04-30 14:52:28 +10:00
|
|
|
WacomDevice **devices = NULL,
|
|
|
|
|
**d;
|
|
|
|
|
WacomDevice *dev;
|
|
|
|
|
uint32_t vid = evdev_device_get_id_vendor(device),
|
|
|
|
|
pid = evdev_device_get_id_product(device);
|
|
|
|
|
|
2019-05-03 13:52:09 +10:00
|
|
|
if ((device->tags & EVDEV_TAG_TABLET_TOUCHPAD) == 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2019-05-27 18:21:09 +10:00
|
|
|
db = libinput_libwacom_ref(li);
|
|
|
|
|
if (!db)
|
2019-04-30 14:52:28 +10:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Check if we have a device with the same vid/pid. If not,
|
|
|
|
|
we need to loop through all devices and check their paired
|
|
|
|
|
device. */
|
|
|
|
|
dev = libwacom_new_from_usbid(db, vid, pid, NULL);
|
|
|
|
|
if (dev) {
|
|
|
|
|
rotate = libwacom_is_reversible(dev);
|
|
|
|
|
libwacom_destroy(dev);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
devices = libwacom_list_devices_from_database(db, NULL);
|
|
|
|
|
if (!devices)
|
|
|
|
|
goto out;
|
|
|
|
|
d = devices;
|
|
|
|
|
while(*d) {
|
|
|
|
|
const WacomMatch *paired;
|
|
|
|
|
|
|
|
|
|
paired = libwacom_get_paired_device(*d);
|
|
|
|
|
if (paired &&
|
|
|
|
|
libwacom_match_get_vendor_id(paired) == vid &&
|
|
|
|
|
libwacom_match_get_product_id(paired) == pid) {
|
|
|
|
|
rotate = libwacom_is_reversible(dev);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
d++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(devices);
|
2019-05-27 18:21:09 +10:00
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
out:
|
2019-05-27 18:21:09 +10:00
|
|
|
/* We don't need to keep it around for the touchpad, we're done with
|
|
|
|
|
* it until the device dies. */
|
2019-04-30 14:52:28 +10:00
|
|
|
if (db)
|
2019-05-27 18:21:09 +10:00
|
|
|
libinput_libwacom_unref(li);
|
2019-04-30 14:52:28 +10:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return rotate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_init_left_handed(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
bool want_left_handed = true;
|
|
|
|
|
|
2019-05-03 15:56:54 +10:00
|
|
|
tp->left_handed.must_rotate = tp_requires_rotation(tp, device);
|
|
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON)
|
|
|
|
|
want_left_handed = false;
|
|
|
|
|
if (want_left_handed)
|
|
|
|
|
evdev_init_left_handed(device, tp_change_to_left_handed);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:05:36 +10:00
|
|
|
struct evdev_dispatch *
|
|
|
|
|
evdev_mt_touchpad_create(struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-06 15:32:32 +10:00
|
|
|
struct tp_dispatch *tp;
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2016-06-30 12:05:35 +10:00
|
|
|
evdev_tag_touchpad(device, device->udev_device);
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
tp = zalloc(sizeof *tp);
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
if (!tp_init(tp, device)) {
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_destroy(&tp->base);
|
2014-02-06 15:05:36 +10:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
device->base.config.sendevents = &tp->sendevents.config;
|
|
|
|
|
|
|
|
|
|
tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
|
|
|
|
tp->sendevents.config.get_modes = tp_sendevents_get_modes;
|
|
|
|
|
tp->sendevents.config.set_mode = tp_sendevents_set_mode;
|
|
|
|
|
tp->sendevents.config.get_mode = tp_sendevents_get_mode;
|
|
|
|
|
tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode;
|
|
|
|
|
|
2019-04-30 14:52:28 +10:00
|
|
|
tp_init_left_handed(tp, device);
|
2014-09-23 14:35:42 +10:00
|
|
|
|
2016-07-19 09:27:50 +10:00
|
|
|
return &tp->base;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|