1/*
2 * Copyright © 2014 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 <check.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <libinput.h>
30#include <unistd.h>
31
32#include "libinput-util.h"
33#include "litest.h"
34
35static inline bool
36has_disable_while_trackpointing(struct litest_device *device)
37{
38	return libinput_device_config_dwtp_is_available(device->libinput_device);
39}
40
41START_TEST(trackpoint_middlebutton)
42{
43	struct litest_device *dev = litest_current_device();
44	struct libinput *li = dev->libinput;
45	struct libinput_event *event;
46	struct libinput_event_pointer *ptrev;
47	uint64_t ptime, rtime;
48
49	litest_drain_events(li);
50
51	/* A quick middle button click should get reported normally */
52	litest_button_click_debounced(dev, li, BTN_MIDDLE, 1);
53	msleep(2);
54	litest_button_click_debounced(dev, li, BTN_MIDDLE, 0);
55
56	litest_wait_for_event(li);
57
58	event = libinput_get_event(li);
59	ptrev = litest_is_button_event(event,
60				       BTN_MIDDLE,
61				       LIBINPUT_BUTTON_STATE_PRESSED);
62	ptime = libinput_event_pointer_get_time(ptrev);
63	libinput_event_destroy(event);
64
65	event = libinput_get_event(li);
66	ptrev = litest_is_button_event(event,
67				       BTN_MIDDLE,
68				       LIBINPUT_BUTTON_STATE_RELEASED);
69	rtime = libinput_event_pointer_get_time(ptrev);
70	libinput_event_destroy(event);
71
72	ck_assert_int_lt(ptime, rtime);
73
74	litest_assert_empty_queue(li);
75}
76END_TEST
77
78START_TEST(trackpoint_scroll)
79{
80	struct litest_device *dev = litest_current_device();
81	struct libinput *li = dev->libinput;
82
83	litest_drain_events(li);
84
85	litest_button_scroll(dev, BTN_MIDDLE, 1, 6);
86	litest_assert_scroll(li,
87			     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
88			     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
89			     6);
90	litest_button_scroll(dev, BTN_MIDDLE, 1, -7);
91	litest_assert_scroll(li,
92			     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
93			     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
94			     -7);
95	litest_button_scroll(dev, BTN_MIDDLE, 8, 1);
96	litest_assert_scroll(li,
97			     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
98			     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
99			     8);
100	litest_button_scroll(dev, BTN_MIDDLE, -9, 1);
101	litest_assert_scroll(li,
102			     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
103			     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
104			     -9);
105
106	/* scroll smaller than the threshold should not generate axis events */
107	litest_button_scroll(dev, BTN_MIDDLE, 1, 1);
108
109	litest_button_scroll(dev, BTN_MIDDLE, 0, 0);
110	litest_assert_button_event(li, BTN_MIDDLE,
111				   LIBINPUT_BUTTON_STATE_PRESSED);
112	litest_assert_button_event(li,
113				   BTN_MIDDLE,
114				   LIBINPUT_BUTTON_STATE_RELEASED);
115
116	litest_assert_empty_queue(li);
117}
118END_TEST
119
120START_TEST(trackpoint_middlebutton_noscroll)
121{
122	struct litest_device *dev = litest_current_device();
123	struct libinput *li = dev->libinput;
124	struct libinput_event *event;
125
126	/* Disable middle button scrolling */
127	libinput_device_config_scroll_set_method(dev->libinput_device,
128					LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
129
130	litest_drain_events(li);
131
132	/* A long middle button click + motion should get reported normally now */
133	litest_button_scroll(dev, BTN_MIDDLE, 0, 10);
134
135	litest_assert_button_event(li, BTN_MIDDLE, 1);
136
137	event = libinput_get_event(li);
138	ck_assert_notnull(event);
139	ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_POINTER_MOTION);
140	libinput_event_destroy(event);
141
142	litest_assert_button_event(li, BTN_MIDDLE, 0);
143
144	litest_assert_empty_queue(li);
145
146	/* Restore default scroll behavior */
147	libinput_device_config_scroll_set_method(dev->libinput_device,
148		libinput_device_config_scroll_get_default_method(
149			dev->libinput_device));
150}
151END_TEST
152
153START_TEST(trackpoint_scroll_source)
154{
155	struct litest_device *dev = litest_current_device();
156	struct libinput *li = dev->libinput;
157	struct libinput_event *event;
158	struct libinput_event_pointer *ptrev;
159
160	litest_drain_events(li);
161
162	litest_button_scroll(dev, BTN_MIDDLE, 0, 6);
163	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
164
165	while ((event = libinput_get_event(li))) {
166		ptrev = libinput_event_get_pointer_event(event);
167
168		ck_assert_int_eq(litest_event_pointer_get_axis_source(ptrev),
169				 LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
170
171		libinput_event_destroy(event);
172	}
173}
174END_TEST
175
176START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint)
177{
178	struct litest_device *touchpad = litest_current_device();
179	struct litest_device *trackpoint;
180	struct libinput *li = touchpad->libinput;
181	enum libinput_config_status status;
182	struct libinput_event *event;
183	struct libinput_device *device;
184
185	litest_disable_hold_gestures(touchpad->libinput_device);
186
187	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
188	litest_drain_events(li);
189	/* touchpad right-handed, trackpoint left-handed */
190	status = libinput_device_config_left_handed_set(
191					trackpoint->libinput_device, 1);
192	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
193
194	litest_touch_down(touchpad, 0, 5, 5);
195	libinput_dispatch(li);
196	litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
197	libinput_dispatch(li);
198
199	event = libinput_get_event(li);
200	litest_is_button_event(event,
201			       BTN_RIGHT,
202			       LIBINPUT_BUTTON_STATE_PRESSED);
203	device = libinput_event_get_device(event);
204	ck_assert(device == trackpoint->libinput_device);
205	libinput_event_destroy(event);
206
207	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
208	libinput_dispatch(li);
209	event = libinput_get_event(li);
210	litest_is_button_event(event,
211			       BTN_RIGHT,
212			       LIBINPUT_BUTTON_STATE_RELEASED);
213	device = libinput_event_get_device(event);
214	ck_assert(device == trackpoint->libinput_device);
215	libinput_event_destroy(event);
216
217	litest_delete_device(trackpoint);
218}
219END_TEST
220
221START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad)
222{
223	struct litest_device *touchpad = litest_current_device();
224	struct litest_device *trackpoint;
225	struct libinput *li = touchpad->libinput;
226	enum libinput_config_status status;
227	struct libinput_event *event;
228	struct libinput_device *device;
229
230	litest_disable_hold_gestures(touchpad->libinput_device);
231
232	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
233	litest_drain_events(li);
234	/* touchpad left-handed, trackpoint right-handed */
235	status = libinput_device_config_left_handed_set(
236					touchpad->libinput_device, 1);
237	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
238
239	litest_touch_down(touchpad, 0, 5, 5);
240	libinput_dispatch(li);
241	litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
242	libinput_dispatch(li);
243
244	event = libinput_get_event(li);
245	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
246	device = libinput_event_get_device(event);
247	ck_assert(device == trackpoint->libinput_device);
248	libinput_event_destroy(event);
249
250	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
251	libinput_dispatch(li);
252	event = libinput_get_event(li);
253	litest_is_button_event(event,
254			       BTN_LEFT,
255			       LIBINPUT_BUTTON_STATE_RELEASED);
256	device = libinput_event_get_device(event);
257	ck_assert(device == trackpoint->libinput_device);
258	libinput_event_destroy(event);
259
260	litest_delete_device(trackpoint);
261}
262END_TEST
263
264START_TEST(trackpoint_topsoftbuttons_left_handed_both)
265{
266	struct litest_device *touchpad = litest_current_device();
267	struct litest_device *trackpoint;
268	struct libinput *li = touchpad->libinput;
269	enum libinput_config_status status;
270	struct libinput_event *event;
271	struct libinput_device *device;
272
273	litest_disable_hold_gestures(touchpad->libinput_device);
274
275	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
276	litest_drain_events(li);
277	/* touchpad left-handed, trackpoint left-handed */
278	status = libinput_device_config_left_handed_set(
279					touchpad->libinput_device, 1);
280	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
281	status = libinput_device_config_left_handed_set(
282					trackpoint->libinput_device, 1);
283	ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
284
285	litest_touch_down(touchpad, 0, 5, 5);
286	libinput_dispatch(li);
287	litest_button_click_debounced(touchpad, li, BTN_LEFT, true);
288	libinput_dispatch(li);
289
290	event = libinput_get_event(li);
291	litest_is_button_event(event,
292			       BTN_RIGHT,
293			       LIBINPUT_BUTTON_STATE_PRESSED);
294	device = libinput_event_get_device(event);
295	ck_assert(device == trackpoint->libinput_device);
296	libinput_event_destroy(event);
297
298	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
299	libinput_dispatch(li);
300	event = libinput_get_event(li);
301	litest_is_button_event(event,
302			       BTN_RIGHT,
303			       LIBINPUT_BUTTON_STATE_RELEASED);
304	device = libinput_event_get_device(event);
305	ck_assert(device == trackpoint->libinput_device);
306	libinput_event_destroy(event);
307
308	litest_delete_device(trackpoint);
309}
310END_TEST
311
312static inline void
313enable_dwtp(struct litest_device *dev)
314{
315	enum libinput_config_status status,
316				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
317	status = libinput_device_config_dwtp_set_enabled(dev->libinput_device,
318						LIBINPUT_CONFIG_DWTP_ENABLED);
319	litest_assert_int_eq(status, expected);
320}
321
322static inline void
323disable_dwtp(struct litest_device *dev)
324{
325	enum libinput_config_status status,
326				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
327	status = libinput_device_config_dwtp_set_enabled(dev->libinput_device,
328						LIBINPUT_CONFIG_DWTP_DISABLED);
329	litest_assert_int_eq(status, expected);
330}
331
332
333START_TEST(trackpoint_palmdetect)
334{
335	struct litest_device *trackpoint = litest_current_device();
336	struct litest_device *touchpad;
337	struct libinput *li = trackpoint->libinput;
338	int i;
339
340	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
341	if (has_disable_while_trackpointing(touchpad))
342		enable_dwtp(touchpad);
343
344	litest_disable_hold_gestures(touchpad->libinput_device);
345	litest_drain_events(li);
346
347	for (i = 0; i < 10; i++) {
348		litest_event(trackpoint, EV_REL, REL_X, 1);
349		litest_event(trackpoint, EV_REL, REL_Y, 1);
350		litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
351		libinput_dispatch(li);
352	}
353	litest_drain_events(li);
354
355	litest_touch_down(touchpad, 0, 30, 30);
356	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
357	litest_touch_up(touchpad, 0);
358	litest_assert_empty_queue(li);
359
360	litest_timeout_trackpoint();
361	libinput_dispatch(li);
362
363	litest_touch_down(touchpad, 0, 30, 30);
364	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
365	litest_touch_up(touchpad, 0);
366	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
367
368	litest_delete_device(touchpad);
369}
370END_TEST
371
372START_TEST(trackpoint_palmdetect_dwtp_disabled)
373{
374	struct litest_device *trackpoint = litest_current_device();
375	struct litest_device *touchpad;
376	struct libinput *li = trackpoint->libinput;
377	int i;
378
379	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
380	if (has_disable_while_trackpointing(touchpad))
381		disable_dwtp(touchpad);
382
383	litest_disable_hold_gestures(touchpad->libinput_device);
384	litest_drain_events(li);
385
386	for (i = 0; i < 10; i++) {
387		litest_event(trackpoint, EV_REL, REL_X, 1);
388		litest_event(trackpoint, EV_REL, REL_Y, 1);
389		litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
390		libinput_dispatch(li);
391	}
392	litest_drain_events(li);
393
394	litest_touch_down(touchpad, 0, 30, 30);
395	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
396	litest_touch_up(touchpad, 0);
397	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
398
399	litest_delete_device(touchpad);
400}
401END_TEST
402
403START_TEST(trackpoint_palmdetect_resume_touch)
404{
405	struct litest_device *trackpoint = litest_current_device();
406	struct litest_device *touchpad;
407	struct libinput *li = trackpoint->libinput;
408	int i;
409
410	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
411
412	if (has_disable_while_trackpointing(touchpad))
413		enable_dwtp(touchpad);
414
415	litest_disable_hold_gestures(touchpad->libinput_device);
416	litest_drain_events(li);
417
418	for (i = 0; i < 10; i++) {
419		litest_event(trackpoint, EV_REL, REL_X, 1);
420		litest_event(trackpoint, EV_REL, REL_Y, 1);
421		litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
422		libinput_dispatch(li);
423	}
424	litest_drain_events(li);
425
426	litest_touch_down(touchpad, 0, 30, 30);
427	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
428	litest_assert_empty_queue(li);
429
430	litest_timeout_trackpoint();
431	libinput_dispatch(li);
432
433	/* touch started after last tp event, expect resume */
434	litest_touch_move_to(touchpad, 0, 80, 80, 30, 30, 10);
435	litest_touch_up(touchpad, 0);
436	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
437
438	litest_delete_device(touchpad);
439}
440END_TEST
441
442START_TEST(trackpoint_palmdetect_require_min_events)
443{
444	struct litest_device *trackpoint = litest_current_device();
445	struct litest_device *touchpad;
446	struct libinput *li = trackpoint->libinput;
447
448	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
449
450	if (has_disable_while_trackpointing(touchpad))
451		enable_dwtp(touchpad);
452
453	litest_disable_hold_gestures(touchpad->libinput_device);
454	litest_drain_events(li);
455
456	/* A single event does not trigger palm detection */
457	litest_event(trackpoint, EV_REL, REL_X, 1);
458	litest_event(trackpoint, EV_REL, REL_Y, 1);
459	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
460	libinput_dispatch(li);
461	litest_drain_events(li);
462
463	litest_touch_down(touchpad, 0, 30, 30);
464	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
465	litest_touch_up(touchpad, 0);
466	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
467
468	litest_delete_device(touchpad);
469}
470END_TEST
471
472START_TEST(trackpoint_palmdetect_require_min_events_timeout)
473{
474	struct litest_device *trackpoint = litest_current_device();
475	struct litest_device *touchpad;
476	struct libinput *li = trackpoint->libinput;
477
478	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
479
480	if (has_disable_while_trackpointing(touchpad))
481		enable_dwtp(touchpad);
482
483	litest_disable_hold_gestures(touchpad->libinput_device);
484	litest_drain_events(li);
485
486	for (int i = 0; i < 10; i++) {
487		/* A single event does not trigger palm detection */
488		litest_event(trackpoint, EV_REL, REL_X, 1);
489		litest_event(trackpoint, EV_REL, REL_Y, 1);
490		litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
491		libinput_dispatch(li);
492		litest_drain_events(li);
493
494		litest_touch_down(touchpad, 0, 30, 30);
495		litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
496		litest_touch_up(touchpad, 0);
497		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
498
499		litest_timeout_trackpoint();
500	}
501
502	litest_delete_device(touchpad);
503}
504END_TEST
505
506TEST_COLLECTION(trackpoint)
507{
508	litest_add(trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY);
509	litest_add(trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY);
510	litest_add(trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY);
511	litest_add(trackpoint_scroll_source, LITEST_POINTINGSTICK, LITEST_ANY);
512	litest_add(trackpoint_topsoftbuttons_left_handed_trackpoint, LITEST_TOPBUTTONPAD, LITEST_ANY);
513	litest_add(trackpoint_topsoftbuttons_left_handed_touchpad, LITEST_TOPBUTTONPAD, LITEST_ANY);
514	litest_add(trackpoint_topsoftbuttons_left_handed_both, LITEST_TOPBUTTONPAD, LITEST_ANY);
515
516	litest_add(trackpoint_palmdetect, LITEST_POINTINGSTICK, LITEST_ANY);
517	litest_add(trackpoint_palmdetect_dwtp_disabled, LITEST_POINTINGSTICK, LITEST_ANY);
518	litest_add(trackpoint_palmdetect_resume_touch, LITEST_POINTINGSTICK, LITEST_ANY);
519	litest_add(trackpoint_palmdetect_require_min_events, LITEST_POINTINGSTICK, LITEST_ANY);
520	litest_add(trackpoint_palmdetect_require_min_events_timeout, LITEST_POINTINGSTICK, LITEST_ANY);
521}
522