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
|
|
|
|
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)
|
|
|
|
|
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200)
|
|
|
|
|
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500)
|
2015-07-29 11:00:17 +10:00
|
|
|
#define THUMB_MOVE_TIMEOUT ms2us(300)
|
2015-04-09 14:23:16 +10:00
|
|
|
#define FAKE_FINGER_OVERFLOW (1 << 7)
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2015-03-11 09:10:13 +10:00
|
|
|
static inline struct device_coords *
|
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,
|
2015-03-24 16:43:24 +01:00
|
|
|
const struct normalized_coords *unaccelerated,
|
2014-12-04 11:44:09 +08:00
|
|
|
uint64_t time)
|
2014-02-06 19:43:48 +10:00
|
|
|
{
|
2015-03-24 16:43:24 +01:00
|
|
|
if (normalized_is_zero(*unaccelerated))
|
|
|
|
|
return *unaccelerated;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2015-03-24 16:43:24 +01:00
|
|
|
return filter_dispatch(tp->device->pointer.filter,
|
|
|
|
|
unaccelerated, 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,
|
|
|
|
|
const struct normalized_coords *unaccelerated,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (normalized_is_zero(*unaccelerated))
|
|
|
|
|
return *unaccelerated;
|
|
|
|
|
|
|
|
|
|
return filter_dispatch_constant(tp->device->pointer.filter,
|
|
|
|
|
unaccelerated, tp, time);
|
|
|
|
|
}
|
|
|
|
|
|
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++;
|
|
|
|
|
|
2015-03-11 09:24:52 +10:00
|
|
|
t->history.samples[motion_index] = t->point;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->history.index = motion_index;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 18:57:10 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_hysteresis(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
2015-03-11 09:24:52 +10:00
|
|
|
int x = t->point.x,
|
|
|
|
|
y = t->point.y;
|
2014-02-06 18:57:10 +10:00
|
|
|
|
|
|
|
|
if (t->history.count == 0) {
|
2015-03-11 09:24:52 +10:00
|
|
|
t->hysteresis_center = t->point;
|
2014-02-06 18:57:10 +10:00
|
|
|
} else {
|
2016-04-13 15:23:44 +10:00
|
|
|
x = evdev_hysteresis(x,
|
|
|
|
|
t->hysteresis_center.x,
|
|
|
|
|
tp->hysteresis_margin.x);
|
|
|
|
|
y = evdev_hysteresis(y,
|
|
|
|
|
t->hysteresis_center.y,
|
|
|
|
|
tp->hysteresis_margin.y);
|
2015-03-11 09:24:52 +10:00
|
|
|
t->hysteresis_center.x = x;
|
|
|
|
|
t->hysteresis_center.y = y;
|
|
|
|
|
t->point.x = x;
|
|
|
|
|
t->point.y = y;
|
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)
|
|
|
|
|
{
|
2016-01-07 15:49:02 +10:00
|
|
|
/* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any
|
|
|
|
|
* time */
|
|
|
|
|
if (__builtin_popcount(
|
|
|
|
|
tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1)) > 1)
|
2016-06-09 11:05:12 +10:00
|
|
|
log_bug_kernel(tp_libinput_context(tp),
|
2016-01-07 15:49:02 +10:00
|
|
|
"Invalid fake finger state %#x\n",
|
|
|
|
|
tp->fake_touches);
|
|
|
|
|
|
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;
|
|
|
|
|
|
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;
|
|
|
|
|
t->state = TOUCH_HOVERING;
|
2014-07-18 11:06:36 +02:00
|
|
|
t->pinned.is_pinned = false;
|
|
|
|
|
t->millis = time;
|
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;
|
|
|
|
|
t->millis = time;
|
2014-07-18 11:06:36 +02:00
|
|
|
tp->nfingers_down++;
|
2015-06-24 10:57:07 +10:00
|
|
|
t->palm.time = time;
|
2015-07-17 11:17:22 +10:00
|
|
|
t->thumb.state = THUMB_STATE_MAYBE;
|
|
|
|
|
t->thumb.first_touch_time = time;
|
2015-05-27 18:25:49 +10:00
|
|
|
t->tap.is_thumb = false;
|
2014-07-18 11:06:36 +02:00
|
|
|
assert(tp->nfingers_down >= 1);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
/**
|
|
|
|
|
* End a touch, even if the touch sequence is still active.
|
|
|
|
|
*/
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_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_HOVERING:
|
|
|
|
|
t->state = TOUCH_NONE;
|
|
|
|
|
/* fallthough */
|
|
|
|
|
case TOUCH_NONE:
|
|
|
|
|
case TOUCH_END:
|
2014-02-06 15:32:32 +10:00
|
|
|
return;
|
2014-12-11 16:39:04 +10:00
|
|
|
case TOUCH_BEGIN:
|
|
|
|
|
case TOUCH_UPDATE:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
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;
|
2014-07-18 11:06:36 +02:00
|
|
|
t->millis = time;
|
2015-05-21 16:32:42 +10:00
|
|
|
t->palm.time = 0;
|
2014-02-06 15:32:32 +10:00
|
|
|
assert(tp->nfingers_down >= 1);
|
|
|
|
|
tp->nfingers_down--;
|
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;
|
|
|
|
|
tp_end_touch(tp, t, time);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static double
|
|
|
|
|
tp_estimate_delta(int x0, int x1, int x2, int x3)
|
|
|
|
|
{
|
2014-07-18 11:06:39 +02:00
|
|
|
return (x0 + x1 - x2 - x3) / 4.0;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2015-03-11 09:58:11 +10:00
|
|
|
struct normalized_coords
|
|
|
|
|
tp_get_delta(struct tp_touch *t)
|
2014-02-06 15:05:36 +10:00
|
|
|
{
|
2015-03-25 11:01:04 +01:00
|
|
|
struct device_float_coords delta;
|
2015-03-24 13:14:18 +01:00
|
|
|
const struct normalized_coords zero = { 0.0, 0.0 };
|
2015-03-11 09:58:11 +10:00
|
|
|
|
|
|
|
|
if (t->history.count < TOUCHPAD_MIN_SAMPLES)
|
2015-03-24 13:14:18 +01:00
|
|
|
return zero;
|
|
|
|
|
|
2015-03-25 11:01:04 +01:00
|
|
|
delta.x = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
|
|
|
|
|
tp_motion_history_offset(t, 1)->x,
|
|
|
|
|
tp_motion_history_offset(t, 2)->x,
|
|
|
|
|
tp_motion_history_offset(t, 3)->x);
|
|
|
|
|
delta.y = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
|
|
|
|
|
tp_motion_history_offset(t, 1)->y,
|
|
|
|
|
tp_motion_history_offset(t, 2)->y,
|
|
|
|
|
tp_motion_history_offset(t, 3)->y);
|
2015-03-24 13:14:18 +01:00
|
|
|
|
|
|
|
|
return tp_normalize_delta(t->tp, delta);
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 14:52:57 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_check_axis_range(struct tp_dispatch *tp,
|
|
|
|
|
unsigned int code,
|
|
|
|
|
int value)
|
|
|
|
|
{
|
|
|
|
|
int min, max;
|
|
|
|
|
|
|
|
|
|
switch(code) {
|
|
|
|
|
case ABS_X:
|
|
|
|
|
case ABS_MT_POSITION_X:
|
|
|
|
|
min = tp->warning_range.min.x;
|
|
|
|
|
max = tp->warning_range.max.x;
|
|
|
|
|
break;
|
|
|
|
|
case ABS_Y:
|
|
|
|
|
case ABS_MT_POSITION_Y:
|
|
|
|
|
min = tp->warning_range.min.y;
|
|
|
|
|
max = tp->warning_range.max.y;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value < min || value > max) {
|
|
|
|
|
log_info_ratelimit(tp_libinput_context(tp),
|
|
|
|
|
&tp->warning_range.range_warn_limit,
|
|
|
|
|
"Axis %#x value %d is outside expected range [%d, %d]\n"
|
|
|
|
|
"See %s/absolute_coordinate_ranges.html for details\n",
|
|
|
|
|
code, value, min, max,
|
|
|
|
|
HTTP_DOC_LINK);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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-05-31 14:52:57 +10:00
|
|
|
tp_check_axis_range(tp, e->code, e->value);
|
2015-03-11 09:24:52 +10:00
|
|
|
t->point.x = e->value;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->millis = time;
|
|
|
|
|
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-05-31 14:52:57 +10:00
|
|
|
tp_check_axis_range(tp, e->code, e->value);
|
2015-03-11 09:24:52 +10:00
|
|
|
t->point.y = e->value;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->millis = time;
|
|
|
|
|
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;
|
2015-05-06 19:41:25 -04:00
|
|
|
case ABS_MT_DISTANCE:
|
|
|
|
|
t->distance = e->value;
|
|
|
|
|
break;
|
2014-02-06 15:32:32 +10:00
|
|
|
case ABS_MT_TRACKING_ID:
|
|
|
|
|
if (e->value != -1)
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_new_touch(tp, t, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
else
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_end_sequence(tp, t, time);
|
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;
|
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;
|
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-05-31 14:52:57 +10:00
|
|
|
tp_check_axis_range(tp, e->code, e->value);
|
2015-03-11 09:24:52 +10:00
|
|
|
t->point.x = e->value;
|
2014-02-14 14:18:27 +10:00
|
|
|
t->millis = time;
|
|
|
|
|
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-05-31 14:52:57 +10:00
|
|
|
tp_check_axis_range(tp, e->code, e->value);
|
2015-03-11 09:24:52 +10:00
|
|
|
t->point.y = e->value;
|
2014-02-14 14:18:27 +10:00
|
|
|
t->millis = time;
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 ||
|
|
|
|
|
tp->nfingers_down == tp->num_slots)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Synaptics devices may end touch 2 on BTN_TOOL_TRIPLETAP
|
|
|
|
|
* and start it again on the next frame with different coordinates
|
|
|
|
|
* (#91352). 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.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < tp->num_slots; i++) {
|
|
|
|
|
struct tp_touch *t = tp_get_touch(tp, i);
|
|
|
|
|
|
|
|
|
|
if (t->state != TOUCH_END)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* new touch, move it through begin to update immediately */
|
|
|
|
|
tp_new_touch(tp, t, time);
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
|
|
|
|
t->state = TOUCH_UPDATE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
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;
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-17 14:24:20 +10:00
|
|
|
static void
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
|
2014-02-17 14:24:20 +10:00
|
|
|
{
|
2015-06-25 12:20:04 +10:00
|
|
|
double xdist, ydist;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
if (!t->pinned.is_pinned)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-03-11 09:24:52 +10:00
|
|
|
xdist = abs(t->point.x - t->pinned.center.x);
|
2015-06-12 17:29:41 +10:00
|
|
|
xdist *= tp->buttons.motion_dist.x_scale_coeff;
|
2015-03-11 09:24:52 +10:00
|
|
|
ydist = abs(t->point.y - t->pinned.center.y);
|
2015-06-12 17:29:41 +10:00
|
|
|
ydist *= tp->buttons.motion_dist.y_scale_coeff;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2015-07-20 11:24:17 +10:00
|
|
|
/* 1.5mm movement -> unpin */
|
|
|
|
|
if (hypot(xdist, ydist) >= 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 &&
|
2015-07-17 11:17:22 +10:00
|
|
|
t->thumb.state != THUMB_STATE_YES &&
|
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
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if (t->point.x > tp->palm.left_edge &&
|
|
|
|
|
t->point.x < tp->palm.right_edge)
|
|
|
|
|
return false;
|
|
|
|
|
|
2016-02-15 17:02:45 +10:00
|
|
|
/* We're inside the left/right palm edge and not in one of the
|
|
|
|
|
* software button areas */
|
|
|
|
|
if (t->point.y < tp->buttons.bottom_area.top_edge) {
|
2015-05-22 15:14:04 +10:00
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: palm-tap detected\n");
|
2015-04-15 15:21:08 +10:00
|
|
|
return true;
|
2015-05-22 15:14:04 +10:00
|
|
|
}
|
2015-04-15 15:21:08 +10:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: touch released, timeout after typing\n");
|
|
|
|
|
}
|
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;
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: touch released, timeout after trackpoint\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
return false;
|
2015-06-23 15:36:05 +10:00
|
|
|
}
|
|
|
|
|
|
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);
|
2014-07-14 16:38:46 +10:00
|
|
|
const int DIRECTIONS = NE|E|SE|SW|W|NW;
|
2015-03-25 15:05:19 +01:00
|
|
|
struct device_float_coords delta;
|
|
|
|
|
int dirs;
|
2014-07-14 16:06:51 +10:00
|
|
|
|
2016-06-15 09:07:48 +10:00
|
|
|
if (time < t->palm.time + PALM_TIMEOUT &&
|
|
|
|
|
(t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) {
|
|
|
|
|
delta = device_delta(t->point, t->palm.first);
|
|
|
|
|
dirs = normalized_get_direction(tp_normalize_delta(tp, delta));
|
|
|
|
|
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 09:07:48 +10:00
|
|
|
static void
|
|
|
|
|
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
if (tp_palm_detect_dwt_triggered(tp, t, time))
|
2015-06-24 11:00:52 +10:00
|
|
|
goto out;
|
2014-07-14 16:06:51 +10:00
|
|
|
|
2016-07-19 10:05:37 +10:00
|
|
|
if (tp_palm_detect_trackpoint_triggered(tp, t, time))
|
2015-06-23 15:36:05 +10:00
|
|
|
goto out;
|
|
|
|
|
|
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;
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: touch released, multiple fingers\n");
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: touch released, out of edge zone\n");
|
2014-07-14 16:06:51 +10:00
|
|
|
}
|
2014-07-10 17:34:08 +10:00
|
|
|
return;
|
2016-06-13 16:41:43 +10:00
|
|
|
} else if (tp_palm_detect_multifinger(tp, t, time)) {
|
|
|
|
|
return;
|
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 */
|
|
|
|
|
if (t->state != TOUCH_BEGIN ||
|
2015-03-11 09:24:52 +10:00
|
|
|
(t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge))
|
2014-07-10 17:34:08 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* 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))
|
|
|
|
|
return;
|
|
|
|
|
|
2015-06-16 15:39:48 +10:00
|
|
|
if (tp_touch_get_edge(tp, t) & EDGE_RIGHT)
|
|
|
|
|
return;
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"palm: palm detected (%s)\n",
|
2015-06-23 15:36:05 +10:00
|
|
|
t->palm.state == PALM_EDGE ? "edge" :
|
|
|
|
|
t->palm.state == PALM_TYPING ? "typing" : "trackpoint");
|
2014-07-10 17:34:08 +10:00
|
|
|
}
|
|
|
|
|
|
2015-07-17 11:17:22 +10:00
|
|
|
static inline const char*
|
|
|
|
|
thumb_state_to_str(enum tp_thumb_state state)
|
|
|
|
|
{
|
|
|
|
|
switch(state){
|
|
|
|
|
CASE_RETURN_STRING(THUMB_STATE_NO);
|
|
|
|
|
CASE_RETURN_STRING(THUMB_STATE_YES);
|
|
|
|
|
CASE_RETURN_STRING(THUMB_STATE_MAYBE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-27 18:25:49 +10:00
|
|
|
static void
|
2015-07-17 11:17:22 +10:00
|
|
|
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
2015-05-27 18:25:49 +10:00
|
|
|
{
|
2015-07-17 11:17:22 +10:00
|
|
|
enum tp_thumb_state state = t->thumb.state;
|
|
|
|
|
|
|
|
|
|
/* once a thumb, always a thumb, once ruled out always ruled out */
|
|
|
|
|
if (!tp->thumb.detect_thumbs ||
|
|
|
|
|
t->thumb.state != THUMB_STATE_MAYBE)
|
2015-05-27 18:25:49 +10:00
|
|
|
return;
|
|
|
|
|
|
2015-07-17 11:17:22 +10:00
|
|
|
if (t->point.y < tp->thumb.upper_thumb_line) {
|
|
|
|
|
/* if a potential thumb is above the line, it won't ever
|
|
|
|
|
* label as thumb */
|
|
|
|
|
t->thumb.state = THUMB_STATE_NO;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-22 11:53:22 +10:00
|
|
|
/* If the thumb moves by more than 7mm, it's not a resting thumb */
|
|
|
|
|
if (t->state == TOUCH_BEGIN)
|
|
|
|
|
t->thumb.initial = t->point;
|
|
|
|
|
else if (t->state == TOUCH_UPDATE) {
|
|
|
|
|
struct device_float_coords delta;
|
|
|
|
|
struct normalized_coords normalized;
|
|
|
|
|
|
|
|
|
|
delta = device_delta(t->point, t->thumb.initial);
|
|
|
|
|
normalized = tp_normalize_delta(tp, delta);
|
|
|
|
|
if (normalized_length(normalized) >
|
|
|
|
|
TP_MM_TO_DPI_NORMALIZED(7)) {
|
|
|
|
|
t->thumb.state = THUMB_STATE_NO;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-27 18:25:49 +10:00
|
|
|
/* Note: a thumb at the edge of the touchpad won't trigger the
|
2015-07-17 11:17:22 +10:00
|
|
|
* threshold, the surface area is usually too small. So we have a
|
|
|
|
|
* two-stage detection: pressure and time within the area.
|
|
|
|
|
* A finger that remains at the very bottom of the touchpad becomes
|
|
|
|
|
* a thumb.
|
2015-05-27 18:25:49 +10:00
|
|
|
*/
|
2015-07-17 11:17:22 +10:00
|
|
|
if (t->pressure > tp->thumb.threshold)
|
|
|
|
|
t->thumb.state = THUMB_STATE_YES;
|
|
|
|
|
else if (t->point.y > tp->thumb.lower_thumb_line &&
|
|
|
|
|
tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE &&
|
2015-07-29 11:00:17 +10:00
|
|
|
t->thumb.first_touch_time + THUMB_MOVE_TIMEOUT < time)
|
2015-07-17 11:17:22 +10:00
|
|
|
t->thumb.state = THUMB_STATE_YES;
|
2015-05-27 18:25:49 +10:00
|
|
|
|
|
|
|
|
/* now what? we marked it as thumb, so:
|
|
|
|
|
*
|
|
|
|
|
* - pointer motion must ignore this touch
|
|
|
|
|
* - clickfinger must ignore this touch for finger count
|
|
|
|
|
* - software buttons are unaffected
|
|
|
|
|
* - edge scrolling unaffected
|
2015-07-15 10:04:46 +10:00
|
|
|
* - gestures: unaffected
|
2015-05-27 18:25:49 +10:00
|
|
|
* - tapping: honour thumb on begin, ignore it otherwise for now,
|
|
|
|
|
* this gets a tad complicated otherwise
|
|
|
|
|
*/
|
2015-07-17 11:17:22 +10:00
|
|
|
out:
|
|
|
|
|
if (t->thumb.state != state)
|
|
|
|
|
log_debug(tp_libinput_context(tp),
|
|
|
|
|
"thumb state: %s → %s\n",
|
|
|
|
|
thumb_state_to_str(state),
|
|
|
|
|
thumb_state_to_str(t->thumb.state));
|
2015-05-27 18:25:49 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-11 16:39:04 +10:00
|
|
|
static void
|
2015-05-06 19:41:25 -04:00
|
|
|
tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
2016-01-18 17:04:39 +10:00
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-05-06 19:41:25 -04:00
|
|
|
if (t->state == TOUCH_HOVERING) {
|
|
|
|
|
if (t->distance == 0) {
|
|
|
|
|
/* avoid jumps when landing a finger */
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
tp_begin_touch(tp, t, time);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (t->distance > 0)
|
|
|
|
|
tp_end_touch(tp, t, time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
for (i = 0; i < (int)tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
tp_end_touch(tp, t, time);
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (tp->reports_distance)
|
|
|
|
|
tp_unhover_abs_distance(tp, time);
|
|
|
|
|
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) {
|
|
|
|
|
log_bug_libinput(tp_libinput_context(tp),
|
|
|
|
|
"Unable to find topmost touch\n");
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* if we're transitioning between slots and fake touches in either
|
|
|
|
|
* direction, we may get a coordinate jump
|
|
|
|
|
*/
|
|
|
|
|
if (tp->nfingers_down != tp->old_nfingers_down &&
|
|
|
|
|
(tp->nfingers_down > tp->num_slots ||
|
|
|
|
|
tp->old_nfingers_down > tp->num_slots))
|
|
|
|
|
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
|
|
|
|
|
tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
struct device_coords *last;
|
|
|
|
|
double dx, dy;
|
|
|
|
|
const int JUMP_THRESHOLD_MM = 20;
|
|
|
|
|
|
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;
|
|
|
|
|
|
2014-09-16 15:13:00 +10:00
|
|
|
if (t->history.count == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* called before tp_motion_history_push, so offset 0 is the most
|
|
|
|
|
* recent coordinate */
|
|
|
|
|
last = tp_motion_history_offset(t, 0);
|
2016-10-24 11:04:18 +10:00
|
|
|
dx = 1.0 * abs(t->point.x - last->x) / tp->device->abs.absinfo_x->resolution;
|
|
|
|
|
dy = 1.0 * abs(t->point.y - last->y) / tp->device->abs.absinfo_y->resolution;
|
2014-09-16 15:13:00 +10:00
|
|
|
|
|
|
|
|
return hypot(dx, dy) > JUMP_THRESHOLD_MM;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2014-07-18 11:06:38 +02:00
|
|
|
unsigned int i;
|
2015-06-10 09:54:06 +10:00
|
|
|
bool restart_filter = false;
|
2015-07-20 13:10:29 +10:00
|
|
|
bool want_motion_reset;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2015-04-08 17:00:50 +10:00
|
|
|
tp_process_fake_touches(tp, time);
|
2014-12-11 16:39:04 +10:00
|
|
|
tp_unhover_touches(tp, time);
|
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
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
for (i = 0; i < tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
2014-07-21 15:25:47 +02:00
|
|
|
|
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
|
|
|
|
2014-07-18 11:06:37 +02:00
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-09-16 15:13:00 +10:00
|
|
|
if (tp_detect_jumps(tp, t)) {
|
2016-06-10 10:30:24 +10:00
|
|
|
if (!tp->semi_mt)
|
|
|
|
|
log_bug_kernel(tp_libinput_context(tp),
|
|
|
|
|
"Touch jump detected and discarded.\n"
|
|
|
|
|
"See %stouchpad_jumping_cursor.html for details\n",
|
|
|
|
|
HTTP_DOC_LINK);
|
2014-09-16 15:13:00 +10:00
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 11:17:22 +10:00
|
|
|
tp_thumb_detect(tp, t, time);
|
2014-07-14 16:06:51 +10:00
|
|
|
tp_palm_detect(tp, t, time);
|
2014-07-10 17:34:08 +10:00
|
|
|
|
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
|
|
|
|
|
|
|
|
tp_unpin_finger(tp, t);
|
2015-06-10 09:54:06 +10:00
|
|
|
|
|
|
|
|
if (t->state == TOUCH_BEGIN)
|
|
|
|
|
restart_filter = true;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
2014-02-17 14:24:20 +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
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:05:49 +10:00
|
|
|
static void
|
|
|
|
|
tp_handle_state(struct tp_dispatch *tp,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
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);
|
2014-09-01 17:05:49 +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
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch *)dispatch;
|
|
|
|
|
|
2016-06-28 11:28:34 +10:00
|
|
|
if (tp->ignore_events)
|
|
|
|
|
return;
|
|
|
|
|
|
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;
|
|
|
|
|
case EV_SYN:
|
2014-09-01 17:05:49 +10:00
|
|
|
tp_handle_state(tp, time);
|
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
|
|
|
{
|
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
|
|
|
|
2015-05-25 10:07:51 +10:00
|
|
|
if (tp->dwt.keyboard)
|
2015-04-20 15:51:20 +10:00
|
|
|
libinput_device_remove_event_listener(
|
2015-05-25 10:07:51 +10:00
|
|
|
&tp->dwt.keyboard_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
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch*)dispatch;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch*)dispatch;
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
tp_suspend(struct tp_dispatch *tp, struct evdev_device *device)
|
|
|
|
|
{
|
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);
|
|
|
|
|
}
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch *)dispatch;
|
|
|
|
|
|
|
|
|
|
tp_clear_state(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static void
|
|
|
|
|
tp_resume(struct tp_dispatch *tp, struct evdev_device *device)
|
|
|
|
|
{
|
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);
|
|
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
|
|
tp_tap_resume(tp, now);
|
2015-06-24 11:22:29 +10:00
|
|
|
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++;
|
|
|
|
|
|
|
|
|
|
/* Require at least three events before enabling palm detection */
|
|
|
|
|
if (tp->palm.trackpoint_event_count < 3)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-06-24 11:22:29 +10:00
|
|
|
if (!tp->palm.trackpoint_active) {
|
2015-02-16 10:24:28 +01:00
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
2015-05-22 10:09:22 +10:00
|
|
|
tp_gesture_cancel(tp, time);
|
2014-09-28 13:21:08 +02:00
|
|
|
tp_tap_suspend(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;
|
|
|
|
|
log_debug(tp_libinput_context(tp), "palm: keyboard timeout refresh\n");
|
|
|
|
|
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
|
|
|
|
|
|
|
|
log_debug(tp_libinput_context(tp), "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;
|
|
|
|
|
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
2015-05-22 10:09:22 +10:00
|
|
|
tp_gesture_cancel(tp, time);
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_tap_suspend(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-07-09 08:14:35 +10:00
|
|
|
static bool
|
|
|
|
|
tp_dwt_device_is_blacklisted(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
unsigned int bus = libevdev_get_id_bustype(device->evdev);
|
|
|
|
|
|
|
|
|
|
/* evemu will set the right bus type */
|
2016-06-30 12:05:35 +10:00
|
|
|
if (bus == BUS_VIRTUAL)
|
2015-07-09 08:14:35 +10:00
|
|
|
return true;
|
|
|
|
|
|
2016-06-30 12:05:35 +10:00
|
|
|
if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
|
2015-07-09 08:14:35 +10:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-22 16:07:10 +10:00
|
|
|
static bool
|
|
|
|
|
tp_want_dwt(struct evdev_device *touchpad,
|
|
|
|
|
struct evdev_device *keyboard)
|
|
|
|
|
{
|
|
|
|
|
unsigned int bus_tp = libevdev_get_id_bustype(touchpad->evdev),
|
|
|
|
|
bus_kbd = libevdev_get_id_bustype(keyboard->evdev);
|
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);
|
2015-05-22 16:07:10 +10:00
|
|
|
|
2015-07-09 08:14:35 +10:00
|
|
|
if (tp_dwt_device_is_blacklisted(touchpad) ||
|
|
|
|
|
tp_dwt_device_is_blacklisted(keyboard))
|
2015-05-22 16:07:10 +10:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* If the touchpad is on serio, the keyboard is too, so ignore any
|
|
|
|
|
other devices */
|
|
|
|
|
if (bus_tp == BUS_I8042 && bus_kbd != bus_tp)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-12-13 22:27:55 -08:00
|
|
|
/* For Apple touchpads, always use its internal keyboard */
|
|
|
|
|
if (vendor_tp == VENDOR_ID_APPLE) {
|
|
|
|
|
return vendor_kbd == vendor_tp &&
|
|
|
|
|
keyboard->model_flags &
|
|
|
|
|
EVDEV_MODEL_APPLE_INTERNAL_KEYBOARD;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-22 16:07:10 +10:00
|
|
|
/* everything else we don't really know, so we have to assume
|
|
|
|
|
they go together */
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
unsigned int bus_kbd = libevdev_get_id_bustype(keyboard->evdev);
|
|
|
|
|
|
|
|
|
|
if (!tp_want_dwt(touchpad, keyboard))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* If we already have a keyboard paired, override it if the new one
|
|
|
|
|
* is a serio device. Otherwise keep the current one */
|
|
|
|
|
if (tp->dwt.keyboard) {
|
|
|
|
|
if (bus_kbd != BUS_I8042)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memset(tp->dwt.key_mask, 0, sizeof(tp->dwt.key_mask));
|
2016-08-04 10:43:25 +10:00
|
|
|
memset(tp->dwt.mod_mask, 0, sizeof(tp->dwt.mod_mask));
|
2016-02-04 11:20:38 +10:00
|
|
|
libinput_device_remove_event_listener(&tp->dwt.keyboard_listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libinput_device_add_event_listener(&keyboard->base,
|
|
|
|
|
&tp->dwt.keyboard_listener,
|
|
|
|
|
tp_keyboard_event, tp);
|
|
|
|
|
tp->dwt.keyboard = keyboard;
|
|
|
|
|
tp->dwt.keyboard_active = false;
|
|
|
|
|
|
|
|
|
|
log_debug(touchpad->base.seat->libinput,
|
|
|
|
|
"palm: dwt activated with %s<->%s\n",
|
|
|
|
|
touchpad->devname,
|
|
|
|
|
keyboard->devname);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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 &&
|
2016-01-04 09:58:36 +10:00
|
|
|
(trackpoint->tags & EVDEV_TAG_TRACKPOINT) &&
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-02-04 11:20:38 +10:00
|
|
|
if (added_device->tags & EVDEV_TAG_KEYBOARD)
|
|
|
|
|
tp_dwt_pair_keyboard(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)
|
|
|
|
|
tp_suspend(tp, device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
struct libinput_device *dev;
|
|
|
|
|
|
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
|
|
|
|
2015-05-25 10:07:51 +10:00
|
|
|
if (removed_device == tp->dwt.keyboard) {
|
2015-04-20 15:51:20 +10:00
|
|
|
libinput_device_remove_event_listener(
|
2015-05-25 10:07:51 +10:00
|
|
|
&tp->dwt.keyboard_listener);
|
|
|
|
|
tp->dwt.keyboard = NULL;
|
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;
|
|
|
|
|
|
|
|
|
|
list_for_each(dev, &device->base.seat->devices_list, link) {
|
|
|
|
|
struct evdev_device *d = (struct evdev_device*)dev;
|
|
|
|
|
if (d != removed_device &&
|
|
|
|
|
(d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp_resume(tp, device);
|
|
|
|
|
}
|
|
|
|
|
|
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 {
|
|
|
|
|
log_info(evdev_libinput_context(device),
|
|
|
|
|
"%s: tagged as unknown value %s\n",
|
|
|
|
|
device->devname,
|
|
|
|
|
prop);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-03 15:45:57 +10:00
|
|
|
|
|
|
|
|
/* simple approach: touchpads on USB or Bluetooth are considered
|
|
|
|
|
* external, anything else is internal. Exception is Apple -
|
|
|
|
|
* internal touchpads are connected over USB and it doesn't have
|
|
|
|
|
* external USB touchpads anyway.
|
|
|
|
|
*/
|
|
|
|
|
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:
|
2015-07-16 15:59:01 +10:00
|
|
|
if (device->model_flags & EVDEV_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) {
|
|
|
|
|
log_bug_libinput(evdev_libinput_context(device),
|
|
|
|
|
"%s: Internal or external? Please file a bug.\n",
|
|
|
|
|
device->devname);
|
|
|
|
|
evdev_tag_touchpad_external(device);
|
|
|
|
|
}
|
2014-09-03 15:45:57 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-28 11:28:34 +10:00
|
|
|
static void
|
|
|
|
|
tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
bool enable)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)dispatch;
|
|
|
|
|
bool ignore_events = !enable;
|
|
|
|
|
|
|
|
|
|
if (ignore_events == tp->ignore_events)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ignore_events)
|
|
|
|
|
tp_clear_state(tp);
|
|
|
|
|
|
|
|
|
|
tp->ignore_events = ignore_events;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static struct evdev_dispatch_interface tp_interface = {
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_process,
|
2015-05-19 15:57:11 +10:00
|
|
|
tp_interface_suspend,
|
2015-05-19 15:52:17 +10:00
|
|
|
tp_interface_remove,
|
|
|
|
|
tp_interface_destroy,
|
|
|
|
|
tp_interface_device_added,
|
|
|
|
|
tp_interface_device_removed,
|
|
|
|
|
tp_interface_device_removed, /* device_suspended, treat as remove */
|
|
|
|
|
tp_interface_device_added, /* device_resumed, treat as add */
|
2015-05-22 14:21:21 +10:00
|
|
|
NULL, /* post_added */
|
2016-06-28 11:28:34 +10:00
|
|
|
tp_interface_toggle_touch,
|
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,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
2014-06-06 17:01:06 +02:00
|
|
|
t->tp = tp;
|
2014-12-11 16:39:04 +10:00
|
|
|
t->has_ended = true;
|
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
|
|
|
}
|
|
|
|
|
|
2015-05-08 13:30:13 +10:00
|
|
|
static void
|
|
|
|
|
tp_sync_touch(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct tp_touch *t,
|
|
|
|
|
int slot)
|
|
|
|
|
{
|
|
|
|
|
struct libevdev *evdev = device->evdev;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
libevdev_fetch_slot_value(evdev, slot, ABS_MT_DISTANCE, &t->distance);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2015-07-30 11:54:38 +10:00
|
|
|
*/
|
2016-01-19 09:05:31 +10:00
|
|
|
if (tp->semi_mt) {
|
2015-07-30 11:54:38 +10:00
|
|
|
tp->num_slots = 1;
|
|
|
|
|
tp->slot = 0;
|
|
|
|
|
tp->has_mt = false;
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2014-07-18 11:06:38 +02:00
|
|
|
tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
|
2014-04-29 01:26:28 +02:00
|
|
|
if (!tp->touches)
|
2016-07-12 11:03:03 +10:00
|
|
|
return false;
|
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++)
|
|
|
|
|
tp_init_touch(tp, &tp->touches[i]);
|
|
|
|
|
|
2015-05-08 13:30:13 +10:00
|
|
|
/* 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 (i = 1; i < tp->num_slots; i++)
|
|
|
|
|
tp_sync_touch(tp, device, &tp->touches[i], i);
|
|
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
return true;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2015-08-27 13:13:47 +10:00
|
|
|
static uint32_t
|
|
|
|
|
tp_accel_config_get_profiles(struct libinput_device *libinput_device)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_accel_config_set_profile(struct libinput_device *libinput_device,
|
|
|
|
|
enum libinput_config_accel_profile profile)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_accel_profile
|
|
|
|
|
tp_accel_config_get_profile(struct libinput_device *libinput_device)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_accel_profile
|
|
|
|
|
tp_accel_config_get_default_profile(struct libinput_device *libinput_device)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
static bool
|
2016-07-13 11:43:12 +10:00
|
|
|
tp_init_accel(struct tp_dispatch *tp)
|
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;
|
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;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2015-12-11 11:10:25 +10:00
|
|
|
if (tp->device->model_flags & EVDEV_MODEL_LENOVO_X230 ||
|
|
|
|
|
tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81)
|
2015-07-28 15:06:13 +10:00
|
|
|
filter = create_pointer_accelerator_filter_lenovo_x230(tp->device->dpi);
|
2015-07-16 15:59:01 +10:00
|
|
|
else
|
2015-07-28 15:06:13 +10:00
|
|
|
filter = create_pointer_accelerator_filter_touchpad(tp->device->dpi);
|
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
|
|
|
|
|
|
|
|
/* we override the profile hooks for accel configuration with hooks
|
|
|
|
|
* that don't allow selection of profiles */
|
|
|
|
|
device->pointer.config.get_profiles = tp_accel_config_get_profiles;
|
|
|
|
|
device->pointer.config.set_profile = tp_accel_config_set_profile;
|
|
|
|
|
device->pointer.config.get_profile = tp_accel_config_get_profile;
|
|
|
|
|
device->pointer.config.get_default_profile = tp_accel_config_get_default_profile;
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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)
|
|
|
|
|
log_bug_libinput(tp_libinput_context(tp),
|
|
|
|
|
"Invalid default scroll method %d\n",
|
|
|
|
|
method);
|
|
|
|
|
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
|
|
|
{
|
2014-11-24 12:16:06 +01:00
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp_dwt_default_enabled(tp) ?
|
|
|
|
|
LIBINPUT_CONFIG_DWT_ENABLED :
|
|
|
|
|
LIBINPUT_CONFIG_DWT_DISABLED;
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (tp_dwt_device_is_blacklisted(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
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2014-07-10 17:34:08 +10:00
|
|
|
tp_init_palmdetect(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
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
|
|
|
|
2014-07-21 11:27:21 +10:00
|
|
|
tp->palm.right_edge = INT_MAX;
|
|
|
|
|
tp->palm.left_edge = INT_MIN;
|
|
|
|
|
|
2016-01-12 12:16:25 +10:00
|
|
|
/* Wacom doesn't have internal touchpads */
|
2015-07-16 15:59:01 +10:00
|
|
|
if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD)
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-07-16 15:54:47 +10:00
|
|
|
|
2016-07-15 11:31:07 +10:00
|
|
|
evdev_device_get_size(device, &width, &height);
|
|
|
|
|
|
2015-07-16 15:54:47 +10:00
|
|
|
/* Enable 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
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
/* palm edges are 5% of the width on each side */
|
2016-07-15 11:31:07 +10:00
|
|
|
mm.x = width * 0.05;
|
|
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->palm.left_edge = edges.x;
|
|
|
|
|
|
|
|
|
|
mm.x = width * 0.95;
|
|
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->palm.right_edge = edges.x;
|
2014-07-10 17:34:08 +10:00
|
|
|
|
2015-06-24 11:41:47 +10:00
|
|
|
tp->palm.monitor_trackpoint = true;
|
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)
|
|
|
|
|
{
|
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),
|
2014-09-28 13:21:08 +02:00
|
|
|
tp_trackpoint_timeout, tp);
|
2015-04-20 15:51:20 +10:00
|
|
|
|
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),
|
2015-04-20 15:51:20 +10:00
|
|
|
tp_keyboard_timeout, tp);
|
2014-09-28 13:21:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-13 07:55:13 +10:00
|
|
|
static void
|
2015-05-27 18:25:49 +10:00
|
|
|
tp_init_thumb(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *device = tp->device;
|
|
|
|
|
const struct input_absinfo *abs;
|
2015-07-17 11:29:40 +10:00
|
|
|
double w = 0.0, h = 0.0;
|
2016-07-15 11:31:07 +10:00
|
|
|
struct device_coords edges;
|
|
|
|
|
struct phys_coords mm = { 0.0, 0.0 };
|
2015-07-20 10:27:29 +10:00
|
|
|
int xres, yres;
|
|
|
|
|
double threshold;
|
2015-05-27 18:25:49 +10:00
|
|
|
|
2015-07-20 16:01:32 +10:00
|
|
|
if (!tp->buttons.is_clickpad)
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-05-27 18:25:49 +10:00
|
|
|
|
2015-07-17 11:29:40 +10:00
|
|
|
/* if the touchpad is less than 50mm high, skip thumb detection.
|
|
|
|
|
* it's too small to meaningfully interact with a thumb on the
|
|
|
|
|
* touchpad */
|
|
|
|
|
evdev_device_get_size(device, &w, &h);
|
|
|
|
|
if (h < 50)
|
2016-07-13 07:55:13 +10:00
|
|
|
return;
|
2015-07-17 11:29:40 +10:00
|
|
|
|
2015-07-28 13:25:45 +10:00
|
|
|
tp->thumb.detect_thumbs = true;
|
|
|
|
|
tp->thumb.threshold = INT_MAX;
|
|
|
|
|
|
|
|
|
|
/* detect thumbs by pressure in the bottom 15mm, detect thumbs by
|
|
|
|
|
* lingering in the bottom 8mm */
|
2016-07-15 11:31:07 +10:00
|
|
|
mm.y = h * 0.85;
|
|
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->thumb.upper_thumb_line = edges.y;
|
|
|
|
|
|
|
|
|
|
mm.y = h * 0.92;
|
|
|
|
|
edges = evdev_device_mm_to_units(device, &mm);
|
|
|
|
|
tp->thumb.lower_thumb_line = edges.y;
|
2015-07-28 13:25:45 +10:00
|
|
|
|
|
|
|
|
abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE);
|
|
|
|
|
if (!abs)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (abs->maximum - abs->minimum < 255)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2015-07-20 10:27:29 +10:00
|
|
|
/* Our reference touchpad is the T440s with 42x42 resolution.
|
|
|
|
|
* Higher-res touchpads exhibit higher pressure for the same
|
|
|
|
|
* interaction. On the T440s, the threshold value is 100, you don't
|
|
|
|
|
* reach that with a normal finger interaction.
|
2015-05-27 18:25:49 +10:00
|
|
|
* Note: "thumb" means massive touch that should not interact, not
|
|
|
|
|
* "using the tip of my thumb for a pinch gestures".
|
|
|
|
|
*/
|
2015-07-20 10:27:29 +10:00
|
|
|
xres = tp->device->abs.absinfo_x->resolution;
|
|
|
|
|
yres = tp->device->abs.absinfo_y->resolution;
|
|
|
|
|
threshold = 100.0 * hypot(xres, yres)/hypot(42, 42);
|
|
|
|
|
tp->thumb.threshold = max(100, threshold);
|
2015-05-27 18:25:49 +10:00
|
|
|
|
2015-07-28 13:25:45 +10:00
|
|
|
out:
|
2015-07-28 13:09:52 +10:00
|
|
|
log_debug(tp_libinput_context(tp),
|
2015-07-28 13:25:45 +10:00
|
|
|
"thumb: enabled thumb detection%s on '%s'\n",
|
|
|
|
|
tp->thumb.threshold != INT_MAX ? " (+pressure)" : "",
|
2015-07-28 13:09:52 +10:00
|
|
|
device->devname);
|
2015-05-27 18:25:49 +10: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;
|
|
|
|
|
struct libinput *libinput = tp_libinput_context(tp);
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
log_bug_kernel(libinput,
|
2015-07-21 11:01:39 +10:00
|
|
|
"device %s failed touchpad sanity checks\n",
|
|
|
|
|
device->devname);
|
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
|
|
|
|
|
* - the udev hwdb didn't override the resoluion
|
|
|
|
|
* - 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.
|
|
|
|
|
*/
|
|
|
|
|
log_info(tp_libinput_context(tp),
|
|
|
|
|
"%s: no resolution or size hints, assuming a size of %dx%dmm\n",
|
|
|
|
|
device->devname,
|
|
|
|
|
touchpad_width_mm,
|
|
|
|
|
touchpad_height_mm);
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
int res_x, res_y;
|
|
|
|
|
|
|
|
|
|
res_x = tp->device->abs.absinfo_x->resolution;
|
|
|
|
|
res_y = tp->device->abs.absinfo_y->resolution;
|
2016-02-17 09:06:09 +10:00
|
|
|
tp->hysteresis_margin.x = res_x/2;
|
|
|
|
|
tp->hysteresis_margin.y = res_y/2;
|
2016-06-03 11:19:23 +10:00
|
|
|
|
2016-02-17 09:06:09 +10:00
|
|
|
return;
|
2016-01-29 16:25:31 +10:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 14:52:57 +10:00
|
|
|
static void
|
|
|
|
|
tp_init_range_warnings(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
int width,
|
|
|
|
|
int height)
|
|
|
|
|
{
|
|
|
|
|
const struct input_absinfo *x, *y;
|
|
|
|
|
|
|
|
|
|
x = device->abs.absinfo_x;
|
|
|
|
|
y = device->abs.absinfo_y;
|
|
|
|
|
|
|
|
|
|
tp->warning_range.min.x = x->minimum - 0.05 * width;
|
|
|
|
|
tp->warning_range.min.y = y->minimum - 0.05 * height;
|
|
|
|
|
tp->warning_range.max.x = x->maximum + 0.05 * width;
|
|
|
|
|
tp->warning_range.max.y = y->maximum + 0.05 * height;
|
|
|
|
|
|
|
|
|
|
/* One warning every 5 min is enough */
|
|
|
|
|
ratelimit_init(&tp->warning_range.range_warn_limit, s2us(3000), 1);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static int
|
|
|
|
|
tp_init(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-06 18:57:10 +10:00
|
|
|
int width, height;
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
tp->base.interface = &tp_interface;
|
|
|
|
|
tp->device = device;
|
|
|
|
|
|
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
|
|
|
|
2015-06-25 14:10:38 +10:00
|
|
|
width = device->abs.dimensions.x;
|
|
|
|
|
height = device->abs.dimensions.y;
|
2014-02-06 18:57:10 +10:00
|
|
|
|
2016-05-31 14:52:57 +10:00
|
|
|
tp_init_range_warnings(tp, device, width, height);
|
|
|
|
|
|
2015-05-06 19:41:25 -04:00
|
|
|
tp->reports_distance = libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_ABS,
|
|
|
|
|
ABS_MT_DISTANCE);
|
|
|
|
|
|
2016-01-29 16:25:31 +10:00
|
|
|
tp_init_hysteresis(tp);
|
2014-02-06 18:57:10 +10:00
|
|
|
|
2016-07-12 11:03:03 +10:00
|
|
|
if (!tp_init_accel(tp))
|
|
|
|
|
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)
|
|
|
|
|
{
|
2014-09-03 15:45:57 +10:00
|
|
|
struct evdev_device *evdev = (struct 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) {
|
|
|
|
|
struct evdev_device *d = (struct evdev_device*)dev;
|
|
|
|
|
if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) {
|
|
|
|
|
tp_suspend(tp, device);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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:
|
|
|
|
|
tp_resume(tp, evdev);
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
|
|
|
|
|
tp_suspend(tp, evdev);
|
|
|
|
|
break;
|
2014-09-03 15:45:57 +10:00
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
|
|
|
|
|
tp_suspend_conditional(tp, evdev);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
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;
|
2014-09-23 14:35:42 +10:00
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
if (!tp)
|
2014-02-06 15:05:36 +10:00
|
|
|
return NULL;
|
|
|
|
|
|
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;
|
|
|
|
|
|
2014-09-23 14:35:42 +10:00
|
|
|
evdev_init_left_handed(device, tp_change_to_left_handed);
|
|
|
|
|
|
2016-07-19 09:27:50 +10:00
|
|
|
return &tp->base;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|