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
38enum scroll_event {
39	SCROLL_EVENT_TOUCH,
40	SCROLL_EVENT_MOTION,
41	SCROLL_EVENT_RELEASE,
42	SCROLL_EVENT_TIMEOUT,
43	SCROLL_EVENT_POSTED,
44};
45
46static inline const char*
47edge_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
59static inline const char*
60edge_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
72uint32_t
73tp_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
89static inline void
90tp_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
107static void
108tp_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
134static void
135tp_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
166static void
167tp_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
203static void
204tp_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
239static void
240tp_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
264static void
265tp_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
296static void
297tp_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
304void
305tp_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
352void
353tp_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
363void
364tp_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
418int
419tp_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
506void
507tp_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
528int
529tp_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