1 /*
2 * Copyright © 2014-2015 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "config.h"
25
26 #include <limits.h>
27 #include <math.h>
28 #include <string.h>
29
30 #include "evdev-mt-touchpad.h"
31
32 /* Use a reasonably large threshold until locked into scrolling mode, to
33 avoid accidentally locking in scrolling mode when trying to use the entire
34 touchpad to move the pointer. The user can wait for the timeout to trigger
35 to do a small scroll. */
36 #define DEFAULT_SCROLL_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3)
37
38 enum scroll_event {
39 SCROLL_EVENT_TOUCH,
40 SCROLL_EVENT_MOTION,
41 SCROLL_EVENT_RELEASE,
42 SCROLL_EVENT_TIMEOUT,
43 SCROLL_EVENT_POSTED,
44 };
45
46 static inline const char*
edge_state_to_str(enum tp_edge_scroll_touch_state state)47 edge_state_to_str(enum tp_edge_scroll_touch_state state)
48 {
49
50 switch (state) {
51 CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_NONE);
52 CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
53 CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE);
54 CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_AREA);
55 }
56 return NULL;
57 }
58
59 static inline const char*
edge_event_to_str(enum scroll_event event)60 edge_event_to_str(enum scroll_event event)
61 {
62 switch (event) {
63 CASE_RETURN_STRING(SCROLL_EVENT_TOUCH);
64 CASE_RETURN_STRING(SCROLL_EVENT_MOTION);
65 CASE_RETURN_STRING(SCROLL_EVENT_RELEASE);
66 CASE_RETURN_STRING(SCROLL_EVENT_TIMEOUT);
67 CASE_RETURN_STRING(SCROLL_EVENT_POSTED);
68 }
69 return NULL;
70 }
71
72 uint32_t
tp_touch_get_edge(const struct tp_dispatch * tp,const struct tp_touch * t)73 tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
74 {
75 uint32_t edge = EDGE_NONE;
76
77 if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE)
78 return EDGE_NONE;
79
80 if (t->point.x > tp->scroll.right_edge)
81 edge |= EDGE_RIGHT;
82
83 if (t->point.y > tp->scroll.bottom_edge)
84 edge |= EDGE_BOTTOM;
85
86 return edge;
87 }
88
89 static inline void
tp_edge_scroll_set_timer(struct tp_dispatch * tp,struct tp_touch * t,uint64_t time)90 tp_edge_scroll_set_timer(struct tp_dispatch *tp,
91 struct tp_touch *t,
92 uint64_t time)
93 {
94 const int DEFAULT_SCROLL_LOCK_TIMEOUT = ms2us(300);
95 /* if we use software buttons, we disable timeout-based
96 * edge scrolling. A finger resting on the button areas is
97 * likely there to trigger a button event.
98 */
99 if (tp->buttons.click_method ==
100 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
101 return;
102
103 libinput_timer_set(&t->scroll.timer,
104 time + DEFAULT_SCROLL_LOCK_TIMEOUT);
105 }
106
107 static void
tp_edge_scroll_set_state(struct tp_dispatch * tp,struct tp_touch * t,enum tp_edge_scroll_touch_state state,uint64_t time)108 tp_edge_scroll_set_state(struct tp_dispatch *tp,
109 struct tp_touch *t,
110 enum tp_edge_scroll_touch_state state,
111 uint64_t time)
112 {
113 libinput_timer_cancel(&t->scroll.timer);
114
115 t->scroll.edge_state = state;
116
117 switch (state) {
118 case EDGE_SCROLL_TOUCH_STATE_NONE:
119 t->scroll.edge = EDGE_NONE;
120 break;
121 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
122 t->scroll.edge = tp_touch_get_edge(tp, t);
123 t->scroll.initial = t->point;
124 tp_edge_scroll_set_timer(tp, t, time);
125 break;
126 case EDGE_SCROLL_TOUCH_STATE_EDGE:
127 break;
128 case EDGE_SCROLL_TOUCH_STATE_AREA:
129 t->scroll.edge = EDGE_NONE;
130 break;
131 }
132 }
133
134 static void
tp_edge_scroll_handle_none(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event,uint64_t time)135 tp_edge_scroll_handle_none(struct tp_dispatch *tp,
136 struct tp_touch *t,
137 enum scroll_event event,
138 uint64_t time)
139 {
140 switch (event) {
141 case SCROLL_EVENT_TOUCH:
142 if (tp_touch_get_edge(tp, t)) {
143 tp_edge_scroll_set_state(tp,
144 t,
145 EDGE_SCROLL_TOUCH_STATE_EDGE_NEW,
146 time);
147 } else {
148 tp_edge_scroll_set_state(tp,
149 t,
150 EDGE_SCROLL_TOUCH_STATE_AREA,
151 time);
152 }
153 break;
154 case SCROLL_EVENT_MOTION:
155 case SCROLL_EVENT_RELEASE:
156 case SCROLL_EVENT_TIMEOUT:
157 case SCROLL_EVENT_POSTED:
158 evdev_log_bug_libinput(tp->device,
159 "edge-scroll: touch %d: unexpected scroll event %d in none state\n",
160 t->index,
161 event);
162 break;
163 }
164 }
165
166 static void
tp_edge_scroll_handle_edge_new(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event,uint64_t time)167 tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp,
168 struct tp_touch *t,
169 enum scroll_event event,
170 uint64_t time)
171 {
172 switch (event) {
173 case SCROLL_EVENT_TOUCH:
174 evdev_log_bug_libinput(tp->device,
175 "edge-scroll: touch %d: unexpected scroll event %d in edge new state\n",
176 t->index,
177 event);
178 break;
179 case SCROLL_EVENT_MOTION:
180 t->scroll.edge &= tp_touch_get_edge(tp, t);
181 if (!t->scroll.edge)
182 tp_edge_scroll_set_state(tp,
183 t,
184 EDGE_SCROLL_TOUCH_STATE_AREA,
185 time);
186 break;
187 case SCROLL_EVENT_RELEASE:
188 tp_edge_scroll_set_state(tp,
189 t,
190 EDGE_SCROLL_TOUCH_STATE_NONE,
191 time);
192 break;
193 case SCROLL_EVENT_TIMEOUT:
194 case SCROLL_EVENT_POSTED:
195 tp_edge_scroll_set_state(tp,
196 t,
197 EDGE_SCROLL_TOUCH_STATE_EDGE,
198 time);
199 break;
200 }
201 }
202
203 static void
tp_edge_scroll_handle_edge(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event,uint64_t time)204 tp_edge_scroll_handle_edge(struct tp_dispatch *tp,
205 struct tp_touch *t,
206 enum scroll_event event,
207 uint64_t time)
208 {
209 switch (event) {
210 case SCROLL_EVENT_TOUCH:
211 case SCROLL_EVENT_TIMEOUT:
212 evdev_log_bug_libinput(tp->device,
213 "edge-scroll: touch %d: unexpected scroll event %d in edge state\n",
214 t->index,
215 event);
216 break;
217 case SCROLL_EVENT_MOTION:
218 /* If started at the bottom right, decide in which dir to scroll */
219 if (t->scroll.edge == (EDGE_RIGHT | EDGE_BOTTOM)) {
220 t->scroll.edge &= tp_touch_get_edge(tp, t);
221 if (!t->scroll.edge)
222 tp_edge_scroll_set_state(tp,
223 t,
224 EDGE_SCROLL_TOUCH_STATE_AREA,
225 time);
226 }
227 break;
228 case SCROLL_EVENT_RELEASE:
229 tp_edge_scroll_set_state(tp,
230 t,
231 EDGE_SCROLL_TOUCH_STATE_NONE,
232 time);
233 break;
234 case SCROLL_EVENT_POSTED:
235 break;
236 }
237 }
238
239 static void
tp_edge_scroll_handle_area(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event,uint64_t time)240 tp_edge_scroll_handle_area(struct tp_dispatch *tp,
241 struct tp_touch *t,
242 enum scroll_event event,
243 uint64_t time)
244 {
245 switch (event) {
246 case SCROLL_EVENT_TOUCH:
247 case SCROLL_EVENT_TIMEOUT:
248 case SCROLL_EVENT_POSTED:
249 evdev_log_bug_libinput(tp->device,
250 "unexpected scroll event %d in area state\n",
251 event);
252 break;
253 case SCROLL_EVENT_MOTION:
254 break;
255 case SCROLL_EVENT_RELEASE:
256 tp_edge_scroll_set_state(tp,
257 t,
258 EDGE_SCROLL_TOUCH_STATE_NONE,
259 time);
260 break;
261 }
262 }
263
264 static void
tp_edge_scroll_handle_event(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event,uint64_t time)265 tp_edge_scroll_handle_event(struct tp_dispatch *tp,
266 struct tp_touch *t,
267 enum scroll_event event,
268 uint64_t time)
269 {
270 enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
271
272 switch (current) {
273 case EDGE_SCROLL_TOUCH_STATE_NONE:
274 tp_edge_scroll_handle_none(tp, t, event, time);
275 break;
276 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
277 tp_edge_scroll_handle_edge_new(tp, t, event, time);
278 break;
279 case EDGE_SCROLL_TOUCH_STATE_EDGE:
280 tp_edge_scroll_handle_edge(tp, t, event, time);
281 break;
282 case EDGE_SCROLL_TOUCH_STATE_AREA:
283 tp_edge_scroll_handle_area(tp, t, event, time);
284 break;
285 }
286
287 if (current != t->scroll.edge_state)
288 evdev_log_debug(tp->device,
289 "edge-scroll: touch %d state %s → %s → %s\n",
290 t->index,
291 edge_state_to_str(current),
292 edge_event_to_str(event),
293 edge_state_to_str(t->scroll.edge_state));
294 }
295
296 static void
tp_edge_scroll_handle_timeout(uint64_t now,void * data)297 tp_edge_scroll_handle_timeout(uint64_t now, void *data)
298 {
299 struct tp_touch *t = data;
300
301 tp_edge_scroll_handle_event(t->tp, t, SCROLL_EVENT_TIMEOUT, now);
302 }
303
304 void
tp_edge_scroll_init(struct tp_dispatch * tp,struct evdev_device * device)305 tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
306 {
307 struct tp_touch *t;
308 double width, height;
309 bool want_horiz_scroll = true;
310 struct device_coords edges;
311 struct phys_coords mm = { 0.0, 0.0 };
312 int i;
313
314 evdev_device_get_size(device, &width, &height);
315 /* Touchpads smaller than 40mm are not tall enough to have a
316 horizontal scroll area, it takes too much space away. But
317 clickpads have enough space here anyway because of the
318 software button area (and all these tiny clickpads were built
319 when software buttons were a thing, e.g. Lenovo *20 series)
320 */
321 if (!tp->buttons.is_clickpad)
322 want_horiz_scroll = (height >= 40);
323
324 /* 7mm edge size */
325 mm.x = width - 7;
326 mm.y = height - 7;
327 edges = evdev_device_mm_to_units(device, &mm);
328
329 tp->scroll.right_edge = edges.x;
330 if (want_horiz_scroll)
331 tp->scroll.bottom_edge = edges.y;
332 else
333 tp->scroll.bottom_edge = INT_MAX;
334
335 i = 0;
336 tp_for_each_touch(tp, t) {
337 char timer_name[64];
338
339 snprintf(timer_name,
340 sizeof(timer_name),
341 "%s (%d) edgescroll",
342 evdev_device_get_sysname(device),
343 i);
344 t->scroll.direction = -1;
345 libinput_timer_init(&t->scroll.timer,
346 tp_libinput_context(tp),
347 timer_name,
348 tp_edge_scroll_handle_timeout, t);
349 }
350 }
351
352 void
tp_remove_edge_scroll(struct tp_dispatch * tp)353 tp_remove_edge_scroll(struct tp_dispatch *tp)
354 {
355 struct tp_touch *t;
356
357 tp_for_each_touch(tp, t) {
358 libinput_timer_cancel(&t->scroll.timer);
359 libinput_timer_destroy(&t->scroll.timer);
360 }
361 }
362
363 void
tp_edge_scroll_handle_state(struct tp_dispatch * tp,uint64_t time)364 tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
365 {
366 struct tp_touch *t;
367
368 if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE) {
369 tp_for_each_touch(tp, t) {
370 if (t->state == TOUCH_BEGIN)
371 t->scroll.edge_state =
372 EDGE_SCROLL_TOUCH_STATE_AREA;
373 else if (t->state == TOUCH_END)
374 t->scroll.edge_state =
375 EDGE_SCROLL_TOUCH_STATE_NONE;
376 }
377 return;
378 }
379
380 tp_for_each_touch(tp, t) {
381 if (!t->dirty)
382 continue;
383
384 switch (t->state) {
385 case TOUCH_NONE:
386 case TOUCH_HOVERING:
387 break;
388 case TOUCH_BEGIN:
389 tp_edge_scroll_handle_event(tp,
390 t,
391 SCROLL_EVENT_TOUCH,
392 time);
393 break;
394 case TOUCH_UPDATE:
395 tp_edge_scroll_handle_event(tp,
396 t,
397 SCROLL_EVENT_MOTION,
398 time);
399 break;
400 case TOUCH_MAYBE_END:
401 /* This shouldn't happen we transfer to TOUCH_END
402 * before processing state */
403 evdev_log_debug(tp->device,
404 "touch %d: unexpected state %d\n",
405 t->index,
406 t->state);
407 _fallthrough_;
408 case TOUCH_END:
409 tp_edge_scroll_handle_event(tp,
410 t,
411 SCROLL_EVENT_RELEASE,
412 time);
413 break;
414 }
415 }
416 }
417
418 int
tp_edge_scroll_post_events(struct tp_dispatch * tp,uint64_t time)419 tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
420 {
421 struct evdev_device *device = tp->device;
422 struct tp_touch *t;
423 enum libinput_pointer_axis axis;
424 double *delta;
425 struct device_coords raw;
426 struct device_float_coords fraw;
427 struct normalized_coords normalized, tmp;
428 const struct normalized_coords zero = { 0.0, 0.0 };
429
430 tp_for_each_touch(tp, t) {
431 if (!t->dirty)
432 continue;
433
434 if (t->palm.state != PALM_NONE || tp_thumb_ignored(tp, t))
435 continue;
436
437 /* only scroll with the finger in the previous edge */
438 if (t->scroll.edge &&
439 (tp_touch_get_edge(tp, t) & t->scroll.edge) == 0)
440 continue;
441
442 switch (t->scroll.edge) {
443 case EDGE_NONE:
444 if (t->scroll.direction != -1) {
445 /* Send stop scroll event */
446 evdev_notify_axis_finger(device,
447 time,
448 bit(t->scroll.direction),
449 &zero);
450 t->scroll.direction = -1;
451 }
452 continue;
453 case EDGE_RIGHT:
454 axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
455 delta = &normalized.y;
456 break;
457 case EDGE_BOTTOM:
458 axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
459 delta = &normalized.x;
460 break;
461 default: /* EDGE_RIGHT | EDGE_BOTTOM */
462 continue; /* Don't know direction yet, skip */
463 }
464
465 raw = tp_get_delta(t);
466 fraw.x = raw.x;
467 fraw.y = raw.y;
468 /* scroll is not accelerated */
469 normalized = tp_filter_motion_unaccelerated(tp, &fraw, time);
470
471 switch (t->scroll.edge_state) {
472 case EDGE_SCROLL_TOUCH_STATE_NONE:
473 case EDGE_SCROLL_TOUCH_STATE_AREA:
474 evdev_log_bug_libinput(device,
475 "unexpected scroll state %d\n",
476 t->scroll.edge_state);
477 break;
478 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
479 tmp = normalized;
480 normalized = tp_normalize_delta(tp,
481 device_delta(t->point,
482 t->scroll.initial));
483 if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD)
484 normalized = zero;
485 else
486 normalized = tmp;
487 break;
488 case EDGE_SCROLL_TOUCH_STATE_EDGE:
489 break;
490 }
491
492 if (*delta == 0.0)
493 continue;
494
495 evdev_notify_axis_finger(device, time,
496 bit(axis),
497 &normalized);
498 t->scroll.direction = axis;
499
500 tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED, time);
501 }
502
503 return 0; /* Edge touches are suppressed by edge_scroll_touch_active */
504 }
505
506 void
tp_edge_scroll_stop_events(struct tp_dispatch * tp,uint64_t time)507 tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
508 {
509 struct evdev_device *device = tp->device;
510 struct tp_touch *t;
511 const struct normalized_coords zero = { 0.0, 0.0 };
512
513 tp_for_each_touch(tp, t) {
514 if (t->scroll.direction != -1) {
515 evdev_notify_axis_finger(device,
516 time,
517 bit(t->scroll.direction),
518 &zero);
519 t->scroll.direction = -1;
520 /* reset touch to area state, avoids loading the
521 * state machine with special case handling */
522 t->scroll.edge = EDGE_NONE;
523 t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_AREA;
524 }
525 }
526 }
527
528 int
tp_edge_scroll_touch_active(const struct tp_dispatch * tp,const struct tp_touch * t)529 tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
530 const struct tp_touch *t)
531 {
532 return t->scroll.edge_state == EDGE_SCROLL_TOUCH_STATE_AREA;
533 }
534