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)90 tp_edge_scroll_set_timer(struct tp_dispatch *tp,
91 struct tp_touch *t)
92 {
93 const int DEFAULT_SCROLL_LOCK_TIMEOUT = ms2us(300);
94 /* if we use software buttons, we disable timeout-based
95 * edge scrolling. A finger resting on the button areas is
96 * likely there to trigger a button event.
97 */
98 if (tp->buttons.click_method ==
99 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
100 return;
101
102 libinput_timer_set(&t->scroll.timer,
103 t->time + DEFAULT_SCROLL_LOCK_TIMEOUT);
104 }
105
106 static void
tp_edge_scroll_set_state(struct tp_dispatch * tp,struct tp_touch * t,enum tp_edge_scroll_touch_state state)107 tp_edge_scroll_set_state(struct tp_dispatch *tp,
108 struct tp_touch *t,
109 enum tp_edge_scroll_touch_state state)
110 {
111 libinput_timer_cancel(&t->scroll.timer);
112
113 t->scroll.edge_state = state;
114
115 switch (state) {
116 case EDGE_SCROLL_TOUCH_STATE_NONE:
117 t->scroll.edge = EDGE_NONE;
118 break;
119 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
120 t->scroll.edge = tp_touch_get_edge(tp, t);
121 t->scroll.initial = t->point;
122 tp_edge_scroll_set_timer(tp, t);
123 break;
124 case EDGE_SCROLL_TOUCH_STATE_EDGE:
125 break;
126 case EDGE_SCROLL_TOUCH_STATE_AREA:
127 t->scroll.edge = EDGE_NONE;
128 break;
129 }
130 }
131
132 static void
tp_edge_scroll_handle_none(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event)133 tp_edge_scroll_handle_none(struct tp_dispatch *tp,
134 struct tp_touch *t,
135 enum scroll_event event)
136 {
137 switch (event) {
138 case SCROLL_EVENT_TOUCH:
139 if (tp_touch_get_edge(tp, t)) {
140 tp_edge_scroll_set_state(tp, t,
141 EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
142 } else {
143 tp_edge_scroll_set_state(tp, t,
144 EDGE_SCROLL_TOUCH_STATE_AREA);
145 }
146 break;
147 case SCROLL_EVENT_MOTION:
148 case SCROLL_EVENT_RELEASE:
149 case SCROLL_EVENT_TIMEOUT:
150 case SCROLL_EVENT_POSTED:
151 evdev_log_bug_libinput(tp->device,
152 "edge-scroll: touch %d: unexpected scroll event %d in none state\n",
153 t->index,
154 event);
155 break;
156 }
157 }
158
159 static void
tp_edge_scroll_handle_edge_new(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event)160 tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp,
161 struct tp_touch *t,
162 enum scroll_event event)
163 {
164 switch (event) {
165 case SCROLL_EVENT_TOUCH:
166 evdev_log_bug_libinput(tp->device,
167 "edge-scroll: touch %d: unexpected scroll event %d in edge new state\n",
168 t->index,
169 event);
170 break;
171 case SCROLL_EVENT_MOTION:
172 t->scroll.edge &= tp_touch_get_edge(tp, t);
173 if (!t->scroll.edge)
174 tp_edge_scroll_set_state(tp, t,
175 EDGE_SCROLL_TOUCH_STATE_AREA);
176 break;
177 case SCROLL_EVENT_RELEASE:
178 tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
179 break;
180 case SCROLL_EVENT_TIMEOUT:
181 case SCROLL_EVENT_POSTED:
182 tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_EDGE);
183 break;
184 }
185 }
186
187 static void
tp_edge_scroll_handle_edge(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event)188 tp_edge_scroll_handle_edge(struct tp_dispatch *tp,
189 struct tp_touch *t,
190 enum scroll_event event)
191 {
192 switch (event) {
193 case SCROLL_EVENT_TOUCH:
194 case SCROLL_EVENT_TIMEOUT:
195 evdev_log_bug_libinput(tp->device,
196 "edge-scroll: touch %d: unexpected scroll event %d in edge state\n",
197 t->index,
198 event);
199 break;
200 case SCROLL_EVENT_MOTION:
201 /* If started at the bottom right, decide in which dir to scroll */
202 if (t->scroll.edge == (EDGE_RIGHT | EDGE_BOTTOM)) {
203 t->scroll.edge &= tp_touch_get_edge(tp, t);
204 if (!t->scroll.edge)
205 tp_edge_scroll_set_state(tp, t,
206 EDGE_SCROLL_TOUCH_STATE_AREA);
207 }
208 break;
209 case SCROLL_EVENT_RELEASE:
210 tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
211 break;
212 case SCROLL_EVENT_POSTED:
213 break;
214 }
215 }
216
217 static void
tp_edge_scroll_handle_area(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event)218 tp_edge_scroll_handle_area(struct tp_dispatch *tp,
219 struct tp_touch *t,
220 enum scroll_event event)
221 {
222 switch (event) {
223 case SCROLL_EVENT_TOUCH:
224 case SCROLL_EVENT_TIMEOUT:
225 case SCROLL_EVENT_POSTED:
226 evdev_log_bug_libinput(tp->device,
227 "unexpected scroll event %d in area state\n",
228 event);
229 break;
230 case SCROLL_EVENT_MOTION:
231 break;
232 case SCROLL_EVENT_RELEASE:
233 tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
234 break;
235 }
236 }
237
238 static void
tp_edge_scroll_handle_event(struct tp_dispatch * tp,struct tp_touch * t,enum scroll_event event)239 tp_edge_scroll_handle_event(struct tp_dispatch *tp,
240 struct tp_touch *t,
241 enum scroll_event event)
242 {
243 enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
244
245 switch (current) {
246 case EDGE_SCROLL_TOUCH_STATE_NONE:
247 tp_edge_scroll_handle_none(tp, t, event);
248 break;
249 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
250 tp_edge_scroll_handle_edge_new(tp, t, event);
251 break;
252 case EDGE_SCROLL_TOUCH_STATE_EDGE:
253 tp_edge_scroll_handle_edge(tp, t, event);
254 break;
255 case EDGE_SCROLL_TOUCH_STATE_AREA:
256 tp_edge_scroll_handle_area(tp, t, event);
257 break;
258 }
259
260 if (current != t->scroll.edge_state)
261 evdev_log_debug(tp->device,
262 "edge-scroll: touch %d state %s → %s → %s\n",
263 t->index,
264 edge_state_to_str(current),
265 edge_event_to_str(event),
266 edge_state_to_str(t->scroll.edge_state));
267 }
268
269 static void
tp_edge_scroll_handle_timeout(uint64_t now,void * data)270 tp_edge_scroll_handle_timeout(uint64_t now, void *data)
271 {
272 struct tp_touch *t = data;
273
274 tp_edge_scroll_handle_event(t->tp, t, SCROLL_EVENT_TIMEOUT);
275 }
276
277 void
tp_edge_scroll_init(struct tp_dispatch * tp,struct evdev_device * device)278 tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
279 {
280 struct tp_touch *t;
281 double width, height;
282 bool want_horiz_scroll = true;
283 struct device_coords edges;
284 struct phys_coords mm = { 0.0, 0.0 };
285 int i;
286
287 evdev_device_get_size(device, &width, &height);
288 /* Touchpads smaller than 40mm are not tall enough to have a
289 horizontal scroll area, it takes too much space away. But
290 clickpads have enough space here anyway because of the
291 software button area (and all these tiny clickpads were built
292 when software buttons were a thing, e.g. Lenovo *20 series)
293 */
294 if (!tp->buttons.is_clickpad)
295 want_horiz_scroll = (height >= 40);
296
297 /* 7mm edge size */
298 mm.x = width - 7;
299 mm.y = height - 7;
300 edges = evdev_device_mm_to_units(device, &mm);
301
302 tp->scroll.right_edge = edges.x;
303 if (want_horiz_scroll)
304 tp->scroll.bottom_edge = edges.y;
305 else
306 tp->scroll.bottom_edge = INT_MAX;
307
308 i = 0;
309 tp_for_each_touch(tp, t) {
310 char timer_name[64];
311
312 snprintf(timer_name,
313 sizeof(timer_name),
314 "%s (%d) edgescroll",
315 evdev_device_get_sysname(device),
316 i);
317 t->scroll.direction = -1;
318 libinput_timer_init(&t->scroll.timer,
319 tp_libinput_context(tp),
320 timer_name,
321 tp_edge_scroll_handle_timeout, t);
322 }
323 }
324
325 void
tp_remove_edge_scroll(struct tp_dispatch * tp)326 tp_remove_edge_scroll(struct tp_dispatch *tp)
327 {
328 struct tp_touch *t;
329
330 tp_for_each_touch(tp, t) {
331 libinput_timer_cancel(&t->scroll.timer);
332 libinput_timer_destroy(&t->scroll.timer);
333 }
334 }
335
336 void
tp_edge_scroll_handle_state(struct tp_dispatch * tp,uint64_t time)337 tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
338 {
339 struct tp_touch *t;
340
341 if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE) {
342 tp_for_each_touch(tp, t) {
343 if (t->state == TOUCH_BEGIN)
344 t->scroll.edge_state =
345 EDGE_SCROLL_TOUCH_STATE_AREA;
346 else if (t->state == TOUCH_END)
347 t->scroll.edge_state =
348 EDGE_SCROLL_TOUCH_STATE_NONE;
349 }
350 return;
351 }
352
353 tp_for_each_touch(tp, t) {
354 if (!t->dirty)
355 continue;
356
357 switch (t->state) {
358 case TOUCH_NONE:
359 case TOUCH_HOVERING:
360 break;
361 case TOUCH_BEGIN:
362 tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_TOUCH);
363 break;
364 case TOUCH_UPDATE:
365 tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_MOTION);
366 break;
367 case TOUCH_MAYBE_END:
368 /* This shouldn't happen we transfer to TOUCH_END
369 * before processing state */
370 evdev_log_debug(tp->device,
371 "touch %d: unexpected state %d\n",
372 t->index,
373 t->state);
374 /* fallthrough */
375 case TOUCH_END:
376 tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_RELEASE);
377 break;
378 }
379 }
380 }
381
382 int
tp_edge_scroll_post_events(struct tp_dispatch * tp,uint64_t time)383 tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
384 {
385 struct evdev_device *device = tp->device;
386 struct tp_touch *t;
387 enum libinput_pointer_axis axis;
388 double *delta;
389 struct device_coords raw;
390 struct device_float_coords fraw;
391 struct normalized_coords normalized, tmp;
392 const struct normalized_coords zero = { 0.0, 0.0 };
393 const struct discrete_coords zero_discrete = { 0.0, 0.0 };
394
395 tp_for_each_touch(tp, t) {
396 if (!t->dirty)
397 continue;
398
399 if (t->palm.state != PALM_NONE || tp_thumb_ignored(tp, t))
400 continue;
401
402 /* only scroll with the finger in the previous edge */
403 if (t->scroll.edge &&
404 (tp_touch_get_edge(tp, t) & t->scroll.edge) == 0)
405 continue;
406
407 switch (t->scroll.edge) {
408 case EDGE_NONE:
409 if (t->scroll.direction != -1) {
410 /* Send stop scroll event */
411 evdev_notify_axis(device, time,
412 bit(t->scroll.direction),
413 LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
414 &zero,
415 &zero_discrete);
416 t->scroll.direction = -1;
417 }
418 continue;
419 case EDGE_RIGHT:
420 axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
421 delta = &normalized.y;
422 break;
423 case EDGE_BOTTOM:
424 axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
425 delta = &normalized.x;
426 break;
427 default: /* EDGE_RIGHT | EDGE_BOTTOM */
428 continue; /* Don't know direction yet, skip */
429 }
430
431 raw = tp_get_delta(t);
432 fraw.x = raw.x;
433 fraw.y = raw.y;
434 /* scroll is not accelerated */
435 normalized = tp_filter_motion_unaccelerated(tp, &fraw, time);
436
437 switch (t->scroll.edge_state) {
438 case EDGE_SCROLL_TOUCH_STATE_NONE:
439 case EDGE_SCROLL_TOUCH_STATE_AREA:
440 evdev_log_bug_libinput(device,
441 "unexpected scroll state %d\n",
442 t->scroll.edge_state);
443 break;
444 case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
445 tmp = normalized;
446 normalized = tp_normalize_delta(tp,
447 device_delta(t->point,
448 t->scroll.initial));
449 if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD)
450 normalized = zero;
451 else
452 normalized = tmp;
453 break;
454 case EDGE_SCROLL_TOUCH_STATE_EDGE:
455 break;
456 }
457
458 if (*delta == 0.0)
459 continue;
460
461 evdev_notify_axis(device, time,
462 bit(axis),
463 LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
464 &normalized,
465 &zero_discrete);
466 t->scroll.direction = axis;
467
468 tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED);
469 }
470
471 return 0; /* Edge touches are suppressed by edge_scroll_touch_active */
472 }
473
474 void
tp_edge_scroll_stop_events(struct tp_dispatch * tp,uint64_t time)475 tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
476 {
477 struct evdev_device *device = tp->device;
478 struct tp_touch *t;
479 const struct normalized_coords zero = { 0.0, 0.0 };
480 const struct discrete_coords zero_discrete = { 0.0, 0.0 };
481
482 tp_for_each_touch(tp, t) {
483 if (t->scroll.direction != -1) {
484 evdev_notify_axis(device, time,
485 bit(t->scroll.direction),
486 LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
487 &zero,
488 &zero_discrete);
489 t->scroll.direction = -1;
490 /* reset touch to area state, avoids loading the
491 * state machine with special case handling */
492 t->scroll.edge = EDGE_NONE;
493 t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_AREA;
494 }
495 }
496 }
497
498 int
tp_edge_scroll_touch_active(const struct tp_dispatch * tp,const struct tp_touch * t)499 tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
500 const struct tp_touch *t)
501 {
502 return t->scroll.edge_state == EDGE_SCROLL_TOUCH_STATE_AREA;
503 }
504