1a46c0ec8Sopenharmony_ci/* 2a46c0ec8Sopenharmony_ci * Copyright © 2014-2015 Red Hat, Inc. 3a46c0ec8Sopenharmony_ci * 4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10a46c0ec8Sopenharmony_ci * 11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13a46c0ec8Sopenharmony_ci * Software. 14a46c0ec8Sopenharmony_ci * 15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 22a46c0ec8Sopenharmony_ci */ 23a46c0ec8Sopenharmony_ci 24a46c0ec8Sopenharmony_ci#include "config.h" 25a46c0ec8Sopenharmony_ci 26a46c0ec8Sopenharmony_ci#include <assert.h> 27a46c0ec8Sopenharmony_ci#include <math.h> 28a46c0ec8Sopenharmony_ci#include <stdbool.h> 29a46c0ec8Sopenharmony_ci#include <limits.h> 30a46c0ec8Sopenharmony_ci 31a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 32a46c0ec8Sopenharmony_ci#include <libwacom/libwacom.h> 33a46c0ec8Sopenharmony_ci#endif 34a46c0ec8Sopenharmony_ci 35a46c0ec8Sopenharmony_ci#include "quirks.h" 36a46c0ec8Sopenharmony_ci#include "evdev-mt-touchpad.h" 37a46c0ec8Sopenharmony_ci#include "util-input-event.h" 38a46c0ec8Sopenharmony_ci 39a46c0ec8Sopenharmony_ci#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300) 40a46c0ec8Sopenharmony_ci#define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40) 41a46c0ec8Sopenharmony_ci#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200) 42a46c0ec8Sopenharmony_ci#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500) 43a46c0ec8Sopenharmony_ci#define FAKE_FINGER_OVERFLOW bit(7) 44a46c0ec8Sopenharmony_ci#define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */ 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_cienum notify { 47a46c0ec8Sopenharmony_ci DONT_NOTIFY, 48a46c0ec8Sopenharmony_ci DO_NOTIFY, 49a46c0ec8Sopenharmony_ci}; 50a46c0ec8Sopenharmony_ci 51a46c0ec8Sopenharmony_cistatic inline struct tp_history_point* 52a46c0ec8Sopenharmony_citp_motion_history_offset(struct tp_touch *t, int offset) 53a46c0ec8Sopenharmony_ci{ 54a46c0ec8Sopenharmony_ci int offset_index = 55a46c0ec8Sopenharmony_ci (t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) % 56a46c0ec8Sopenharmony_ci TOUCHPAD_HISTORY_LENGTH; 57a46c0ec8Sopenharmony_ci 58a46c0ec8Sopenharmony_ci return &t->history.samples[offset_index]; 59a46c0ec8Sopenharmony_ci} 60a46c0ec8Sopenharmony_ci 61a46c0ec8Sopenharmony_cistruct normalized_coords 62a46c0ec8Sopenharmony_citp_filter_motion(struct tp_dispatch *tp, 63a46c0ec8Sopenharmony_ci const struct device_float_coords *unaccelerated, 64a46c0ec8Sopenharmony_ci uint64_t time) 65a46c0ec8Sopenharmony_ci{ 66a46c0ec8Sopenharmony_ci struct device_float_coords raw; 67a46c0ec8Sopenharmony_ci const struct normalized_coords zero = { 0.0, 0.0 }; 68a46c0ec8Sopenharmony_ci 69a46c0ec8Sopenharmony_ci if (device_float_is_zero(*unaccelerated)) 70a46c0ec8Sopenharmony_ci return zero; 71a46c0ec8Sopenharmony_ci 72a46c0ec8Sopenharmony_ci /* Convert to device units with x/y in the same resolution */ 73a46c0ec8Sopenharmony_ci raw = tp_scale_to_xaxis(tp, *unaccelerated); 74a46c0ec8Sopenharmony_ci 75a46c0ec8Sopenharmony_ci return filter_dispatch(tp->device->pointer.filter, 76a46c0ec8Sopenharmony_ci &raw, tp, time); 77a46c0ec8Sopenharmony_ci} 78a46c0ec8Sopenharmony_ci 79a46c0ec8Sopenharmony_cistruct normalized_coords 80a46c0ec8Sopenharmony_citp_filter_motion_unaccelerated(struct tp_dispatch *tp, 81a46c0ec8Sopenharmony_ci const struct device_float_coords *unaccelerated, 82a46c0ec8Sopenharmony_ci uint64_t time) 83a46c0ec8Sopenharmony_ci{ 84a46c0ec8Sopenharmony_ci struct device_float_coords raw; 85a46c0ec8Sopenharmony_ci const struct normalized_coords zero = { 0.0, 0.0 }; 86a46c0ec8Sopenharmony_ci 87a46c0ec8Sopenharmony_ci if (device_float_is_zero(*unaccelerated)) 88a46c0ec8Sopenharmony_ci return zero; 89a46c0ec8Sopenharmony_ci 90a46c0ec8Sopenharmony_ci /* Convert to device units with x/y in the same resolution */ 91a46c0ec8Sopenharmony_ci raw = tp_scale_to_xaxis(tp, *unaccelerated); 92a46c0ec8Sopenharmony_ci 93a46c0ec8Sopenharmony_ci return filter_dispatch_constant(tp->device->pointer.filter, 94a46c0ec8Sopenharmony_ci &raw, tp, time); 95a46c0ec8Sopenharmony_ci} 96a46c0ec8Sopenharmony_ci 97a46c0ec8Sopenharmony_cistruct normalized_coords 98a46c0ec8Sopenharmony_citp_filter_scroll(struct tp_dispatch *tp, 99a46c0ec8Sopenharmony_ci const struct device_float_coords *unaccelerated, 100a46c0ec8Sopenharmony_ci uint64_t time) 101a46c0ec8Sopenharmony_ci{ 102a46c0ec8Sopenharmony_ci struct device_float_coords raw; 103a46c0ec8Sopenharmony_ci const struct normalized_coords zero = { 0.0, 0.0 }; 104a46c0ec8Sopenharmony_ci 105a46c0ec8Sopenharmony_ci if (device_float_is_zero(*unaccelerated)) 106a46c0ec8Sopenharmony_ci return zero; 107a46c0ec8Sopenharmony_ci 108a46c0ec8Sopenharmony_ci /* Convert to device units with x/y in the same resolution */ 109a46c0ec8Sopenharmony_ci raw = tp_scale_to_xaxis(tp, *unaccelerated); 110a46c0ec8Sopenharmony_ci 111a46c0ec8Sopenharmony_ci return filter_dispatch_scroll(tp->device->pointer.filter, 112a46c0ec8Sopenharmony_ci &raw, tp, time); 113a46c0ec8Sopenharmony_ci} 114a46c0ec8Sopenharmony_ci 115a46c0ec8Sopenharmony_cistatic inline void 116a46c0ec8Sopenharmony_citp_calculate_motion_speed(struct tp_dispatch *tp, 117a46c0ec8Sopenharmony_ci struct tp_touch *t, 118a46c0ec8Sopenharmony_ci uint64_t time) 119a46c0ec8Sopenharmony_ci{ 120a46c0ec8Sopenharmony_ci const struct tp_history_point *last; 121a46c0ec8Sopenharmony_ci struct device_coords delta; 122a46c0ec8Sopenharmony_ci struct phys_coords mm; 123a46c0ec8Sopenharmony_ci double distance; 124a46c0ec8Sopenharmony_ci double speed; 125a46c0ec8Sopenharmony_ci 126a46c0ec8Sopenharmony_ci /* Don't do this on single-touch or semi-mt devices */ 127a46c0ec8Sopenharmony_ci if (!tp->has_mt || tp->semi_mt) 128a46c0ec8Sopenharmony_ci return; 129a46c0ec8Sopenharmony_ci 130a46c0ec8Sopenharmony_ci if (t->state != TOUCH_UPDATE) 131a46c0ec8Sopenharmony_ci return; 132a46c0ec8Sopenharmony_ci 133a46c0ec8Sopenharmony_ci /* This doesn't kick in until we have at least 4 events in the 134a46c0ec8Sopenharmony_ci * motion history. As a side-effect, this automatically handles the 135a46c0ec8Sopenharmony_ci * 2fg scroll where a finger is down and moving fast before the 136a46c0ec8Sopenharmony_ci * other finger comes down for the scroll. 137a46c0ec8Sopenharmony_ci * 138a46c0ec8Sopenharmony_ci * We do *not* reset the speed to 0 here though. The motion history 139a46c0ec8Sopenharmony_ci * is reset whenever a new finger is down, so we'd be resetting the 140a46c0ec8Sopenharmony_ci * speed and failing. 141a46c0ec8Sopenharmony_ci */ 142a46c0ec8Sopenharmony_ci if (t->history.count < 4) 143a46c0ec8Sopenharmony_ci return; 144a46c0ec8Sopenharmony_ci 145a46c0ec8Sopenharmony_ci /* TODO: we probably need a speed history here so we can average 146a46c0ec8Sopenharmony_ci * across a few events */ 147a46c0ec8Sopenharmony_ci last = tp_motion_history_offset(t, 1); 148a46c0ec8Sopenharmony_ci delta.x = abs(t->point.x - last->point.x); 149a46c0ec8Sopenharmony_ci delta.y = abs(t->point.y - last->point.y); 150a46c0ec8Sopenharmony_ci mm = evdev_device_unit_delta_to_mm(tp->device, &delta); 151a46c0ec8Sopenharmony_ci 152a46c0ec8Sopenharmony_ci distance = length_in_mm(mm); 153a46c0ec8Sopenharmony_ci speed = distance/(time - last->time); /* mm/us */ 154a46c0ec8Sopenharmony_ci speed *= 1000000; /* mm/s */ 155a46c0ec8Sopenharmony_ci 156a46c0ec8Sopenharmony_ci t->speed.last_speed = speed; 157a46c0ec8Sopenharmony_ci} 158a46c0ec8Sopenharmony_ci 159a46c0ec8Sopenharmony_cistatic inline void 160a46c0ec8Sopenharmony_citp_motion_history_push(struct tp_touch *t, uint64_t time) 161a46c0ec8Sopenharmony_ci{ 162a46c0ec8Sopenharmony_ci int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH; 163a46c0ec8Sopenharmony_ci 164a46c0ec8Sopenharmony_ci if (t->history.count < TOUCHPAD_HISTORY_LENGTH) 165a46c0ec8Sopenharmony_ci t->history.count++; 166a46c0ec8Sopenharmony_ci 167a46c0ec8Sopenharmony_ci t->history.samples[motion_index].point = t->point; 168a46c0ec8Sopenharmony_ci t->history.samples[motion_index].time = time; 169a46c0ec8Sopenharmony_ci t->history.index = motion_index; 170a46c0ec8Sopenharmony_ci} 171a46c0ec8Sopenharmony_ci 172a46c0ec8Sopenharmony_ci/* Idea: if we got a tuple of *very* quick moves like {Left, Right, 173a46c0ec8Sopenharmony_ci * Left}, or {Right, Left, Right}, it means touchpad jitters since no 174a46c0ec8Sopenharmony_ci * human can move like that within thresholds. 175a46c0ec8Sopenharmony_ci * 176a46c0ec8Sopenharmony_ci * We encode left moves as zeroes, and right as ones. We also drop 177a46c0ec8Sopenharmony_ci * the array to all zeroes when constraints are not satisfied. Then we 178a46c0ec8Sopenharmony_ci * search for the pattern {1,0,1}. It can't match {Left, Right, Left}, 179a46c0ec8Sopenharmony_ci * but it does match {Left, Right, Left, Right}, so it's okay. 180a46c0ec8Sopenharmony_ci * 181a46c0ec8Sopenharmony_ci * This only looks at x changes, y changes are ignored. 182a46c0ec8Sopenharmony_ci */ 183a46c0ec8Sopenharmony_cistatic inline void 184a46c0ec8Sopenharmony_citp_detect_wobbling(struct tp_dispatch *tp, 185a46c0ec8Sopenharmony_ci struct tp_touch *t, 186a46c0ec8Sopenharmony_ci uint64_t time) 187a46c0ec8Sopenharmony_ci{ 188a46c0ec8Sopenharmony_ci int dx, dy; 189a46c0ec8Sopenharmony_ci uint64_t dtime; 190a46c0ec8Sopenharmony_ci const struct device_coords* prev_point; 191a46c0ec8Sopenharmony_ci 192a46c0ec8Sopenharmony_ci if (tp->nfingers_down != 1 || 193a46c0ec8Sopenharmony_ci tp->nfingers_down != tp->old_nfingers_down) 194a46c0ec8Sopenharmony_ci return; 195a46c0ec8Sopenharmony_ci 196a46c0ec8Sopenharmony_ci if (tp->hysteresis.enabled || t->history.count == 0) 197a46c0ec8Sopenharmony_ci return; 198a46c0ec8Sopenharmony_ci 199a46c0ec8Sopenharmony_ci if (!(tp->queued & TOUCHPAD_EVENT_MOTION)) { 200a46c0ec8Sopenharmony_ci t->hysteresis.x_motion_history = 0; 201a46c0ec8Sopenharmony_ci return; 202a46c0ec8Sopenharmony_ci } 203a46c0ec8Sopenharmony_ci 204a46c0ec8Sopenharmony_ci prev_point = &tp_motion_history_offset(t, 0)->point; 205a46c0ec8Sopenharmony_ci dx = prev_point->x - t->point.x; 206a46c0ec8Sopenharmony_ci dy = prev_point->y - t->point.y; 207a46c0ec8Sopenharmony_ci dtime = time - tp->hysteresis.last_motion_time; 208a46c0ec8Sopenharmony_ci 209a46c0ec8Sopenharmony_ci tp->hysteresis.last_motion_time = time; 210a46c0ec8Sopenharmony_ci 211a46c0ec8Sopenharmony_ci if ((dx == 0 && dy != 0) || dtime > ms2us(40)) { 212a46c0ec8Sopenharmony_ci t->hysteresis.x_motion_history = 0; 213a46c0ec8Sopenharmony_ci return; 214a46c0ec8Sopenharmony_ci } 215a46c0ec8Sopenharmony_ci 216a46c0ec8Sopenharmony_ci t->hysteresis.x_motion_history >>= 1; 217a46c0ec8Sopenharmony_ci if (dx > 0) { /* right move */ 218a46c0ec8Sopenharmony_ci static const char r_l_r = 0x5; /* {Right, Left, Right} */ 219a46c0ec8Sopenharmony_ci 220a46c0ec8Sopenharmony_ci t->hysteresis.x_motion_history |= bit(2); 221a46c0ec8Sopenharmony_ci if (t->hysteresis.x_motion_history == r_l_r) { 222a46c0ec8Sopenharmony_ci tp->hysteresis.enabled = true; 223a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 224a46c0ec8Sopenharmony_ci "hysteresis enabled. " 225a46c0ec8Sopenharmony_ci "See %s/touchpad-jitter.html for details\n", 226a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 227a46c0ec8Sopenharmony_ci } 228a46c0ec8Sopenharmony_ci } 229a46c0ec8Sopenharmony_ci} 230a46c0ec8Sopenharmony_ci 231a46c0ec8Sopenharmony_cistatic inline void 232a46c0ec8Sopenharmony_citp_motion_hysteresis(struct tp_dispatch *tp, 233a46c0ec8Sopenharmony_ci struct tp_touch *t) 234a46c0ec8Sopenharmony_ci{ 235a46c0ec8Sopenharmony_ci if (!tp->hysteresis.enabled) 236a46c0ec8Sopenharmony_ci return; 237a46c0ec8Sopenharmony_ci 238a46c0ec8Sopenharmony_ci if (t->history.count > 0) 239a46c0ec8Sopenharmony_ci t->point = evdev_hysteresis(&t->point, 240a46c0ec8Sopenharmony_ci &t->hysteresis.center, 241a46c0ec8Sopenharmony_ci &tp->hysteresis.margin); 242a46c0ec8Sopenharmony_ci 243a46c0ec8Sopenharmony_ci t->hysteresis.center = t->point; 244a46c0ec8Sopenharmony_ci} 245a46c0ec8Sopenharmony_ci 246a46c0ec8Sopenharmony_cistatic inline void 247a46c0ec8Sopenharmony_citp_motion_history_reset(struct tp_touch *t) 248a46c0ec8Sopenharmony_ci{ 249a46c0ec8Sopenharmony_ci t->history.count = 0; 250a46c0ec8Sopenharmony_ci} 251a46c0ec8Sopenharmony_ci 252a46c0ec8Sopenharmony_cistatic inline struct tp_touch * 253a46c0ec8Sopenharmony_citp_current_touch(struct tp_dispatch *tp) 254a46c0ec8Sopenharmony_ci{ 255a46c0ec8Sopenharmony_ci return &tp->touches[min(tp->slot, tp->ntouches - 1)]; 256a46c0ec8Sopenharmony_ci} 257a46c0ec8Sopenharmony_ci 258a46c0ec8Sopenharmony_cistatic inline struct tp_touch * 259a46c0ec8Sopenharmony_citp_get_touch(struct tp_dispatch *tp, unsigned int slot) 260a46c0ec8Sopenharmony_ci{ 261a46c0ec8Sopenharmony_ci assert(slot < tp->ntouches); 262a46c0ec8Sopenharmony_ci return &tp->touches[slot]; 263a46c0ec8Sopenharmony_ci} 264a46c0ec8Sopenharmony_ci 265a46c0ec8Sopenharmony_cistatic inline unsigned int 266a46c0ec8Sopenharmony_citp_fake_finger_count(struct tp_dispatch *tp) 267a46c0ec8Sopenharmony_ci{ 268a46c0ec8Sopenharmony_ci unsigned int fake_touches = 269a46c0ec8Sopenharmony_ci tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1); 270a46c0ec8Sopenharmony_ci 271a46c0ec8Sopenharmony_ci /* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any 272a46c0ec8Sopenharmony_ci * time */ 273a46c0ec8Sopenharmony_ci if (fake_touches & (fake_touches - 1)) 274a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(tp->device, 275a46c0ec8Sopenharmony_ci "Invalid fake finger state %#x\n", 276a46c0ec8Sopenharmony_ci tp->fake_touches); 277a46c0ec8Sopenharmony_ci 278a46c0ec8Sopenharmony_ci if (tp->fake_touches & FAKE_FINGER_OVERFLOW) 279a46c0ec8Sopenharmony_ci return FAKE_FINGER_OVERFLOW; 280a46c0ec8Sopenharmony_ci 281a46c0ec8Sopenharmony_ci /* don't count BTN_TOUCH */ 282a46c0ec8Sopenharmony_ci return ffs(tp->fake_touches >> 1); 283a46c0ec8Sopenharmony_ci} 284a46c0ec8Sopenharmony_ci 285a46c0ec8Sopenharmony_cistatic inline bool 286a46c0ec8Sopenharmony_citp_fake_finger_is_touching(struct tp_dispatch *tp) 287a46c0ec8Sopenharmony_ci{ 288a46c0ec8Sopenharmony_ci return tp->fake_touches & 0x1; 289a46c0ec8Sopenharmony_ci} 290a46c0ec8Sopenharmony_ci 291a46c0ec8Sopenharmony_cistatic inline void 292a46c0ec8Sopenharmony_citp_fake_finger_set(struct tp_dispatch *tp, 293a46c0ec8Sopenharmony_ci unsigned int code, 294a46c0ec8Sopenharmony_ci bool is_press) 295a46c0ec8Sopenharmony_ci{ 296a46c0ec8Sopenharmony_ci unsigned int shift; 297a46c0ec8Sopenharmony_ci 298a46c0ec8Sopenharmony_ci switch (code) { 299a46c0ec8Sopenharmony_ci case BTN_TOUCH: 300a46c0ec8Sopenharmony_ci if (!is_press) 301a46c0ec8Sopenharmony_ci tp->fake_touches &= ~FAKE_FINGER_OVERFLOW; 302a46c0ec8Sopenharmony_ci shift = 0; 303a46c0ec8Sopenharmony_ci break; 304a46c0ec8Sopenharmony_ci case BTN_TOOL_FINGER: 305a46c0ec8Sopenharmony_ci shift = 1; 306a46c0ec8Sopenharmony_ci break; 307a46c0ec8Sopenharmony_ci case BTN_TOOL_DOUBLETAP: 308a46c0ec8Sopenharmony_ci case BTN_TOOL_TRIPLETAP: 309a46c0ec8Sopenharmony_ci case BTN_TOOL_QUADTAP: 310a46c0ec8Sopenharmony_ci shift = code - BTN_TOOL_DOUBLETAP + 2; 311a46c0ec8Sopenharmony_ci break; 312a46c0ec8Sopenharmony_ci /* when QUINTTAP is released we're either switching to 6 fingers 313a46c0ec8Sopenharmony_ci (flag stays in place until BTN_TOUCH is released) or 314a46c0ec8Sopenharmony_ci one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */ 315a46c0ec8Sopenharmony_ci case BTN_TOOL_QUINTTAP: 316a46c0ec8Sopenharmony_ci if (is_press) 317a46c0ec8Sopenharmony_ci tp->fake_touches |= FAKE_FINGER_OVERFLOW; 318a46c0ec8Sopenharmony_ci return; 319a46c0ec8Sopenharmony_ci default: 320a46c0ec8Sopenharmony_ci return; 321a46c0ec8Sopenharmony_ci } 322a46c0ec8Sopenharmony_ci 323a46c0ec8Sopenharmony_ci if (is_press) { 324a46c0ec8Sopenharmony_ci tp->fake_touches &= ~FAKE_FINGER_OVERFLOW; 325a46c0ec8Sopenharmony_ci tp->fake_touches |= bit(shift); 326a46c0ec8Sopenharmony_ci 327a46c0ec8Sopenharmony_ci } else { 328a46c0ec8Sopenharmony_ci tp->fake_touches &= ~bit(shift); 329a46c0ec8Sopenharmony_ci } 330a46c0ec8Sopenharmony_ci} 331a46c0ec8Sopenharmony_ci 332a46c0ec8Sopenharmony_cistatic inline void 333a46c0ec8Sopenharmony_citp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 334a46c0ec8Sopenharmony_ci{ 335a46c0ec8Sopenharmony_ci if (t->state == TOUCH_BEGIN || 336a46c0ec8Sopenharmony_ci t->state == TOUCH_UPDATE || 337a46c0ec8Sopenharmony_ci t->state == TOUCH_HOVERING) 338a46c0ec8Sopenharmony_ci return; 339a46c0ec8Sopenharmony_ci 340a46c0ec8Sopenharmony_ci /* Bug #161: touch ends in the same event frame where it restarts 341a46c0ec8Sopenharmony_ci again. That's a kernel bug, so let's complain. */ 342a46c0ec8Sopenharmony_ci if (t->state == TOUCH_MAYBE_END) { 343a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(tp->device, 344a46c0ec8Sopenharmony_ci "touch %d ended and began in in same frame.\n", 345a46c0ec8Sopenharmony_ci t->index); 346a46c0ec8Sopenharmony_ci tp->nfingers_down++; 347a46c0ec8Sopenharmony_ci t->state = TOUCH_UPDATE; 348a46c0ec8Sopenharmony_ci t->has_ended = false; 349a46c0ec8Sopenharmony_ci return; 350a46c0ec8Sopenharmony_ci } 351a46c0ec8Sopenharmony_ci 352a46c0ec8Sopenharmony_ci /* we begin the touch as hovering because until BTN_TOUCH happens we 353a46c0ec8Sopenharmony_ci * don't know if it's a touch down or not. And BTN_TOUCH may happen 354a46c0ec8Sopenharmony_ci * after ABS_MT_TRACKING_ID */ 355a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 356a46c0ec8Sopenharmony_ci t->dirty = true; 357a46c0ec8Sopenharmony_ci t->has_ended = false; 358a46c0ec8Sopenharmony_ci t->was_down = false; 359a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 360a46c0ec8Sopenharmony_ci t->state = TOUCH_HOVERING; 361a46c0ec8Sopenharmony_ci t->pinned.is_pinned = false; 362a46c0ec8Sopenharmony_ci t->speed.last_speed = 0; 363a46c0ec8Sopenharmony_ci t->speed.exceeded_count = 0; 364a46c0ec8Sopenharmony_ci t->hysteresis.x_motion_history = 0; 365a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 366a46c0ec8Sopenharmony_ci} 367a46c0ec8Sopenharmony_ci 368a46c0ec8Sopenharmony_cistatic inline void 369a46c0ec8Sopenharmony_citp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 370a46c0ec8Sopenharmony_ci{ 371a46c0ec8Sopenharmony_ci t->dirty = true; 372a46c0ec8Sopenharmony_ci t->state = TOUCH_BEGIN; 373a46c0ec8Sopenharmony_ci t->initial_time = time; 374a46c0ec8Sopenharmony_ci t->was_down = true; 375a46c0ec8Sopenharmony_ci tp->nfingers_down++; 376a46c0ec8Sopenharmony_ci t->palm.time = time; 377a46c0ec8Sopenharmony_ci t->tap.is_thumb = false; 378a46c0ec8Sopenharmony_ci t->tap.is_palm = false; 379a46c0ec8Sopenharmony_ci t->speed.exceeded_count = 0; 380a46c0ec8Sopenharmony_ci assert(tp->nfingers_down >= 1); 381a46c0ec8Sopenharmony_ci tp->hysteresis.last_motion_time = time; 382a46c0ec8Sopenharmony_ci} 383a46c0ec8Sopenharmony_ci 384a46c0ec8Sopenharmony_ci/** 385a46c0ec8Sopenharmony_ci * Schedule a touch to be ended, based on either the events or some 386a46c0ec8Sopenharmony_ci * attributes of the touch (size, pressure). In some cases we need to 387a46c0ec8Sopenharmony_ci * resurrect a touch that has ended, so this doesn't actually end the touch 388a46c0ec8Sopenharmony_ci * yet. All the TOUCH_MAYBE_END touches get properly ended once the device 389a46c0ec8Sopenharmony_ci * state has been processed once and we know how many zombie touches we 390a46c0ec8Sopenharmony_ci * need. 391a46c0ec8Sopenharmony_ci */ 392a46c0ec8Sopenharmony_cistatic inline void 393a46c0ec8Sopenharmony_citp_maybe_end_touch(struct tp_dispatch *tp, 394a46c0ec8Sopenharmony_ci struct tp_touch *t, 395a46c0ec8Sopenharmony_ci uint64_t time) 396a46c0ec8Sopenharmony_ci{ 397a46c0ec8Sopenharmony_ci switch (t->state) { 398a46c0ec8Sopenharmony_ci case TOUCH_NONE: 399a46c0ec8Sopenharmony_ci case TOUCH_MAYBE_END: 400a46c0ec8Sopenharmony_ci return; 401a46c0ec8Sopenharmony_ci case TOUCH_END: 402a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 403a46c0ec8Sopenharmony_ci "touch %d: already in TOUCH_END\n", 404a46c0ec8Sopenharmony_ci t->index); 405a46c0ec8Sopenharmony_ci return; 406a46c0ec8Sopenharmony_ci case TOUCH_HOVERING: 407a46c0ec8Sopenharmony_ci case TOUCH_BEGIN: 408a46c0ec8Sopenharmony_ci case TOUCH_UPDATE: 409a46c0ec8Sopenharmony_ci break; 410a46c0ec8Sopenharmony_ci } 411a46c0ec8Sopenharmony_ci 412a46c0ec8Sopenharmony_ci if (t->state != TOUCH_HOVERING) { 413a46c0ec8Sopenharmony_ci assert(tp->nfingers_down >= 1); 414a46c0ec8Sopenharmony_ci tp->nfingers_down--; 415a46c0ec8Sopenharmony_ci t->state = TOUCH_MAYBE_END; 416a46c0ec8Sopenharmony_ci } else { 417a46c0ec8Sopenharmony_ci t->state = TOUCH_NONE; 418a46c0ec8Sopenharmony_ci } 419a46c0ec8Sopenharmony_ci 420a46c0ec8Sopenharmony_ci t->dirty = true; 421a46c0ec8Sopenharmony_ci} 422a46c0ec8Sopenharmony_ci 423a46c0ec8Sopenharmony_ci/** 424a46c0ec8Sopenharmony_ci * Inverse to tp_maybe_end_touch(), restores a touch back to its previous 425a46c0ec8Sopenharmony_ci * state. 426a46c0ec8Sopenharmony_ci */ 427a46c0ec8Sopenharmony_cistatic inline void 428a46c0ec8Sopenharmony_citp_recover_ended_touch(struct tp_dispatch *tp, 429a46c0ec8Sopenharmony_ci struct tp_touch *t) 430a46c0ec8Sopenharmony_ci{ 431a46c0ec8Sopenharmony_ci t->dirty = true; 432a46c0ec8Sopenharmony_ci t->state = TOUCH_UPDATE; 433a46c0ec8Sopenharmony_ci tp->nfingers_down++; 434a46c0ec8Sopenharmony_ci} 435a46c0ec8Sopenharmony_ci 436a46c0ec8Sopenharmony_ci/** 437a46c0ec8Sopenharmony_ci * End a touch, even if the touch sequence is still active. 438a46c0ec8Sopenharmony_ci * Use tp_maybe_end_touch() instead. 439a46c0ec8Sopenharmony_ci */ 440a46c0ec8Sopenharmony_cistatic inline void 441a46c0ec8Sopenharmony_citp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 442a46c0ec8Sopenharmony_ci{ 443a46c0ec8Sopenharmony_ci if (t->state != TOUCH_MAYBE_END) { 444a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 445a46c0ec8Sopenharmony_ci "touch %d should be MAYBE_END, is %d\n", 446a46c0ec8Sopenharmony_ci t->index, 447a46c0ec8Sopenharmony_ci t->state); 448a46c0ec8Sopenharmony_ci return; 449a46c0ec8Sopenharmony_ci } 450a46c0ec8Sopenharmony_ci 451a46c0ec8Sopenharmony_ci t->dirty = true; 452a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 453a46c0ec8Sopenharmony_ci t->state = TOUCH_END; 454a46c0ec8Sopenharmony_ci t->pinned.is_pinned = false; 455a46c0ec8Sopenharmony_ci t->palm.time = 0; 456a46c0ec8Sopenharmony_ci t->speed.exceeded_count = 0; 457a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 458a46c0ec8Sopenharmony_ci} 459a46c0ec8Sopenharmony_ci 460a46c0ec8Sopenharmony_ci/** 461a46c0ec8Sopenharmony_ci * End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received. 462a46c0ec8Sopenharmony_ci */ 463a46c0ec8Sopenharmony_cistatic inline void 464a46c0ec8Sopenharmony_citp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 465a46c0ec8Sopenharmony_ci{ 466a46c0ec8Sopenharmony_ci t->has_ended = true; 467a46c0ec8Sopenharmony_ci tp_maybe_end_touch(tp, t, time); 468a46c0ec8Sopenharmony_ci} 469a46c0ec8Sopenharmony_ci 470a46c0ec8Sopenharmony_cistatic void 471a46c0ec8Sopenharmony_citp_stop_actions(struct tp_dispatch *tp, uint64_t time) 472a46c0ec8Sopenharmony_ci{ 473a46c0ec8Sopenharmony_ci tp_edge_scroll_stop_events(tp, time); 474a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 475a46c0ec8Sopenharmony_ci tp_tap_suspend(tp, time); 476a46c0ec8Sopenharmony_ci} 477a46c0ec8Sopenharmony_ci 478a46c0ec8Sopenharmony_cistruct device_coords 479a46c0ec8Sopenharmony_citp_get_delta(struct tp_touch *t) 480a46c0ec8Sopenharmony_ci{ 481a46c0ec8Sopenharmony_ci struct device_coords delta; 482a46c0ec8Sopenharmony_ci const struct device_coords zero = { 0.0, 0.0 }; 483a46c0ec8Sopenharmony_ci 484a46c0ec8Sopenharmony_ci if (t->history.count <= 1) 485a46c0ec8Sopenharmony_ci return zero; 486a46c0ec8Sopenharmony_ci 487a46c0ec8Sopenharmony_ci delta.x = tp_motion_history_offset(t, 0)->point.x - 488a46c0ec8Sopenharmony_ci tp_motion_history_offset(t, 1)->point.x; 489a46c0ec8Sopenharmony_ci delta.y = tp_motion_history_offset(t, 0)->point.y - 490a46c0ec8Sopenharmony_ci tp_motion_history_offset(t, 1)->point.y; 491a46c0ec8Sopenharmony_ci 492a46c0ec8Sopenharmony_ci return delta; 493a46c0ec8Sopenharmony_ci} 494a46c0ec8Sopenharmony_ci 495a46c0ec8Sopenharmony_cistatic inline int32_t 496a46c0ec8Sopenharmony_cirotated(struct tp_dispatch *tp, unsigned int code, int value) 497a46c0ec8Sopenharmony_ci{ 498a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 499a46c0ec8Sopenharmony_ci 500a46c0ec8Sopenharmony_ci if (!tp->left_handed.rotate) 501a46c0ec8Sopenharmony_ci return value; 502a46c0ec8Sopenharmony_ci 503a46c0ec8Sopenharmony_ci switch (code) { 504a46c0ec8Sopenharmony_ci case ABS_X: 505a46c0ec8Sopenharmony_ci case ABS_MT_POSITION_X: 506a46c0ec8Sopenharmony_ci absinfo = tp->device->abs.absinfo_x; 507a46c0ec8Sopenharmony_ci break; 508a46c0ec8Sopenharmony_ci case ABS_Y: 509a46c0ec8Sopenharmony_ci case ABS_MT_POSITION_Y: 510a46c0ec8Sopenharmony_ci absinfo = tp->device->abs.absinfo_y; 511a46c0ec8Sopenharmony_ci break; 512a46c0ec8Sopenharmony_ci default: 513a46c0ec8Sopenharmony_ci abort(); 514a46c0ec8Sopenharmony_ci } 515a46c0ec8Sopenharmony_ci return absinfo->maximum - (value - absinfo->minimum); 516a46c0ec8Sopenharmony_ci} 517a46c0ec8Sopenharmony_ci 518a46c0ec8Sopenharmony_cistatic void 519a46c0ec8Sopenharmony_citp_process_absolute(struct tp_dispatch *tp, 520a46c0ec8Sopenharmony_ci const struct input_event *e, 521a46c0ec8Sopenharmony_ci uint64_t time) 522a46c0ec8Sopenharmony_ci{ 523a46c0ec8Sopenharmony_ci struct tp_touch *t = tp_current_touch(tp); 524a46c0ec8Sopenharmony_ci 525a46c0ec8Sopenharmony_ci switch(e->code) { 526a46c0ec8Sopenharmony_ci case ABS_MT_POSITION_X: 527a46c0ec8Sopenharmony_ci evdev_device_check_abs_axis_range(tp->device, 528a46c0ec8Sopenharmony_ci e->code, 529a46c0ec8Sopenharmony_ci e->value); 530a46c0ec8Sopenharmony_ci t->point.x = rotated(tp, e->code, e->value); 531a46c0ec8Sopenharmony_ci t->dirty = true; 532a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 533a46c0ec8Sopenharmony_ci break; 534a46c0ec8Sopenharmony_ci case ABS_MT_POSITION_Y: 535a46c0ec8Sopenharmony_ci evdev_device_check_abs_axis_range(tp->device, 536a46c0ec8Sopenharmony_ci e->code, 537a46c0ec8Sopenharmony_ci e->value); 538a46c0ec8Sopenharmony_ci t->point.y = rotated(tp, e->code, e->value); 539a46c0ec8Sopenharmony_ci t->dirty = true; 540a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 541a46c0ec8Sopenharmony_ci break; 542a46c0ec8Sopenharmony_ci case ABS_MT_SLOT: 543a46c0ec8Sopenharmony_ci tp->slot = e->value; 544a46c0ec8Sopenharmony_ci break; 545a46c0ec8Sopenharmony_ci case ABS_MT_TRACKING_ID: 546a46c0ec8Sopenharmony_ci if (e->value != -1) { 547a46c0ec8Sopenharmony_ci tp->nactive_slots += 1; 548a46c0ec8Sopenharmony_ci tp_new_touch(tp, t, time); 549a46c0ec8Sopenharmony_ci } else { 550a46c0ec8Sopenharmony_ci assert(tp->nactive_slots >= 1); 551a46c0ec8Sopenharmony_ci tp->nactive_slots -= 1; 552a46c0ec8Sopenharmony_ci tp_end_sequence(tp, t, time); 553a46c0ec8Sopenharmony_ci } 554a46c0ec8Sopenharmony_ci break; 555a46c0ec8Sopenharmony_ci case ABS_MT_PRESSURE: 556a46c0ec8Sopenharmony_ci t->pressure = e->value; 557a46c0ec8Sopenharmony_ci t->dirty = true; 558a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_OTHERAXIS; 559a46c0ec8Sopenharmony_ci break; 560a46c0ec8Sopenharmony_ci case ABS_MT_TOOL_TYPE: 561a46c0ec8Sopenharmony_ci t->is_tool_palm = e->value == MT_TOOL_PALM; 562a46c0ec8Sopenharmony_ci t->dirty = true; 563a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_OTHERAXIS; 564a46c0ec8Sopenharmony_ci break; 565a46c0ec8Sopenharmony_ci case ABS_MT_TOUCH_MAJOR: 566a46c0ec8Sopenharmony_ci t->major = e->value; 567a46c0ec8Sopenharmony_ci t->dirty = true; 568a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_OTHERAXIS; 569a46c0ec8Sopenharmony_ci break; 570a46c0ec8Sopenharmony_ci case ABS_MT_TOUCH_MINOR: 571a46c0ec8Sopenharmony_ci t->minor = e->value; 572a46c0ec8Sopenharmony_ci t->dirty = true; 573a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_OTHERAXIS; 574a46c0ec8Sopenharmony_ci break; 575a46c0ec8Sopenharmony_ci } 576a46c0ec8Sopenharmony_ci} 577a46c0ec8Sopenharmony_ci 578a46c0ec8Sopenharmony_cistatic void 579a46c0ec8Sopenharmony_citp_process_absolute_st(struct tp_dispatch *tp, 580a46c0ec8Sopenharmony_ci const struct input_event *e, 581a46c0ec8Sopenharmony_ci uint64_t time) 582a46c0ec8Sopenharmony_ci{ 583a46c0ec8Sopenharmony_ci struct tp_touch *t = tp_current_touch(tp); 584a46c0ec8Sopenharmony_ci 585a46c0ec8Sopenharmony_ci switch(e->code) { 586a46c0ec8Sopenharmony_ci case ABS_X: 587a46c0ec8Sopenharmony_ci evdev_device_check_abs_axis_range(tp->device, 588a46c0ec8Sopenharmony_ci e->code, 589a46c0ec8Sopenharmony_ci e->value); 590a46c0ec8Sopenharmony_ci t->point.x = rotated(tp, e->code, e->value); 591a46c0ec8Sopenharmony_ci t->dirty = true; 592a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 593a46c0ec8Sopenharmony_ci break; 594a46c0ec8Sopenharmony_ci case ABS_Y: 595a46c0ec8Sopenharmony_ci evdev_device_check_abs_axis_range(tp->device, 596a46c0ec8Sopenharmony_ci e->code, 597a46c0ec8Sopenharmony_ci e->value); 598a46c0ec8Sopenharmony_ci t->point.y = rotated(tp, e->code, e->value); 599a46c0ec8Sopenharmony_ci t->dirty = true; 600a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_MOTION; 601a46c0ec8Sopenharmony_ci break; 602a46c0ec8Sopenharmony_ci case ABS_PRESSURE: 603a46c0ec8Sopenharmony_ci t->pressure = e->value; 604a46c0ec8Sopenharmony_ci t->dirty = true; 605a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_OTHERAXIS; 606a46c0ec8Sopenharmony_ci break; 607a46c0ec8Sopenharmony_ci } 608a46c0ec8Sopenharmony_ci} 609a46c0ec8Sopenharmony_ci 610a46c0ec8Sopenharmony_cistatic inline void 611a46c0ec8Sopenharmony_citp_restore_synaptics_touches(struct tp_dispatch *tp, 612a46c0ec8Sopenharmony_ci uint64_t time) 613a46c0ec8Sopenharmony_ci{ 614a46c0ec8Sopenharmony_ci unsigned int i; 615a46c0ec8Sopenharmony_ci unsigned int nfake_touches; 616a46c0ec8Sopenharmony_ci 617a46c0ec8Sopenharmony_ci nfake_touches = tp_fake_finger_count(tp); 618a46c0ec8Sopenharmony_ci if (nfake_touches < 3) 619a46c0ec8Sopenharmony_ci return; 620a46c0ec8Sopenharmony_ci 621a46c0ec8Sopenharmony_ci if (tp->nfingers_down >= nfake_touches || 622a46c0ec8Sopenharmony_ci (tp->nfingers_down == tp->num_slots && nfake_touches == tp->num_slots)) 623a46c0ec8Sopenharmony_ci return; 624a46c0ec8Sopenharmony_ci 625a46c0ec8Sopenharmony_ci /* Synaptics devices may end touch 2 on transition to/from 626a46c0ec8Sopenharmony_ci * BTN_TOOL_TRIPLETAP and start it again on the next frame with 627a46c0ec8Sopenharmony_ci * different coordinates (bz#91352, gitlab#434). We search the 628a46c0ec8Sopenharmony_ci * touches we have, if there is one that has just ended despite us 629a46c0ec8Sopenharmony_ci * being on tripletap, we move it back to update. 630a46c0ec8Sopenharmony_ci * 631a46c0ec8Sopenharmony_ci * Note: we only handle the transition from 2 to 3 touches, not the 632a46c0ec8Sopenharmony_ci * other way round (see gitlab#434) 633a46c0ec8Sopenharmony_ci */ 634a46c0ec8Sopenharmony_ci for (i = 0; i < tp->num_slots; i++) { 635a46c0ec8Sopenharmony_ci struct tp_touch *t = tp_get_touch(tp, i); 636a46c0ec8Sopenharmony_ci 637a46c0ec8Sopenharmony_ci if (t->state != TOUCH_MAYBE_END) 638a46c0ec8Sopenharmony_ci continue; 639a46c0ec8Sopenharmony_ci 640a46c0ec8Sopenharmony_ci /* new touch, move it through begin to update immediately */ 641a46c0ec8Sopenharmony_ci tp_recover_ended_touch(tp, t); 642a46c0ec8Sopenharmony_ci } 643a46c0ec8Sopenharmony_ci} 644a46c0ec8Sopenharmony_ci 645a46c0ec8Sopenharmony_cistatic void 646a46c0ec8Sopenharmony_citp_process_fake_touches(struct tp_dispatch *tp, 647a46c0ec8Sopenharmony_ci uint64_t time) 648a46c0ec8Sopenharmony_ci{ 649a46c0ec8Sopenharmony_ci struct tp_touch *t; 650a46c0ec8Sopenharmony_ci unsigned int nfake_touches; 651a46c0ec8Sopenharmony_ci unsigned int i, start; 652a46c0ec8Sopenharmony_ci 653a46c0ec8Sopenharmony_ci nfake_touches = tp_fake_finger_count(tp); 654a46c0ec8Sopenharmony_ci if (nfake_touches == FAKE_FINGER_OVERFLOW) 655a46c0ec8Sopenharmony_ci return; 656a46c0ec8Sopenharmony_ci 657a46c0ec8Sopenharmony_ci if (tp->device->model_flags & 658a46c0ec8Sopenharmony_ci EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD) 659a46c0ec8Sopenharmony_ci tp_restore_synaptics_touches(tp, time); 660a46c0ec8Sopenharmony_ci 661a46c0ec8Sopenharmony_ci /* ALPS serial touchpads always set 3 slots in the kernel, even 662a46c0ec8Sopenharmony_ci * where they support less than that. So we get BTN_TOOL_TRIPLETAP 663a46c0ec8Sopenharmony_ci * but never slot 2 because our slot count is wrong. 664a46c0ec8Sopenharmony_ci * This also means that the third touch falls through the cracks and 665a46c0ec8Sopenharmony_ci * is ignored. 666a46c0ec8Sopenharmony_ci * 667a46c0ec8Sopenharmony_ci * See https://gitlab.freedesktop.org/libinput/libinput/issues/408 668a46c0ec8Sopenharmony_ci * 669a46c0ec8Sopenharmony_ci * All touchpad devices have at least one slot so we only do this 670a46c0ec8Sopenharmony_ci * for 2 touches or higher. 671a46c0ec8Sopenharmony_ci * 672a46c0ec8Sopenharmony_ci * There's an bug in libevdev < 1.9.0 affecting slots after a 673a46c0ec8Sopenharmony_ci * SYN_DROPPED. Where a user release one or more touches during 674a46c0ec8Sopenharmony_ci * SYN_DROPPED and places new ones on the touchpad, we may end up 675a46c0ec8Sopenharmony_ci * with fake touches but no active slots. 676a46c0ec8Sopenharmony_ci * So let's check for nactive_slots > 0 to make sure we don't lose 677a46c0ec8Sopenharmony_ci * all fingers. That's a workaround only, this must be fixed in 678a46c0ec8Sopenharmony_ci * libevdev. 679a46c0ec8Sopenharmony_ci * 680a46c0ec8Sopenharmony_ci * For a long explanation of what happens, see 681a46c0ec8Sopenharmony_ci * https://gitlab.freedesktop.org/libevdev/libevdev/merge_requests/19 682a46c0ec8Sopenharmony_ci */ 683a46c0ec8Sopenharmony_ci if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD && 684a46c0ec8Sopenharmony_ci nfake_touches > 1 && tp->has_mt && 685a46c0ec8Sopenharmony_ci tp->nactive_slots > 0 && 686a46c0ec8Sopenharmony_ci nfake_touches > tp->nactive_slots && 687a46c0ec8Sopenharmony_ci tp->nactive_slots < tp->num_slots) { 688a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(tp->device, 689a46c0ec8Sopenharmony_ci "Wrong slot count (%d), reducing to %d\n", 690a46c0ec8Sopenharmony_ci tp->num_slots, 691a46c0ec8Sopenharmony_ci tp->nactive_slots); 692a46c0ec8Sopenharmony_ci /* This should be safe since we fill the slots from the 693a46c0ec8Sopenharmony_ci * first one so hiding the excessive slots shouldn't matter. 694a46c0ec8Sopenharmony_ci * There are sequences where we could accidentally lose an 695a46c0ec8Sopenharmony_ci * actual touch point but that requires specially crafted 696a46c0ec8Sopenharmony_ci * sequences and let's deal with that when it happens. 697a46c0ec8Sopenharmony_ci */ 698a46c0ec8Sopenharmony_ci tp->num_slots = tp->nactive_slots; 699a46c0ec8Sopenharmony_ci } 700a46c0ec8Sopenharmony_ci 701a46c0ec8Sopenharmony_ci start = tp->has_mt ? tp->num_slots : 0; 702a46c0ec8Sopenharmony_ci for (i = start; i < tp->ntouches; i++) { 703a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 704a46c0ec8Sopenharmony_ci if (i < nfake_touches) 705a46c0ec8Sopenharmony_ci tp_new_touch(tp, t, time); 706a46c0ec8Sopenharmony_ci else 707a46c0ec8Sopenharmony_ci tp_end_sequence(tp, t, time); 708a46c0ec8Sopenharmony_ci } 709a46c0ec8Sopenharmony_ci} 710a46c0ec8Sopenharmony_ci 711a46c0ec8Sopenharmony_cistatic void 712a46c0ec8Sopenharmony_citp_process_trackpoint_button(struct tp_dispatch *tp, 713a46c0ec8Sopenharmony_ci const struct input_event *e, 714a46c0ec8Sopenharmony_ci uint64_t time) 715a46c0ec8Sopenharmony_ci{ 716a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch; 717a46c0ec8Sopenharmony_ci struct input_event event; 718a46c0ec8Sopenharmony_ci struct input_event syn_report = { 719a46c0ec8Sopenharmony_ci .input_event_sec = 0, 720a46c0ec8Sopenharmony_ci .input_event_usec = 0, 721a46c0ec8Sopenharmony_ci .type = EV_SYN, 722a46c0ec8Sopenharmony_ci .code = SYN_REPORT, 723a46c0ec8Sopenharmony_ci .value = 0 724a46c0ec8Sopenharmony_ci }; 725a46c0ec8Sopenharmony_ci 726a46c0ec8Sopenharmony_ci if (!tp->buttons.trackpoint) 727a46c0ec8Sopenharmony_ci return; 728a46c0ec8Sopenharmony_ci 729a46c0ec8Sopenharmony_ci dispatch = tp->buttons.trackpoint->dispatch; 730a46c0ec8Sopenharmony_ci 731a46c0ec8Sopenharmony_ci event = *e; 732a46c0ec8Sopenharmony_ci syn_report.input_event_sec = e->input_event_sec; 733a46c0ec8Sopenharmony_ci syn_report.input_event_usec = e->input_event_usec; 734a46c0ec8Sopenharmony_ci 735a46c0ec8Sopenharmony_ci switch (event.code) { 736a46c0ec8Sopenharmony_ci case BTN_0: 737a46c0ec8Sopenharmony_ci event.code = BTN_LEFT; 738a46c0ec8Sopenharmony_ci break; 739a46c0ec8Sopenharmony_ci case BTN_1: 740a46c0ec8Sopenharmony_ci event.code = BTN_RIGHT; 741a46c0ec8Sopenharmony_ci break; 742a46c0ec8Sopenharmony_ci case BTN_2: 743a46c0ec8Sopenharmony_ci event.code = BTN_MIDDLE; 744a46c0ec8Sopenharmony_ci break; 745a46c0ec8Sopenharmony_ci default: 746a46c0ec8Sopenharmony_ci return; 747a46c0ec8Sopenharmony_ci } 748a46c0ec8Sopenharmony_ci 749a46c0ec8Sopenharmony_ci dispatch->interface->process(dispatch, 750a46c0ec8Sopenharmony_ci tp->buttons.trackpoint, 751a46c0ec8Sopenharmony_ci &event, time); 752a46c0ec8Sopenharmony_ci dispatch->interface->process(dispatch, 753a46c0ec8Sopenharmony_ci tp->buttons.trackpoint, 754a46c0ec8Sopenharmony_ci &syn_report, time); 755a46c0ec8Sopenharmony_ci} 756a46c0ec8Sopenharmony_ci 757a46c0ec8Sopenharmony_cistatic void 758a46c0ec8Sopenharmony_citp_process_key(struct tp_dispatch *tp, 759a46c0ec8Sopenharmony_ci const struct input_event *e, 760a46c0ec8Sopenharmony_ci uint64_t time) 761a46c0ec8Sopenharmony_ci{ 762a46c0ec8Sopenharmony_ci /* ignore kernel key repeat */ 763a46c0ec8Sopenharmony_ci if (e->value == 2) 764a46c0ec8Sopenharmony_ci return; 765a46c0ec8Sopenharmony_ci 766a46c0ec8Sopenharmony_ci switch (e->code) { 767a46c0ec8Sopenharmony_ci case BTN_LEFT: 768a46c0ec8Sopenharmony_ci case BTN_MIDDLE: 769a46c0ec8Sopenharmony_ci case BTN_RIGHT: 770a46c0ec8Sopenharmony_ci tp_process_button(tp, e, time); 771a46c0ec8Sopenharmony_ci break; 772a46c0ec8Sopenharmony_ci case BTN_TOUCH: 773a46c0ec8Sopenharmony_ci case BTN_TOOL_FINGER: 774a46c0ec8Sopenharmony_ci case BTN_TOOL_DOUBLETAP: 775a46c0ec8Sopenharmony_ci case BTN_TOOL_TRIPLETAP: 776a46c0ec8Sopenharmony_ci case BTN_TOOL_QUADTAP: 777a46c0ec8Sopenharmony_ci case BTN_TOOL_QUINTTAP: 778a46c0ec8Sopenharmony_ci tp_fake_finger_set(tp, e->code, !!e->value); 779a46c0ec8Sopenharmony_ci break; 780a46c0ec8Sopenharmony_ci case BTN_0: 781a46c0ec8Sopenharmony_ci case BTN_1: 782a46c0ec8Sopenharmony_ci case BTN_2: 783a46c0ec8Sopenharmony_ci tp_process_trackpoint_button(tp, e, time); 784a46c0ec8Sopenharmony_ci break; 785a46c0ec8Sopenharmony_ci } 786a46c0ec8Sopenharmony_ci} 787a46c0ec8Sopenharmony_ci 788a46c0ec8Sopenharmony_cistatic void 789a46c0ec8Sopenharmony_citp_process_msc(struct tp_dispatch *tp, 790a46c0ec8Sopenharmony_ci const struct input_event *e, 791a46c0ec8Sopenharmony_ci uint64_t time) 792a46c0ec8Sopenharmony_ci{ 793a46c0ec8Sopenharmony_ci if (e->code != MSC_TIMESTAMP) 794a46c0ec8Sopenharmony_ci return; 795a46c0ec8Sopenharmony_ci 796a46c0ec8Sopenharmony_ci tp->quirks.msc_timestamp.now = e->value; 797a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_TIMESTAMP; 798a46c0ec8Sopenharmony_ci} 799a46c0ec8Sopenharmony_ci 800a46c0ec8Sopenharmony_cistatic void 801a46c0ec8Sopenharmony_citp_unpin_finger(const struct tp_dispatch *tp, struct tp_touch *t) 802a46c0ec8Sopenharmony_ci{ 803a46c0ec8Sopenharmony_ci struct phys_coords mm; 804a46c0ec8Sopenharmony_ci struct device_coords delta; 805a46c0ec8Sopenharmony_ci 806a46c0ec8Sopenharmony_ci if (!t->pinned.is_pinned) 807a46c0ec8Sopenharmony_ci return; 808a46c0ec8Sopenharmony_ci 809a46c0ec8Sopenharmony_ci delta.x = abs(t->point.x - t->pinned.center.x); 810a46c0ec8Sopenharmony_ci delta.y = abs(t->point.y - t->pinned.center.y); 811a46c0ec8Sopenharmony_ci 812a46c0ec8Sopenharmony_ci mm = evdev_device_unit_delta_to_mm(tp->device, &delta); 813a46c0ec8Sopenharmony_ci 814a46c0ec8Sopenharmony_ci /* 1.5mm movement -> unpin */ 815a46c0ec8Sopenharmony_ci if (hypot(mm.x, mm.y) >= 1.5) { 816a46c0ec8Sopenharmony_ci t->pinned.is_pinned = false; 817a46c0ec8Sopenharmony_ci return; 818a46c0ec8Sopenharmony_ci } 819a46c0ec8Sopenharmony_ci} 820a46c0ec8Sopenharmony_ci 821a46c0ec8Sopenharmony_cistatic void 822a46c0ec8Sopenharmony_citp_pin_fingers(struct tp_dispatch *tp) 823a46c0ec8Sopenharmony_ci{ 824a46c0ec8Sopenharmony_ci struct tp_touch *t; 825a46c0ec8Sopenharmony_ci 826a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 827a46c0ec8Sopenharmony_ci t->pinned.is_pinned = true; 828a46c0ec8Sopenharmony_ci t->pinned.center = t->point; 829a46c0ec8Sopenharmony_ci } 830a46c0ec8Sopenharmony_ci} 831a46c0ec8Sopenharmony_ci 832a46c0ec8Sopenharmony_cibool 833a46c0ec8Sopenharmony_citp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t) 834a46c0ec8Sopenharmony_ci{ 835a46c0ec8Sopenharmony_ci return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && 836a46c0ec8Sopenharmony_ci t->palm.state == PALM_NONE && 837a46c0ec8Sopenharmony_ci !t->pinned.is_pinned && 838a46c0ec8Sopenharmony_ci !tp_thumb_ignored(tp, t) && 839a46c0ec8Sopenharmony_ci tp_button_touch_active(tp, t) && 840a46c0ec8Sopenharmony_ci tp_edge_scroll_touch_active(tp, t); 841a46c0ec8Sopenharmony_ci} 842a46c0ec8Sopenharmony_ci 843a46c0ec8Sopenharmony_cibool 844a46c0ec8Sopenharmony_citp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t) 845a46c0ec8Sopenharmony_ci{ 846a46c0ec8Sopenharmony_ci return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && 847a46c0ec8Sopenharmony_ci t->palm.state == PALM_NONE && 848a46c0ec8Sopenharmony_ci !t->pinned.is_pinned && 849a46c0ec8Sopenharmony_ci !tp_thumb_ignored_for_gesture(tp, t) && 850a46c0ec8Sopenharmony_ci tp_button_touch_active(tp, t) && 851a46c0ec8Sopenharmony_ci tp_edge_scroll_touch_active(tp, t); 852a46c0ec8Sopenharmony_ci} 853a46c0ec8Sopenharmony_ci 854a46c0ec8Sopenharmony_cistatic inline bool 855a46c0ec8Sopenharmony_citp_palm_was_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t) 856a46c0ec8Sopenharmony_ci{ 857a46c0ec8Sopenharmony_ci return t->palm.first.x < tp->palm.left_edge || 858a46c0ec8Sopenharmony_ci t->palm.first.x > tp->palm.right_edge; 859a46c0ec8Sopenharmony_ci} 860a46c0ec8Sopenharmony_ci 861a46c0ec8Sopenharmony_cistatic inline bool 862a46c0ec8Sopenharmony_citp_palm_was_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t) 863a46c0ec8Sopenharmony_ci{ 864a46c0ec8Sopenharmony_ci return t->palm.first.y < tp->palm.upper_edge; 865a46c0ec8Sopenharmony_ci} 866a46c0ec8Sopenharmony_ci 867a46c0ec8Sopenharmony_cistatic inline bool 868a46c0ec8Sopenharmony_citp_palm_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t) 869a46c0ec8Sopenharmony_ci{ 870a46c0ec8Sopenharmony_ci return t->point.x < tp->palm.left_edge || 871a46c0ec8Sopenharmony_ci t->point.x > tp->palm.right_edge; 872a46c0ec8Sopenharmony_ci} 873a46c0ec8Sopenharmony_ci 874a46c0ec8Sopenharmony_cistatic inline bool 875a46c0ec8Sopenharmony_citp_palm_in_top_edge(const struct tp_dispatch *tp, const struct tp_touch *t) 876a46c0ec8Sopenharmony_ci{ 877a46c0ec8Sopenharmony_ci return t->point.y < tp->palm.upper_edge; 878a46c0ec8Sopenharmony_ci} 879a46c0ec8Sopenharmony_ci 880a46c0ec8Sopenharmony_cistatic inline bool 881a46c0ec8Sopenharmony_citp_palm_in_edge(const struct tp_dispatch *tp, const struct tp_touch *t) 882a46c0ec8Sopenharmony_ci{ 883a46c0ec8Sopenharmony_ci return tp_palm_in_side_edge(tp, t) || tp_palm_in_top_edge(tp, t); 884a46c0ec8Sopenharmony_ci} 885a46c0ec8Sopenharmony_ci 886a46c0ec8Sopenharmony_cistatic bool 887a46c0ec8Sopenharmony_citp_palm_detect_dwt_triggered(struct tp_dispatch *tp, 888a46c0ec8Sopenharmony_ci struct tp_touch *t, 889a46c0ec8Sopenharmony_ci uint64_t time) 890a46c0ec8Sopenharmony_ci{ 891a46c0ec8Sopenharmony_ci if (tp->dwt.dwt_enabled && 892a46c0ec8Sopenharmony_ci tp->dwt.keyboard_active && 893a46c0ec8Sopenharmony_ci t->state == TOUCH_BEGIN) { 894a46c0ec8Sopenharmony_ci t->palm.state = PALM_TYPING; 895a46c0ec8Sopenharmony_ci t->palm.first = t->point; 896a46c0ec8Sopenharmony_ci return true; 897a46c0ec8Sopenharmony_ci } 898a46c0ec8Sopenharmony_ci 899a46c0ec8Sopenharmony_ci if (!tp->dwt.keyboard_active && 900a46c0ec8Sopenharmony_ci t->state == TOUCH_UPDATE && 901a46c0ec8Sopenharmony_ci t->palm.state == PALM_TYPING) { 902a46c0ec8Sopenharmony_ci /* If a touch has started before the first or after the last 903a46c0ec8Sopenharmony_ci key press, release it on timeout. Benefit: a palm rested 904a46c0ec8Sopenharmony_ci while typing on the touchpad will be ignored, but a touch 905a46c0ec8Sopenharmony_ci started once we stop typing will be able to control the 906a46c0ec8Sopenharmony_ci pointer (alas not tap, etc.). 907a46c0ec8Sopenharmony_ci */ 908a46c0ec8Sopenharmony_ci if (t->palm.time == 0 || 909a46c0ec8Sopenharmony_ci t->palm.time > tp->dwt.keyboard_last_press_time) { 910a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 911a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 912a46c0ec8Sopenharmony_ci "palm: touch %d released, timeout after typing\n", 913a46c0ec8Sopenharmony_ci t->index); 914a46c0ec8Sopenharmony_ci } 915a46c0ec8Sopenharmony_ci } 916a46c0ec8Sopenharmony_ci 917a46c0ec8Sopenharmony_ci return false; 918a46c0ec8Sopenharmony_ci} 919a46c0ec8Sopenharmony_ci 920a46c0ec8Sopenharmony_cistatic bool 921a46c0ec8Sopenharmony_citp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp, 922a46c0ec8Sopenharmony_ci struct tp_touch *t, 923a46c0ec8Sopenharmony_ci uint64_t time) 924a46c0ec8Sopenharmony_ci{ 925a46c0ec8Sopenharmony_ci if (!tp->palm.monitor_trackpoint) 926a46c0ec8Sopenharmony_ci return false; 927a46c0ec8Sopenharmony_ci 928a46c0ec8Sopenharmony_ci if (t->palm.state == PALM_NONE && 929a46c0ec8Sopenharmony_ci t->state == TOUCH_BEGIN && 930a46c0ec8Sopenharmony_ci tp->palm.trackpoint_active) { 931a46c0ec8Sopenharmony_ci t->palm.state = PALM_TRACKPOINT; 932a46c0ec8Sopenharmony_ci return true; 933a46c0ec8Sopenharmony_ci } 934a46c0ec8Sopenharmony_ci 935a46c0ec8Sopenharmony_ci if (t->palm.state == PALM_TRACKPOINT && 936a46c0ec8Sopenharmony_ci t->state == TOUCH_UPDATE && 937a46c0ec8Sopenharmony_ci !tp->palm.trackpoint_active) { 938a46c0ec8Sopenharmony_ci 939a46c0ec8Sopenharmony_ci if (t->palm.time == 0 || 940a46c0ec8Sopenharmony_ci t->palm.time > tp->palm.trackpoint_last_event_time) { 941a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 942a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 943a46c0ec8Sopenharmony_ci "palm: touch %d released, timeout after trackpoint\n", t->index); 944a46c0ec8Sopenharmony_ci } 945a46c0ec8Sopenharmony_ci } 946a46c0ec8Sopenharmony_ci 947a46c0ec8Sopenharmony_ci return false; 948a46c0ec8Sopenharmony_ci} 949a46c0ec8Sopenharmony_ci 950a46c0ec8Sopenharmony_cistatic bool 951a46c0ec8Sopenharmony_citp_palm_detect_tool_triggered(struct tp_dispatch *tp, 952a46c0ec8Sopenharmony_ci struct tp_touch *t, 953a46c0ec8Sopenharmony_ci uint64_t time) 954a46c0ec8Sopenharmony_ci{ 955a46c0ec8Sopenharmony_ci if (!tp->palm.use_mt_tool) 956a46c0ec8Sopenharmony_ci return false; 957a46c0ec8Sopenharmony_ci 958a46c0ec8Sopenharmony_ci if (t->palm.state != PALM_NONE && 959a46c0ec8Sopenharmony_ci t->palm.state != PALM_TOOL_PALM) 960a46c0ec8Sopenharmony_ci return false; 961a46c0ec8Sopenharmony_ci 962a46c0ec8Sopenharmony_ci if (t->palm.state == PALM_NONE && 963a46c0ec8Sopenharmony_ci t->is_tool_palm) 964a46c0ec8Sopenharmony_ci t->palm.state = PALM_TOOL_PALM; 965a46c0ec8Sopenharmony_ci else if (t->palm.state == PALM_TOOL_PALM && 966a46c0ec8Sopenharmony_ci !t->is_tool_palm) 967a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 968a46c0ec8Sopenharmony_ci 969a46c0ec8Sopenharmony_ci return t->palm.state == PALM_TOOL_PALM; 970a46c0ec8Sopenharmony_ci} 971a46c0ec8Sopenharmony_ci 972a46c0ec8Sopenharmony_cistatic inline bool 973a46c0ec8Sopenharmony_citp_palm_detect_move_out_of_edge(struct tp_dispatch *tp, 974a46c0ec8Sopenharmony_ci struct tp_touch *t, 975a46c0ec8Sopenharmony_ci uint64_t time) 976a46c0ec8Sopenharmony_ci{ 977a46c0ec8Sopenharmony_ci const int PALM_TIMEOUT = ms2us(200); 978a46c0ec8Sopenharmony_ci int directions = 0; 979a46c0ec8Sopenharmony_ci struct device_float_coords delta; 980a46c0ec8Sopenharmony_ci int dirs; 981a46c0ec8Sopenharmony_ci 982a46c0ec8Sopenharmony_ci if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) { 983a46c0ec8Sopenharmony_ci if (tp_palm_was_in_side_edge(tp, t)) 984a46c0ec8Sopenharmony_ci directions = NE|E|SE|SW|W|NW; 985a46c0ec8Sopenharmony_ci else if (tp_palm_was_in_top_edge(tp, t)) 986a46c0ec8Sopenharmony_ci directions = S|SE|SW; 987a46c0ec8Sopenharmony_ci 988a46c0ec8Sopenharmony_ci if (directions) { 989a46c0ec8Sopenharmony_ci delta = device_delta(t->point, t->palm.first); 990a46c0ec8Sopenharmony_ci dirs = phys_get_direction(tp_phys_delta(tp, delta)); 991a46c0ec8Sopenharmony_ci if ((dirs & directions) && !(dirs & ~directions)) 992a46c0ec8Sopenharmony_ci return true; 993a46c0ec8Sopenharmony_ci } 994a46c0ec8Sopenharmony_ci } 995a46c0ec8Sopenharmony_ci 996a46c0ec8Sopenharmony_ci return false; 997a46c0ec8Sopenharmony_ci} 998a46c0ec8Sopenharmony_ci 999a46c0ec8Sopenharmony_cistatic inline bool 1000a46c0ec8Sopenharmony_citp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 1001a46c0ec8Sopenharmony_ci{ 1002a46c0ec8Sopenharmony_ci struct tp_touch *other; 1003a46c0ec8Sopenharmony_ci 1004a46c0ec8Sopenharmony_ci if (tp->nfingers_down < 2) 1005a46c0ec8Sopenharmony_ci return false; 1006a46c0ec8Sopenharmony_ci 1007a46c0ec8Sopenharmony_ci /* If we have at least one other active non-palm touch make this 1008a46c0ec8Sopenharmony_ci * touch non-palm too. This avoids palm detection during two-finger 1009a46c0ec8Sopenharmony_ci * scrolling. 1010a46c0ec8Sopenharmony_ci * 1011a46c0ec8Sopenharmony_ci * Note: if both touches start in the palm zone within the same 1012a46c0ec8Sopenharmony_ci * frame the second touch will still be PALM_NONE and thus detected 1013a46c0ec8Sopenharmony_ci * here as non-palm touch. This is too niche to worry about for now. 1014a46c0ec8Sopenharmony_ci */ 1015a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, other) { 1016a46c0ec8Sopenharmony_ci if (other == t) 1017a46c0ec8Sopenharmony_ci continue; 1018a46c0ec8Sopenharmony_ci 1019a46c0ec8Sopenharmony_ci if (tp_touch_active(tp, other) && 1020a46c0ec8Sopenharmony_ci other->palm.state == PALM_NONE) { 1021a46c0ec8Sopenharmony_ci return true; 1022a46c0ec8Sopenharmony_ci } 1023a46c0ec8Sopenharmony_ci } 1024a46c0ec8Sopenharmony_ci 1025a46c0ec8Sopenharmony_ci return false; 1026a46c0ec8Sopenharmony_ci} 1027a46c0ec8Sopenharmony_ci 1028a46c0ec8Sopenharmony_cistatic inline bool 1029a46c0ec8Sopenharmony_citp_palm_detect_touch_size_triggered(struct tp_dispatch *tp, 1030a46c0ec8Sopenharmony_ci struct tp_touch *t, 1031a46c0ec8Sopenharmony_ci uint64_t time) 1032a46c0ec8Sopenharmony_ci{ 1033a46c0ec8Sopenharmony_ci if (!tp->palm.use_size) 1034a46c0ec8Sopenharmony_ci return false; 1035a46c0ec8Sopenharmony_ci 1036a46c0ec8Sopenharmony_ci /* If a finger size is large enough for palm, we stick with that and 1037a46c0ec8Sopenharmony_ci * force the user to release and reset the finger */ 1038a46c0ec8Sopenharmony_ci if (t->palm.state != PALM_NONE && t->palm.state != PALM_TOUCH_SIZE) 1039a46c0ec8Sopenharmony_ci return false; 1040a46c0ec8Sopenharmony_ci 1041a46c0ec8Sopenharmony_ci if (t->major > tp->palm.size_threshold || 1042a46c0ec8Sopenharmony_ci t->minor > tp->palm.size_threshold) { 1043a46c0ec8Sopenharmony_ci if (t->palm.state != PALM_TOUCH_SIZE) 1044a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1045a46c0ec8Sopenharmony_ci "palm: touch %d size exceeded\n", 1046a46c0ec8Sopenharmony_ci t->index); 1047a46c0ec8Sopenharmony_ci t->palm.state = PALM_TOUCH_SIZE; 1048a46c0ec8Sopenharmony_ci return true; 1049a46c0ec8Sopenharmony_ci } 1050a46c0ec8Sopenharmony_ci 1051a46c0ec8Sopenharmony_ci return false; 1052a46c0ec8Sopenharmony_ci} 1053a46c0ec8Sopenharmony_ci 1054a46c0ec8Sopenharmony_cistatic inline bool 1055a46c0ec8Sopenharmony_citp_palm_detect_edge(struct tp_dispatch *tp, 1056a46c0ec8Sopenharmony_ci struct tp_touch *t, 1057a46c0ec8Sopenharmony_ci uint64_t time) 1058a46c0ec8Sopenharmony_ci{ 1059a46c0ec8Sopenharmony_ci if (t->palm.state == PALM_EDGE) { 1060a46c0ec8Sopenharmony_ci if (tp_palm_detect_multifinger(tp, t, time)) { 1061a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 1062a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1063a46c0ec8Sopenharmony_ci "palm: touch %d released, multiple fingers\n", 1064a46c0ec8Sopenharmony_ci t->index); 1065a46c0ec8Sopenharmony_ci 1066a46c0ec8Sopenharmony_ci /* If labelled a touch as palm, we unlabel as palm when 1067a46c0ec8Sopenharmony_ci we move out of the palm edge zone within the timeout, provided 1068a46c0ec8Sopenharmony_ci the direction is within 45 degrees of the horizontal. 1069a46c0ec8Sopenharmony_ci */ 1070a46c0ec8Sopenharmony_ci } else if (tp_palm_detect_move_out_of_edge(tp, t, time)) { 1071a46c0ec8Sopenharmony_ci t->palm.state = PALM_NONE; 1072a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1073a46c0ec8Sopenharmony_ci "palm: touch %d released, out of edge zone\n", 1074a46c0ec8Sopenharmony_ci t->index); 1075a46c0ec8Sopenharmony_ci } 1076a46c0ec8Sopenharmony_ci return false; 1077a46c0ec8Sopenharmony_ci } 1078a46c0ec8Sopenharmony_ci 1079a46c0ec8Sopenharmony_ci if (tp_palm_detect_multifinger(tp, t, time)) { 1080a46c0ec8Sopenharmony_ci return false; 1081a46c0ec8Sopenharmony_ci } 1082a46c0ec8Sopenharmony_ci 1083a46c0ec8Sopenharmony_ci /* palm must start in exclusion zone, it's ok to move into 1084a46c0ec8Sopenharmony_ci the zone without being a palm */ 1085a46c0ec8Sopenharmony_ci if (t->state != TOUCH_BEGIN || !tp_palm_in_edge(tp, t)) 1086a46c0ec8Sopenharmony_ci return false; 1087a46c0ec8Sopenharmony_ci 1088a46c0ec8Sopenharmony_ci if (tp_touch_get_edge(tp, t) & EDGE_RIGHT) 1089a46c0ec8Sopenharmony_ci return false; 1090a46c0ec8Sopenharmony_ci 1091a46c0ec8Sopenharmony_ci t->palm.state = PALM_EDGE; 1092a46c0ec8Sopenharmony_ci t->palm.time = time; 1093a46c0ec8Sopenharmony_ci t->palm.first = t->point; 1094a46c0ec8Sopenharmony_ci 1095a46c0ec8Sopenharmony_ci return true; 1096a46c0ec8Sopenharmony_ci} 1097a46c0ec8Sopenharmony_ci 1098a46c0ec8Sopenharmony_cistatic bool 1099a46c0ec8Sopenharmony_citp_palm_detect_pressure_triggered(struct tp_dispatch *tp, 1100a46c0ec8Sopenharmony_ci struct tp_touch *t, 1101a46c0ec8Sopenharmony_ci uint64_t time) 1102a46c0ec8Sopenharmony_ci{ 1103a46c0ec8Sopenharmony_ci if (!tp->palm.use_pressure) 1104a46c0ec8Sopenharmony_ci return false; 1105a46c0ec8Sopenharmony_ci 1106a46c0ec8Sopenharmony_ci if (t->palm.state != PALM_NONE && 1107a46c0ec8Sopenharmony_ci t->palm.state != PALM_PRESSURE) 1108a46c0ec8Sopenharmony_ci return false; 1109a46c0ec8Sopenharmony_ci 1110a46c0ec8Sopenharmony_ci if (t->pressure > tp->palm.pressure_threshold) 1111a46c0ec8Sopenharmony_ci t->palm.state = PALM_PRESSURE; 1112a46c0ec8Sopenharmony_ci 1113a46c0ec8Sopenharmony_ci return t->palm.state == PALM_PRESSURE; 1114a46c0ec8Sopenharmony_ci} 1115a46c0ec8Sopenharmony_ci 1116a46c0ec8Sopenharmony_cistatic bool 1117a46c0ec8Sopenharmony_citp_palm_detect_arbitration_triggered(struct tp_dispatch *tp, 1118a46c0ec8Sopenharmony_ci struct tp_touch *t, 1119a46c0ec8Sopenharmony_ci uint64_t time) 1120a46c0ec8Sopenharmony_ci{ 1121a46c0ec8Sopenharmony_ci if (tp->arbitration.state == ARBITRATION_NOT_ACTIVE) 1122a46c0ec8Sopenharmony_ci return false; 1123a46c0ec8Sopenharmony_ci 1124a46c0ec8Sopenharmony_ci t->palm.state = PALM_ARBITRATION; 1125a46c0ec8Sopenharmony_ci 1126a46c0ec8Sopenharmony_ci return true; 1127a46c0ec8Sopenharmony_ci} 1128a46c0ec8Sopenharmony_ci 1129a46c0ec8Sopenharmony_cistatic void 1130a46c0ec8Sopenharmony_citp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 1131a46c0ec8Sopenharmony_ci{ 1132a46c0ec8Sopenharmony_ci const char *palm_state; 1133a46c0ec8Sopenharmony_ci enum touch_palm_state oldstate = t->palm.state; 1134a46c0ec8Sopenharmony_ci 1135a46c0ec8Sopenharmony_ci if (tp_palm_detect_pressure_triggered(tp, t, time)) 1136a46c0ec8Sopenharmony_ci goto out; 1137a46c0ec8Sopenharmony_ci 1138a46c0ec8Sopenharmony_ci if (tp_palm_detect_arbitration_triggered(tp, t, time)) 1139a46c0ec8Sopenharmony_ci goto out; 1140a46c0ec8Sopenharmony_ci 1141a46c0ec8Sopenharmony_ci if (tp_palm_detect_dwt_triggered(tp, t, time)) 1142a46c0ec8Sopenharmony_ci goto out; 1143a46c0ec8Sopenharmony_ci 1144a46c0ec8Sopenharmony_ci if (tp_palm_detect_trackpoint_triggered(tp, t, time)) 1145a46c0ec8Sopenharmony_ci goto out; 1146a46c0ec8Sopenharmony_ci 1147a46c0ec8Sopenharmony_ci if (tp_palm_detect_tool_triggered(tp, t, time)) 1148a46c0ec8Sopenharmony_ci goto out; 1149a46c0ec8Sopenharmony_ci 1150a46c0ec8Sopenharmony_ci if (tp_palm_detect_touch_size_triggered(tp, t, time)) 1151a46c0ec8Sopenharmony_ci goto out; 1152a46c0ec8Sopenharmony_ci 1153a46c0ec8Sopenharmony_ci if (tp_palm_detect_edge(tp, t, time)) 1154a46c0ec8Sopenharmony_ci goto out; 1155a46c0ec8Sopenharmony_ci 1156a46c0ec8Sopenharmony_ci /* Pressure is highest priority because it cannot be released and 1157a46c0ec8Sopenharmony_ci * overrides all other checks. So we check once before anything else 1158a46c0ec8Sopenharmony_ci * in case pressure triggers on a non-palm touch. And again after 1159a46c0ec8Sopenharmony_ci * everything in case one of the others released but we have a 1160a46c0ec8Sopenharmony_ci * pressure trigger now. 1161a46c0ec8Sopenharmony_ci */ 1162a46c0ec8Sopenharmony_ci if (tp_palm_detect_pressure_triggered(tp, t, time)) 1163a46c0ec8Sopenharmony_ci goto out; 1164a46c0ec8Sopenharmony_ci 1165a46c0ec8Sopenharmony_ci return; 1166a46c0ec8Sopenharmony_ciout: 1167a46c0ec8Sopenharmony_ci 1168a46c0ec8Sopenharmony_ci if (oldstate == t->palm.state) 1169a46c0ec8Sopenharmony_ci return; 1170a46c0ec8Sopenharmony_ci 1171a46c0ec8Sopenharmony_ci switch (t->palm.state) { 1172a46c0ec8Sopenharmony_ci case PALM_EDGE: 1173a46c0ec8Sopenharmony_ci palm_state = "edge"; 1174a46c0ec8Sopenharmony_ci break; 1175a46c0ec8Sopenharmony_ci case PALM_TYPING: 1176a46c0ec8Sopenharmony_ci palm_state = "typing"; 1177a46c0ec8Sopenharmony_ci break; 1178a46c0ec8Sopenharmony_ci case PALM_TRACKPOINT: 1179a46c0ec8Sopenharmony_ci palm_state = "trackpoint"; 1180a46c0ec8Sopenharmony_ci break; 1181a46c0ec8Sopenharmony_ci case PALM_TOOL_PALM: 1182a46c0ec8Sopenharmony_ci palm_state = "tool-palm"; 1183a46c0ec8Sopenharmony_ci break; 1184a46c0ec8Sopenharmony_ci case PALM_PRESSURE: 1185a46c0ec8Sopenharmony_ci palm_state = "pressure"; 1186a46c0ec8Sopenharmony_ci break; 1187a46c0ec8Sopenharmony_ci case PALM_TOUCH_SIZE: 1188a46c0ec8Sopenharmony_ci palm_state = "touch size"; 1189a46c0ec8Sopenharmony_ci break; 1190a46c0ec8Sopenharmony_ci case PALM_ARBITRATION: 1191a46c0ec8Sopenharmony_ci palm_state = "arbitration"; 1192a46c0ec8Sopenharmony_ci break; 1193a46c0ec8Sopenharmony_ci case PALM_NONE: 1194a46c0ec8Sopenharmony_ci default: 1195a46c0ec8Sopenharmony_ci abort(); 1196a46c0ec8Sopenharmony_ci break; 1197a46c0ec8Sopenharmony_ci } 1198a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1199a46c0ec8Sopenharmony_ci "palm: touch %d (%s), palm detected (%s)\n", 1200a46c0ec8Sopenharmony_ci t->index, 1201a46c0ec8Sopenharmony_ci touch_state_to_str(t->state), 1202a46c0ec8Sopenharmony_ci palm_state); 1203a46c0ec8Sopenharmony_ci} 1204a46c0ec8Sopenharmony_ci 1205a46c0ec8Sopenharmony_cistatic void 1206a46c0ec8Sopenharmony_citp_unhover_pressure(struct tp_dispatch *tp, uint64_t time) 1207a46c0ec8Sopenharmony_ci{ 1208a46c0ec8Sopenharmony_ci struct tp_touch *t; 1209a46c0ec8Sopenharmony_ci int i; 1210a46c0ec8Sopenharmony_ci unsigned int nfake_touches; 1211a46c0ec8Sopenharmony_ci unsigned int real_fingers_down = 0; 1212a46c0ec8Sopenharmony_ci 1213a46c0ec8Sopenharmony_ci nfake_touches = tp_fake_finger_count(tp); 1214a46c0ec8Sopenharmony_ci if (nfake_touches == FAKE_FINGER_OVERFLOW) 1215a46c0ec8Sopenharmony_ci nfake_touches = 0; 1216a46c0ec8Sopenharmony_ci 1217a46c0ec8Sopenharmony_ci for (i = 0; i < (int)tp->num_slots; i++) { 1218a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1219a46c0ec8Sopenharmony_ci 1220a46c0ec8Sopenharmony_ci if (t->state == TOUCH_NONE) 1221a46c0ec8Sopenharmony_ci continue; 1222a46c0ec8Sopenharmony_ci 1223a46c0ec8Sopenharmony_ci if (t->dirty) { 1224a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING) { 1225a46c0ec8Sopenharmony_ci if (t->pressure >= tp->pressure.high) { 1226a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1227a46c0ec8Sopenharmony_ci "pressure: begin touch %d\n", 1228a46c0ec8Sopenharmony_ci t->index); 1229a46c0ec8Sopenharmony_ci /* avoid jumps when landing a finger */ 1230a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1231a46c0ec8Sopenharmony_ci tp_begin_touch(tp, t, time); 1232a46c0ec8Sopenharmony_ci } 1233a46c0ec8Sopenharmony_ci /* don't unhover for pressure if we have too many 1234a46c0ec8Sopenharmony_ci * fake fingers down, see comment below. Except 1235a46c0ec8Sopenharmony_ci * for single-finger touches where the real touch 1236a46c0ec8Sopenharmony_ci * decides for the rest. 1237a46c0ec8Sopenharmony_ci */ 1238a46c0ec8Sopenharmony_ci } else if (nfake_touches <= tp->num_slots || 1239a46c0ec8Sopenharmony_ci tp->num_slots == 1) { 1240a46c0ec8Sopenharmony_ci if (t->pressure < tp->pressure.low) { 1241a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1242a46c0ec8Sopenharmony_ci "pressure: end touch %d\n", 1243a46c0ec8Sopenharmony_ci t->index); 1244a46c0ec8Sopenharmony_ci tp_maybe_end_touch(tp, t, time); 1245a46c0ec8Sopenharmony_ci } 1246a46c0ec8Sopenharmony_ci } 1247a46c0ec8Sopenharmony_ci } 1248a46c0ec8Sopenharmony_ci 1249a46c0ec8Sopenharmony_ci if (t->state == TOUCH_BEGIN || 1250a46c0ec8Sopenharmony_ci t->state == TOUCH_UPDATE) 1251a46c0ec8Sopenharmony_ci real_fingers_down++; 1252a46c0ec8Sopenharmony_ci } 1253a46c0ec8Sopenharmony_ci 1254a46c0ec8Sopenharmony_ci if (nfake_touches <= tp->num_slots || 1255a46c0ec8Sopenharmony_ci tp->nfingers_down == 0) 1256a46c0ec8Sopenharmony_ci return; 1257a46c0ec8Sopenharmony_ci 1258a46c0ec8Sopenharmony_ci /* if we have more fake fingers down than slots, we assume 1259a46c0ec8Sopenharmony_ci * _all_ fingers have enough pressure, even if some of the slotted 1260a46c0ec8Sopenharmony_ci * ones don't. Anything else gets insane quickly. 1261a46c0ec8Sopenharmony_ci */ 1262a46c0ec8Sopenharmony_ci if (real_fingers_down > 0) { 1263a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1264a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING) { 1265a46c0ec8Sopenharmony_ci /* avoid jumps when landing a finger */ 1266a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1267a46c0ec8Sopenharmony_ci tp_begin_touch(tp, t, time); 1268a46c0ec8Sopenharmony_ci 1269a46c0ec8Sopenharmony_ci if (tp->nfingers_down >= nfake_touches) 1270a46c0ec8Sopenharmony_ci break; 1271a46c0ec8Sopenharmony_ci } 1272a46c0ec8Sopenharmony_ci } 1273a46c0ec8Sopenharmony_ci } 1274a46c0ec8Sopenharmony_ci 1275a46c0ec8Sopenharmony_ci if (tp->nfingers_down > nfake_touches || 1276a46c0ec8Sopenharmony_ci real_fingers_down == 0) { 1277a46c0ec8Sopenharmony_ci for (i = tp->ntouches - 1; i >= 0; i--) { 1278a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1279a46c0ec8Sopenharmony_ci 1280a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING || 1281a46c0ec8Sopenharmony_ci t->state == TOUCH_NONE || 1282a46c0ec8Sopenharmony_ci t->state == TOUCH_MAYBE_END) 1283a46c0ec8Sopenharmony_ci continue; 1284a46c0ec8Sopenharmony_ci 1285a46c0ec8Sopenharmony_ci tp_maybe_end_touch(tp, t, time); 1286a46c0ec8Sopenharmony_ci 1287a46c0ec8Sopenharmony_ci if (real_fingers_down > 0 && 1288a46c0ec8Sopenharmony_ci tp->nfingers_down == nfake_touches) 1289a46c0ec8Sopenharmony_ci break; 1290a46c0ec8Sopenharmony_ci } 1291a46c0ec8Sopenharmony_ci } 1292a46c0ec8Sopenharmony_ci} 1293a46c0ec8Sopenharmony_ci 1294a46c0ec8Sopenharmony_cistatic void 1295a46c0ec8Sopenharmony_citp_unhover_size(struct tp_dispatch *tp, uint64_t time) 1296a46c0ec8Sopenharmony_ci{ 1297a46c0ec8Sopenharmony_ci struct tp_touch *t; 1298a46c0ec8Sopenharmony_ci int low = tp->touch_size.low, 1299a46c0ec8Sopenharmony_ci high = tp->touch_size.high; 1300a46c0ec8Sopenharmony_ci int i; 1301a46c0ec8Sopenharmony_ci 1302a46c0ec8Sopenharmony_ci /* We require 5 slots for size handling, so we don't need to care 1303a46c0ec8Sopenharmony_ci * about fake touches here */ 1304a46c0ec8Sopenharmony_ci 1305a46c0ec8Sopenharmony_ci for (i = 0; i < (int)tp->num_slots; i++) { 1306a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1307a46c0ec8Sopenharmony_ci 1308a46c0ec8Sopenharmony_ci if (t->state == TOUCH_NONE) 1309a46c0ec8Sopenharmony_ci continue; 1310a46c0ec8Sopenharmony_ci 1311a46c0ec8Sopenharmony_ci if (!t->dirty) 1312a46c0ec8Sopenharmony_ci continue; 1313a46c0ec8Sopenharmony_ci 1314a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING) { 1315a46c0ec8Sopenharmony_ci if ((t->major > high && t->minor > low) || 1316a46c0ec8Sopenharmony_ci (t->major > low && t->minor > high)) { 1317a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1318a46c0ec8Sopenharmony_ci "touch-size: begin touch %d\n", 1319a46c0ec8Sopenharmony_ci t->index); 1320a46c0ec8Sopenharmony_ci /* avoid jumps when landing a finger */ 1321a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1322a46c0ec8Sopenharmony_ci tp_begin_touch(tp, t, time); 1323a46c0ec8Sopenharmony_ci } 1324a46c0ec8Sopenharmony_ci } else { 1325a46c0ec8Sopenharmony_ci if (t->major < low || t->minor < low) { 1326a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 1327a46c0ec8Sopenharmony_ci "touch-size: end touch %d\n", 1328a46c0ec8Sopenharmony_ci t->index); 1329a46c0ec8Sopenharmony_ci tp_maybe_end_touch(tp, t, time); 1330a46c0ec8Sopenharmony_ci } 1331a46c0ec8Sopenharmony_ci } 1332a46c0ec8Sopenharmony_ci } 1333a46c0ec8Sopenharmony_ci} 1334a46c0ec8Sopenharmony_ci 1335a46c0ec8Sopenharmony_cistatic void 1336a46c0ec8Sopenharmony_citp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time) 1337a46c0ec8Sopenharmony_ci{ 1338a46c0ec8Sopenharmony_ci struct tp_touch *t; 1339a46c0ec8Sopenharmony_ci unsigned int nfake_touches; 1340a46c0ec8Sopenharmony_ci int i; 1341a46c0ec8Sopenharmony_ci 1342a46c0ec8Sopenharmony_ci if (!tp->fake_touches && !tp->nfingers_down) 1343a46c0ec8Sopenharmony_ci return; 1344a46c0ec8Sopenharmony_ci 1345a46c0ec8Sopenharmony_ci nfake_touches = tp_fake_finger_count(tp); 1346a46c0ec8Sopenharmony_ci if (nfake_touches == FAKE_FINGER_OVERFLOW) 1347a46c0ec8Sopenharmony_ci return; 1348a46c0ec8Sopenharmony_ci 1349a46c0ec8Sopenharmony_ci if (tp->nfingers_down == nfake_touches && 1350a46c0ec8Sopenharmony_ci ((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) || 1351a46c0ec8Sopenharmony_ci (tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp)))) 1352a46c0ec8Sopenharmony_ci return; 1353a46c0ec8Sopenharmony_ci 1354a46c0ec8Sopenharmony_ci /* if BTN_TOUCH is set and we have less fingers down than fake 1355a46c0ec8Sopenharmony_ci * touches, switch each hovering touch to BEGIN 1356a46c0ec8Sopenharmony_ci * until nfingers_down matches nfake_touches 1357a46c0ec8Sopenharmony_ci */ 1358a46c0ec8Sopenharmony_ci if (tp_fake_finger_is_touching(tp) && 1359a46c0ec8Sopenharmony_ci tp->nfingers_down < nfake_touches) { 1360a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1361a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING) { 1362a46c0ec8Sopenharmony_ci tp_begin_touch(tp, t, time); 1363a46c0ec8Sopenharmony_ci 1364a46c0ec8Sopenharmony_ci if (tp->nfingers_down >= nfake_touches) 1365a46c0ec8Sopenharmony_ci break; 1366a46c0ec8Sopenharmony_ci } 1367a46c0ec8Sopenharmony_ci } 1368a46c0ec8Sopenharmony_ci } 1369a46c0ec8Sopenharmony_ci 1370a46c0ec8Sopenharmony_ci /* if BTN_TOUCH is unset end all touches, we're hovering now. If we 1371a46c0ec8Sopenharmony_ci * have too many touches also end some of them. This is done in 1372a46c0ec8Sopenharmony_ci * reverse order. 1373a46c0ec8Sopenharmony_ci */ 1374a46c0ec8Sopenharmony_ci if (tp->nfingers_down > nfake_touches || 1375a46c0ec8Sopenharmony_ci !tp_fake_finger_is_touching(tp)) { 1376a46c0ec8Sopenharmony_ci for (i = tp->ntouches - 1; i >= 0; i--) { 1377a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1378a46c0ec8Sopenharmony_ci 1379a46c0ec8Sopenharmony_ci if (t->state == TOUCH_HOVERING || 1380a46c0ec8Sopenharmony_ci t->state == TOUCH_NONE) 1381a46c0ec8Sopenharmony_ci continue; 1382a46c0ec8Sopenharmony_ci 1383a46c0ec8Sopenharmony_ci tp_maybe_end_touch(tp, t, time); 1384a46c0ec8Sopenharmony_ci 1385a46c0ec8Sopenharmony_ci if (tp_fake_finger_is_touching(tp) && 1386a46c0ec8Sopenharmony_ci tp->nfingers_down == nfake_touches) 1387a46c0ec8Sopenharmony_ci break; 1388a46c0ec8Sopenharmony_ci } 1389a46c0ec8Sopenharmony_ci } 1390a46c0ec8Sopenharmony_ci} 1391a46c0ec8Sopenharmony_ci 1392a46c0ec8Sopenharmony_cistatic void 1393a46c0ec8Sopenharmony_citp_unhover_touches(struct tp_dispatch *tp, uint64_t time) 1394a46c0ec8Sopenharmony_ci{ 1395a46c0ec8Sopenharmony_ci if (tp->pressure.use_pressure) 1396a46c0ec8Sopenharmony_ci tp_unhover_pressure(tp, time); 1397a46c0ec8Sopenharmony_ci else if (tp->touch_size.use_touch_size) 1398a46c0ec8Sopenharmony_ci tp_unhover_size(tp, time); 1399a46c0ec8Sopenharmony_ci else 1400a46c0ec8Sopenharmony_ci tp_unhover_fake_touches(tp, time); 1401a46c0ec8Sopenharmony_ci 1402a46c0ec8Sopenharmony_ci} 1403a46c0ec8Sopenharmony_ci 1404a46c0ec8Sopenharmony_cistatic inline void 1405a46c0ec8Sopenharmony_citp_position_fake_touches(struct tp_dispatch *tp) 1406a46c0ec8Sopenharmony_ci{ 1407a46c0ec8Sopenharmony_ci struct tp_touch *t; 1408a46c0ec8Sopenharmony_ci struct tp_touch *topmost = NULL; 1409a46c0ec8Sopenharmony_ci unsigned int start, i; 1410a46c0ec8Sopenharmony_ci 1411a46c0ec8Sopenharmony_ci if (tp_fake_finger_count(tp) <= tp->num_slots || 1412a46c0ec8Sopenharmony_ci tp->nfingers_down == 0) 1413a46c0ec8Sopenharmony_ci return; 1414a46c0ec8Sopenharmony_ci 1415a46c0ec8Sopenharmony_ci /* We have at least one fake touch down. Find the top-most real 1416a46c0ec8Sopenharmony_ci * touch and copy its coordinates over to to all fake touches. 1417a46c0ec8Sopenharmony_ci * This is more reliable than just taking the first touch. 1418a46c0ec8Sopenharmony_ci */ 1419a46c0ec8Sopenharmony_ci for (i = 0; i < tp->num_slots; i++) { 1420a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1421a46c0ec8Sopenharmony_ci if (t->state == TOUCH_END || 1422a46c0ec8Sopenharmony_ci t->state == TOUCH_NONE) 1423a46c0ec8Sopenharmony_ci continue; 1424a46c0ec8Sopenharmony_ci 1425a46c0ec8Sopenharmony_ci if (topmost == NULL || t->point.y < topmost->point.y) 1426a46c0ec8Sopenharmony_ci topmost = t; 1427a46c0ec8Sopenharmony_ci } 1428a46c0ec8Sopenharmony_ci 1429a46c0ec8Sopenharmony_ci if (!topmost) { 1430a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 1431a46c0ec8Sopenharmony_ci "Unable to find topmost touch\n"); 1432a46c0ec8Sopenharmony_ci return; 1433a46c0ec8Sopenharmony_ci } 1434a46c0ec8Sopenharmony_ci 1435a46c0ec8Sopenharmony_ci start = tp->has_mt ? tp->num_slots : 1; 1436a46c0ec8Sopenharmony_ci for (i = start; i < tp->ntouches; i++) { 1437a46c0ec8Sopenharmony_ci t = tp_get_touch(tp, i); 1438a46c0ec8Sopenharmony_ci if (t->state == TOUCH_NONE) 1439a46c0ec8Sopenharmony_ci continue; 1440a46c0ec8Sopenharmony_ci 1441a46c0ec8Sopenharmony_ci t->point = topmost->point; 1442a46c0ec8Sopenharmony_ci t->pressure = topmost->pressure; 1443a46c0ec8Sopenharmony_ci if (!t->dirty) 1444a46c0ec8Sopenharmony_ci t->dirty = topmost->dirty; 1445a46c0ec8Sopenharmony_ci } 1446a46c0ec8Sopenharmony_ci} 1447a46c0ec8Sopenharmony_ci 1448a46c0ec8Sopenharmony_cistatic inline bool 1449a46c0ec8Sopenharmony_citp_need_motion_history_reset(struct tp_dispatch *tp) 1450a46c0ec8Sopenharmony_ci{ 1451a46c0ec8Sopenharmony_ci bool rc = false; 1452a46c0ec8Sopenharmony_ci 1453a46c0ec8Sopenharmony_ci /* Changing the numbers of fingers can cause a jump in the 1454a46c0ec8Sopenharmony_ci * coordinates, always reset the motion history for all touches when 1455a46c0ec8Sopenharmony_ci * that happens. 1456a46c0ec8Sopenharmony_ci */ 1457a46c0ec8Sopenharmony_ci if (tp->nfingers_down != tp->old_nfingers_down) 1458a46c0ec8Sopenharmony_ci return true; 1459a46c0ec8Sopenharmony_ci 1460a46c0ec8Sopenharmony_ci /* Quirk: if we had multiple events without x/y axis 1461a46c0ec8Sopenharmony_ci information, the next x/y event is going to be a jump. So we 1462a46c0ec8Sopenharmony_ci reset that touch to non-dirty effectively swallowing that event 1463a46c0ec8Sopenharmony_ci and restarting with the next event again. 1464a46c0ec8Sopenharmony_ci */ 1465a46c0ec8Sopenharmony_ci if (tp->device->model_flags & EVDEV_MODEL_LENOVO_T450_TOUCHPAD) { 1466a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_MOTION) { 1467a46c0ec8Sopenharmony_ci if (tp->quirks.nonmotion_event_count > 10) { 1468a46c0ec8Sopenharmony_ci tp->queued &= ~TOUCHPAD_EVENT_MOTION; 1469a46c0ec8Sopenharmony_ci rc = true; 1470a46c0ec8Sopenharmony_ci } 1471a46c0ec8Sopenharmony_ci tp->quirks.nonmotion_event_count = 0; 1472a46c0ec8Sopenharmony_ci } 1473a46c0ec8Sopenharmony_ci 1474a46c0ec8Sopenharmony_ci if ((tp->queued & (TOUCHPAD_EVENT_OTHERAXIS|TOUCHPAD_EVENT_MOTION)) == 1475a46c0ec8Sopenharmony_ci TOUCHPAD_EVENT_OTHERAXIS) 1476a46c0ec8Sopenharmony_ci tp->quirks.nonmotion_event_count++; 1477a46c0ec8Sopenharmony_ci } 1478a46c0ec8Sopenharmony_ci 1479a46c0ec8Sopenharmony_ci return rc; 1480a46c0ec8Sopenharmony_ci} 1481a46c0ec8Sopenharmony_ci 1482a46c0ec8Sopenharmony_cistatic bool 1483a46c0ec8Sopenharmony_citp_detect_jumps(const struct tp_dispatch *tp, 1484a46c0ec8Sopenharmony_ci struct tp_touch *t, 1485a46c0ec8Sopenharmony_ci uint64_t time) 1486a46c0ec8Sopenharmony_ci{ 1487a46c0ec8Sopenharmony_ci struct device_coords delta; 1488a46c0ec8Sopenharmony_ci struct phys_coords mm; 1489a46c0ec8Sopenharmony_ci struct tp_history_point *last; 1490a46c0ec8Sopenharmony_ci double abs_distance, rel_distance; 1491a46c0ec8Sopenharmony_ci bool is_jump = false; 1492a46c0ec8Sopenharmony_ci uint64_t tdelta; 1493a46c0ec8Sopenharmony_ci /* Reference interval from the touchpad the various thresholds 1494a46c0ec8Sopenharmony_ci * were measured from */ 1495a46c0ec8Sopenharmony_ci unsigned int reference_interval = ms2us(12); 1496a46c0ec8Sopenharmony_ci 1497a46c0ec8Sopenharmony_ci /* On some touchpads the firmware does funky stuff and we cannot 1498a46c0ec8Sopenharmony_ci * have our own jump detection, e.g. Lenovo Carbon X1 Gen 6 (see 1499a46c0ec8Sopenharmony_ci * issue #506) 1500a46c0ec8Sopenharmony_ci */ 1501a46c0ec8Sopenharmony_ci if (tp->jump.detection_disabled) 1502a46c0ec8Sopenharmony_ci return false; 1503a46c0ec8Sopenharmony_ci 1504a46c0ec8Sopenharmony_ci /* We haven't seen pointer jumps on Wacom tablets yet, so exclude 1505a46c0ec8Sopenharmony_ci * those. 1506a46c0ec8Sopenharmony_ci */ 1507a46c0ec8Sopenharmony_ci if (tp->device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD) 1508a46c0ec8Sopenharmony_ci return false; 1509a46c0ec8Sopenharmony_ci 1510a46c0ec8Sopenharmony_ci if (t->history.count == 0) { 1511a46c0ec8Sopenharmony_ci t->jumps.last_delta_mm = 0.0; 1512a46c0ec8Sopenharmony_ci return false; 1513a46c0ec8Sopenharmony_ci } 1514a46c0ec8Sopenharmony_ci 1515a46c0ec8Sopenharmony_ci /* called before tp_motion_history_push, so offset 0 is the most 1516a46c0ec8Sopenharmony_ci * recent coordinate */ 1517a46c0ec8Sopenharmony_ci last = tp_motion_history_offset(t, 0); 1518a46c0ec8Sopenharmony_ci tdelta = time - last->time; 1519a46c0ec8Sopenharmony_ci 1520a46c0ec8Sopenharmony_ci /* For test devices we always force the time delta to 12, at least 1521a46c0ec8Sopenharmony_ci until the test suite actually does proper intervals. */ 1522a46c0ec8Sopenharmony_ci if (tp->device->model_flags & EVDEV_MODEL_TEST_DEVICE) 1523a46c0ec8Sopenharmony_ci reference_interval = tdelta; 1524a46c0ec8Sopenharmony_ci 1525a46c0ec8Sopenharmony_ci /* If the last frame is more than 30ms ago, we have irregular 1526a46c0ec8Sopenharmony_ci * frames, who knows what's a pointer jump here and what's 1527a46c0ec8Sopenharmony_ci * legitimate movement.... */ 1528a46c0ec8Sopenharmony_ci if (tdelta > 2.5 * reference_interval || tdelta == 0) 1529a46c0ec8Sopenharmony_ci return false; 1530a46c0ec8Sopenharmony_ci 1531a46c0ec8Sopenharmony_ci /* We historically expected ~12ms frame intervals, so the numbers 1532a46c0ec8Sopenharmony_ci below are normalized to that (and that's also where the 1533a46c0ec8Sopenharmony_ci measured data came from) */ 1534a46c0ec8Sopenharmony_ci delta.x = abs(t->point.x - last->point.x); 1535a46c0ec8Sopenharmony_ci delta.y = abs(t->point.y - last->point.y); 1536a46c0ec8Sopenharmony_ci mm = evdev_device_unit_delta_to_mm(tp->device, &delta); 1537a46c0ec8Sopenharmony_ci abs_distance = hypot(mm.x, mm.y) * reference_interval/tdelta; 1538a46c0ec8Sopenharmony_ci rel_distance = abs_distance - t->jumps.last_delta_mm; 1539a46c0ec8Sopenharmony_ci 1540a46c0ec8Sopenharmony_ci /* Special case for the ALPS devices in the Lenovo ThinkPad E465, 1541a46c0ec8Sopenharmony_ci * E550. These devices send occasional 4095/0 events on two fingers 1542a46c0ec8Sopenharmony_ci * before snapping back to the correct position. 1543a46c0ec8Sopenharmony_ci * https://gitlab.freedesktop.org/libinput/libinput/-/issues/492 1544a46c0ec8Sopenharmony_ci * The specific values are hardcoded here, if this ever happens on 1545a46c0ec8Sopenharmony_ci * any other device we can make it absmax/absmin instead. 1546a46c0ec8Sopenharmony_ci */ 1547a46c0ec8Sopenharmony_ci if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD && 1548a46c0ec8Sopenharmony_ci t->point.x == 4095 && t->point.y == 0) { 1549a46c0ec8Sopenharmony_ci t->point = last->point; 1550a46c0ec8Sopenharmony_ci return true; 1551a46c0ec8Sopenharmony_ci } 1552a46c0ec8Sopenharmony_ci 1553a46c0ec8Sopenharmony_ci /* Cursor jump if: 1554a46c0ec8Sopenharmony_ci * - current single-event delta is >20mm, or 1555a46c0ec8Sopenharmony_ci * - we increased the delta by over 7mm within a 12ms frame. 1556a46c0ec8Sopenharmony_ci * (12ms simply because that's what I measured) 1557a46c0ec8Sopenharmony_ci */ 1558a46c0ec8Sopenharmony_ci is_jump = abs_distance > 20.0 || rel_distance > 7; 1559a46c0ec8Sopenharmony_ci t->jumps.last_delta_mm = abs_distance; 1560a46c0ec8Sopenharmony_ci 1561a46c0ec8Sopenharmony_ci return is_jump; 1562a46c0ec8Sopenharmony_ci} 1563a46c0ec8Sopenharmony_ci 1564a46c0ec8Sopenharmony_ci/** 1565a46c0ec8Sopenharmony_ci * Rewrite the motion history so that previous points' timestamps are the 1566a46c0ec8Sopenharmony_ci * current point's timestamp minus whatever MSC_TIMESTAMP gives us. 1567a46c0ec8Sopenharmony_ci * 1568a46c0ec8Sopenharmony_ci * This must be called before tp_motion_history_push() 1569a46c0ec8Sopenharmony_ci * 1570a46c0ec8Sopenharmony_ci * @param t The touch point 1571a46c0ec8Sopenharmony_ci * @param jumping_interval The large time interval in µs 1572a46c0ec8Sopenharmony_ci * @param normal_interval Normal hw interval in µs 1573a46c0ec8Sopenharmony_ci * @param time Current time in µs 1574a46c0ec8Sopenharmony_ci */ 1575a46c0ec8Sopenharmony_cistatic inline void 1576a46c0ec8Sopenharmony_citp_motion_history_fix_last(struct tp_dispatch *tp, 1577a46c0ec8Sopenharmony_ci struct tp_touch *t, 1578a46c0ec8Sopenharmony_ci unsigned int jumping_interval, 1579a46c0ec8Sopenharmony_ci unsigned int normal_interval, 1580a46c0ec8Sopenharmony_ci uint64_t time) 1581a46c0ec8Sopenharmony_ci{ 1582a46c0ec8Sopenharmony_ci if (t->state != TOUCH_UPDATE) 1583a46c0ec8Sopenharmony_ci return; 1584a46c0ec8Sopenharmony_ci 1585a46c0ec8Sopenharmony_ci /* We know the coordinates are correct because the touchpad should 1586a46c0ec8Sopenharmony_ci * get that bit right. But the timestamps we got from the kernel are 1587a46c0ec8Sopenharmony_ci * messed up, so we go back in the history and fix them. 1588a46c0ec8Sopenharmony_ci * 1589a46c0ec8Sopenharmony_ci * This way the next delta is huge but it's over a large time, so 1590a46c0ec8Sopenharmony_ci * the pointer accel code should do the right thing. 1591a46c0ec8Sopenharmony_ci */ 1592a46c0ec8Sopenharmony_ci for (int i = 0; i < (int)t->history.count; i++) { 1593a46c0ec8Sopenharmony_ci struct tp_history_point *p; 1594a46c0ec8Sopenharmony_ci 1595a46c0ec8Sopenharmony_ci p = tp_motion_history_offset(t, i); 1596a46c0ec8Sopenharmony_ci p->time = time - jumping_interval - normal_interval * i; 1597a46c0ec8Sopenharmony_ci } 1598a46c0ec8Sopenharmony_ci} 1599a46c0ec8Sopenharmony_ci 1600a46c0ec8Sopenharmony_cistatic void 1601a46c0ec8Sopenharmony_citp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time) 1602a46c0ec8Sopenharmony_ci{ 1603a46c0ec8Sopenharmony_ci struct msc_timestamp *m = &tp->quirks.msc_timestamp; 1604a46c0ec8Sopenharmony_ci 1605a46c0ec8Sopenharmony_ci /* Pointer jump detection based on MSC_TIMESTAMP. 1606a46c0ec8Sopenharmony_ci 1607a46c0ec8Sopenharmony_ci MSC_TIMESTAMP gets reset after a kernel timeout (1s) and on some 1608a46c0ec8Sopenharmony_ci devices (Dell XPS) the i2c controller sleeps after a timeout. On 1609a46c0ec8Sopenharmony_ci wakeup, some events are swallowed, triggering a cursor jump. The 1610a46c0ec8Sopenharmony_ci event sequence after a sleep is always: 1611a46c0ec8Sopenharmony_ci 1612a46c0ec8Sopenharmony_ci initial finger down: 1613a46c0ec8Sopenharmony_ci ABS_X/Y x/y 1614a46c0ec8Sopenharmony_ci MSC_TIMESTAMP 0 1615a46c0ec8Sopenharmony_ci SYN_REPORT +2500ms 1616a46c0ec8Sopenharmony_ci second event: 1617a46c0ec8Sopenharmony_ci ABS_X/Y x+n/y+n # normal movement 1618a46c0ec8Sopenharmony_ci MSC_TIMESTAMP 7300 # the hw interval 1619a46c0ec8Sopenharmony_ci SYN_REPORT +2ms 1620a46c0ec8Sopenharmony_ci third event: 1621a46c0ec8Sopenharmony_ci ABS_X/Y x+lots/y+lots # pointer jump! 1622a46c0ec8Sopenharmony_ci MSC_TIMESTAMP 123456 # well above the hw interval 1623a46c0ec8Sopenharmony_ci SYN_REPORT +2ms 1624a46c0ec8Sopenharmony_ci fourth event: 1625a46c0ec8Sopenharmony_ci ABS_X/Y x+lots+n/y+lots+n # all normal again 1626a46c0ec8Sopenharmony_ci MSC_TIMESTAMP 123456 + 7300 1627a46c0ec8Sopenharmony_ci SYN_REPORT +8ms 1628a46c0ec8Sopenharmony_ci 1629a46c0ec8Sopenharmony_ci Our approach is to detect the 0 timestamp, check the interval on 1630a46c0ec8Sopenharmony_ci the next event and then calculate the movement for one fictitious 1631a46c0ec8Sopenharmony_ci event instead, swallowing all other movements. So if the time 1632a46c0ec8Sopenharmony_ci delta is equivalent to 10 events and the movement is x, we 1633a46c0ec8Sopenharmony_ci instead pretend there was movement of x/10. 1634a46c0ec8Sopenharmony_ci */ 1635a46c0ec8Sopenharmony_ci if (m->now == 0) { 1636a46c0ec8Sopenharmony_ci m->state = JUMP_STATE_EXPECT_FIRST; 1637a46c0ec8Sopenharmony_ci m->interval = 0; 1638a46c0ec8Sopenharmony_ci return; 1639a46c0ec8Sopenharmony_ci } 1640a46c0ec8Sopenharmony_ci 1641a46c0ec8Sopenharmony_ci switch(m->state) { 1642a46c0ec8Sopenharmony_ci case JUMP_STATE_EXPECT_FIRST: 1643a46c0ec8Sopenharmony_ci if (m->now > ms2us(20)) { 1644a46c0ec8Sopenharmony_ci m->state = JUMP_STATE_IGNORE; 1645a46c0ec8Sopenharmony_ci } else { 1646a46c0ec8Sopenharmony_ci m->state = JUMP_STATE_EXPECT_DELAY; 1647a46c0ec8Sopenharmony_ci m->interval = m->now; 1648a46c0ec8Sopenharmony_ci } 1649a46c0ec8Sopenharmony_ci break; 1650a46c0ec8Sopenharmony_ci case JUMP_STATE_EXPECT_DELAY: 1651a46c0ec8Sopenharmony_ci if (m->now > m->interval * 2) { 1652a46c0ec8Sopenharmony_ci uint32_t tdelta; /* µs */ 1653a46c0ec8Sopenharmony_ci struct tp_touch *t; 1654a46c0ec8Sopenharmony_ci 1655a46c0ec8Sopenharmony_ci /* The current time is > 2 times the interval so we 1656a46c0ec8Sopenharmony_ci * have a jump. Fix the motion history */ 1657a46c0ec8Sopenharmony_ci tdelta = m->now - m->interval; 1658a46c0ec8Sopenharmony_ci 1659a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1660a46c0ec8Sopenharmony_ci tp_motion_history_fix_last(tp, 1661a46c0ec8Sopenharmony_ci t, 1662a46c0ec8Sopenharmony_ci tdelta, 1663a46c0ec8Sopenharmony_ci m->interval, 1664a46c0ec8Sopenharmony_ci time); 1665a46c0ec8Sopenharmony_ci } 1666a46c0ec8Sopenharmony_ci m->state = JUMP_STATE_IGNORE; 1667a46c0ec8Sopenharmony_ci 1668a46c0ec8Sopenharmony_ci /* We need to restart the acceleration filter to forget its history. 1669a46c0ec8Sopenharmony_ci * The current point becomes the first point in the history there 1670a46c0ec8Sopenharmony_ci * (including timestamp) and that accelerates correctly. 1671a46c0ec8Sopenharmony_ci * This has a potential to be incorrect but since we only ever see 1672a46c0ec8Sopenharmony_ci * those jumps over the first three events it doesn't matter. 1673a46c0ec8Sopenharmony_ci */ 1674a46c0ec8Sopenharmony_ci filter_restart(tp->device->pointer.filter, tp, time - tdelta); 1675a46c0ec8Sopenharmony_ci } 1676a46c0ec8Sopenharmony_ci break; 1677a46c0ec8Sopenharmony_ci case JUMP_STATE_IGNORE: 1678a46c0ec8Sopenharmony_ci break; 1679a46c0ec8Sopenharmony_ci } 1680a46c0ec8Sopenharmony_ci} 1681a46c0ec8Sopenharmony_ci 1682a46c0ec8Sopenharmony_cistatic void 1683a46c0ec8Sopenharmony_citp_pre_process_state(struct tp_dispatch *tp, uint64_t time) 1684a46c0ec8Sopenharmony_ci{ 1685a46c0ec8Sopenharmony_ci struct tp_touch *t; 1686a46c0ec8Sopenharmony_ci 1687a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_TIMESTAMP) 1688a46c0ec8Sopenharmony_ci tp_process_msc_timestamp(tp, time); 1689a46c0ec8Sopenharmony_ci 1690a46c0ec8Sopenharmony_ci tp_process_fake_touches(tp, time); 1691a46c0ec8Sopenharmony_ci tp_unhover_touches(tp, time); 1692a46c0ec8Sopenharmony_ci 1693a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1694a46c0ec8Sopenharmony_ci if (t->state == TOUCH_MAYBE_END) 1695a46c0ec8Sopenharmony_ci tp_end_touch(tp, t, time); 1696a46c0ec8Sopenharmony_ci 1697a46c0ec8Sopenharmony_ci /* Ignore motion when pressure/touch size fell below the 1698a46c0ec8Sopenharmony_ci * threshold, thus ending the touch */ 1699a46c0ec8Sopenharmony_ci if (t->state == TOUCH_END && t->history.count > 0) 1700a46c0ec8Sopenharmony_ci t->point = tp_motion_history_offset(t, 0)->point; 1701a46c0ec8Sopenharmony_ci } 1702a46c0ec8Sopenharmony_ci 1703a46c0ec8Sopenharmony_ci} 1704a46c0ec8Sopenharmony_ci 1705a46c0ec8Sopenharmony_cistatic void 1706a46c0ec8Sopenharmony_citp_process_state(struct tp_dispatch *tp, uint64_t time) 1707a46c0ec8Sopenharmony_ci{ 1708a46c0ec8Sopenharmony_ci struct tp_touch *t; 1709a46c0ec8Sopenharmony_ci bool restart_filter = false; 1710a46c0ec8Sopenharmony_ci bool want_motion_reset; 1711a46c0ec8Sopenharmony_ci bool have_new_touch = false; 1712a46c0ec8Sopenharmony_ci unsigned int speed_exceeded_count = 0; 1713a46c0ec8Sopenharmony_ci 1714a46c0ec8Sopenharmony_ci tp_position_fake_touches(tp); 1715a46c0ec8Sopenharmony_ci 1716a46c0ec8Sopenharmony_ci want_motion_reset = tp_need_motion_history_reset(tp); 1717a46c0ec8Sopenharmony_ci 1718a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1719a46c0ec8Sopenharmony_ci if (t->state == TOUCH_NONE) 1720a46c0ec8Sopenharmony_ci continue; 1721a46c0ec8Sopenharmony_ci 1722a46c0ec8Sopenharmony_ci if (want_motion_reset) { 1723a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1724a46c0ec8Sopenharmony_ci t->quirks.reset_motion_history = true; 1725a46c0ec8Sopenharmony_ci } else if (t->quirks.reset_motion_history) { 1726a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1727a46c0ec8Sopenharmony_ci t->quirks.reset_motion_history = false; 1728a46c0ec8Sopenharmony_ci } 1729a46c0ec8Sopenharmony_ci 1730a46c0ec8Sopenharmony_ci if (!t->dirty) { 1731a46c0ec8Sopenharmony_ci /* A non-dirty touch must be below the speed limit */ 1732a46c0ec8Sopenharmony_ci if (t->speed.exceeded_count > 0) 1733a46c0ec8Sopenharmony_ci t->speed.exceeded_count--; 1734a46c0ec8Sopenharmony_ci 1735a46c0ec8Sopenharmony_ci speed_exceeded_count = max(speed_exceeded_count, 1736a46c0ec8Sopenharmony_ci t->speed.exceeded_count); 1737a46c0ec8Sopenharmony_ci 1738a46c0ec8Sopenharmony_ci /* A touch that hasn't moved must be in the same 1739a46c0ec8Sopenharmony_ci * position, so let's add this to the motion 1740a46c0ec8Sopenharmony_ci * history. 1741a46c0ec8Sopenharmony_ci */ 1742a46c0ec8Sopenharmony_ci tp_motion_history_push(t, time); 1743a46c0ec8Sopenharmony_ci continue; 1744a46c0ec8Sopenharmony_ci } 1745a46c0ec8Sopenharmony_ci 1746a46c0ec8Sopenharmony_ci if (tp_detect_jumps(tp, t, time)) { 1747a46c0ec8Sopenharmony_ci if (!tp->semi_mt) 1748a46c0ec8Sopenharmony_ci evdev_log_bug_kernel_ratelimit(tp->device, 1749a46c0ec8Sopenharmony_ci &tp->jump.warning, 1750a46c0ec8Sopenharmony_ci "Touch jump detected and discarded.\n" 1751a46c0ec8Sopenharmony_ci "See %s/touchpad-jumping-cursors.html for details\n", 1752a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 1753a46c0ec8Sopenharmony_ci tp_motion_history_reset(t); 1754a46c0ec8Sopenharmony_ci } 1755a46c0ec8Sopenharmony_ci 1756a46c0ec8Sopenharmony_ci tp_thumb_update_touch(tp, t, time); 1757a46c0ec8Sopenharmony_ci tp_palm_detect(tp, t, time); 1758a46c0ec8Sopenharmony_ci tp_detect_wobbling(tp, t, time); 1759a46c0ec8Sopenharmony_ci tp_motion_hysteresis(tp, t); 1760a46c0ec8Sopenharmony_ci tp_motion_history_push(t, time); 1761a46c0ec8Sopenharmony_ci 1762a46c0ec8Sopenharmony_ci /* Touch speed handling: if we'are above the threshold, 1763a46c0ec8Sopenharmony_ci * count each event that we're over the threshold up to 10 1764a46c0ec8Sopenharmony_ci * events. Count down when we are below the speed. 1765a46c0ec8Sopenharmony_ci * 1766a46c0ec8Sopenharmony_ci * Take the touch with the highest speed excess, if it is 1767a46c0ec8Sopenharmony_ci * above a certain threshold (5, see below), assume a 1768a46c0ec8Sopenharmony_ci * dropped finger is a thumb. 1769a46c0ec8Sopenharmony_ci * 1770a46c0ec8Sopenharmony_ci * Yes, this relies on the touchpad to keep sending us 1771a46c0ec8Sopenharmony_ci * events even if the finger doesn't move, otherwise we 1772a46c0ec8Sopenharmony_ci * never count down. Let's see how far we get with that. 1773a46c0ec8Sopenharmony_ci */ 1774a46c0ec8Sopenharmony_ci if (t->speed.last_speed > THUMB_IGNORE_SPEED_THRESHOLD) { 1775a46c0ec8Sopenharmony_ci if (t->speed.exceeded_count < 15) 1776a46c0ec8Sopenharmony_ci t->speed.exceeded_count++; 1777a46c0ec8Sopenharmony_ci } else if (t->speed.exceeded_count > 0) { 1778a46c0ec8Sopenharmony_ci t->speed.exceeded_count--; 1779a46c0ec8Sopenharmony_ci } 1780a46c0ec8Sopenharmony_ci 1781a46c0ec8Sopenharmony_ci speed_exceeded_count = max(speed_exceeded_count, 1782a46c0ec8Sopenharmony_ci t->speed.exceeded_count); 1783a46c0ec8Sopenharmony_ci 1784a46c0ec8Sopenharmony_ci tp_calculate_motion_speed(tp, t, time); 1785a46c0ec8Sopenharmony_ci 1786a46c0ec8Sopenharmony_ci tp_unpin_finger(tp, t); 1787a46c0ec8Sopenharmony_ci 1788a46c0ec8Sopenharmony_ci if (t->state == TOUCH_BEGIN) { 1789a46c0ec8Sopenharmony_ci have_new_touch = true; 1790a46c0ec8Sopenharmony_ci restart_filter = true; 1791a46c0ec8Sopenharmony_ci } 1792a46c0ec8Sopenharmony_ci } 1793a46c0ec8Sopenharmony_ci 1794a46c0ec8Sopenharmony_ci if (tp->thumb.detect_thumbs && 1795a46c0ec8Sopenharmony_ci have_new_touch && 1796a46c0ec8Sopenharmony_ci tp->nfingers_down >= 2) 1797a46c0ec8Sopenharmony_ci tp_thumb_update_multifinger(tp); 1798a46c0ec8Sopenharmony_ci 1799a46c0ec8Sopenharmony_ci if (restart_filter) 1800a46c0ec8Sopenharmony_ci filter_restart(tp->device->pointer.filter, tp, time); 1801a46c0ec8Sopenharmony_ci 1802a46c0ec8Sopenharmony_ci tp_button_handle_state(tp, time); 1803a46c0ec8Sopenharmony_ci tp_edge_scroll_handle_state(tp, time); 1804a46c0ec8Sopenharmony_ci 1805a46c0ec8Sopenharmony_ci /* 1806a46c0ec8Sopenharmony_ci * We have a physical button down event on a clickpad. To avoid 1807a46c0ec8Sopenharmony_ci * spurious pointer moves by the clicking finger we pin all fingers. 1808a46c0ec8Sopenharmony_ci * We unpin fingers when they move more then a certain threshold to 1809a46c0ec8Sopenharmony_ci * to allow drag and drop. 1810a46c0ec8Sopenharmony_ci */ 1811a46c0ec8Sopenharmony_ci if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) && 1812a46c0ec8Sopenharmony_ci tp->buttons.is_clickpad) 1813a46c0ec8Sopenharmony_ci tp_pin_fingers(tp); 1814a46c0ec8Sopenharmony_ci 1815a46c0ec8Sopenharmony_ci tp_gesture_handle_state(tp, time); 1816a46c0ec8Sopenharmony_ci} 1817a46c0ec8Sopenharmony_ci 1818a46c0ec8Sopenharmony_cistatic void 1819a46c0ec8Sopenharmony_citp_post_process_state(struct tp_dispatch *tp, uint64_t time) 1820a46c0ec8Sopenharmony_ci{ 1821a46c0ec8Sopenharmony_ci struct tp_touch *t; 1822a46c0ec8Sopenharmony_ci 1823a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1824a46c0ec8Sopenharmony_ci 1825a46c0ec8Sopenharmony_ci if (!t->dirty) 1826a46c0ec8Sopenharmony_ci continue; 1827a46c0ec8Sopenharmony_ci 1828a46c0ec8Sopenharmony_ci if (t->state == TOUCH_END) { 1829a46c0ec8Sopenharmony_ci if (t->has_ended) 1830a46c0ec8Sopenharmony_ci t->state = TOUCH_NONE; 1831a46c0ec8Sopenharmony_ci else 1832a46c0ec8Sopenharmony_ci t->state = TOUCH_HOVERING; 1833a46c0ec8Sopenharmony_ci } else if (t->state == TOUCH_BEGIN) { 1834a46c0ec8Sopenharmony_ci t->state = TOUCH_UPDATE; 1835a46c0ec8Sopenharmony_ci } 1836a46c0ec8Sopenharmony_ci 1837a46c0ec8Sopenharmony_ci t->dirty = false; 1838a46c0ec8Sopenharmony_ci } 1839a46c0ec8Sopenharmony_ci 1840a46c0ec8Sopenharmony_ci tp->old_nfingers_down = tp->nfingers_down; 1841a46c0ec8Sopenharmony_ci tp->buttons.old_state = tp->buttons.state; 1842a46c0ec8Sopenharmony_ci 1843a46c0ec8Sopenharmony_ci tp->queued = TOUCHPAD_EVENT_NONE; 1844a46c0ec8Sopenharmony_ci 1845a46c0ec8Sopenharmony_ci if (tp->nfingers_down == 0) 1846a46c0ec8Sopenharmony_ci tp_thumb_reset(tp); 1847a46c0ec8Sopenharmony_ci 1848a46c0ec8Sopenharmony_ci tp_tap_post_process_state(tp); 1849a46c0ec8Sopenharmony_ci} 1850a46c0ec8Sopenharmony_ci 1851a46c0ec8Sopenharmony_cistatic void 1852a46c0ec8Sopenharmony_citp_post_events(struct tp_dispatch *tp, uint64_t time) 1853a46c0ec8Sopenharmony_ci{ 1854a46c0ec8Sopenharmony_ci bool ignore_motion = false; 1855a46c0ec8Sopenharmony_ci 1856a46c0ec8Sopenharmony_ci /* Only post (top) button events while suspended */ 1857a46c0ec8Sopenharmony_ci if (tp->device->is_suspended) { 1858a46c0ec8Sopenharmony_ci tp_post_button_events(tp, time); 1859a46c0ec8Sopenharmony_ci return; 1860a46c0ec8Sopenharmony_ci } 1861a46c0ec8Sopenharmony_ci 1862a46c0ec8Sopenharmony_ci ignore_motion |= tp_tap_handle_state(tp, time); 1863a46c0ec8Sopenharmony_ci ignore_motion |= tp_post_button_events(tp, time); 1864a46c0ec8Sopenharmony_ci 1865a46c0ec8Sopenharmony_ci if (tp->palm.trackpoint_active || tp->dwt.keyboard_active) { 1866a46c0ec8Sopenharmony_ci tp_edge_scroll_stop_events(tp, time); 1867a46c0ec8Sopenharmony_ci tp_gesture_cancel(tp, time); 1868a46c0ec8Sopenharmony_ci return; 1869a46c0ec8Sopenharmony_ci } 1870a46c0ec8Sopenharmony_ci 1871a46c0ec8Sopenharmony_ci if (ignore_motion) { 1872a46c0ec8Sopenharmony_ci tp_edge_scroll_stop_events(tp, time); 1873a46c0ec8Sopenharmony_ci tp_gesture_cancel_motion_gestures(tp, time); 1874a46c0ec8Sopenharmony_ci tp_gesture_post_events(tp, time, true); 1875a46c0ec8Sopenharmony_ci return; 1876a46c0ec8Sopenharmony_ci } 1877a46c0ec8Sopenharmony_ci 1878a46c0ec8Sopenharmony_ci if (tp_edge_scroll_post_events(tp, time) != 0) 1879a46c0ec8Sopenharmony_ci return; 1880a46c0ec8Sopenharmony_ci 1881a46c0ec8Sopenharmony_ci tp_gesture_post_events(tp, time, false); 1882a46c0ec8Sopenharmony_ci} 1883a46c0ec8Sopenharmony_ci 1884a46c0ec8Sopenharmony_cistatic void 1885a46c0ec8Sopenharmony_citp_apply_rotation(struct evdev_device *device) 1886a46c0ec8Sopenharmony_ci{ 1887a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch; 1888a46c0ec8Sopenharmony_ci 1889a46c0ec8Sopenharmony_ci if (tp->left_handed.want_rotate == tp->left_handed.rotate) 1890a46c0ec8Sopenharmony_ci return; 1891a46c0ec8Sopenharmony_ci 1892a46c0ec8Sopenharmony_ci if (tp->nfingers_down) 1893a46c0ec8Sopenharmony_ci return; 1894a46c0ec8Sopenharmony_ci 1895a46c0ec8Sopenharmony_ci tp->left_handed.rotate = tp->left_handed.want_rotate; 1896a46c0ec8Sopenharmony_ci 1897a46c0ec8Sopenharmony_ci evdev_log_debug(device, 1898a46c0ec8Sopenharmony_ci "touchpad-rotation: rotation is %s\n", 1899a46c0ec8Sopenharmony_ci tp->left_handed.rotate ? "on" : "off"); 1900a46c0ec8Sopenharmony_ci} 1901a46c0ec8Sopenharmony_ci 1902a46c0ec8Sopenharmony_cistatic void 1903a46c0ec8Sopenharmony_citp_handle_state(struct tp_dispatch *tp, 1904a46c0ec8Sopenharmony_ci uint64_t time) 1905a46c0ec8Sopenharmony_ci{ 1906a46c0ec8Sopenharmony_ci tp_pre_process_state(tp, time); 1907a46c0ec8Sopenharmony_ci tp_process_state(tp, time); 1908a46c0ec8Sopenharmony_ci tp_post_events(tp, time); 1909a46c0ec8Sopenharmony_ci tp_post_process_state(tp, time); 1910a46c0ec8Sopenharmony_ci 1911a46c0ec8Sopenharmony_ci tp_clickpad_middlebutton_apply_config(tp->device); 1912a46c0ec8Sopenharmony_ci tp_apply_rotation(tp->device); 1913a46c0ec8Sopenharmony_ci} 1914a46c0ec8Sopenharmony_ci 1915a46c0ec8Sopenharmony_ciLIBINPUT_UNUSED 1916a46c0ec8Sopenharmony_cistatic inline void 1917a46c0ec8Sopenharmony_citp_debug_touch_state(struct tp_dispatch *tp, 1918a46c0ec8Sopenharmony_ci struct evdev_device *device) 1919a46c0ec8Sopenharmony_ci{ 1920a46c0ec8Sopenharmony_ci char buf[1024] = {0}; 1921a46c0ec8Sopenharmony_ci struct tp_touch *t; 1922a46c0ec8Sopenharmony_ci size_t i = 0; 1923a46c0ec8Sopenharmony_ci 1924a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1925a46c0ec8Sopenharmony_ci if (i >= tp->nfingers_down) 1926a46c0ec8Sopenharmony_ci break; 1927a46c0ec8Sopenharmony_ci sprintf(&buf[strlen(buf)], 1928a46c0ec8Sopenharmony_ci "slot %zd: %04d/%04d p%03d %s |", 1929a46c0ec8Sopenharmony_ci i++, 1930a46c0ec8Sopenharmony_ci t->point.x, 1931a46c0ec8Sopenharmony_ci t->point.y, 1932a46c0ec8Sopenharmony_ci t->pressure, 1933a46c0ec8Sopenharmony_ci tp_touch_active(tp, t) ? "" : "inactive"); 1934a46c0ec8Sopenharmony_ci } 1935a46c0ec8Sopenharmony_ci if (buf[0] != '\0') 1936a46c0ec8Sopenharmony_ci evdev_log_debug(device, "touch state: %s\n", buf); 1937a46c0ec8Sopenharmony_ci} 1938a46c0ec8Sopenharmony_ci 1939a46c0ec8Sopenharmony_cistatic void 1940a46c0ec8Sopenharmony_citp_interface_process(struct evdev_dispatch *dispatch, 1941a46c0ec8Sopenharmony_ci struct evdev_device *device, 1942a46c0ec8Sopenharmony_ci struct input_event *e, 1943a46c0ec8Sopenharmony_ci uint64_t time) 1944a46c0ec8Sopenharmony_ci{ 1945a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 1946a46c0ec8Sopenharmony_ci 1947a46c0ec8Sopenharmony_ci switch (e->type) { 1948a46c0ec8Sopenharmony_ci case EV_ABS: 1949a46c0ec8Sopenharmony_ci if (tp->has_mt) 1950a46c0ec8Sopenharmony_ci tp_process_absolute(tp, e, time); 1951a46c0ec8Sopenharmony_ci else 1952a46c0ec8Sopenharmony_ci tp_process_absolute_st(tp, e, time); 1953a46c0ec8Sopenharmony_ci break; 1954a46c0ec8Sopenharmony_ci case EV_KEY: 1955a46c0ec8Sopenharmony_ci tp_process_key(tp, e, time); 1956a46c0ec8Sopenharmony_ci break; 1957a46c0ec8Sopenharmony_ci case EV_MSC: 1958a46c0ec8Sopenharmony_ci tp_process_msc(tp, e, time); 1959a46c0ec8Sopenharmony_ci break; 1960a46c0ec8Sopenharmony_ci case EV_SYN: 1961a46c0ec8Sopenharmony_ci tp_handle_state(tp, time); 1962a46c0ec8Sopenharmony_ci#if 0 1963a46c0ec8Sopenharmony_ci tp_debug_touch_state(tp, device); 1964a46c0ec8Sopenharmony_ci#endif 1965a46c0ec8Sopenharmony_ci break; 1966a46c0ec8Sopenharmony_ci } 1967a46c0ec8Sopenharmony_ci} 1968a46c0ec8Sopenharmony_ci 1969a46c0ec8Sopenharmony_cistatic void 1970a46c0ec8Sopenharmony_citp_remove_sendevents(struct tp_dispatch *tp) 1971a46c0ec8Sopenharmony_ci{ 1972a46c0ec8Sopenharmony_ci struct evdev_paired_keyboard *kbd; 1973a46c0ec8Sopenharmony_ci 1974a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->palm.trackpoint_timer); 1975a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->dwt.keyboard_timer); 1976a46c0ec8Sopenharmony_ci 1977a46c0ec8Sopenharmony_ci if (tp->buttons.trackpoint && 1978a46c0ec8Sopenharmony_ci tp->palm.monitor_trackpoint) 1979a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 1980a46c0ec8Sopenharmony_ci &tp->palm.trackpoint_listener); 1981a46c0ec8Sopenharmony_ci 1982a46c0ec8Sopenharmony_ci list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) { 1983a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener(&kbd->listener); 1984a46c0ec8Sopenharmony_ci } 1985a46c0ec8Sopenharmony_ci 1986a46c0ec8Sopenharmony_ci if (tp->lid_switch.lid_switch) 1987a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 1988a46c0ec8Sopenharmony_ci &tp->lid_switch.listener); 1989a46c0ec8Sopenharmony_ci 1990a46c0ec8Sopenharmony_ci if (tp->tablet_mode_switch.tablet_mode_switch) 1991a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 1992a46c0ec8Sopenharmony_ci &tp->tablet_mode_switch.listener); 1993a46c0ec8Sopenharmony_ci} 1994a46c0ec8Sopenharmony_ci 1995a46c0ec8Sopenharmony_cistatic void 1996a46c0ec8Sopenharmony_citp_interface_remove(struct evdev_dispatch *dispatch) 1997a46c0ec8Sopenharmony_ci{ 1998a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 1999a46c0ec8Sopenharmony_ci struct evdev_paired_keyboard *kbd; 2000a46c0ec8Sopenharmony_ci 2001a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->arbitration.arbitration_timer); 2002a46c0ec8Sopenharmony_ci 2003a46c0ec8Sopenharmony_ci list_for_each_safe(kbd, &tp->dwt.paired_keyboard_list, link) { 2004a46c0ec8Sopenharmony_ci evdev_paired_keyboard_destroy(kbd); 2005a46c0ec8Sopenharmony_ci } 2006a46c0ec8Sopenharmony_ci tp->dwt.keyboard_active = false; 2007a46c0ec8Sopenharmony_ci 2008a46c0ec8Sopenharmony_ci tp_remove_tap(tp); 2009a46c0ec8Sopenharmony_ci tp_remove_buttons(tp); 2010a46c0ec8Sopenharmony_ci tp_remove_sendevents(tp); 2011a46c0ec8Sopenharmony_ci tp_remove_edge_scroll(tp); 2012a46c0ec8Sopenharmony_ci tp_remove_gesture(tp); 2013a46c0ec8Sopenharmony_ci} 2014a46c0ec8Sopenharmony_ci 2015a46c0ec8Sopenharmony_cistatic void 2016a46c0ec8Sopenharmony_citp_interface_destroy(struct evdev_dispatch *dispatch) 2017a46c0ec8Sopenharmony_ci{ 2018a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 2019a46c0ec8Sopenharmony_ci 2020a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->arbitration.arbitration_timer); 2021a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->palm.trackpoint_timer); 2022a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->dwt.keyboard_timer); 2023a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->tap.timer); 2024a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->gesture.finger_count_switch_timer); 2025a46c0ec8Sopenharmony_ci libinput_timer_destroy(&tp->gesture.hold_timer); 2026a46c0ec8Sopenharmony_ci free(tp->touches); 2027a46c0ec8Sopenharmony_ci free(tp); 2028a46c0ec8Sopenharmony_ci} 2029a46c0ec8Sopenharmony_ci 2030a46c0ec8Sopenharmony_cistatic void 2031a46c0ec8Sopenharmony_citp_release_fake_touches(struct tp_dispatch *tp) 2032a46c0ec8Sopenharmony_ci{ 2033a46c0ec8Sopenharmony_ci tp->fake_touches = 0; 2034a46c0ec8Sopenharmony_ci} 2035a46c0ec8Sopenharmony_ci 2036a46c0ec8Sopenharmony_cistatic void 2037a46c0ec8Sopenharmony_citp_clear_state(struct tp_dispatch *tp) 2038a46c0ec8Sopenharmony_ci{ 2039a46c0ec8Sopenharmony_ci uint64_t now = libinput_now(tp_libinput_context(tp)); 2040a46c0ec8Sopenharmony_ci struct tp_touch *t; 2041a46c0ec8Sopenharmony_ci 2042a46c0ec8Sopenharmony_ci /* Unroll the touchpad state. 2043a46c0ec8Sopenharmony_ci * Release buttons first. If tp is a clickpad, the button event 2044a46c0ec8Sopenharmony_ci * must come before the touch up. If it isn't, the order doesn't 2045a46c0ec8Sopenharmony_ci * matter anyway 2046a46c0ec8Sopenharmony_ci * 2047a46c0ec8Sopenharmony_ci * Then cancel all timeouts on the taps, triggering the last set 2048a46c0ec8Sopenharmony_ci * of events. 2049a46c0ec8Sopenharmony_ci * 2050a46c0ec8Sopenharmony_ci * Then lift all touches so the touchpad is in a neutral state. 2051a46c0ec8Sopenharmony_ci * 2052a46c0ec8Sopenharmony_ci * Then reset thumb state. 2053a46c0ec8Sopenharmony_ci * 2054a46c0ec8Sopenharmony_ci */ 2055a46c0ec8Sopenharmony_ci tp_release_all_buttons(tp, now); 2056a46c0ec8Sopenharmony_ci tp_release_all_taps(tp, now); 2057a46c0ec8Sopenharmony_ci 2058a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 2059a46c0ec8Sopenharmony_ci tp_end_sequence(tp, t, now); 2060a46c0ec8Sopenharmony_ci } 2061a46c0ec8Sopenharmony_ci tp_release_fake_touches(tp); 2062a46c0ec8Sopenharmony_ci 2063a46c0ec8Sopenharmony_ci tp_thumb_reset(tp); 2064a46c0ec8Sopenharmony_ci 2065a46c0ec8Sopenharmony_ci tp_handle_state(tp, now); 2066a46c0ec8Sopenharmony_ci} 2067a46c0ec8Sopenharmony_ci 2068a46c0ec8Sopenharmony_cistatic void 2069a46c0ec8Sopenharmony_citp_suspend(struct tp_dispatch *tp, 2070a46c0ec8Sopenharmony_ci struct evdev_device *device, 2071a46c0ec8Sopenharmony_ci enum suspend_trigger trigger) 2072a46c0ec8Sopenharmony_ci{ 2073a46c0ec8Sopenharmony_ci if (tp->suspend_reason & trigger) 2074a46c0ec8Sopenharmony_ci return; 2075a46c0ec8Sopenharmony_ci 2076a46c0ec8Sopenharmony_ci if (tp->suspend_reason != 0) 2077a46c0ec8Sopenharmony_ci goto out; 2078a46c0ec8Sopenharmony_ci 2079a46c0ec8Sopenharmony_ci tp_clear_state(tp); 2080a46c0ec8Sopenharmony_ci 2081a46c0ec8Sopenharmony_ci /* On devices with top softwarebuttons we don't actually suspend the 2082a46c0ec8Sopenharmony_ci * device, to keep the "trackpoint" buttons working. tp_post_events() 2083a46c0ec8Sopenharmony_ci * will only send events for the trackpoint while suspended. 2084a46c0ec8Sopenharmony_ci */ 2085a46c0ec8Sopenharmony_ci if (tp->buttons.has_topbuttons) { 2086a46c0ec8Sopenharmony_ci evdev_notify_suspended_device(device); 2087a46c0ec8Sopenharmony_ci /* Enlarge topbutton area while suspended */ 2088a46c0ec8Sopenharmony_ci tp_init_top_softbuttons(tp, device, 3.0); 2089a46c0ec8Sopenharmony_ci } else { 2090a46c0ec8Sopenharmony_ci evdev_device_suspend(device); 2091a46c0ec8Sopenharmony_ci } 2092a46c0ec8Sopenharmony_ci 2093a46c0ec8Sopenharmony_ciout: 2094a46c0ec8Sopenharmony_ci tp->suspend_reason |= trigger; 2095a46c0ec8Sopenharmony_ci} 2096a46c0ec8Sopenharmony_ci 2097a46c0ec8Sopenharmony_cistatic void 2098a46c0ec8Sopenharmony_citp_interface_suspend(struct evdev_dispatch *dispatch, 2099a46c0ec8Sopenharmony_ci struct evdev_device *device) 2100a46c0ec8Sopenharmony_ci{ 2101a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 2102a46c0ec8Sopenharmony_ci 2103a46c0ec8Sopenharmony_ci tp_clear_state(tp); 2104a46c0ec8Sopenharmony_ci} 2105a46c0ec8Sopenharmony_ci 2106a46c0ec8Sopenharmony_cistatic inline void 2107a46c0ec8Sopenharmony_citp_sync_touch(struct tp_dispatch *tp, 2108a46c0ec8Sopenharmony_ci struct evdev_device *device, 2109a46c0ec8Sopenharmony_ci struct tp_touch *t, 2110a46c0ec8Sopenharmony_ci int slot) 2111a46c0ec8Sopenharmony_ci{ 2112a46c0ec8Sopenharmony_ci struct libevdev *evdev = device->evdev; 2113a46c0ec8Sopenharmony_ci int tracking_id; 2114a46c0ec8Sopenharmony_ci 2115a46c0ec8Sopenharmony_ci if (!libevdev_fetch_slot_value(evdev, 2116a46c0ec8Sopenharmony_ci slot, 2117a46c0ec8Sopenharmony_ci ABS_MT_POSITION_X, 2118a46c0ec8Sopenharmony_ci &t->point.x)) 2119a46c0ec8Sopenharmony_ci t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X); 2120a46c0ec8Sopenharmony_ci if (!libevdev_fetch_slot_value(evdev, 2121a46c0ec8Sopenharmony_ci slot, 2122a46c0ec8Sopenharmony_ci ABS_MT_POSITION_Y, 2123a46c0ec8Sopenharmony_ci &t->point.y)) 2124a46c0ec8Sopenharmony_ci t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y); 2125a46c0ec8Sopenharmony_ci 2126a46c0ec8Sopenharmony_ci if (!libevdev_fetch_slot_value(evdev, 2127a46c0ec8Sopenharmony_ci slot, 2128a46c0ec8Sopenharmony_ci ABS_MT_PRESSURE, 2129a46c0ec8Sopenharmony_ci &t->pressure)) 2130a46c0ec8Sopenharmony_ci t->pressure = libevdev_get_event_value(evdev, 2131a46c0ec8Sopenharmony_ci EV_ABS, 2132a46c0ec8Sopenharmony_ci ABS_PRESSURE); 2133a46c0ec8Sopenharmony_ci 2134a46c0ec8Sopenharmony_ci libevdev_fetch_slot_value(evdev, 2135a46c0ec8Sopenharmony_ci slot, 2136a46c0ec8Sopenharmony_ci ABS_MT_TOUCH_MAJOR, 2137a46c0ec8Sopenharmony_ci &t->major); 2138a46c0ec8Sopenharmony_ci libevdev_fetch_slot_value(evdev, 2139a46c0ec8Sopenharmony_ci slot, 2140a46c0ec8Sopenharmony_ci ABS_MT_TOUCH_MINOR, 2141a46c0ec8Sopenharmony_ci &t->minor); 2142a46c0ec8Sopenharmony_ci 2143a46c0ec8Sopenharmony_ci if (libevdev_fetch_slot_value(evdev, 2144a46c0ec8Sopenharmony_ci slot, 2145a46c0ec8Sopenharmony_ci ABS_MT_TRACKING_ID, 2146a46c0ec8Sopenharmony_ci &tracking_id) && 2147a46c0ec8Sopenharmony_ci tracking_id != -1) 2148a46c0ec8Sopenharmony_ci tp->nactive_slots++; 2149a46c0ec8Sopenharmony_ci} 2150a46c0ec8Sopenharmony_ci 2151a46c0ec8Sopenharmony_cistatic void 2152a46c0ec8Sopenharmony_citp_sync_slots(struct tp_dispatch *tp, 2153a46c0ec8Sopenharmony_ci struct evdev_device *device) 2154a46c0ec8Sopenharmony_ci{ 2155a46c0ec8Sopenharmony_ci /* Always sync the first touch so we get ABS_X/Y synced on 2156a46c0ec8Sopenharmony_ci * single-touch touchpads */ 2157a46c0ec8Sopenharmony_ci tp_sync_touch(tp, device, &tp->touches[0], 0); 2158a46c0ec8Sopenharmony_ci for (unsigned int i = 1; i < tp->num_slots; i++) 2159a46c0ec8Sopenharmony_ci tp_sync_touch(tp, device, &tp->touches[i], i); 2160a46c0ec8Sopenharmony_ci} 2161a46c0ec8Sopenharmony_ci 2162a46c0ec8Sopenharmony_cistatic void 2163a46c0ec8Sopenharmony_citp_resume(struct tp_dispatch *tp, 2164a46c0ec8Sopenharmony_ci struct evdev_device *device, 2165a46c0ec8Sopenharmony_ci enum suspend_trigger trigger) 2166a46c0ec8Sopenharmony_ci{ 2167a46c0ec8Sopenharmony_ci tp->suspend_reason &= ~trigger; 2168a46c0ec8Sopenharmony_ci if (tp->suspend_reason != 0) 2169a46c0ec8Sopenharmony_ci return; 2170a46c0ec8Sopenharmony_ci 2171a46c0ec8Sopenharmony_ci if (tp->buttons.has_topbuttons) { 2172a46c0ec8Sopenharmony_ci /* tap state-machine is offline while suspended, reset state */ 2173a46c0ec8Sopenharmony_ci tp_clear_state(tp); 2174a46c0ec8Sopenharmony_ci /* restore original topbutton area size */ 2175a46c0ec8Sopenharmony_ci tp_init_top_softbuttons(tp, device, 1.0); 2176a46c0ec8Sopenharmony_ci evdev_notify_resumed_device(device); 2177a46c0ec8Sopenharmony_ci } else { 2178a46c0ec8Sopenharmony_ci evdev_device_resume(device); 2179a46c0ec8Sopenharmony_ci } 2180a46c0ec8Sopenharmony_ci 2181a46c0ec8Sopenharmony_ci tp_sync_slots(tp, device); 2182a46c0ec8Sopenharmony_ci} 2183a46c0ec8Sopenharmony_ci 2184a46c0ec8Sopenharmony_cistatic void 2185a46c0ec8Sopenharmony_citp_trackpoint_timeout(uint64_t now, void *data) 2186a46c0ec8Sopenharmony_ci{ 2187a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2188a46c0ec8Sopenharmony_ci 2189a46c0ec8Sopenharmony_ci if (tp->palm.trackpoint_active) { 2190a46c0ec8Sopenharmony_ci tp_tap_resume(tp, now); 2191a46c0ec8Sopenharmony_ci tp->palm.trackpoint_active = false; 2192a46c0ec8Sopenharmony_ci } 2193a46c0ec8Sopenharmony_ci tp->palm.trackpoint_event_count = 0; 2194a46c0ec8Sopenharmony_ci} 2195a46c0ec8Sopenharmony_ci 2196a46c0ec8Sopenharmony_cistatic void 2197a46c0ec8Sopenharmony_citp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data) 2198a46c0ec8Sopenharmony_ci{ 2199a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2200a46c0ec8Sopenharmony_ci 2201a46c0ec8Sopenharmony_ci if (!tp->palm.dwtp_enabled) 2202a46c0ec8Sopenharmony_ci return; 2203a46c0ec8Sopenharmony_ci 2204a46c0ec8Sopenharmony_ci /* Buttons do not count as trackpad activity, as people may use 2205a46c0ec8Sopenharmony_ci the trackpoint buttons in combination with the touchpad. */ 2206a46c0ec8Sopenharmony_ci if (event->type == LIBINPUT_EVENT_POINTER_BUTTON) 2207a46c0ec8Sopenharmony_ci return; 2208a46c0ec8Sopenharmony_ci 2209a46c0ec8Sopenharmony_ci tp->palm.trackpoint_last_event_time = time; 2210a46c0ec8Sopenharmony_ci tp->palm.trackpoint_event_count++; 2211a46c0ec8Sopenharmony_ci 2212a46c0ec8Sopenharmony_ci /* Require at least three events before enabling palm detection */ 2213a46c0ec8Sopenharmony_ci if (tp->palm.trackpoint_event_count < 3) { 2214a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->palm.trackpoint_timer, 2215a46c0ec8Sopenharmony_ci time + DEFAULT_TRACKPOINT_EVENT_TIMEOUT); 2216a46c0ec8Sopenharmony_ci return; 2217a46c0ec8Sopenharmony_ci } 2218a46c0ec8Sopenharmony_ci 2219a46c0ec8Sopenharmony_ci if (!tp->palm.trackpoint_active) { 2220a46c0ec8Sopenharmony_ci tp_stop_actions(tp, time); 2221a46c0ec8Sopenharmony_ci tp->palm.trackpoint_active = true; 2222a46c0ec8Sopenharmony_ci } 2223a46c0ec8Sopenharmony_ci 2224a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->palm.trackpoint_timer, 2225a46c0ec8Sopenharmony_ci time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT); 2226a46c0ec8Sopenharmony_ci} 2227a46c0ec8Sopenharmony_ci 2228a46c0ec8Sopenharmony_cistatic void 2229a46c0ec8Sopenharmony_citp_keyboard_timeout(uint64_t now, void *data) 2230a46c0ec8Sopenharmony_ci{ 2231a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2232a46c0ec8Sopenharmony_ci 2233a46c0ec8Sopenharmony_ci if (tp->dwt.dwt_enabled && 2234a46c0ec8Sopenharmony_ci long_any_bit_set(tp->dwt.key_mask, 2235a46c0ec8Sopenharmony_ci ARRAY_LENGTH(tp->dwt.key_mask))) { 2236a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->dwt.keyboard_timer, 2237a46c0ec8Sopenharmony_ci now + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2); 2238a46c0ec8Sopenharmony_ci tp->dwt.keyboard_last_press_time = now; 2239a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "palm: keyboard timeout refresh\n"); 2240a46c0ec8Sopenharmony_ci return; 2241a46c0ec8Sopenharmony_ci } 2242a46c0ec8Sopenharmony_ci 2243a46c0ec8Sopenharmony_ci tp_tap_resume(tp, now); 2244a46c0ec8Sopenharmony_ci 2245a46c0ec8Sopenharmony_ci tp->dwt.keyboard_active = false; 2246a46c0ec8Sopenharmony_ci 2247a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "palm: keyboard timeout\n"); 2248a46c0ec8Sopenharmony_ci} 2249a46c0ec8Sopenharmony_ci 2250a46c0ec8Sopenharmony_cistatic inline bool 2251a46c0ec8Sopenharmony_citp_key_is_modifier(unsigned int keycode) 2252a46c0ec8Sopenharmony_ci{ 2253a46c0ec8Sopenharmony_ci switch (keycode) { 2254a46c0ec8Sopenharmony_ci /* Ignore modifiers to be responsive to ctrl-click, alt-tab, etc. */ 2255a46c0ec8Sopenharmony_ci case KEY_LEFTCTRL: 2256a46c0ec8Sopenharmony_ci case KEY_RIGHTCTRL: 2257a46c0ec8Sopenharmony_ci case KEY_LEFTALT: 2258a46c0ec8Sopenharmony_ci case KEY_RIGHTALT: 2259a46c0ec8Sopenharmony_ci case KEY_LEFTSHIFT: 2260a46c0ec8Sopenharmony_ci case KEY_RIGHTSHIFT: 2261a46c0ec8Sopenharmony_ci case KEY_FN: 2262a46c0ec8Sopenharmony_ci case KEY_CAPSLOCK: 2263a46c0ec8Sopenharmony_ci case KEY_TAB: 2264a46c0ec8Sopenharmony_ci case KEY_COMPOSE: 2265a46c0ec8Sopenharmony_ci case KEY_RIGHTMETA: 2266a46c0ec8Sopenharmony_ci case KEY_LEFTMETA: 2267a46c0ec8Sopenharmony_ci return true; 2268a46c0ec8Sopenharmony_ci default: 2269a46c0ec8Sopenharmony_ci return false; 2270a46c0ec8Sopenharmony_ci } 2271a46c0ec8Sopenharmony_ci} 2272a46c0ec8Sopenharmony_ci 2273a46c0ec8Sopenharmony_cistatic inline bool 2274a46c0ec8Sopenharmony_citp_key_ignore_for_dwt(unsigned int keycode) 2275a46c0ec8Sopenharmony_ci{ 2276a46c0ec8Sopenharmony_ci /* Ignore keys not part of the "typewriter set", i.e. F-keys, 2277a46c0ec8Sopenharmony_ci * multimedia keys, numpad, etc. 2278a46c0ec8Sopenharmony_ci */ 2279a46c0ec8Sopenharmony_ci 2280a46c0ec8Sopenharmony_ci if (tp_key_is_modifier(keycode)) 2281a46c0ec8Sopenharmony_ci return false; 2282a46c0ec8Sopenharmony_ci 2283a46c0ec8Sopenharmony_ci switch (keycode) { 2284a46c0ec8Sopenharmony_ci case KEY_ESC: 2285a46c0ec8Sopenharmony_ci case KEY_KPASTERISK: 2286a46c0ec8Sopenharmony_ci return true; 2287a46c0ec8Sopenharmony_ci default: 2288a46c0ec8Sopenharmony_ci return keycode >= KEY_F1; 2289a46c0ec8Sopenharmony_ci } 2290a46c0ec8Sopenharmony_ci} 2291a46c0ec8Sopenharmony_ci 2292a46c0ec8Sopenharmony_cistatic void 2293a46c0ec8Sopenharmony_citp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) 2294a46c0ec8Sopenharmony_ci{ 2295a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2296a46c0ec8Sopenharmony_ci struct libinput_event_keyboard *kbdev; 2297a46c0ec8Sopenharmony_ci unsigned int timeout; 2298a46c0ec8Sopenharmony_ci unsigned int key; 2299a46c0ec8Sopenharmony_ci bool is_modifier; 2300a46c0ec8Sopenharmony_ci 2301a46c0ec8Sopenharmony_ci if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) 2302a46c0ec8Sopenharmony_ci return; 2303a46c0ec8Sopenharmony_ci 2304a46c0ec8Sopenharmony_ci kbdev = libinput_event_get_keyboard_event(event); 2305a46c0ec8Sopenharmony_ci key = libinput_event_keyboard_get_key(kbdev); 2306a46c0ec8Sopenharmony_ci 2307a46c0ec8Sopenharmony_ci /* Only trigger the timer on key down. */ 2308a46c0ec8Sopenharmony_ci if (libinput_event_keyboard_get_key_state(kbdev) != 2309a46c0ec8Sopenharmony_ci LIBINPUT_KEY_STATE_PRESSED) { 2310a46c0ec8Sopenharmony_ci long_clear_bit(tp->dwt.key_mask, key); 2311a46c0ec8Sopenharmony_ci long_clear_bit(tp->dwt.mod_mask, key); 2312a46c0ec8Sopenharmony_ci return; 2313a46c0ec8Sopenharmony_ci } 2314a46c0ec8Sopenharmony_ci 2315a46c0ec8Sopenharmony_ci if (!tp->dwt.dwt_enabled) 2316a46c0ec8Sopenharmony_ci return; 2317a46c0ec8Sopenharmony_ci 2318a46c0ec8Sopenharmony_ci if (tp_key_ignore_for_dwt(key)) 2319a46c0ec8Sopenharmony_ci return; 2320a46c0ec8Sopenharmony_ci 2321a46c0ec8Sopenharmony_ci /* modifier keys don't trigger disable-while-typing so things like 2322a46c0ec8Sopenharmony_ci * ctrl+zoom or ctrl+click are possible */ 2323a46c0ec8Sopenharmony_ci is_modifier = tp_key_is_modifier(key); 2324a46c0ec8Sopenharmony_ci if (is_modifier) { 2325a46c0ec8Sopenharmony_ci long_set_bit(tp->dwt.mod_mask, key); 2326a46c0ec8Sopenharmony_ci return; 2327a46c0ec8Sopenharmony_ci } 2328a46c0ec8Sopenharmony_ci 2329a46c0ec8Sopenharmony_ci if (!tp->dwt.keyboard_active) { 2330a46c0ec8Sopenharmony_ci /* This is the first non-modifier key press. Check if the 2331a46c0ec8Sopenharmony_ci * modifier mask is set. If any modifier is down we don't 2332a46c0ec8Sopenharmony_ci * trigger dwt because it's likely to be combination like 2333a46c0ec8Sopenharmony_ci * Ctrl+S or similar */ 2334a46c0ec8Sopenharmony_ci 2335a46c0ec8Sopenharmony_ci if (long_any_bit_set(tp->dwt.mod_mask, 2336a46c0ec8Sopenharmony_ci ARRAY_LENGTH(tp->dwt.mod_mask))) 2337a46c0ec8Sopenharmony_ci return; 2338a46c0ec8Sopenharmony_ci 2339a46c0ec8Sopenharmony_ci tp_stop_actions(tp, time); 2340a46c0ec8Sopenharmony_ci tp->dwt.keyboard_active = true; 2341a46c0ec8Sopenharmony_ci timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; 2342a46c0ec8Sopenharmony_ci } else { 2343a46c0ec8Sopenharmony_ci timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2; 2344a46c0ec8Sopenharmony_ci } 2345a46c0ec8Sopenharmony_ci 2346a46c0ec8Sopenharmony_ci tp->dwt.keyboard_last_press_time = time; 2347a46c0ec8Sopenharmony_ci long_set_bit(tp->dwt.key_mask, key); 2348a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->dwt.keyboard_timer, 2349a46c0ec8Sopenharmony_ci time + timeout); 2350a46c0ec8Sopenharmony_ci} 2351a46c0ec8Sopenharmony_ci 2352a46c0ec8Sopenharmony_cistatic bool 2353a46c0ec8Sopenharmony_citp_want_dwt(struct evdev_device *touchpad, 2354a46c0ec8Sopenharmony_ci struct evdev_device *keyboard) 2355a46c0ec8Sopenharmony_ci{ 2356a46c0ec8Sopenharmony_ci unsigned int vendor_tp = evdev_device_get_id_vendor(touchpad); 2357a46c0ec8Sopenharmony_ci unsigned int vendor_kbd = evdev_device_get_id_vendor(keyboard); 2358a46c0ec8Sopenharmony_ci unsigned int product_tp = evdev_device_get_id_product(touchpad); 2359a46c0ec8Sopenharmony_ci unsigned int product_kbd = evdev_device_get_id_product(keyboard); 2360a46c0ec8Sopenharmony_ci 2361a46c0ec8Sopenharmony_ci /* External touchpads with the same vid/pid as the keyboard are 2362a46c0ec8Sopenharmony_ci considered a happy couple */ 2363a46c0ec8Sopenharmony_ci if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD) 2364a46c0ec8Sopenharmony_ci return vendor_tp == vendor_kbd && product_tp == product_kbd; 2365a46c0ec8Sopenharmony_ci 2366a46c0ec8Sopenharmony_ci if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) 2367a46c0ec8Sopenharmony_ci return true; 2368a46c0ec8Sopenharmony_ci 2369a46c0ec8Sopenharmony_ci /* keyboard is not tagged as internal keyboard and it's not part of 2370a46c0ec8Sopenharmony_ci * a combo */ 2371a46c0ec8Sopenharmony_ci return false; 2372a46c0ec8Sopenharmony_ci} 2373a46c0ec8Sopenharmony_ci 2374a46c0ec8Sopenharmony_cistatic void 2375a46c0ec8Sopenharmony_citp_dwt_pair_keyboard(struct evdev_device *touchpad, 2376a46c0ec8Sopenharmony_ci struct evdev_device *keyboard) 2377a46c0ec8Sopenharmony_ci{ 2378a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; 2379a46c0ec8Sopenharmony_ci struct evdev_paired_keyboard *kbd; 2380a46c0ec8Sopenharmony_ci size_t count = 0; 2381a46c0ec8Sopenharmony_ci 2382a46c0ec8Sopenharmony_ci if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0) 2383a46c0ec8Sopenharmony_ci return; 2384a46c0ec8Sopenharmony_ci 2385a46c0ec8Sopenharmony_ci if (!tp_want_dwt(touchpad, keyboard)) 2386a46c0ec8Sopenharmony_ci return; 2387a46c0ec8Sopenharmony_ci 2388a46c0ec8Sopenharmony_ci list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) { 2389a46c0ec8Sopenharmony_ci count++; 2390a46c0ec8Sopenharmony_ci if (count > 3) { 2391a46c0ec8Sopenharmony_ci evdev_log_info(touchpad, 2392a46c0ec8Sopenharmony_ci "too many internal keyboards for dwt\n"); 2393a46c0ec8Sopenharmony_ci break; 2394a46c0ec8Sopenharmony_ci } 2395a46c0ec8Sopenharmony_ci } 2396a46c0ec8Sopenharmony_ci 2397a46c0ec8Sopenharmony_ci kbd = zalloc(sizeof(*kbd)); 2398a46c0ec8Sopenharmony_ci kbd->device = keyboard; 2399a46c0ec8Sopenharmony_ci libinput_device_add_event_listener(&keyboard->base, 2400a46c0ec8Sopenharmony_ci &kbd->listener, 2401a46c0ec8Sopenharmony_ci tp_keyboard_event, tp); 2402a46c0ec8Sopenharmony_ci list_insert(&tp->dwt.paired_keyboard_list, &kbd->link); 2403a46c0ec8Sopenharmony_ci evdev_log_debug(touchpad, 2404a46c0ec8Sopenharmony_ci "palm: dwt activated with %s<->%s\n", 2405a46c0ec8Sopenharmony_ci touchpad->devname, 2406a46c0ec8Sopenharmony_ci keyboard->devname); 2407a46c0ec8Sopenharmony_ci} 2408a46c0ec8Sopenharmony_ci 2409a46c0ec8Sopenharmony_cistatic void 2410a46c0ec8Sopenharmony_citp_pair_trackpoint(struct evdev_device *touchpad, 2411a46c0ec8Sopenharmony_ci struct evdev_device *trackpoint) 2412a46c0ec8Sopenharmony_ci{ 2413a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; 2414a46c0ec8Sopenharmony_ci unsigned int bus_trp = libevdev_get_id_bustype(trackpoint->evdev); 2415a46c0ec8Sopenharmony_ci bool tp_is_internal, trp_is_internal; 2416a46c0ec8Sopenharmony_ci 2417a46c0ec8Sopenharmony_ci if ((trackpoint->tags & EVDEV_TAG_TRACKPOINT) == 0) 2418a46c0ec8Sopenharmony_ci return; 2419a46c0ec8Sopenharmony_ci 2420a46c0ec8Sopenharmony_ci tp_is_internal = !!(touchpad->tags & EVDEV_TAG_INTERNAL_TOUCHPAD); 2421a46c0ec8Sopenharmony_ci trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH; 2422a46c0ec8Sopenharmony_ci 2423a46c0ec8Sopenharmony_ci if (tp->buttons.trackpoint == NULL && 2424a46c0ec8Sopenharmony_ci tp_is_internal && trp_is_internal) { 2425a46c0ec8Sopenharmony_ci /* Don't send any pending releases to the new trackpoint */ 2426a46c0ec8Sopenharmony_ci tp->buttons.active_is_topbutton = false; 2427a46c0ec8Sopenharmony_ci tp->buttons.trackpoint = trackpoint; 2428a46c0ec8Sopenharmony_ci if (tp->palm.monitor_trackpoint) 2429a46c0ec8Sopenharmony_ci libinput_device_add_event_listener(&trackpoint->base, 2430a46c0ec8Sopenharmony_ci &tp->palm.trackpoint_listener, 2431a46c0ec8Sopenharmony_ci tp_trackpoint_event, tp); 2432a46c0ec8Sopenharmony_ci } 2433a46c0ec8Sopenharmony_ci} 2434a46c0ec8Sopenharmony_ci 2435a46c0ec8Sopenharmony_cistatic void 2436a46c0ec8Sopenharmony_citp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data) 2437a46c0ec8Sopenharmony_ci{ 2438a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2439a46c0ec8Sopenharmony_ci struct libinput_event_switch *swev; 2440a46c0ec8Sopenharmony_ci 2441a46c0ec8Sopenharmony_ci if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE) 2442a46c0ec8Sopenharmony_ci return; 2443a46c0ec8Sopenharmony_ci 2444a46c0ec8Sopenharmony_ci swev = libinput_event_get_switch_event(event); 2445a46c0ec8Sopenharmony_ci if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_LID) 2446a46c0ec8Sopenharmony_ci return; 2447a46c0ec8Sopenharmony_ci 2448a46c0ec8Sopenharmony_ci switch (libinput_event_switch_get_switch_state(swev)) { 2449a46c0ec8Sopenharmony_ci case LIBINPUT_SWITCH_STATE_OFF: 2450a46c0ec8Sopenharmony_ci tp_resume(tp, tp->device, SUSPEND_LID); 2451a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "lid: resume touchpad\n"); 2452a46c0ec8Sopenharmony_ci break; 2453a46c0ec8Sopenharmony_ci case LIBINPUT_SWITCH_STATE_ON: 2454a46c0ec8Sopenharmony_ci tp_suspend(tp, tp->device, SUSPEND_LID); 2455a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "lid: suspending touchpad\n"); 2456a46c0ec8Sopenharmony_ci break; 2457a46c0ec8Sopenharmony_ci } 2458a46c0ec8Sopenharmony_ci} 2459a46c0ec8Sopenharmony_ci 2460a46c0ec8Sopenharmony_cistatic void 2461a46c0ec8Sopenharmony_citp_tablet_mode_switch_event(uint64_t time, 2462a46c0ec8Sopenharmony_ci struct libinput_event *event, 2463a46c0ec8Sopenharmony_ci void *data) 2464a46c0ec8Sopenharmony_ci{ 2465a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2466a46c0ec8Sopenharmony_ci struct libinput_event_switch *swev; 2467a46c0ec8Sopenharmony_ci 2468a46c0ec8Sopenharmony_ci if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE) 2469a46c0ec8Sopenharmony_ci return; 2470a46c0ec8Sopenharmony_ci 2471a46c0ec8Sopenharmony_ci swev = libinput_event_get_switch_event(event); 2472a46c0ec8Sopenharmony_ci if (libinput_event_switch_get_switch(swev) != 2473a46c0ec8Sopenharmony_ci LIBINPUT_SWITCH_TABLET_MODE) 2474a46c0ec8Sopenharmony_ci return; 2475a46c0ec8Sopenharmony_ci 2476a46c0ec8Sopenharmony_ci switch (libinput_event_switch_get_switch_state(swev)) { 2477a46c0ec8Sopenharmony_ci case LIBINPUT_SWITCH_STATE_OFF: 2478a46c0ec8Sopenharmony_ci tp_resume(tp, tp->device, SUSPEND_TABLET_MODE); 2479a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "tablet-mode: resume touchpad\n"); 2480a46c0ec8Sopenharmony_ci break; 2481a46c0ec8Sopenharmony_ci case LIBINPUT_SWITCH_STATE_ON: 2482a46c0ec8Sopenharmony_ci tp_suspend(tp, tp->device, SUSPEND_TABLET_MODE); 2483a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, "tablet-mode: suspending touchpad\n"); 2484a46c0ec8Sopenharmony_ci break; 2485a46c0ec8Sopenharmony_ci } 2486a46c0ec8Sopenharmony_ci} 2487a46c0ec8Sopenharmony_ci 2488a46c0ec8Sopenharmony_cistatic void 2489a46c0ec8Sopenharmony_citp_pair_lid_switch(struct evdev_device *touchpad, 2490a46c0ec8Sopenharmony_ci struct evdev_device *lid_switch) 2491a46c0ec8Sopenharmony_ci{ 2492a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; 2493a46c0ec8Sopenharmony_ci 2494a46c0ec8Sopenharmony_ci if ((lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0) 2495a46c0ec8Sopenharmony_ci return; 2496a46c0ec8Sopenharmony_ci 2497a46c0ec8Sopenharmony_ci if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD) 2498a46c0ec8Sopenharmony_ci return; 2499a46c0ec8Sopenharmony_ci 2500a46c0ec8Sopenharmony_ci if (tp->lid_switch.lid_switch == NULL) { 2501a46c0ec8Sopenharmony_ci evdev_log_debug(touchpad, 2502a46c0ec8Sopenharmony_ci "lid: activated for %s<->%s\n", 2503a46c0ec8Sopenharmony_ci touchpad->devname, 2504a46c0ec8Sopenharmony_ci lid_switch->devname); 2505a46c0ec8Sopenharmony_ci 2506a46c0ec8Sopenharmony_ci libinput_device_add_event_listener(&lid_switch->base, 2507a46c0ec8Sopenharmony_ci &tp->lid_switch.listener, 2508a46c0ec8Sopenharmony_ci tp_lid_switch_event, tp); 2509a46c0ec8Sopenharmony_ci tp->lid_switch.lid_switch = lid_switch; 2510a46c0ec8Sopenharmony_ci } 2511a46c0ec8Sopenharmony_ci} 2512a46c0ec8Sopenharmony_ci 2513a46c0ec8Sopenharmony_cistatic void 2514a46c0ec8Sopenharmony_citp_pair_tablet_mode_switch(struct evdev_device *touchpad, 2515a46c0ec8Sopenharmony_ci struct evdev_device *tablet_mode_switch) 2516a46c0ec8Sopenharmony_ci{ 2517a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; 2518a46c0ec8Sopenharmony_ci 2519a46c0ec8Sopenharmony_ci if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0) 2520a46c0ec8Sopenharmony_ci return; 2521a46c0ec8Sopenharmony_ci 2522a46c0ec8Sopenharmony_ci if (tp->tablet_mode_switch.tablet_mode_switch) 2523a46c0ec8Sopenharmony_ci return; 2524a46c0ec8Sopenharmony_ci 2525a46c0ec8Sopenharmony_ci if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD) 2526a46c0ec8Sopenharmony_ci return; 2527a46c0ec8Sopenharmony_ci 2528a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(touchpad, 2529a46c0ec8Sopenharmony_ci QUIRK_MODEL_TABLET_MODE_NO_SUSPEND)) 2530a46c0ec8Sopenharmony_ci return; 2531a46c0ec8Sopenharmony_ci 2532a46c0ec8Sopenharmony_ci evdev_log_debug(touchpad, 2533a46c0ec8Sopenharmony_ci "tablet-mode: activated for %s<->%s\n", 2534a46c0ec8Sopenharmony_ci touchpad->devname, 2535a46c0ec8Sopenharmony_ci tablet_mode_switch->devname); 2536a46c0ec8Sopenharmony_ci 2537a46c0ec8Sopenharmony_ci libinput_device_add_event_listener(&tablet_mode_switch->base, 2538a46c0ec8Sopenharmony_ci &tp->tablet_mode_switch.listener, 2539a46c0ec8Sopenharmony_ci tp_tablet_mode_switch_event, tp); 2540a46c0ec8Sopenharmony_ci tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch; 2541a46c0ec8Sopenharmony_ci 2542a46c0ec8Sopenharmony_ci if (evdev_device_switch_get_state(tablet_mode_switch, 2543a46c0ec8Sopenharmony_ci LIBINPUT_SWITCH_TABLET_MODE) 2544a46c0ec8Sopenharmony_ci == LIBINPUT_SWITCH_STATE_ON) { 2545a46c0ec8Sopenharmony_ci tp_suspend(tp, touchpad, SUSPEND_TABLET_MODE); 2546a46c0ec8Sopenharmony_ci } 2547a46c0ec8Sopenharmony_ci} 2548a46c0ec8Sopenharmony_ci 2549a46c0ec8Sopenharmony_cistatic void 2550a46c0ec8Sopenharmony_citp_change_rotation(struct evdev_device *device, enum notify notify) 2551a46c0ec8Sopenharmony_ci{ 2552a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch; 2553a46c0ec8Sopenharmony_ci struct evdev_device *tablet_device = tp->left_handed.tablet_device; 2554a46c0ec8Sopenharmony_ci bool tablet_is_left, touchpad_is_left; 2555a46c0ec8Sopenharmony_ci 2556a46c0ec8Sopenharmony_ci if (!tp->left_handed.must_rotate) 2557a46c0ec8Sopenharmony_ci return; 2558a46c0ec8Sopenharmony_ci 2559a46c0ec8Sopenharmony_ci touchpad_is_left = device->left_handed.enabled; 2560a46c0ec8Sopenharmony_ci tablet_is_left = tp->left_handed.tablet_left_handed_state; 2561a46c0ec8Sopenharmony_ci 2562a46c0ec8Sopenharmony_ci tp->left_handed.want_rotate = touchpad_is_left || tablet_is_left; 2563a46c0ec8Sopenharmony_ci 2564a46c0ec8Sopenharmony_ci tp_apply_rotation(device); 2565a46c0ec8Sopenharmony_ci 2566a46c0ec8Sopenharmony_ci if (notify == DO_NOTIFY && tablet_device) { 2567a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch = tablet_device->dispatch; 2568a46c0ec8Sopenharmony_ci 2569a46c0ec8Sopenharmony_ci if (dispatch->interface->left_handed_toggle) 2570a46c0ec8Sopenharmony_ci dispatch->interface->left_handed_toggle(dispatch, 2571a46c0ec8Sopenharmony_ci tablet_device, 2572a46c0ec8Sopenharmony_ci tp->left_handed.want_rotate); 2573a46c0ec8Sopenharmony_ci } 2574a46c0ec8Sopenharmony_ci} 2575a46c0ec8Sopenharmony_ci 2576a46c0ec8Sopenharmony_cistatic void 2577a46c0ec8Sopenharmony_citp_pair_tablet(struct evdev_device *touchpad, 2578a46c0ec8Sopenharmony_ci struct evdev_device *tablet) 2579a46c0ec8Sopenharmony_ci{ 2580a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch; 2581a46c0ec8Sopenharmony_ci 2582a46c0ec8Sopenharmony_ci if (!tp->left_handed.must_rotate) 2583a46c0ec8Sopenharmony_ci return; 2584a46c0ec8Sopenharmony_ci 2585a46c0ec8Sopenharmony_ci if ((tablet->seat_caps & EVDEV_DEVICE_TABLET) == 0) 2586a46c0ec8Sopenharmony_ci return; 2587a46c0ec8Sopenharmony_ci 2588a46c0ec8Sopenharmony_ci if (libinput_device_get_device_group(&touchpad->base) != 2589a46c0ec8Sopenharmony_ci libinput_device_get_device_group(&tablet->base)) 2590a46c0ec8Sopenharmony_ci return; 2591a46c0ec8Sopenharmony_ci 2592a46c0ec8Sopenharmony_ci tp->left_handed.tablet_device = tablet; 2593a46c0ec8Sopenharmony_ci 2594a46c0ec8Sopenharmony_ci evdev_log_debug(touchpad, 2595a46c0ec8Sopenharmony_ci "touchpad-rotation: %s will rotate %s\n", 2596a46c0ec8Sopenharmony_ci touchpad->devname, 2597a46c0ec8Sopenharmony_ci tablet->devname); 2598a46c0ec8Sopenharmony_ci 2599a46c0ec8Sopenharmony_ci if (libinput_device_config_left_handed_get(&tablet->base)) { 2600a46c0ec8Sopenharmony_ci tp->left_handed.want_rotate = true; 2601a46c0ec8Sopenharmony_ci tp->left_handed.tablet_left_handed_state = true; 2602a46c0ec8Sopenharmony_ci tp_change_rotation(touchpad, DONT_NOTIFY); 2603a46c0ec8Sopenharmony_ci } 2604a46c0ec8Sopenharmony_ci} 2605a46c0ec8Sopenharmony_ci 2606a46c0ec8Sopenharmony_cistatic void 2607a46c0ec8Sopenharmony_citp_interface_device_added(struct evdev_device *device, 2608a46c0ec8Sopenharmony_ci struct evdev_device *added_device) 2609a46c0ec8Sopenharmony_ci{ 2610a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; 2611a46c0ec8Sopenharmony_ci 2612a46c0ec8Sopenharmony_ci tp_pair_trackpoint(device, added_device); 2613a46c0ec8Sopenharmony_ci tp_dwt_pair_keyboard(device, added_device); 2614a46c0ec8Sopenharmony_ci tp_pair_lid_switch(device, added_device); 2615a46c0ec8Sopenharmony_ci tp_pair_tablet_mode_switch(device, added_device); 2616a46c0ec8Sopenharmony_ci tp_pair_tablet(device, added_device); 2617a46c0ec8Sopenharmony_ci 2618a46c0ec8Sopenharmony_ci if (tp->sendevents.current_mode != 2619a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) 2620a46c0ec8Sopenharmony_ci return; 2621a46c0ec8Sopenharmony_ci 2622a46c0ec8Sopenharmony_ci if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE) 2623a46c0ec8Sopenharmony_ci tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE); 2624a46c0ec8Sopenharmony_ci} 2625a46c0ec8Sopenharmony_ci 2626a46c0ec8Sopenharmony_cistatic void 2627a46c0ec8Sopenharmony_citp_interface_device_removed(struct evdev_device *device, 2628a46c0ec8Sopenharmony_ci struct evdev_device *removed_device) 2629a46c0ec8Sopenharmony_ci{ 2630a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; 2631a46c0ec8Sopenharmony_ci struct evdev_paired_keyboard *kbd; 2632a46c0ec8Sopenharmony_ci 2633a46c0ec8Sopenharmony_ci if (removed_device == tp->buttons.trackpoint) { 2634a46c0ec8Sopenharmony_ci /* Clear any pending releases for the trackpoint */ 2635a46c0ec8Sopenharmony_ci if (tp->buttons.active && tp->buttons.active_is_topbutton) { 2636a46c0ec8Sopenharmony_ci tp->buttons.active = 0; 2637a46c0ec8Sopenharmony_ci tp->buttons.active_is_topbutton = false; 2638a46c0ec8Sopenharmony_ci } 2639a46c0ec8Sopenharmony_ci if (tp->palm.monitor_trackpoint) 2640a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 2641a46c0ec8Sopenharmony_ci &tp->palm.trackpoint_listener); 2642a46c0ec8Sopenharmony_ci tp->buttons.trackpoint = NULL; 2643a46c0ec8Sopenharmony_ci } 2644a46c0ec8Sopenharmony_ci 2645a46c0ec8Sopenharmony_ci list_for_each_safe(kbd, &tp->dwt.paired_keyboard_list, link) { 2646a46c0ec8Sopenharmony_ci if (kbd->device == removed_device) { 2647a46c0ec8Sopenharmony_ci evdev_paired_keyboard_destroy(kbd); 2648a46c0ec8Sopenharmony_ci tp->dwt.keyboard_active = false; 2649a46c0ec8Sopenharmony_ci } 2650a46c0ec8Sopenharmony_ci } 2651a46c0ec8Sopenharmony_ci 2652a46c0ec8Sopenharmony_ci if (removed_device == tp->lid_switch.lid_switch) { 2653a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 2654a46c0ec8Sopenharmony_ci &tp->lid_switch.listener); 2655a46c0ec8Sopenharmony_ci tp->lid_switch.lid_switch = NULL; 2656a46c0ec8Sopenharmony_ci tp_resume(tp, device, SUSPEND_LID); 2657a46c0ec8Sopenharmony_ci } 2658a46c0ec8Sopenharmony_ci 2659a46c0ec8Sopenharmony_ci if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) { 2660a46c0ec8Sopenharmony_ci libinput_device_remove_event_listener( 2661a46c0ec8Sopenharmony_ci &tp->tablet_mode_switch.listener); 2662a46c0ec8Sopenharmony_ci tp->tablet_mode_switch.tablet_mode_switch = NULL; 2663a46c0ec8Sopenharmony_ci tp_resume(tp, device, SUSPEND_TABLET_MODE); 2664a46c0ec8Sopenharmony_ci } 2665a46c0ec8Sopenharmony_ci 2666a46c0ec8Sopenharmony_ci if (tp->sendevents.current_mode == 2667a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) { 2668a46c0ec8Sopenharmony_ci struct libinput_device *dev; 2669a46c0ec8Sopenharmony_ci bool found = false; 2670a46c0ec8Sopenharmony_ci 2671a46c0ec8Sopenharmony_ci list_for_each(dev, &device->base.seat->devices_list, link) { 2672a46c0ec8Sopenharmony_ci struct evdev_device *d = evdev_device(dev); 2673a46c0ec8Sopenharmony_ci if (d != removed_device && 2674a46c0ec8Sopenharmony_ci (d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) { 2675a46c0ec8Sopenharmony_ci found = true; 2676a46c0ec8Sopenharmony_ci break; 2677a46c0ec8Sopenharmony_ci } 2678a46c0ec8Sopenharmony_ci } 2679a46c0ec8Sopenharmony_ci if (!found) 2680a46c0ec8Sopenharmony_ci tp_resume(tp, device, SUSPEND_EXTERNAL_MOUSE); 2681a46c0ec8Sopenharmony_ci } 2682a46c0ec8Sopenharmony_ci 2683a46c0ec8Sopenharmony_ci if (removed_device == tp->left_handed.tablet_device) { 2684a46c0ec8Sopenharmony_ci tp->left_handed.tablet_device = NULL; 2685a46c0ec8Sopenharmony_ci tp->left_handed.tablet_left_handed_state = false; 2686a46c0ec8Sopenharmony_ci 2687a46c0ec8Sopenharmony_ci /* Slight awkwardness: removing the tablet causes the 2688a46c0ec8Sopenharmony_ci * touchpad to rotate back to normal if only the tablet was 2689a46c0ec8Sopenharmony_ci * set to left-handed. Niche case, nothing to worry about 2690a46c0ec8Sopenharmony_ci */ 2691a46c0ec8Sopenharmony_ci tp_change_rotation(device, DO_NOTIFY); 2692a46c0ec8Sopenharmony_ci } 2693a46c0ec8Sopenharmony_ci} 2694a46c0ec8Sopenharmony_ci 2695a46c0ec8Sopenharmony_cistatic inline void 2696a46c0ec8Sopenharmony_cievdev_tag_touchpad_internal(struct evdev_device *device) 2697a46c0ec8Sopenharmony_ci{ 2698a46c0ec8Sopenharmony_ci device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; 2699a46c0ec8Sopenharmony_ci device->tags &= ~EVDEV_TAG_EXTERNAL_TOUCHPAD; 2700a46c0ec8Sopenharmony_ci} 2701a46c0ec8Sopenharmony_ci 2702a46c0ec8Sopenharmony_cistatic inline void 2703a46c0ec8Sopenharmony_cievdev_tag_touchpad_external(struct evdev_device *device) 2704a46c0ec8Sopenharmony_ci{ 2705a46c0ec8Sopenharmony_ci device->tags |= EVDEV_TAG_EXTERNAL_TOUCHPAD; 2706a46c0ec8Sopenharmony_ci device->tags &= ~EVDEV_TAG_INTERNAL_TOUCHPAD; 2707a46c0ec8Sopenharmony_ci} 2708a46c0ec8Sopenharmony_ci 2709a46c0ec8Sopenharmony_cistatic void 2710a46c0ec8Sopenharmony_cievdev_tag_touchpad(struct evdev_device *device, 2711a46c0ec8Sopenharmony_ci struct udev_device *udev_device) 2712a46c0ec8Sopenharmony_ci{ 2713a46c0ec8Sopenharmony_ci int bustype, vendor; 2714a46c0ec8Sopenharmony_ci const char *prop; 2715a46c0ec8Sopenharmony_ci 2716a46c0ec8Sopenharmony_ci prop = udev_device_get_property_value(udev_device, 2717a46c0ec8Sopenharmony_ci "ID_INPUT_TOUCHPAD_INTEGRATION"); 2718a46c0ec8Sopenharmony_ci if (prop) { 2719a46c0ec8Sopenharmony_ci if (streq(prop, "internal")) { 2720a46c0ec8Sopenharmony_ci evdev_tag_touchpad_internal(device); 2721a46c0ec8Sopenharmony_ci return; 2722a46c0ec8Sopenharmony_ci } 2723a46c0ec8Sopenharmony_ci 2724a46c0ec8Sopenharmony_ci if (streq(prop, "external")) { 2725a46c0ec8Sopenharmony_ci evdev_tag_touchpad_external(device); 2726a46c0ec8Sopenharmony_ci return; 2727a46c0ec8Sopenharmony_ci } 2728a46c0ec8Sopenharmony_ci 2729a46c0ec8Sopenharmony_ci evdev_log_info(device, 2730a46c0ec8Sopenharmony_ci "tagged with unknown value %s\n", 2731a46c0ec8Sopenharmony_ci prop); 2732a46c0ec8Sopenharmony_ci } 2733a46c0ec8Sopenharmony_ci 2734a46c0ec8Sopenharmony_ci /* The hwdb is the authority on integration, these heuristics are 2735a46c0ec8Sopenharmony_ci * the fallback only (they precede the hwdb too). 2736a46c0ec8Sopenharmony_ci * 2737a46c0ec8Sopenharmony_ci * Simple approach: 2738a46c0ec8Sopenharmony_ci * Bluetooth touchpads are considered external, anything else is 2739a46c0ec8Sopenharmony_ci * internal. Except the ones from some vendors that only make external 2740a46c0ec8Sopenharmony_ci * touchpads. 2741a46c0ec8Sopenharmony_ci */ 2742a46c0ec8Sopenharmony_ci bustype = libevdev_get_id_bustype(device->evdev); 2743a46c0ec8Sopenharmony_ci vendor = libevdev_get_id_vendor(device->evdev); 2744a46c0ec8Sopenharmony_ci 2745a46c0ec8Sopenharmony_ci switch (bustype) { 2746a46c0ec8Sopenharmony_ci case BUS_BLUETOOTH: 2747a46c0ec8Sopenharmony_ci evdev_tag_touchpad_external(device); 2748a46c0ec8Sopenharmony_ci break; 2749a46c0ec8Sopenharmony_ci default: 2750a46c0ec8Sopenharmony_ci evdev_tag_touchpad_internal(device); 2751a46c0ec8Sopenharmony_ci break; 2752a46c0ec8Sopenharmony_ci } 2753a46c0ec8Sopenharmony_ci 2754a46c0ec8Sopenharmony_ci switch (vendor) { 2755a46c0ec8Sopenharmony_ci /* Logitech does not have internal touchpads */ 2756a46c0ec8Sopenharmony_ci case VENDOR_ID_LOGITECH: 2757a46c0ec8Sopenharmony_ci evdev_tag_touchpad_external(device); 2758a46c0ec8Sopenharmony_ci break; 2759a46c0ec8Sopenharmony_ci } 2760a46c0ec8Sopenharmony_ci 2761a46c0ec8Sopenharmony_ci /* Wacom makes touchpads, but not internal ones */ 2762a46c0ec8Sopenharmony_ci if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD) 2763a46c0ec8Sopenharmony_ci evdev_tag_touchpad_external(device); 2764a46c0ec8Sopenharmony_ci 2765a46c0ec8Sopenharmony_ci if ((device->tags & 2766a46c0ec8Sopenharmony_ci (EVDEV_TAG_EXTERNAL_TOUCHPAD|EVDEV_TAG_INTERNAL_TOUCHPAD)) == 0) { 2767a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 2768a46c0ec8Sopenharmony_ci "Internal or external? Please file a bug.\n"); 2769a46c0ec8Sopenharmony_ci evdev_tag_touchpad_external(device); 2770a46c0ec8Sopenharmony_ci } 2771a46c0ec8Sopenharmony_ci} 2772a46c0ec8Sopenharmony_ci 2773a46c0ec8Sopenharmony_cistatic void 2774a46c0ec8Sopenharmony_citp_arbitration_timeout(uint64_t now, void *data) 2775a46c0ec8Sopenharmony_ci{ 2776a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = data; 2777a46c0ec8Sopenharmony_ci 2778a46c0ec8Sopenharmony_ci if (tp->arbitration.state != ARBITRATION_NOT_ACTIVE) 2779a46c0ec8Sopenharmony_ci tp->arbitration.state = ARBITRATION_NOT_ACTIVE; 2780a46c0ec8Sopenharmony_ci} 2781a46c0ec8Sopenharmony_ci 2782a46c0ec8Sopenharmony_cistatic void 2783a46c0ec8Sopenharmony_citp_interface_toggle_touch(struct evdev_dispatch *dispatch, 2784a46c0ec8Sopenharmony_ci struct evdev_device *device, 2785a46c0ec8Sopenharmony_ci enum evdev_arbitration_state which, 2786a46c0ec8Sopenharmony_ci const struct phys_rect *rect, 2787a46c0ec8Sopenharmony_ci uint64_t time) 2788a46c0ec8Sopenharmony_ci{ 2789a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 2790a46c0ec8Sopenharmony_ci 2791a46c0ec8Sopenharmony_ci if (which == tp->arbitration.state) 2792a46c0ec8Sopenharmony_ci return; 2793a46c0ec8Sopenharmony_ci 2794a46c0ec8Sopenharmony_ci switch (which) { 2795a46c0ec8Sopenharmony_ci case ARBITRATION_IGNORE_ALL: 2796a46c0ec8Sopenharmony_ci case ARBITRATION_IGNORE_RECT: 2797a46c0ec8Sopenharmony_ci libinput_timer_cancel(&tp->arbitration.arbitration_timer); 2798a46c0ec8Sopenharmony_ci tp_clear_state(tp); 2799a46c0ec8Sopenharmony_ci tp->arbitration.state = which; 2800a46c0ec8Sopenharmony_ci break; 2801a46c0ec8Sopenharmony_ci case ARBITRATION_NOT_ACTIVE: 2802a46c0ec8Sopenharmony_ci /* if in-kernel arbitration is in use and there is a touch 2803a46c0ec8Sopenharmony_ci * and a pen in proximity, lifting the pen out of proximity 2804a46c0ec8Sopenharmony_ci * causes a touch begin for the touch. On a hand-lift the 2805a46c0ec8Sopenharmony_ci * proximity out precedes the touch up by a few ms, so we 2806a46c0ec8Sopenharmony_ci * get what looks like a tap. Fix this by delaying 2807a46c0ec8Sopenharmony_ci * arbitration by just a little bit so that any touch in 2808a46c0ec8Sopenharmony_ci * event is caught as palm touch. */ 2809a46c0ec8Sopenharmony_ci libinput_timer_set(&tp->arbitration.arbitration_timer, 2810a46c0ec8Sopenharmony_ci time + ms2us(90)); 2811a46c0ec8Sopenharmony_ci break; 2812a46c0ec8Sopenharmony_ci } 2813a46c0ec8Sopenharmony_ci} 2814a46c0ec8Sopenharmony_ci 2815a46c0ec8Sopenharmony_ci/* Called when the tablet toggles to left-handed */ 2816a46c0ec8Sopenharmony_cistatic void 2817a46c0ec8Sopenharmony_citouchpad_left_handed_toggled(struct evdev_dispatch *dispatch, 2818a46c0ec8Sopenharmony_ci struct evdev_device *device, 2819a46c0ec8Sopenharmony_ci bool left_handed_enabled) 2820a46c0ec8Sopenharmony_ci{ 2821a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(dispatch); 2822a46c0ec8Sopenharmony_ci 2823a46c0ec8Sopenharmony_ci if (!tp->left_handed.tablet_device) 2824a46c0ec8Sopenharmony_ci return; 2825a46c0ec8Sopenharmony_ci 2826a46c0ec8Sopenharmony_ci evdev_log_debug(device, 2827a46c0ec8Sopenharmony_ci "touchpad-rotation: tablet is %s\n", 2828a46c0ec8Sopenharmony_ci left_handed_enabled ? "left-handed" : "right-handed"); 2829a46c0ec8Sopenharmony_ci 2830a46c0ec8Sopenharmony_ci /* Our left-handed config is independent even though rotation is 2831a46c0ec8Sopenharmony_ci * locked. So we rotate when either device is left-handed. But it 2832a46c0ec8Sopenharmony_ci * can only be actually changed when the device is in a neutral 2833a46c0ec8Sopenharmony_ci * state, hence the want_rotate. 2834a46c0ec8Sopenharmony_ci */ 2835a46c0ec8Sopenharmony_ci tp->left_handed.tablet_left_handed_state = left_handed_enabled; 2836a46c0ec8Sopenharmony_ci tp_change_rotation(device, DONT_NOTIFY); 2837a46c0ec8Sopenharmony_ci} 2838a46c0ec8Sopenharmony_ci 2839a46c0ec8Sopenharmony_cistatic struct evdev_dispatch_interface tp_interface = { 2840a46c0ec8Sopenharmony_ci .process = tp_interface_process, 2841a46c0ec8Sopenharmony_ci .suspend = tp_interface_suspend, 2842a46c0ec8Sopenharmony_ci .remove = tp_interface_remove, 2843a46c0ec8Sopenharmony_ci .destroy = tp_interface_destroy, 2844a46c0ec8Sopenharmony_ci .device_added = tp_interface_device_added, 2845a46c0ec8Sopenharmony_ci .device_removed = tp_interface_device_removed, 2846a46c0ec8Sopenharmony_ci .device_suspended = tp_interface_device_removed, /* treat as remove */ 2847a46c0ec8Sopenharmony_ci .device_resumed = tp_interface_device_added, /* treat as add */ 2848a46c0ec8Sopenharmony_ci .post_added = NULL, 2849a46c0ec8Sopenharmony_ci .touch_arbitration_toggle = tp_interface_toggle_touch, 2850a46c0ec8Sopenharmony_ci .touch_arbitration_update_rect = NULL, 2851a46c0ec8Sopenharmony_ci .get_switch_state = NULL, 2852a46c0ec8Sopenharmony_ci .left_handed_toggle = touchpad_left_handed_toggled, 2853a46c0ec8Sopenharmony_ci}; 2854a46c0ec8Sopenharmony_ci 2855a46c0ec8Sopenharmony_cistatic void 2856a46c0ec8Sopenharmony_citp_init_touch(struct tp_dispatch *tp, 2857a46c0ec8Sopenharmony_ci struct tp_touch *t, 2858a46c0ec8Sopenharmony_ci unsigned int index) 2859a46c0ec8Sopenharmony_ci{ 2860a46c0ec8Sopenharmony_ci t->tp = tp; 2861a46c0ec8Sopenharmony_ci t->has_ended = true; 2862a46c0ec8Sopenharmony_ci t->index = index; 2863a46c0ec8Sopenharmony_ci} 2864a46c0ec8Sopenharmony_ci 2865a46c0ec8Sopenharmony_cistatic inline void 2866a46c0ec8Sopenharmony_citp_disable_abs_mt(struct evdev_device *device) 2867a46c0ec8Sopenharmony_ci{ 2868a46c0ec8Sopenharmony_ci struct libevdev *evdev = device->evdev; 2869a46c0ec8Sopenharmony_ci unsigned int code; 2870a46c0ec8Sopenharmony_ci 2871a46c0ec8Sopenharmony_ci for (code = ABS_MT_SLOT; code <= ABS_MAX; code++) 2872a46c0ec8Sopenharmony_ci libevdev_disable_event_code(evdev, EV_ABS, code); 2873a46c0ec8Sopenharmony_ci} 2874a46c0ec8Sopenharmony_ci 2875a46c0ec8Sopenharmony_cistatic bool 2876a46c0ec8Sopenharmony_citp_init_slots(struct tp_dispatch *tp, 2877a46c0ec8Sopenharmony_ci struct evdev_device *device) 2878a46c0ec8Sopenharmony_ci{ 2879a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo; 2880a46c0ec8Sopenharmony_ci struct map { 2881a46c0ec8Sopenharmony_ci unsigned int code; 2882a46c0ec8Sopenharmony_ci int ntouches; 2883a46c0ec8Sopenharmony_ci } max_touches[] = { 2884a46c0ec8Sopenharmony_ci { BTN_TOOL_QUINTTAP, 5 }, 2885a46c0ec8Sopenharmony_ci { BTN_TOOL_QUADTAP, 4 }, 2886a46c0ec8Sopenharmony_ci { BTN_TOOL_TRIPLETAP, 3 }, 2887a46c0ec8Sopenharmony_ci { BTN_TOOL_DOUBLETAP, 2 }, 2888a46c0ec8Sopenharmony_ci }; 2889a46c0ec8Sopenharmony_ci unsigned int i, n_btn_tool_touches = 1; 2890a46c0ec8Sopenharmony_ci 2891a46c0ec8Sopenharmony_ci absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT); 2892a46c0ec8Sopenharmony_ci if (absinfo) { 2893a46c0ec8Sopenharmony_ci tp->num_slots = absinfo->maximum + 1; 2894a46c0ec8Sopenharmony_ci tp->slot = absinfo->value; 2895a46c0ec8Sopenharmony_ci tp->has_mt = true; 2896a46c0ec8Sopenharmony_ci } else { 2897a46c0ec8Sopenharmony_ci tp->num_slots = 1; 2898a46c0ec8Sopenharmony_ci tp->slot = 0; 2899a46c0ec8Sopenharmony_ci tp->has_mt = false; 2900a46c0ec8Sopenharmony_ci } 2901a46c0ec8Sopenharmony_ci 2902a46c0ec8Sopenharmony_ci tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT); 2903a46c0ec8Sopenharmony_ci 2904a46c0ec8Sopenharmony_ci /* Semi-mt devices are not reliable for true multitouch data, so we 2905a46c0ec8Sopenharmony_ci * simply pretend they're single touch touchpads with BTN_TOOL bits. 2906a46c0ec8Sopenharmony_ci * Synaptics: 2907a46c0ec8Sopenharmony_ci * Terrible resolution when two fingers are down, 2908a46c0ec8Sopenharmony_ci * causing scroll jumps. The single-touch emulation ABS_X/Y is 2909a46c0ec8Sopenharmony_ci * accurate but the ABS_MT_POSITION touchpoints report the bounding 2910a46c0ec8Sopenharmony_ci * box and that causes jumps. See https://bugzilla.redhat.com/1235175 2911a46c0ec8Sopenharmony_ci * Elantech: 2912a46c0ec8Sopenharmony_ci * On three-finger taps/clicks, one slot doesn't get a coordinate 2913a46c0ec8Sopenharmony_ci * assigned. See https://bugs.freedesktop.org/show_bug.cgi?id=93583 2914a46c0ec8Sopenharmony_ci * Alps: 2915a46c0ec8Sopenharmony_ci * If three fingers are set down in the same frame, one slot has the 2916a46c0ec8Sopenharmony_ci * coordinates 0/0 and may not get updated for several frames. 2917a46c0ec8Sopenharmony_ci * See https://bugzilla.redhat.com/show_bug.cgi?id=1295073 2918a46c0ec8Sopenharmony_ci * 2919a46c0ec8Sopenharmony_ci * The HP Pavilion DM4 touchpad has random jumps in slots, including 2920a46c0ec8Sopenharmony_ci * for single-finger movement. See fdo bug 91135 2921a46c0ec8Sopenharmony_ci */ 2922a46c0ec8Sopenharmony_ci if (tp->semi_mt || 2923a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(tp->device, 2924a46c0ec8Sopenharmony_ci QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD)) { 2925a46c0ec8Sopenharmony_ci tp->num_slots = 1; 2926a46c0ec8Sopenharmony_ci tp->slot = 0; 2927a46c0ec8Sopenharmony_ci tp->has_mt = false; 2928a46c0ec8Sopenharmony_ci } 2929a46c0ec8Sopenharmony_ci 2930a46c0ec8Sopenharmony_ci if (!tp->has_mt) 2931a46c0ec8Sopenharmony_ci tp_disable_abs_mt(device); 2932a46c0ec8Sopenharmony_ci 2933a46c0ec8Sopenharmony_ci ARRAY_FOR_EACH(max_touches, m) { 2934a46c0ec8Sopenharmony_ci if (libevdev_has_event_code(device->evdev, 2935a46c0ec8Sopenharmony_ci EV_KEY, 2936a46c0ec8Sopenharmony_ci m->code)) { 2937a46c0ec8Sopenharmony_ci n_btn_tool_touches = m->ntouches; 2938a46c0ec8Sopenharmony_ci break; 2939a46c0ec8Sopenharmony_ci } 2940a46c0ec8Sopenharmony_ci } 2941a46c0ec8Sopenharmony_ci 2942a46c0ec8Sopenharmony_ci tp->ntouches = max(tp->num_slots, n_btn_tool_touches); 2943a46c0ec8Sopenharmony_ci tp->touches = zalloc(tp->ntouches * sizeof(struct tp_touch)); 2944a46c0ec8Sopenharmony_ci 2945a46c0ec8Sopenharmony_ci for (i = 0; i < tp->ntouches; i++) 2946a46c0ec8Sopenharmony_ci tp_init_touch(tp, &tp->touches[i], i); 2947a46c0ec8Sopenharmony_ci 2948a46c0ec8Sopenharmony_ci tp_sync_slots(tp, device); 2949a46c0ec8Sopenharmony_ci 2950a46c0ec8Sopenharmony_ci /* Some touchpads don't reset BTN_TOOL_FINGER on touch up and only 2951a46c0ec8Sopenharmony_ci * change to/from it when BTN_TOOL_DOUBLETAP is set. This causes us 2952a46c0ec8Sopenharmony_ci * to ignore the first touches events until a two-finger gesture is 2953a46c0ec8Sopenharmony_ci * performed. 2954a46c0ec8Sopenharmony_ci */ 2955a46c0ec8Sopenharmony_ci if (libevdev_get_event_value(device->evdev, EV_KEY, BTN_TOOL_FINGER)) 2956a46c0ec8Sopenharmony_ci tp_fake_finger_set(tp, BTN_TOOL_FINGER, 1); 2957a46c0ec8Sopenharmony_ci 2958a46c0ec8Sopenharmony_ci return true; 2959a46c0ec8Sopenharmony_ci} 2960a46c0ec8Sopenharmony_ci 2961a46c0ec8Sopenharmony_cistatic enum libinput_config_status 2962a46c0ec8Sopenharmony_citp_accel_config_set_profile(struct libinput_device *libinput_device, 2963a46c0ec8Sopenharmony_ci enum libinput_config_accel_profile profile); 2964a46c0ec8Sopenharmony_ci 2965a46c0ec8Sopenharmony_cistatic bool 2966a46c0ec8Sopenharmony_citp_init_accel(struct tp_dispatch *tp, enum libinput_config_accel_profile which) 2967a46c0ec8Sopenharmony_ci{ 2968a46c0ec8Sopenharmony_ci struct evdev_device *device = tp->device; 2969a46c0ec8Sopenharmony_ci int res_x, res_y; 2970a46c0ec8Sopenharmony_ci struct motion_filter *filter; 2971a46c0ec8Sopenharmony_ci int dpi = device->dpi; 2972a46c0ec8Sopenharmony_ci bool use_v_avg = device->use_velocity_averaging; 2973a46c0ec8Sopenharmony_ci 2974a46c0ec8Sopenharmony_ci res_x = tp->device->abs.absinfo_x->resolution; 2975a46c0ec8Sopenharmony_ci res_y = tp->device->abs.absinfo_y->resolution; 2976a46c0ec8Sopenharmony_ci 2977a46c0ec8Sopenharmony_ci /* 2978a46c0ec8Sopenharmony_ci * Not all touchpads report the same amount of units/mm (resolution). 2979a46c0ec8Sopenharmony_ci * Normalize motion events to the default mouse DPI as base 2980a46c0ec8Sopenharmony_ci * (unaccelerated) speed. This also evens out any differences in x 2981a46c0ec8Sopenharmony_ci * and y resolution, so that a circle on the 2982a46c0ec8Sopenharmony_ci * touchpad does not turn into an ellipse on the screen. 2983a46c0ec8Sopenharmony_ci */ 2984a46c0ec8Sopenharmony_ci tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x; 2985a46c0ec8Sopenharmony_ci tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y; 2986a46c0ec8Sopenharmony_ci tp->accel.xy_scale_coeff = 1.0 * res_x/res_y; 2987a46c0ec8Sopenharmony_ci 2988a46c0ec8Sopenharmony_ci if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) { 2989a46c0ec8Sopenharmony_ci filter = create_pointer_accelerator_filter_touchpad_flat(dpi); 2990a46c0ec8Sopenharmony_ci } else if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) { 2991a46c0ec8Sopenharmony_ci filter = create_custom_accelerator_filter(); 2992a46c0ec8Sopenharmony_ci } else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) || 2993a46c0ec8Sopenharmony_ci tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) { 2994a46c0ec8Sopenharmony_ci filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg); 2995a46c0ec8Sopenharmony_ci } else { 2996a46c0ec8Sopenharmony_ci uint64_t eds_threshold = 0; 2997a46c0ec8Sopenharmony_ci uint64_t eds_value = 0; 2998a46c0ec8Sopenharmony_ci 2999a46c0ec8Sopenharmony_ci if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH) { 3000a46c0ec8Sopenharmony_ci eds_threshold = ms2us(50); 3001a46c0ec8Sopenharmony_ci eds_value = ms2us(10); 3002a46c0ec8Sopenharmony_ci } 3003a46c0ec8Sopenharmony_ci filter = create_pointer_accelerator_filter_touchpad(dpi, 3004a46c0ec8Sopenharmony_ci eds_threshold, 3005a46c0ec8Sopenharmony_ci eds_value, 3006a46c0ec8Sopenharmony_ci use_v_avg); 3007a46c0ec8Sopenharmony_ci } 3008a46c0ec8Sopenharmony_ci 3009a46c0ec8Sopenharmony_ci if (!filter) 3010a46c0ec8Sopenharmony_ci return false; 3011a46c0ec8Sopenharmony_ci 3012a46c0ec8Sopenharmony_ci evdev_device_init_pointer_acceleration(tp->device, filter); 3013a46c0ec8Sopenharmony_ci 3014a46c0ec8Sopenharmony_ci device->pointer.config.set_profile = tp_accel_config_set_profile; 3015a46c0ec8Sopenharmony_ci 3016a46c0ec8Sopenharmony_ci return true; 3017a46c0ec8Sopenharmony_ci} 3018a46c0ec8Sopenharmony_ci 3019a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3020a46c0ec8Sopenharmony_citp_accel_config_set_speed(struct libinput_device *device, double speed) 3021a46c0ec8Sopenharmony_ci{ 3022a46c0ec8Sopenharmony_ci struct evdev_device *dev = evdev_device(device); 3023a46c0ec8Sopenharmony_ci 3024a46c0ec8Sopenharmony_ci if (!filter_set_speed(dev->pointer.filter, speed)) 3025a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_INVALID; 3026a46c0ec8Sopenharmony_ci 3027a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3028a46c0ec8Sopenharmony_ci} 3029a46c0ec8Sopenharmony_ci 3030a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3031a46c0ec8Sopenharmony_citp_accel_config_set_profile(struct libinput_device *libinput_device, 3032a46c0ec8Sopenharmony_ci enum libinput_config_accel_profile profile) 3033a46c0ec8Sopenharmony_ci{ 3034a46c0ec8Sopenharmony_ci struct evdev_device *device = evdev_device(libinput_device); 3035a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = tp_dispatch(device->dispatch); 3036a46c0ec8Sopenharmony_ci struct motion_filter *filter; 3037a46c0ec8Sopenharmony_ci double speed; 3038a46c0ec8Sopenharmony_ci 3039a46c0ec8Sopenharmony_ci filter = device->pointer.filter; 3040a46c0ec8Sopenharmony_ci if (filter_get_type(filter) == profile) 3041a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3042a46c0ec8Sopenharmony_ci 3043a46c0ec8Sopenharmony_ci speed = filter_get_speed(filter); 3044a46c0ec8Sopenharmony_ci device->pointer.filter = NULL; 3045a46c0ec8Sopenharmony_ci 3046a46c0ec8Sopenharmony_ci if (tp_init_accel(tp, profile)) { 3047a46c0ec8Sopenharmony_ci tp_accel_config_set_speed(libinput_device, speed); 3048a46c0ec8Sopenharmony_ci filter_destroy(filter); 3049a46c0ec8Sopenharmony_ci } else { 3050a46c0ec8Sopenharmony_ci device->pointer.filter = filter; 3051a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; 3052a46c0ec8Sopenharmony_ci } 3053a46c0ec8Sopenharmony_ci 3054a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3055a46c0ec8Sopenharmony_ci} 3056a46c0ec8Sopenharmony_ci 3057a46c0ec8Sopenharmony_cistatic uint32_t 3058a46c0ec8Sopenharmony_citp_scroll_get_methods(struct tp_dispatch *tp) 3059a46c0ec8Sopenharmony_ci{ 3060a46c0ec8Sopenharmony_ci uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE; 3061a46c0ec8Sopenharmony_ci 3062a46c0ec8Sopenharmony_ci /* Any movement with more than one finger has random cursor 3063a46c0ec8Sopenharmony_ci * jumps. Don't allow for 2fg scrolling on this device, see 3064a46c0ec8Sopenharmony_ci * fdo bug 91135 */ 3065a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(tp->device, 3066a46c0ec8Sopenharmony_ci QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD)) 3067a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_SCROLL_EDGE; 3068a46c0ec8Sopenharmony_ci 3069a46c0ec8Sopenharmony_ci if (tp->ntouches >= 2) 3070a46c0ec8Sopenharmony_ci methods |= LIBINPUT_CONFIG_SCROLL_2FG; 3071a46c0ec8Sopenharmony_ci 3072a46c0ec8Sopenharmony_ci return methods; 3073a46c0ec8Sopenharmony_ci} 3074a46c0ec8Sopenharmony_ci 3075a46c0ec8Sopenharmony_cistatic uint32_t 3076a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_methods(struct libinput_device *device) 3077a46c0ec8Sopenharmony_ci{ 3078a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3079a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3080a46c0ec8Sopenharmony_ci 3081a46c0ec8Sopenharmony_ci return tp_scroll_get_methods(tp); 3082a46c0ec8Sopenharmony_ci} 3083a46c0ec8Sopenharmony_ci 3084a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3085a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_set_method(struct libinput_device *device, 3086a46c0ec8Sopenharmony_ci enum libinput_config_scroll_method method) 3087a46c0ec8Sopenharmony_ci{ 3088a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3089a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3090a46c0ec8Sopenharmony_ci uint64_t time = libinput_now(tp_libinput_context(tp)); 3091a46c0ec8Sopenharmony_ci 3092a46c0ec8Sopenharmony_ci if (method == tp->scroll.method) 3093a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3094a46c0ec8Sopenharmony_ci 3095a46c0ec8Sopenharmony_ci tp_edge_scroll_stop_events(tp, time); 3096a46c0ec8Sopenharmony_ci tp_gesture_stop_twofinger_scroll(tp, time); 3097a46c0ec8Sopenharmony_ci 3098a46c0ec8Sopenharmony_ci tp->scroll.method = method; 3099a46c0ec8Sopenharmony_ci 3100a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3101a46c0ec8Sopenharmony_ci} 3102a46c0ec8Sopenharmony_ci 3103a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method 3104a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_method(struct libinput_device *device) 3105a46c0ec8Sopenharmony_ci{ 3106a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3107a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3108a46c0ec8Sopenharmony_ci 3109a46c0ec8Sopenharmony_ci return tp->scroll.method; 3110a46c0ec8Sopenharmony_ci} 3111a46c0ec8Sopenharmony_ci 3112a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method 3113a46c0ec8Sopenharmony_citp_scroll_get_default_method(struct tp_dispatch *tp) 3114a46c0ec8Sopenharmony_ci{ 3115a46c0ec8Sopenharmony_ci uint32_t methods; 3116a46c0ec8Sopenharmony_ci enum libinput_config_scroll_method method; 3117a46c0ec8Sopenharmony_ci 3118a46c0ec8Sopenharmony_ci methods = tp_scroll_get_methods(tp); 3119a46c0ec8Sopenharmony_ci 3120a46c0ec8Sopenharmony_ci if (methods & LIBINPUT_CONFIG_SCROLL_2FG) 3121a46c0ec8Sopenharmony_ci method = LIBINPUT_CONFIG_SCROLL_2FG; 3122a46c0ec8Sopenharmony_ci else 3123a46c0ec8Sopenharmony_ci method = LIBINPUT_CONFIG_SCROLL_EDGE; 3124a46c0ec8Sopenharmony_ci 3125a46c0ec8Sopenharmony_ci if ((methods & method) == 0) 3126a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(tp->device, 3127a46c0ec8Sopenharmony_ci "invalid default scroll method %d\n", 3128a46c0ec8Sopenharmony_ci method); 3129a46c0ec8Sopenharmony_ci return method; 3130a46c0ec8Sopenharmony_ci} 3131a46c0ec8Sopenharmony_ci 3132a46c0ec8Sopenharmony_cistatic enum libinput_config_scroll_method 3133a46c0ec8Sopenharmony_citp_scroll_config_scroll_method_get_default_method(struct libinput_device *device) 3134a46c0ec8Sopenharmony_ci{ 3135a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3136a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3137a46c0ec8Sopenharmony_ci 3138a46c0ec8Sopenharmony_ci return tp_scroll_get_default_method(tp); 3139a46c0ec8Sopenharmony_ci} 3140a46c0ec8Sopenharmony_ci 3141a46c0ec8Sopenharmony_cistatic int 3142a46c0ec8Sopenharmony_citp_scroll_config_natural_get_default(struct libinput_device *device) 3143a46c0ec8Sopenharmony_ci{ 3144a46c0ec8Sopenharmony_ci struct evdev_device *dev = evdev_device(device); 3145a46c0ec8Sopenharmony_ci 3146a46c0ec8Sopenharmony_ci return (evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD) || 3147a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON)); 3148a46c0ec8Sopenharmony_ci} 3149a46c0ec8Sopenharmony_ci 3150a46c0ec8Sopenharmony_cistatic void 3151a46c0ec8Sopenharmony_citp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device) 3152a46c0ec8Sopenharmony_ci{ 3153a46c0ec8Sopenharmony_ci tp_edge_scroll_init(tp, device); 3154a46c0ec8Sopenharmony_ci 3155a46c0ec8Sopenharmony_ci evdev_init_natural_scroll(device); 3156a46c0ec8Sopenharmony_ci /* Override natural scroll config for Apple touchpads */ 3157a46c0ec8Sopenharmony_ci device->scroll.config_natural.get_default_enabled = tp_scroll_config_natural_get_default; 3158a46c0ec8Sopenharmony_ci device->scroll.natural_scrolling_enabled = tp_scroll_config_natural_get_default(&device->base); 3159a46c0ec8Sopenharmony_ci 3160a46c0ec8Sopenharmony_ci tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods; 3161a46c0ec8Sopenharmony_ci tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method; 3162a46c0ec8Sopenharmony_ci tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method; 3163a46c0ec8Sopenharmony_ci tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method; 3164a46c0ec8Sopenharmony_ci tp->scroll.method = tp_scroll_get_default_method(tp); 3165a46c0ec8Sopenharmony_ci tp->device->base.config.scroll_method = &tp->scroll.config_method; 3166a46c0ec8Sopenharmony_ci 3167a46c0ec8Sopenharmony_ci /* In mm for touchpads with valid resolution, see tp_init_accel() */ 3168a46c0ec8Sopenharmony_ci tp->device->scroll.threshold = 0.0; 3169a46c0ec8Sopenharmony_ci tp->device->scroll.direction_lock_threshold = 5.0; 3170a46c0ec8Sopenharmony_ci} 3171a46c0ec8Sopenharmony_ci 3172a46c0ec8Sopenharmony_cistatic int 3173a46c0ec8Sopenharmony_citp_dwt_config_is_available(struct libinput_device *device) 3174a46c0ec8Sopenharmony_ci{ 3175a46c0ec8Sopenharmony_ci return 1; 3176a46c0ec8Sopenharmony_ci} 3177a46c0ec8Sopenharmony_ci 3178a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3179a46c0ec8Sopenharmony_citp_dwt_config_set(struct libinput_device *device, 3180a46c0ec8Sopenharmony_ci enum libinput_config_dwt_state enable) 3181a46c0ec8Sopenharmony_ci{ 3182a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3183a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3184a46c0ec8Sopenharmony_ci 3185a46c0ec8Sopenharmony_ci switch(enable) { 3186a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_DWT_ENABLED: 3187a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_DWT_DISABLED: 3188a46c0ec8Sopenharmony_ci break; 3189a46c0ec8Sopenharmony_ci default: 3190a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_INVALID; 3191a46c0ec8Sopenharmony_ci } 3192a46c0ec8Sopenharmony_ci 3193a46c0ec8Sopenharmony_ci tp->dwt.dwt_enabled = (enable == LIBINPUT_CONFIG_DWT_ENABLED); 3194a46c0ec8Sopenharmony_ci 3195a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3196a46c0ec8Sopenharmony_ci} 3197a46c0ec8Sopenharmony_ci 3198a46c0ec8Sopenharmony_cistatic enum libinput_config_dwt_state 3199a46c0ec8Sopenharmony_citp_dwt_config_get(struct libinput_device *device) 3200a46c0ec8Sopenharmony_ci{ 3201a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3202a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3203a46c0ec8Sopenharmony_ci 3204a46c0ec8Sopenharmony_ci return tp->dwt.dwt_enabled ? 3205a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWT_ENABLED : 3206a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWT_DISABLED; 3207a46c0ec8Sopenharmony_ci} 3208a46c0ec8Sopenharmony_ci 3209a46c0ec8Sopenharmony_cistatic bool 3210a46c0ec8Sopenharmony_citp_dwt_default_enabled(struct tp_dispatch *tp) 3211a46c0ec8Sopenharmony_ci{ 3212a46c0ec8Sopenharmony_ci return true; 3213a46c0ec8Sopenharmony_ci} 3214a46c0ec8Sopenharmony_ci 3215a46c0ec8Sopenharmony_cistatic enum libinput_config_dwt_state 3216a46c0ec8Sopenharmony_citp_dwt_config_get_default(struct libinput_device *device) 3217a46c0ec8Sopenharmony_ci{ 3218a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3219a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3220a46c0ec8Sopenharmony_ci 3221a46c0ec8Sopenharmony_ci return tp_dwt_default_enabled(tp) ? 3222a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWT_ENABLED : 3223a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWT_DISABLED; 3224a46c0ec8Sopenharmony_ci} 3225a46c0ec8Sopenharmony_ci 3226a46c0ec8Sopenharmony_cistatic int 3227a46c0ec8Sopenharmony_citp_dwtp_config_is_available(struct libinput_device *device) 3228a46c0ec8Sopenharmony_ci{ 3229a46c0ec8Sopenharmony_ci return 1; 3230a46c0ec8Sopenharmony_ci} 3231a46c0ec8Sopenharmony_ci 3232a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3233a46c0ec8Sopenharmony_citp_dwtp_config_set(struct libinput_device *device, 3234a46c0ec8Sopenharmony_ci enum libinput_config_dwtp_state enable) 3235a46c0ec8Sopenharmony_ci{ 3236a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3237a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3238a46c0ec8Sopenharmony_ci 3239a46c0ec8Sopenharmony_ci switch(enable) { 3240a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_DWTP_ENABLED: 3241a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_DWTP_DISABLED: 3242a46c0ec8Sopenharmony_ci break; 3243a46c0ec8Sopenharmony_ci default: 3244a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_INVALID; 3245a46c0ec8Sopenharmony_ci } 3246a46c0ec8Sopenharmony_ci 3247a46c0ec8Sopenharmony_ci tp->palm.dwtp_enabled = (enable == LIBINPUT_CONFIG_DWTP_ENABLED); 3248a46c0ec8Sopenharmony_ci 3249a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3250a46c0ec8Sopenharmony_ci} 3251a46c0ec8Sopenharmony_ci 3252a46c0ec8Sopenharmony_cistatic enum libinput_config_dwtp_state 3253a46c0ec8Sopenharmony_citp_dwtp_config_get(struct libinput_device *device) 3254a46c0ec8Sopenharmony_ci{ 3255a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3256a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3257a46c0ec8Sopenharmony_ci 3258a46c0ec8Sopenharmony_ci return tp->palm.dwtp_enabled ? 3259a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWTP_ENABLED : 3260a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWTP_DISABLED; 3261a46c0ec8Sopenharmony_ci} 3262a46c0ec8Sopenharmony_ci 3263a46c0ec8Sopenharmony_cistatic bool 3264a46c0ec8Sopenharmony_citp_dwtp_default_enabled(struct tp_dispatch *tp) 3265a46c0ec8Sopenharmony_ci{ 3266a46c0ec8Sopenharmony_ci return true; 3267a46c0ec8Sopenharmony_ci} 3268a46c0ec8Sopenharmony_ci 3269a46c0ec8Sopenharmony_cistatic enum libinput_config_dwtp_state 3270a46c0ec8Sopenharmony_citp_dwtp_config_get_default(struct libinput_device *device) 3271a46c0ec8Sopenharmony_ci{ 3272a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3273a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3274a46c0ec8Sopenharmony_ci 3275a46c0ec8Sopenharmony_ci return tp_dwtp_default_enabled(tp) ? 3276a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWTP_ENABLED : 3277a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_DWTP_DISABLED; 3278a46c0ec8Sopenharmony_ci} 3279a46c0ec8Sopenharmony_ci 3280a46c0ec8Sopenharmony_cistatic inline bool 3281a46c0ec8Sopenharmony_citp_is_tpkb_combo_below(struct evdev_device *device) 3282a46c0ec8Sopenharmony_ci{ 3283a46c0ec8Sopenharmony_ci struct quirks_context *quirks; 3284a46c0ec8Sopenharmony_ci struct quirks *q; 3285a46c0ec8Sopenharmony_ci char *prop; 3286a46c0ec8Sopenharmony_ci enum tpkbcombo_layout layout = TPKBCOMBO_LAYOUT_UNKNOWN; 3287a46c0ec8Sopenharmony_ci int rc = false; 3288a46c0ec8Sopenharmony_ci 3289a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 3290a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 3291a46c0ec8Sopenharmony_ci if (!q) 3292a46c0ec8Sopenharmony_ci return false; 3293a46c0ec8Sopenharmony_ci 3294a46c0ec8Sopenharmony_ci if (quirks_get_string(q, QUIRK_ATTR_TPKBCOMBO_LAYOUT, &prop)) { 3295a46c0ec8Sopenharmony_ci rc = parse_tpkbcombo_layout_poperty(prop, &layout) && 3296a46c0ec8Sopenharmony_ci layout == TPKBCOMBO_LAYOUT_BELOW; 3297a46c0ec8Sopenharmony_ci } 3298a46c0ec8Sopenharmony_ci 3299a46c0ec8Sopenharmony_ci quirks_unref(q); 3300a46c0ec8Sopenharmony_ci 3301a46c0ec8Sopenharmony_ci return rc; 3302a46c0ec8Sopenharmony_ci} 3303a46c0ec8Sopenharmony_ci 3304a46c0ec8Sopenharmony_cistatic inline bool 3305a46c0ec8Sopenharmony_citp_is_tablet(struct evdev_device *device) 3306a46c0ec8Sopenharmony_ci{ 3307a46c0ec8Sopenharmony_ci return device->tags & EVDEV_TAG_TABLET_TOUCHPAD; 3308a46c0ec8Sopenharmony_ci} 3309a46c0ec8Sopenharmony_ci 3310a46c0ec8Sopenharmony_cistatic void 3311a46c0ec8Sopenharmony_citp_init_dwt(struct tp_dispatch *tp, 3312a46c0ec8Sopenharmony_ci struct evdev_device *device) 3313a46c0ec8Sopenharmony_ci{ 3314a46c0ec8Sopenharmony_ci if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD && 3315a46c0ec8Sopenharmony_ci !tp_is_tpkb_combo_below(device)) 3316a46c0ec8Sopenharmony_ci return; 3317a46c0ec8Sopenharmony_ci 3318a46c0ec8Sopenharmony_ci tp->dwt.config.is_available = tp_dwt_config_is_available; 3319a46c0ec8Sopenharmony_ci tp->dwt.config.set_enabled = tp_dwt_config_set; 3320a46c0ec8Sopenharmony_ci tp->dwt.config.get_enabled = tp_dwt_config_get; 3321a46c0ec8Sopenharmony_ci tp->dwt.config.get_default_enabled = tp_dwt_config_get_default; 3322a46c0ec8Sopenharmony_ci tp->dwt.dwt_enabled = tp_dwt_default_enabled(tp); 3323a46c0ec8Sopenharmony_ci device->base.config.dwt = &tp->dwt.config; 3324a46c0ec8Sopenharmony_ci} 3325a46c0ec8Sopenharmony_ci 3326a46c0ec8Sopenharmony_cistatic void 3327a46c0ec8Sopenharmony_citp_init_dwtp(struct tp_dispatch *tp, 3328a46c0ec8Sopenharmony_ci struct evdev_device *device) 3329a46c0ec8Sopenharmony_ci{ 3330a46c0ec8Sopenharmony_ci tp->palm.dwtp_enabled = tp_dwtp_default_enabled(tp); 3331a46c0ec8Sopenharmony_ci 3332a46c0ec8Sopenharmony_ci if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD) 3333a46c0ec8Sopenharmony_ci return; 3334a46c0ec8Sopenharmony_ci 3335a46c0ec8Sopenharmony_ci tp->palm.config.is_available = tp_dwtp_config_is_available; 3336a46c0ec8Sopenharmony_ci tp->palm.config.set_enabled = tp_dwtp_config_set; 3337a46c0ec8Sopenharmony_ci tp->palm.config.get_enabled = tp_dwtp_config_get; 3338a46c0ec8Sopenharmony_ci tp->palm.config.get_default_enabled = tp_dwtp_config_get_default; 3339a46c0ec8Sopenharmony_ci device->base.config.dwtp = &tp->palm.config; 3340a46c0ec8Sopenharmony_ci} 3341a46c0ec8Sopenharmony_ci 3342a46c0ec8Sopenharmony_cistatic inline void 3343a46c0ec8Sopenharmony_citp_init_palmdetect_edge(struct tp_dispatch *tp, 3344a46c0ec8Sopenharmony_ci struct evdev_device *device) 3345a46c0ec8Sopenharmony_ci{ 3346a46c0ec8Sopenharmony_ci double width, height; 3347a46c0ec8Sopenharmony_ci struct phys_coords mm = { 0.0, 0.0 }; 3348a46c0ec8Sopenharmony_ci struct device_coords edges; 3349a46c0ec8Sopenharmony_ci 3350a46c0ec8Sopenharmony_ci if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD && 3351a46c0ec8Sopenharmony_ci !tp_is_tpkb_combo_below(device)) 3352a46c0ec8Sopenharmony_ci return; 3353a46c0ec8Sopenharmony_ci 3354a46c0ec8Sopenharmony_ci /* Edge palm detection hurts more than it helps on Apple touchpads. */ 3355a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(device, QUIRK_MODEL_APPLE_TOUCHPAD)) 3356a46c0ec8Sopenharmony_ci return; 3357a46c0ec8Sopenharmony_ci 3358a46c0ec8Sopenharmony_ci evdev_device_get_size(device, &width, &height); 3359a46c0ec8Sopenharmony_ci 3360a46c0ec8Sopenharmony_ci /* Enable edge palm detection on touchpads >= 70 mm. Anything 3361a46c0ec8Sopenharmony_ci smaller probably won't need it, until we find out it does */ 3362a46c0ec8Sopenharmony_ci if (width < 70.0) 3363a46c0ec8Sopenharmony_ci return; 3364a46c0ec8Sopenharmony_ci 3365a46c0ec8Sopenharmony_ci /* palm edges are 8% of the width on each side */ 3366a46c0ec8Sopenharmony_ci mm.x = min(8, width * 0.08); 3367a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 3368a46c0ec8Sopenharmony_ci tp->palm.left_edge = edges.x; 3369a46c0ec8Sopenharmony_ci 3370a46c0ec8Sopenharmony_ci mm.x = width - min(8, width * 0.08); 3371a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 3372a46c0ec8Sopenharmony_ci tp->palm.right_edge = edges.x; 3373a46c0ec8Sopenharmony_ci 3374a46c0ec8Sopenharmony_ci if (!tp->buttons.has_topbuttons && height > 55) { 3375a46c0ec8Sopenharmony_ci /* top edge is 5% of the height */ 3376a46c0ec8Sopenharmony_ci mm.y = height * 0.05; 3377a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 3378a46c0ec8Sopenharmony_ci tp->palm.upper_edge = edges.y; 3379a46c0ec8Sopenharmony_ci } 3380a46c0ec8Sopenharmony_ci} 3381a46c0ec8Sopenharmony_ci 3382a46c0ec8Sopenharmony_cistatic int 3383a46c0ec8Sopenharmony_citp_read_palm_pressure_prop(struct tp_dispatch *tp, 3384a46c0ec8Sopenharmony_ci const struct evdev_device *device) 3385a46c0ec8Sopenharmony_ci{ 3386a46c0ec8Sopenharmony_ci const int default_palm_threshold = 130; 3387a46c0ec8Sopenharmony_ci uint32_t threshold = default_palm_threshold; 3388a46c0ec8Sopenharmony_ci struct quirks_context *quirks; 3389a46c0ec8Sopenharmony_ci struct quirks *q; 3390a46c0ec8Sopenharmony_ci 3391a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 3392a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 3393a46c0ec8Sopenharmony_ci if (!q) 3394a46c0ec8Sopenharmony_ci return threshold; 3395a46c0ec8Sopenharmony_ci 3396a46c0ec8Sopenharmony_ci quirks_get_uint32(q, QUIRK_ATTR_PALM_PRESSURE_THRESHOLD, &threshold); 3397a46c0ec8Sopenharmony_ci quirks_unref(q); 3398a46c0ec8Sopenharmony_ci 3399a46c0ec8Sopenharmony_ci return threshold; 3400a46c0ec8Sopenharmony_ci} 3401a46c0ec8Sopenharmony_ci 3402a46c0ec8Sopenharmony_cistatic inline void 3403a46c0ec8Sopenharmony_citp_init_palmdetect_pressure(struct tp_dispatch *tp, 3404a46c0ec8Sopenharmony_ci struct evdev_device *device) 3405a46c0ec8Sopenharmony_ci{ 3406a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) { 3407a46c0ec8Sopenharmony_ci tp->palm.use_pressure = false; 3408a46c0ec8Sopenharmony_ci return; 3409a46c0ec8Sopenharmony_ci } 3410a46c0ec8Sopenharmony_ci 3411a46c0ec8Sopenharmony_ci tp->palm.pressure_threshold = tp_read_palm_pressure_prop(tp, device); 3412a46c0ec8Sopenharmony_ci if (tp->palm.pressure_threshold != 0) { 3413a46c0ec8Sopenharmony_ci tp->palm.use_pressure = true; 3414a46c0ec8Sopenharmony_ci 3415a46c0ec8Sopenharmony_ci evdev_log_debug(device, 3416a46c0ec8Sopenharmony_ci "palm: pressure threshold is %d\n", 3417a46c0ec8Sopenharmony_ci tp->palm.pressure_threshold); 3418a46c0ec8Sopenharmony_ci } 3419a46c0ec8Sopenharmony_ci} 3420a46c0ec8Sopenharmony_ci 3421a46c0ec8Sopenharmony_cistatic inline void 3422a46c0ec8Sopenharmony_citp_init_palmdetect_size(struct tp_dispatch *tp, 3423a46c0ec8Sopenharmony_ci struct evdev_device *device) 3424a46c0ec8Sopenharmony_ci{ 3425a46c0ec8Sopenharmony_ci struct quirks_context *quirks; 3426a46c0ec8Sopenharmony_ci struct quirks *q; 3427a46c0ec8Sopenharmony_ci uint32_t threshold; 3428a46c0ec8Sopenharmony_ci 3429a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 3430a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 3431a46c0ec8Sopenharmony_ci if (!q) 3432a46c0ec8Sopenharmony_ci return; 3433a46c0ec8Sopenharmony_ci 3434a46c0ec8Sopenharmony_ci if (quirks_get_uint32(q, QUIRK_ATTR_PALM_SIZE_THRESHOLD, &threshold)) { 3435a46c0ec8Sopenharmony_ci if (threshold != 0) { 3436a46c0ec8Sopenharmony_ci tp->palm.use_size = true; 3437a46c0ec8Sopenharmony_ci tp->palm.size_threshold = threshold; 3438a46c0ec8Sopenharmony_ci } 3439a46c0ec8Sopenharmony_ci } 3440a46c0ec8Sopenharmony_ci quirks_unref(q); 3441a46c0ec8Sopenharmony_ci} 3442a46c0ec8Sopenharmony_ci 3443a46c0ec8Sopenharmony_cistatic inline void 3444a46c0ec8Sopenharmony_citp_init_palmdetect_arbitration(struct tp_dispatch *tp, 3445a46c0ec8Sopenharmony_ci struct evdev_device *device) 3446a46c0ec8Sopenharmony_ci{ 3447a46c0ec8Sopenharmony_ci char timer_name[64]; 3448a46c0ec8Sopenharmony_ci 3449a46c0ec8Sopenharmony_ci snprintf(timer_name, 3450a46c0ec8Sopenharmony_ci sizeof(timer_name), 3451a46c0ec8Sopenharmony_ci "%s arbitration", 3452a46c0ec8Sopenharmony_ci evdev_device_get_sysname(device)); 3453a46c0ec8Sopenharmony_ci libinput_timer_init(&tp->arbitration.arbitration_timer, 3454a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 3455a46c0ec8Sopenharmony_ci timer_name, 3456a46c0ec8Sopenharmony_ci tp_arbitration_timeout, tp); 3457a46c0ec8Sopenharmony_ci tp->arbitration.state = ARBITRATION_NOT_ACTIVE; 3458a46c0ec8Sopenharmony_ci} 3459a46c0ec8Sopenharmony_ci 3460a46c0ec8Sopenharmony_cistatic void 3461a46c0ec8Sopenharmony_citp_init_palmdetect(struct tp_dispatch *tp, 3462a46c0ec8Sopenharmony_ci struct evdev_device *device) 3463a46c0ec8Sopenharmony_ci{ 3464a46c0ec8Sopenharmony_ci 3465a46c0ec8Sopenharmony_ci tp->palm.right_edge = INT_MAX; 3466a46c0ec8Sopenharmony_ci tp->palm.left_edge = INT_MIN; 3467a46c0ec8Sopenharmony_ci tp->palm.upper_edge = INT_MIN; 3468a46c0ec8Sopenharmony_ci 3469a46c0ec8Sopenharmony_ci tp_init_palmdetect_arbitration(tp, device); 3470a46c0ec8Sopenharmony_ci 3471a46c0ec8Sopenharmony_ci if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD && 3472a46c0ec8Sopenharmony_ci !tp_is_tpkb_combo_below(device) && 3473a46c0ec8Sopenharmony_ci !tp_is_tablet(device)) 3474a46c0ec8Sopenharmony_ci return; 3475a46c0ec8Sopenharmony_ci 3476a46c0ec8Sopenharmony_ci if (!tp_is_tablet(device)) 3477a46c0ec8Sopenharmony_ci tp->palm.monitor_trackpoint = true; 3478a46c0ec8Sopenharmony_ci 3479a46c0ec8Sopenharmony_ci if (libevdev_has_event_code(device->evdev, 3480a46c0ec8Sopenharmony_ci EV_ABS, 3481a46c0ec8Sopenharmony_ci ABS_MT_TOOL_TYPE)) 3482a46c0ec8Sopenharmony_ci tp->palm.use_mt_tool = true; 3483a46c0ec8Sopenharmony_ci 3484a46c0ec8Sopenharmony_ci if (!tp_is_tablet(device)) 3485a46c0ec8Sopenharmony_ci tp_init_palmdetect_edge(tp, device); 3486a46c0ec8Sopenharmony_ci tp_init_palmdetect_pressure(tp, device); 3487a46c0ec8Sopenharmony_ci tp_init_palmdetect_size(tp, device); 3488a46c0ec8Sopenharmony_ci} 3489a46c0ec8Sopenharmony_ci 3490a46c0ec8Sopenharmony_cistatic void 3491a46c0ec8Sopenharmony_citp_init_sendevents(struct tp_dispatch *tp, 3492a46c0ec8Sopenharmony_ci struct evdev_device *device) 3493a46c0ec8Sopenharmony_ci{ 3494a46c0ec8Sopenharmony_ci char timer_name[64]; 3495a46c0ec8Sopenharmony_ci 3496a46c0ec8Sopenharmony_ci snprintf(timer_name, 3497a46c0ec8Sopenharmony_ci sizeof(timer_name), 3498a46c0ec8Sopenharmony_ci "%s trackpoint", 3499a46c0ec8Sopenharmony_ci evdev_device_get_sysname(device)); 3500a46c0ec8Sopenharmony_ci libinput_timer_init(&tp->palm.trackpoint_timer, 3501a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 3502a46c0ec8Sopenharmony_ci timer_name, 3503a46c0ec8Sopenharmony_ci tp_trackpoint_timeout, tp); 3504a46c0ec8Sopenharmony_ci 3505a46c0ec8Sopenharmony_ci snprintf(timer_name, 3506a46c0ec8Sopenharmony_ci sizeof(timer_name), 3507a46c0ec8Sopenharmony_ci "%s keyboard", 3508a46c0ec8Sopenharmony_ci evdev_device_get_sysname(device)); 3509a46c0ec8Sopenharmony_ci libinput_timer_init(&tp->dwt.keyboard_timer, 3510a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 3511a46c0ec8Sopenharmony_ci timer_name, 3512a46c0ec8Sopenharmony_ci tp_keyboard_timeout, tp); 3513a46c0ec8Sopenharmony_ci} 3514a46c0ec8Sopenharmony_ci 3515a46c0ec8Sopenharmony_cistatic bool 3516a46c0ec8Sopenharmony_citp_pass_sanity_check(struct tp_dispatch *tp, 3517a46c0ec8Sopenharmony_ci struct evdev_device *device) 3518a46c0ec8Sopenharmony_ci{ 3519a46c0ec8Sopenharmony_ci struct libevdev *evdev = device->evdev; 3520a46c0ec8Sopenharmony_ci 3521a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X)) 3522a46c0ec8Sopenharmony_ci goto error; 3523a46c0ec8Sopenharmony_ci 3524a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) 3525a46c0ec8Sopenharmony_ci goto error; 3526a46c0ec8Sopenharmony_ci 3527a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER)) 3528a46c0ec8Sopenharmony_ci goto error; 3529a46c0ec8Sopenharmony_ci 3530a46c0ec8Sopenharmony_ci return true; 3531a46c0ec8Sopenharmony_ci 3532a46c0ec8Sopenharmony_cierror: 3533a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(device, 3534a46c0ec8Sopenharmony_ci "device failed touchpad sanity checks\n"); 3535a46c0ec8Sopenharmony_ci return false; 3536a46c0ec8Sopenharmony_ci} 3537a46c0ec8Sopenharmony_ci 3538a46c0ec8Sopenharmony_cistatic void 3539a46c0ec8Sopenharmony_citp_init_default_resolution(struct tp_dispatch *tp, 3540a46c0ec8Sopenharmony_ci struct evdev_device *device) 3541a46c0ec8Sopenharmony_ci{ 3542a46c0ec8Sopenharmony_ci const int touchpad_width_mm = 69, /* 1 under palm detection */ 3543a46c0ec8Sopenharmony_ci touchpad_height_mm = 50; 3544a46c0ec8Sopenharmony_ci int xres, yres; 3545a46c0ec8Sopenharmony_ci 3546a46c0ec8Sopenharmony_ci if (!device->abs.is_fake_resolution) 3547a46c0ec8Sopenharmony_ci return; 3548a46c0ec8Sopenharmony_ci 3549a46c0ec8Sopenharmony_ci /* we only get here if 3550a46c0ec8Sopenharmony_ci * - the touchpad provides no resolution 3551a46c0ec8Sopenharmony_ci * - the udev hwdb didn't override the resolution 3552a46c0ec8Sopenharmony_ci * - no ATTR_SIZE_HINT is set 3553a46c0ec8Sopenharmony_ci * 3554a46c0ec8Sopenharmony_ci * The majority of touchpads that triggers all these conditions 3555a46c0ec8Sopenharmony_ci * are old ones, so let's assume a small touchpad size and assume 3556a46c0ec8Sopenharmony_ci * that. 3557a46c0ec8Sopenharmony_ci */ 3558a46c0ec8Sopenharmony_ci evdev_log_info(device, 3559a46c0ec8Sopenharmony_ci "no resolution or size hints, assuming a size of %dx%dmm\n", 3560a46c0ec8Sopenharmony_ci touchpad_width_mm, 3561a46c0ec8Sopenharmony_ci touchpad_height_mm); 3562a46c0ec8Sopenharmony_ci 3563a46c0ec8Sopenharmony_ci xres = device->abs.dimensions.x/touchpad_width_mm; 3564a46c0ec8Sopenharmony_ci yres = device->abs.dimensions.y/touchpad_height_mm; 3565a46c0ec8Sopenharmony_ci libevdev_set_abs_resolution(device->evdev, ABS_X, xres); 3566a46c0ec8Sopenharmony_ci libevdev_set_abs_resolution(device->evdev, ABS_Y, yres); 3567a46c0ec8Sopenharmony_ci libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_X, xres); 3568a46c0ec8Sopenharmony_ci libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_Y, yres); 3569a46c0ec8Sopenharmony_ci device->abs.is_fake_resolution = false; 3570a46c0ec8Sopenharmony_ci} 3571a46c0ec8Sopenharmony_ci 3572a46c0ec8Sopenharmony_cistatic inline void 3573a46c0ec8Sopenharmony_citp_init_hysteresis(struct tp_dispatch *tp) 3574a46c0ec8Sopenharmony_ci{ 3575a46c0ec8Sopenharmony_ci int xmargin, ymargin; 3576a46c0ec8Sopenharmony_ci const struct input_absinfo *ax = tp->device->abs.absinfo_x, 3577a46c0ec8Sopenharmony_ci *ay = tp->device->abs.absinfo_y; 3578a46c0ec8Sopenharmony_ci 3579a46c0ec8Sopenharmony_ci if (ax->fuzz) 3580a46c0ec8Sopenharmony_ci xmargin = ax->fuzz; 3581a46c0ec8Sopenharmony_ci else 3582a46c0ec8Sopenharmony_ci xmargin = ax->resolution/4; 3583a46c0ec8Sopenharmony_ci 3584a46c0ec8Sopenharmony_ci if (ay->fuzz) 3585a46c0ec8Sopenharmony_ci ymargin = ay->fuzz; 3586a46c0ec8Sopenharmony_ci else 3587a46c0ec8Sopenharmony_ci ymargin = ay->resolution/4; 3588a46c0ec8Sopenharmony_ci 3589a46c0ec8Sopenharmony_ci tp->hysteresis.margin.x = xmargin; 3590a46c0ec8Sopenharmony_ci tp->hysteresis.margin.y = ymargin; 3591a46c0ec8Sopenharmony_ci tp->hysteresis.enabled = (ax->fuzz || ay->fuzz); 3592a46c0ec8Sopenharmony_ci if (tp->hysteresis.enabled) 3593a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 3594a46c0ec8Sopenharmony_ci "hysteresis enabled. " 3595a46c0ec8Sopenharmony_ci "See %s/touchpad-jitter.html for details\n", 3596a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 3597a46c0ec8Sopenharmony_ci} 3598a46c0ec8Sopenharmony_ci 3599a46c0ec8Sopenharmony_cistatic void 3600a46c0ec8Sopenharmony_citp_init_pressure(struct tp_dispatch *tp, 3601a46c0ec8Sopenharmony_ci struct evdev_device *device) 3602a46c0ec8Sopenharmony_ci{ 3603a46c0ec8Sopenharmony_ci const struct input_absinfo *abs; 3604a46c0ec8Sopenharmony_ci unsigned int code; 3605a46c0ec8Sopenharmony_ci struct quirks_context *quirks; 3606a46c0ec8Sopenharmony_ci struct quirks *q; 3607a46c0ec8Sopenharmony_ci struct quirk_range r; 3608a46c0ec8Sopenharmony_ci int hi, lo; 3609a46c0ec8Sopenharmony_ci 3610a46c0ec8Sopenharmony_ci code = tp->has_mt ? ABS_MT_PRESSURE : ABS_PRESSURE; 3611a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) { 3612a46c0ec8Sopenharmony_ci tp->pressure.use_pressure = false; 3613a46c0ec8Sopenharmony_ci return; 3614a46c0ec8Sopenharmony_ci } 3615a46c0ec8Sopenharmony_ci 3616a46c0ec8Sopenharmony_ci abs = libevdev_get_abs_info(device->evdev, code); 3617a46c0ec8Sopenharmony_ci assert(abs); 3618a46c0ec8Sopenharmony_ci 3619a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 3620a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 3621a46c0ec8Sopenharmony_ci if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) { 3622a46c0ec8Sopenharmony_ci hi = r.upper; 3623a46c0ec8Sopenharmony_ci lo = r.lower; 3624a46c0ec8Sopenharmony_ci 3625a46c0ec8Sopenharmony_ci if (hi == 0 && lo == 0) { 3626a46c0ec8Sopenharmony_ci evdev_log_info(device, 3627a46c0ec8Sopenharmony_ci "pressure-based touch detection disabled\n"); 3628a46c0ec8Sopenharmony_ci goto out; 3629a46c0ec8Sopenharmony_ci } 3630a46c0ec8Sopenharmony_ci } else { 3631a46c0ec8Sopenharmony_ci double range = absinfo_range(abs); 3632a46c0ec8Sopenharmony_ci 3633a46c0ec8Sopenharmony_ci /* Approximately the synaptics defaults */ 3634a46c0ec8Sopenharmony_ci hi = abs->minimum + 0.12 * range; 3635a46c0ec8Sopenharmony_ci lo = abs->minimum + 0.10 * range; 3636a46c0ec8Sopenharmony_ci } 3637a46c0ec8Sopenharmony_ci 3638a46c0ec8Sopenharmony_ci if (hi > abs->maximum || hi < abs->minimum || 3639a46c0ec8Sopenharmony_ci lo > abs->maximum || lo < abs->minimum) { 3640a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 3641a46c0ec8Sopenharmony_ci "discarding out-of-bounds pressure range %d:%d\n", 3642a46c0ec8Sopenharmony_ci hi, lo); 3643a46c0ec8Sopenharmony_ci goto out; 3644a46c0ec8Sopenharmony_ci } 3645a46c0ec8Sopenharmony_ci 3646a46c0ec8Sopenharmony_ci tp->pressure.use_pressure = true; 3647a46c0ec8Sopenharmony_ci tp->pressure.high = hi; 3648a46c0ec8Sopenharmony_ci tp->pressure.low = lo; 3649a46c0ec8Sopenharmony_ci 3650a46c0ec8Sopenharmony_ci evdev_log_debug(device, 3651a46c0ec8Sopenharmony_ci "using pressure-based touch detection (%d:%d)\n", 3652a46c0ec8Sopenharmony_ci lo, 3653a46c0ec8Sopenharmony_ci hi); 3654a46c0ec8Sopenharmony_ciout: 3655a46c0ec8Sopenharmony_ci quirks_unref(q); 3656a46c0ec8Sopenharmony_ci} 3657a46c0ec8Sopenharmony_ci 3658a46c0ec8Sopenharmony_cistatic bool 3659a46c0ec8Sopenharmony_citp_init_touch_size(struct tp_dispatch *tp, 3660a46c0ec8Sopenharmony_ci struct evdev_device *device) 3661a46c0ec8Sopenharmony_ci{ 3662a46c0ec8Sopenharmony_ci struct quirks_context *quirks; 3663a46c0ec8Sopenharmony_ci struct quirks *q; 3664a46c0ec8Sopenharmony_ci struct quirk_range r; 3665a46c0ec8Sopenharmony_ci int lo, hi; 3666a46c0ec8Sopenharmony_ci int rc = false; 3667a46c0ec8Sopenharmony_ci 3668a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, 3669a46c0ec8Sopenharmony_ci EV_ABS, 3670a46c0ec8Sopenharmony_ci ABS_MT_TOUCH_MAJOR)) { 3671a46c0ec8Sopenharmony_ci return false; 3672a46c0ec8Sopenharmony_ci } 3673a46c0ec8Sopenharmony_ci 3674a46c0ec8Sopenharmony_ci quirks = evdev_libinput_context(device)->quirks; 3675a46c0ec8Sopenharmony_ci q = quirks_fetch_for_device(quirks, device->udev_device); 3676a46c0ec8Sopenharmony_ci if (q && quirks_get_range(q, QUIRK_ATTR_TOUCH_SIZE_RANGE, &r)) { 3677a46c0ec8Sopenharmony_ci hi = r.upper; 3678a46c0ec8Sopenharmony_ci lo = r.lower; 3679a46c0ec8Sopenharmony_ci } else { 3680a46c0ec8Sopenharmony_ci goto out; 3681a46c0ec8Sopenharmony_ci } 3682a46c0ec8Sopenharmony_ci 3683a46c0ec8Sopenharmony_ci if (libevdev_get_num_slots(device->evdev) < 5) { 3684a46c0ec8Sopenharmony_ci evdev_log_bug_libinput(device, 3685a46c0ec8Sopenharmony_ci "Expected 5+ slots for touch size detection\n"); 3686a46c0ec8Sopenharmony_ci goto out; 3687a46c0ec8Sopenharmony_ci } 3688a46c0ec8Sopenharmony_ci 3689a46c0ec8Sopenharmony_ci if (hi == 0 && lo == 0) { 3690a46c0ec8Sopenharmony_ci evdev_log_info(device, 3691a46c0ec8Sopenharmony_ci "touch size based touch detection disabled\n"); 3692a46c0ec8Sopenharmony_ci goto out; 3693a46c0ec8Sopenharmony_ci } 3694a46c0ec8Sopenharmony_ci 3695a46c0ec8Sopenharmony_ci /* Thresholds apply for both major or minor */ 3696a46c0ec8Sopenharmony_ci tp->touch_size.low = lo; 3697a46c0ec8Sopenharmony_ci tp->touch_size.high = hi; 3698a46c0ec8Sopenharmony_ci tp->touch_size.use_touch_size = true; 3699a46c0ec8Sopenharmony_ci 3700a46c0ec8Sopenharmony_ci evdev_log_debug(device, 3701a46c0ec8Sopenharmony_ci "using size-based touch detection (%d:%d)\n", 3702a46c0ec8Sopenharmony_ci hi, lo); 3703a46c0ec8Sopenharmony_ci 3704a46c0ec8Sopenharmony_ci rc = true; 3705a46c0ec8Sopenharmony_ciout: 3706a46c0ec8Sopenharmony_ci quirks_unref(q); 3707a46c0ec8Sopenharmony_ci return rc; 3708a46c0ec8Sopenharmony_ci} 3709a46c0ec8Sopenharmony_ci 3710a46c0ec8Sopenharmony_cistatic void 3711a46c0ec8Sopenharmony_citp_init_pressurepad(struct tp_dispatch *tp, 3712a46c0ec8Sopenharmony_ci struct evdev_device *device) 3713a46c0ec8Sopenharmony_ci{ 3714a46c0ec8Sopenharmony_ci /* On traditional touchpads, the pressure value equals contact 3715a46c0ec8Sopenharmony_ci * size. On PressurePads, pressure is a real physical axis for the 3716a46c0ec8Sopenharmony_ci * force down. So we disable it here because we don't do anything 3717a46c0ec8Sopenharmony_ci * with it anyway and using it for touch size messes things up. 3718a46c0ec8Sopenharmony_ci * 3719a46c0ec8Sopenharmony_ci * The kernel/udev set the resolution to non-zero on those devices 3720a46c0ec8Sopenharmony_ci * to indicate that the value is in a known axis space. 3721a46c0ec8Sopenharmony_ci * 3722a46c0ec8Sopenharmony_ci * See also #562 3723a46c0ec8Sopenharmony_ci */ 3724a46c0ec8Sopenharmony_ci if (libevdev_get_abs_resolution(device->evdev, ABS_MT_PRESSURE) != 0 || 3725a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_PRESSURE_PAD)) { 3726a46c0ec8Sopenharmony_ci libevdev_disable_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE); 3727a46c0ec8Sopenharmony_ci libevdev_disable_event_code(device->evdev, EV_ABS, ABS_PRESSURE); 3728a46c0ec8Sopenharmony_ci } 3729a46c0ec8Sopenharmony_ci} 3730a46c0ec8Sopenharmony_ci 3731a46c0ec8Sopenharmony_cistatic int 3732a46c0ec8Sopenharmony_citp_init(struct tp_dispatch *tp, 3733a46c0ec8Sopenharmony_ci struct evdev_device *device) 3734a46c0ec8Sopenharmony_ci{ 3735a46c0ec8Sopenharmony_ci bool use_touch_size = false; 3736a46c0ec8Sopenharmony_ci 3737a46c0ec8Sopenharmony_ci tp->base.dispatch_type = DISPATCH_TOUCHPAD; 3738a46c0ec8Sopenharmony_ci tp->base.interface = &tp_interface; 3739a46c0ec8Sopenharmony_ci tp->device = device; 3740a46c0ec8Sopenharmony_ci list_init(&tp->dwt.paired_keyboard_list); 3741a46c0ec8Sopenharmony_ci 3742a46c0ec8Sopenharmony_ci if (!tp_pass_sanity_check(tp, device)) 3743a46c0ec8Sopenharmony_ci return false; 3744a46c0ec8Sopenharmony_ci 3745a46c0ec8Sopenharmony_ci tp_init_default_resolution(tp, device); 3746a46c0ec8Sopenharmony_ci tp_init_pressurepad(tp, device); 3747a46c0ec8Sopenharmony_ci 3748a46c0ec8Sopenharmony_ci if (!tp_init_slots(tp, device)) 3749a46c0ec8Sopenharmony_ci return false; 3750a46c0ec8Sopenharmony_ci 3751a46c0ec8Sopenharmony_ci evdev_device_init_abs_range_warnings(device); 3752a46c0ec8Sopenharmony_ci use_touch_size = tp_init_touch_size(tp, device); 3753a46c0ec8Sopenharmony_ci 3754a46c0ec8Sopenharmony_ci if (!use_touch_size) 3755a46c0ec8Sopenharmony_ci tp_init_pressure(tp, device); 3756a46c0ec8Sopenharmony_ci 3757a46c0ec8Sopenharmony_ci /* 5 warnings per 24 hours should be enough */ 3758a46c0ec8Sopenharmony_ci ratelimit_init(&tp->jump.warning, h2us(24), 5); 3759a46c0ec8Sopenharmony_ci 3760a46c0ec8Sopenharmony_ci /* Set the dpi to that of the x axis, because that's what we normalize 3761a46c0ec8Sopenharmony_ci to when needed*/ 3762a46c0ec8Sopenharmony_ci device->dpi = device->abs.absinfo_x->resolution * 25.4; 3763a46c0ec8Sopenharmony_ci 3764a46c0ec8Sopenharmony_ci tp_init_hysteresis(tp); 3765a46c0ec8Sopenharmony_ci 3766a46c0ec8Sopenharmony_ci if (!tp_init_accel(tp, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)) 3767a46c0ec8Sopenharmony_ci return false; 3768a46c0ec8Sopenharmony_ci 3769a46c0ec8Sopenharmony_ci tp_init_tap(tp); 3770a46c0ec8Sopenharmony_ci tp_init_buttons(tp, device); 3771a46c0ec8Sopenharmony_ci tp_init_dwt(tp, device); 3772a46c0ec8Sopenharmony_ci tp_init_dwtp(tp, device); 3773a46c0ec8Sopenharmony_ci tp_init_palmdetect(tp, device); 3774a46c0ec8Sopenharmony_ci tp_init_sendevents(tp, device); 3775a46c0ec8Sopenharmony_ci tp_init_scroll(tp, device); 3776a46c0ec8Sopenharmony_ci tp_init_gesture(tp); 3777a46c0ec8Sopenharmony_ci tp_init_thumb(tp); 3778a46c0ec8Sopenharmony_ci 3779a46c0ec8Sopenharmony_ci /* Lenovo X1 Gen6 buffers the events in a weird way, making jump 3780a46c0ec8Sopenharmony_ci * detection impossible. See 3781a46c0ec8Sopenharmony_ci * https://gitlab.freedesktop.org/libinput/libinput/-/issues/506 3782a46c0ec8Sopenharmony_ci */ 3783a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(device, 3784a46c0ec8Sopenharmony_ci QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD)) 3785a46c0ec8Sopenharmony_ci tp->jump.detection_disabled = true; 3786a46c0ec8Sopenharmony_ci 3787a46c0ec8Sopenharmony_ci device->seat_caps |= EVDEV_DEVICE_POINTER; 3788a46c0ec8Sopenharmony_ci if (tp->gesture.enabled) 3789a46c0ec8Sopenharmony_ci device->seat_caps |= EVDEV_DEVICE_GESTURE; 3790a46c0ec8Sopenharmony_ci 3791a46c0ec8Sopenharmony_ci return true; 3792a46c0ec8Sopenharmony_ci} 3793a46c0ec8Sopenharmony_ci 3794a46c0ec8Sopenharmony_cistatic uint32_t 3795a46c0ec8Sopenharmony_citp_sendevents_get_modes(struct libinput_device *device) 3796a46c0ec8Sopenharmony_ci{ 3797a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3798a46c0ec8Sopenharmony_ci uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; 3799a46c0ec8Sopenharmony_ci 3800a46c0ec8Sopenharmony_ci if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD) 3801a46c0ec8Sopenharmony_ci modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 3802a46c0ec8Sopenharmony_ci 3803a46c0ec8Sopenharmony_ci return modes; 3804a46c0ec8Sopenharmony_ci} 3805a46c0ec8Sopenharmony_ci 3806a46c0ec8Sopenharmony_cistatic void 3807a46c0ec8Sopenharmony_citp_suspend_conditional(struct tp_dispatch *tp, 3808a46c0ec8Sopenharmony_ci struct evdev_device *device) 3809a46c0ec8Sopenharmony_ci{ 3810a46c0ec8Sopenharmony_ci struct libinput_device *dev; 3811a46c0ec8Sopenharmony_ci 3812a46c0ec8Sopenharmony_ci list_for_each(dev, &device->base.seat->devices_list, link) { 3813a46c0ec8Sopenharmony_ci struct evdev_device *d = evdev_device(dev); 3814a46c0ec8Sopenharmony_ci if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) { 3815a46c0ec8Sopenharmony_ci tp_suspend(tp, device, SUSPEND_EXTERNAL_MOUSE); 3816a46c0ec8Sopenharmony_ci break; 3817a46c0ec8Sopenharmony_ci } 3818a46c0ec8Sopenharmony_ci } 3819a46c0ec8Sopenharmony_ci} 3820a46c0ec8Sopenharmony_ci 3821a46c0ec8Sopenharmony_cistatic enum libinput_config_status 3822a46c0ec8Sopenharmony_citp_sendevents_set_mode(struct libinput_device *device, 3823a46c0ec8Sopenharmony_ci enum libinput_config_send_events_mode mode) 3824a46c0ec8Sopenharmony_ci{ 3825a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3826a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 3827a46c0ec8Sopenharmony_ci 3828a46c0ec8Sopenharmony_ci /* DISABLED overrides any DISABLED_ON_ */ 3829a46c0ec8Sopenharmony_ci if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) && 3830a46c0ec8Sopenharmony_ci (mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)) 3831a46c0ec8Sopenharmony_ci mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 3832a46c0ec8Sopenharmony_ci 3833a46c0ec8Sopenharmony_ci if (mode == tp->sendevents.current_mode) 3834a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3835a46c0ec8Sopenharmony_ci 3836a46c0ec8Sopenharmony_ci switch(mode) { 3837a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: 3838a46c0ec8Sopenharmony_ci tp_resume(tp, evdev, SUSPEND_SENDEVENTS); 3839a46c0ec8Sopenharmony_ci tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE); 3840a46c0ec8Sopenharmony_ci break; 3841a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: 3842a46c0ec8Sopenharmony_ci tp_suspend(tp, evdev, SUSPEND_SENDEVENTS); 3843a46c0ec8Sopenharmony_ci tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE); 3844a46c0ec8Sopenharmony_ci break; 3845a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: 3846a46c0ec8Sopenharmony_ci tp_suspend_conditional(tp, evdev); 3847a46c0ec8Sopenharmony_ci tp_resume(tp, evdev, SUSPEND_SENDEVENTS); 3848a46c0ec8Sopenharmony_ci break; 3849a46c0ec8Sopenharmony_ci default: 3850a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; 3851a46c0ec8Sopenharmony_ci } 3852a46c0ec8Sopenharmony_ci 3853a46c0ec8Sopenharmony_ci tp->sendevents.current_mode = mode; 3854a46c0ec8Sopenharmony_ci 3855a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 3856a46c0ec8Sopenharmony_ci} 3857a46c0ec8Sopenharmony_ci 3858a46c0ec8Sopenharmony_cistatic enum libinput_config_send_events_mode 3859a46c0ec8Sopenharmony_citp_sendevents_get_mode(struct libinput_device *device) 3860a46c0ec8Sopenharmony_ci{ 3861a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 3862a46c0ec8Sopenharmony_ci struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch; 3863a46c0ec8Sopenharmony_ci 3864a46c0ec8Sopenharmony_ci return dispatch->sendevents.current_mode; 3865a46c0ec8Sopenharmony_ci} 3866a46c0ec8Sopenharmony_ci 3867a46c0ec8Sopenharmony_cistatic enum libinput_config_send_events_mode 3868a46c0ec8Sopenharmony_citp_sendevents_get_default_mode(struct libinput_device *device) 3869a46c0ec8Sopenharmony_ci{ 3870a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; 3871a46c0ec8Sopenharmony_ci} 3872a46c0ec8Sopenharmony_ci 3873a46c0ec8Sopenharmony_cistatic void 3874a46c0ec8Sopenharmony_citp_change_to_left_handed(struct evdev_device *device) 3875a46c0ec8Sopenharmony_ci{ 3876a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch; 3877a46c0ec8Sopenharmony_ci 3878a46c0ec8Sopenharmony_ci if (device->left_handed.want_enabled == device->left_handed.enabled) 3879a46c0ec8Sopenharmony_ci return; 3880a46c0ec8Sopenharmony_ci 3881a46c0ec8Sopenharmony_ci if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */ 3882a46c0ec8Sopenharmony_ci return; 3883a46c0ec8Sopenharmony_ci 3884a46c0ec8Sopenharmony_ci /* tapping and clickfinger aren't affected by left-handed config, 3885a46c0ec8Sopenharmony_ci * so checking physical buttons is enough */ 3886a46c0ec8Sopenharmony_ci 3887a46c0ec8Sopenharmony_ci device->left_handed.enabled = device->left_handed.want_enabled; 3888a46c0ec8Sopenharmony_ci tp_change_rotation(device, DO_NOTIFY); 3889a46c0ec8Sopenharmony_ci} 3890a46c0ec8Sopenharmony_ci 3891a46c0ec8Sopenharmony_cistatic bool 3892a46c0ec8Sopenharmony_citp_requires_rotation(struct tp_dispatch *tp, struct evdev_device *device) 3893a46c0ec8Sopenharmony_ci{ 3894a46c0ec8Sopenharmony_ci bool rotate = false; 3895a46c0ec8Sopenharmony_ci#if HAVE_LIBWACOM 3896a46c0ec8Sopenharmony_ci struct libinput *li = tp_libinput_context(tp); 3897a46c0ec8Sopenharmony_ci WacomDeviceDatabase *db = NULL; 3898a46c0ec8Sopenharmony_ci WacomDevice **devices = NULL, 3899a46c0ec8Sopenharmony_ci **d; 3900a46c0ec8Sopenharmony_ci WacomDevice *dev; 3901a46c0ec8Sopenharmony_ci uint32_t vid = evdev_device_get_id_vendor(device), 3902a46c0ec8Sopenharmony_ci pid = evdev_device_get_id_product(device); 3903a46c0ec8Sopenharmony_ci 3904a46c0ec8Sopenharmony_ci if ((device->tags & EVDEV_TAG_TABLET_TOUCHPAD) == 0) 3905a46c0ec8Sopenharmony_ci goto out; 3906a46c0ec8Sopenharmony_ci 3907a46c0ec8Sopenharmony_ci db = libinput_libwacom_ref(li); 3908a46c0ec8Sopenharmony_ci if (!db) 3909a46c0ec8Sopenharmony_ci goto out; 3910a46c0ec8Sopenharmony_ci 3911a46c0ec8Sopenharmony_ci /* Check if we have a device with the same vid/pid. If not, 3912a46c0ec8Sopenharmony_ci we need to loop through all devices and check their paired 3913a46c0ec8Sopenharmony_ci device. */ 3914a46c0ec8Sopenharmony_ci dev = libwacom_new_from_usbid(db, vid, pid, NULL); 3915a46c0ec8Sopenharmony_ci if (dev) { 3916a46c0ec8Sopenharmony_ci rotate = libwacom_is_reversible(dev); 3917a46c0ec8Sopenharmony_ci libwacom_destroy(dev); 3918a46c0ec8Sopenharmony_ci goto out; 3919a46c0ec8Sopenharmony_ci } 3920a46c0ec8Sopenharmony_ci 3921a46c0ec8Sopenharmony_ci devices = libwacom_list_devices_from_database(db, NULL); 3922a46c0ec8Sopenharmony_ci if (!devices) 3923a46c0ec8Sopenharmony_ci goto out; 3924a46c0ec8Sopenharmony_ci d = devices; 3925a46c0ec8Sopenharmony_ci while(*d) { 3926a46c0ec8Sopenharmony_ci const WacomMatch *paired; 3927a46c0ec8Sopenharmony_ci 3928a46c0ec8Sopenharmony_ci paired = libwacom_get_paired_device(*d); 3929a46c0ec8Sopenharmony_ci if (paired && 3930a46c0ec8Sopenharmony_ci libwacom_match_get_vendor_id(paired) == vid && 3931a46c0ec8Sopenharmony_ci libwacom_match_get_product_id(paired) == pid) { 3932a46c0ec8Sopenharmony_ci rotate = libwacom_is_reversible(dev); 3933a46c0ec8Sopenharmony_ci break; 3934a46c0ec8Sopenharmony_ci } 3935a46c0ec8Sopenharmony_ci d++; 3936a46c0ec8Sopenharmony_ci } 3937a46c0ec8Sopenharmony_ci 3938a46c0ec8Sopenharmony_ci free(devices); 3939a46c0ec8Sopenharmony_ci 3940a46c0ec8Sopenharmony_ciout: 3941a46c0ec8Sopenharmony_ci /* We don't need to keep it around for the touchpad, we're done with 3942a46c0ec8Sopenharmony_ci * it until the device dies. */ 3943a46c0ec8Sopenharmony_ci if (db) 3944a46c0ec8Sopenharmony_ci libinput_libwacom_unref(li); 3945a46c0ec8Sopenharmony_ci#endif 3946a46c0ec8Sopenharmony_ci 3947a46c0ec8Sopenharmony_ci return rotate; 3948a46c0ec8Sopenharmony_ci} 3949a46c0ec8Sopenharmony_ci 3950a46c0ec8Sopenharmony_cistatic void 3951a46c0ec8Sopenharmony_citp_init_left_handed(struct tp_dispatch *tp, 3952a46c0ec8Sopenharmony_ci struct evdev_device *device) 3953a46c0ec8Sopenharmony_ci{ 3954a46c0ec8Sopenharmony_ci bool want_left_handed = true; 3955a46c0ec8Sopenharmony_ci 3956a46c0ec8Sopenharmony_ci tp->left_handed.must_rotate = tp_requires_rotation(tp, device); 3957a46c0ec8Sopenharmony_ci 3958a46c0ec8Sopenharmony_ci if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) 3959a46c0ec8Sopenharmony_ci want_left_handed = false; 3960a46c0ec8Sopenharmony_ci if (want_left_handed) 3961a46c0ec8Sopenharmony_ci evdev_init_left_handed(device, tp_change_to_left_handed); 3962a46c0ec8Sopenharmony_ci 3963a46c0ec8Sopenharmony_ci} 3964a46c0ec8Sopenharmony_ci 3965a46c0ec8Sopenharmony_cistruct evdev_dispatch * 3966a46c0ec8Sopenharmony_cievdev_mt_touchpad_create(struct evdev_device *device) 3967a46c0ec8Sopenharmony_ci{ 3968a46c0ec8Sopenharmony_ci struct tp_dispatch *tp; 3969a46c0ec8Sopenharmony_ci 3970a46c0ec8Sopenharmony_ci evdev_tag_touchpad(device, device->udev_device); 3971a46c0ec8Sopenharmony_ci 3972a46c0ec8Sopenharmony_ci tp = zalloc(sizeof *tp); 3973a46c0ec8Sopenharmony_ci 3974a46c0ec8Sopenharmony_ci if (!tp_init(tp, device)) { 3975a46c0ec8Sopenharmony_ci tp_interface_destroy(&tp->base); 3976a46c0ec8Sopenharmony_ci return NULL; 3977a46c0ec8Sopenharmony_ci } 3978a46c0ec8Sopenharmony_ci 3979a46c0ec8Sopenharmony_ci device->base.config.sendevents = &tp->sendevents.config; 3980a46c0ec8Sopenharmony_ci 3981a46c0ec8Sopenharmony_ci tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; 3982a46c0ec8Sopenharmony_ci tp->sendevents.config.get_modes = tp_sendevents_get_modes; 3983a46c0ec8Sopenharmony_ci tp->sendevents.config.set_mode = tp_sendevents_set_mode; 3984a46c0ec8Sopenharmony_ci tp->sendevents.config.get_mode = tp_sendevents_get_mode; 3985a46c0ec8Sopenharmony_ci tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode; 3986a46c0ec8Sopenharmony_ci 3987a46c0ec8Sopenharmony_ci tp_init_left_handed(tp, device); 3988a46c0ec8Sopenharmony_ci 3989a46c0ec8Sopenharmony_ci return &tp->base; 3990a46c0ec8Sopenharmony_ci} 3991