• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <config.h>
24 
25 #include <linux/input.h>
26 
27 #include <assert.h>
28 #include <cairo.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <getopt.h>
32 #include <math.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <gtk/gtk.h>
40 #include <glib.h>
41 #include <glib-unix.h>
42 #include <libevdev/libevdev.h>
43 
44 #include <libinput.h>
45 #include "util-strings.h"
46 #include "util-macros.h"
47 #include "util-list.h"
48 
49 #include "shared.h"
50 
51 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
52 
53 enum touch_state {
54 	TOUCH_ACTIVE,
55 	TOUCH_ENDED,
56 	TOUCH_CANCELLED,
57 };
58 
59 struct touch {
60 	enum touch_state state;
61 	int x, y;
62 };
63 
64 struct point {
65 	double x, y;
66 };
67 
68 struct device_user_data {
69 	struct point scroll_accumulated;
70 };
71 
72 struct evdev_device {
73 	struct list node;
74 	struct libevdev *evdev;
75 	struct libinput_device *libinput_device;
76 	int fd;
77 	guint source_id;
78 };
79 
80 struct window {
81 	bool grab;
82 	struct tools_options options;
83 	struct list evdev_devices;
84 
85 	GtkWidget *win;
86 	GtkWidget *area;
87 	int width, height; /* of window */
88 
89 	/* sprite position */
90 	double x, y;
91 
92 	/* these are for the delta coordinates, but they're not
93 	 * deltas, they are converted into abs positions */
94 	size_t ndeltas;
95 	struct point deltas[64];
96 
97 	/* abs position */
98 	int absx, absy;
99 
100 	/* scroll bar positions */
101 	struct {
102 		double vx, vy;
103 		double hx, hy;
104 
105 		double vx_discrete, vy_discrete;
106 		double hx_discrete, hy_discrete;
107 	} scroll;
108 
109 	/* touch positions */
110 	struct touch touches[32];
111 
112 	/* l/m/r mouse buttons */
113 	struct {
114 		bool l, m, r;
115 		bool other;
116 		const char *other_name;
117 	} buttons;
118 
119 	/* touchpad swipe */
120 	struct {
121 		int nfingers;
122 		double x, y;
123 	} swipe;
124 
125 	struct {
126 		int nfingers;
127 		double scale;
128 		double angle;
129 		double x, y;
130 	} pinch;
131 
132 	struct {
133 		double x, y;
134 		double x_in, y_in;
135 		double x_down, y_down;
136 		double x_up, y_up;
137 		double pressure;
138 		double distance;
139 		double tilt_x, tilt_y;
140 		double rotation;
141 		double size_major, size_minor;
142 		bool is_down;
143 
144 		/* these are for the delta coordinates, but they're not
145 		 * deltas, they are converted into abs positions */
146 		size_t ndeltas;
147 		struct point deltas[64];
148 	} tool;
149 
150 	struct {
151 		struct {
152 			double position;
153 			int number;
154 		} ring;
155 		struct {
156 			double position;
157 			int number;
158 		} strip;
159 	} pad;
160 
161 	struct {
162 		int rel_x, rel_y; /* REL_X/Y */
163 		int x, y;	  /* ABS_X/Y */
164 		struct {
165 			int x, y; /* ABS_MT_POSITION_X/Y */
166 			bool active;
167 		} slots[16];
168 		unsigned int slot; /* ABS_MT_SLOT */
169 		/* So we know when to re-fetch the abs axes */
170 		uintptr_t device, last_device;
171 	} evdev;
172 
173 	struct libinput_device *devices[50];
174 };
175 
176 LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
177 static inline void
msg(const char * fmt,...)178 msg(const char *fmt, ...)
179 {
180 	va_list args;
181 	printf("info: ");
182 
183 	va_start(args, fmt);
184 	vprintf(fmt, args);
185 	va_end(args);
186 }
187 
188 static inline void
draw_evdev_rel(struct window * w,cairo_t * cr)189 draw_evdev_rel(struct window *w, cairo_t *cr)
190 {
191 	int center_x, center_y;
192 
193 	cairo_save(cr);
194 	cairo_set_source_rgb(cr, .2, .2, .8);
195 	center_x = w->width/2 - 400;
196 	center_y = w->height/2;
197 
198 	cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
199 	cairo_stroke(cr);
200 
201 	if (w->evdev.rel_x) {
202 		int dir = w->evdev.rel_x > 0 ? 1 : -1;
203 		for (int i = 0; i < abs(w->evdev.rel_x); i++) {
204 			cairo_move_to(cr,
205 				      center_x + (i + 1) * 20 * dir,
206 				      center_y - 20);
207 			cairo_rel_line_to(cr, 0, 40);
208 			cairo_rel_line_to(cr, 20 * dir, -20);
209 			cairo_rel_line_to(cr, -20 * dir, -20);
210 			cairo_fill(cr);
211 		}
212         }
213 
214 	if (w->evdev.rel_y) {
215 		int dir = w->evdev.rel_y > 0 ? 1 : -1;
216 		for (int i = 0; i < abs(w->evdev.rel_y); i++) {
217 			cairo_move_to(cr,
218 				      center_x - 20,
219 				      center_y + (i + 1) * 20 * dir);
220 			cairo_rel_line_to(cr, 40, 0);
221 			cairo_rel_line_to(cr, -20, 20 * dir);
222 			cairo_rel_line_to(cr, -20, -20 * dir);
223 			cairo_fill(cr);
224 		}
225         }
226 
227 	cairo_restore(cr);
228 }
229 
230 static inline void
draw_evdev_abs(struct window * w,cairo_t * cr)231 draw_evdev_abs(struct window *w, cairo_t *cr)
232 {
233 	static const struct input_absinfo *ax = NULL, *ay = NULL;
234 	const int normalized_width = 200;
235 	int outline_width = normalized_width,
236 	    outline_height = normalized_width * 0.75;
237 	int center_x, center_y;
238 	int width, height;
239 	int x, y;
240 
241 	cairo_save(cr);
242 	cairo_set_source_rgb(cr, .2, .2, .8);
243 
244 	center_x = w->width/2 + 400;
245 	center_y = w->height/2;
246 
247 	/* Always the outline even if we didn't get any abs events yet so it
248 	 * doesn't just appear out of nowhere */
249 	if (w->evdev.device == 0)
250 		goto draw_outline;
251 
252 	/* device has changed, so the abs proportions/dimensions have
253 	 * changed. */
254 	if (w->evdev.device != w->evdev.last_device) {
255 		struct evdev_device *d;
256 
257 		ax = NULL;
258 		ay = NULL;
259 
260 		list_for_each(d, &w->evdev_devices, node) {
261 			if ((uintptr_t)d->libinput_device != w->evdev.device)
262 				continue;
263 
264 			ax = libevdev_get_abs_info(d->evdev, ABS_X);
265 			ay = libevdev_get_abs_info(d->evdev, ABS_Y);
266 			w->evdev.last_device = w->evdev.device;
267 		}
268 
269 	}
270 	if (ax == NULL || ay == NULL)
271 		goto draw_outline;
272 
273 	width = ax->maximum - ax->minimum;
274 	height = ay->maximum - ay->minimum;
275 	outline_height = 1.0 * height/width * normalized_width;
276 	outline_width = normalized_width;
277 
278 	x = 1.0 * (w->evdev.x - ax->minimum)/width * outline_width;
279 	y = 1.0 * (w->evdev.y - ay->minimum)/height * outline_height;
280 	x += center_x - outline_width/2;
281 	y += center_y - outline_height/2;
282 	cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
283 	cairo_fill(cr);
284 
285 	for (size_t i = 0; i < ARRAY_LENGTH(w->evdev.slots); i++) {
286 		if (!w->evdev.slots[i].active)
287 			continue;
288 
289 		x = w->evdev.slots[i].x;
290 		y = w->evdev.slots[i].y;
291 		x = 1.0 * (x - ax->minimum)/width * outline_width;
292 		y = 1.0 * (y - ay->minimum)/height * outline_height;
293 		x += center_x - outline_width/2;
294 		y += center_y - outline_height/2;
295 		cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
296 		cairo_fill(cr);
297 	}
298 
299 draw_outline:
300 	/* The touchpad outline */
301 	cairo_rectangle(cr,
302 			center_x - outline_width/2,
303 			center_y - outline_height/2,
304 			outline_width,
305 			outline_height);
306 	cairo_stroke(cr);
307 	cairo_restore(cr);
308 }
309 
310 static inline void
draw_gestures(struct window * w,cairo_t * cr)311 draw_gestures(struct window *w, cairo_t *cr)
312 {
313 	int i;
314 	int offset;
315 
316 	/* swipe */
317 	cairo_save(cr);
318 	cairo_translate(cr, w->swipe.x, w->swipe.y);
319 	for (i = 0; i < w->swipe.nfingers; i++) {
320 		cairo_set_source_rgb(cr, .8, .8, .4);
321 		cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
322 		cairo_fill(cr);
323 	}
324 
325 	for (i = 0; i < 4; i++) { /* 4 fg max */
326 		cairo_set_source_rgb(cr, 0, 0, 0);
327 		cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
328 		cairo_stroke(cr);
329 	}
330 	cairo_restore(cr);
331 
332 	/* pinch */
333 	cairo_save(cr);
334 	offset = w->pinch.scale * 100;
335 	cairo_translate(cr, w->pinch.x, w->pinch.y);
336 	cairo_rotate(cr, w->pinch.angle * M_PI/180.0);
337 	if (w->pinch.nfingers > 0) {
338 		cairo_set_source_rgb(cr, .4, .4, .8);
339 		cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
340 		cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
341 		cairo_fill(cr);
342 	}
343 
344 	cairo_set_source_rgb(cr, 0, 0, 0);
345 	cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
346 	cairo_stroke(cr);
347 	cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
348 	cairo_stroke(cr);
349 
350 	cairo_restore(cr);
351 }
352 
353 static inline void
draw_scrollbars(struct window * w,cairo_t * cr)354 draw_scrollbars(struct window *w, cairo_t *cr)
355 {
356 
357 	/* normal scrollbars */
358 	cairo_save(cr);
359 	cairo_set_source_rgb(cr, .4, .8, 0);
360 	cairo_rectangle(cr, w->scroll.vx - 10, w->scroll.vy - 20, 20, 40);
361 	cairo_rectangle(cr, w->scroll.hx - 20, w->scroll.hy - 10, 40, 20);
362 	cairo_fill(cr);
363 
364 	/* discrete scrollbars */
365 	cairo_set_source_rgb(cr, .8, .4, 0);
366 	cairo_rectangle(cr, w->scroll.vx_discrete - 5, w->scroll.vy_discrete - 10, 10, 20);
367 	cairo_rectangle(cr, w->scroll.hx_discrete - 10, w->scroll.hy_discrete - 5, 20, 10);
368 	cairo_fill(cr);
369 
370 	cairo_restore(cr);
371 }
372 
373 static inline void
draw_touchpoints(struct window * w,cairo_t * cr)374 draw_touchpoints(struct window *w, cairo_t *cr)
375 {
376 	struct touch *t;
377 
378 	cairo_save(cr);
379 	ARRAY_FOR_EACH(w->touches, t) {
380 		if (t->state == TOUCH_ACTIVE)
381 			cairo_set_source_rgb(cr, .8, .2, .2);
382 		else
383 			cairo_set_source_rgb(cr, .8, .4, .4);
384 		cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
385 		if (t->state == TOUCH_CANCELLED)
386 			cairo_stroke(cr);
387 		else
388 			cairo_fill(cr);
389 	}
390 	cairo_restore(cr);
391 }
392 
393 static inline void
draw_abs_pointer(struct window * w,cairo_t * cr)394 draw_abs_pointer(struct window *w, cairo_t *cr)
395 {
396 
397 	cairo_save(cr);
398 	cairo_set_source_rgb(cr, .2, .4, .8);
399 	cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI);
400 	cairo_fill(cr);
401 	cairo_restore(cr);
402 }
403 
404 static inline void
draw_text(cairo_t * cr,const char * text,double x,double y)405 draw_text(cairo_t *cr, const char *text, double x, double y)
406 {
407 	cairo_text_extents_t te;
408 	cairo_font_extents_t fe;
409 
410 	cairo_text_extents(cr, text, &te);
411 	cairo_font_extents(cr, &fe);
412 	/* center of the rectangle */
413 	cairo_move_to(cr, x, y);
414 	cairo_rel_move_to(cr, -te.width/2, -fe.descent + te.height/2);
415 	cairo_show_text(cr, text);
416 }
417 
418 static inline void
draw_other_button(struct window * w,cairo_t * cr)419 draw_other_button (struct window *w, cairo_t *cr)
420 {
421 	const char *name = w->buttons.other_name;
422 
423 	cairo_save(cr);
424 
425 	if (!w->buttons.other)
426 		goto outline;
427 
428 	if (!name)
429 		name = "undefined";
430 
431 	cairo_set_source_rgb(cr, .2, .8, .8);
432 	cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
433 	cairo_fill(cr);
434 
435 	cairo_set_source_rgb(cr, 0, 0, 0);
436 
437 	draw_text(cr, name, w->width/2, w->height - 150 + 15);
438 
439 outline:
440 	cairo_set_source_rgb(cr, 0, 0, 0);
441 	cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
442 	cairo_stroke(cr);
443 	cairo_restore(cr);
444 }
445 
446 static inline void
draw_buttons(struct window * w,cairo_t * cr)447 draw_buttons(struct window *w, cairo_t *cr)
448 {
449 	cairo_save(cr);
450 
451 	if (w->buttons.l || w->buttons.m || w->buttons.r) {
452 		cairo_set_source_rgb(cr, .2, .8, .8);
453 		if (w->buttons.l)
454 			cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
455 		if (w->buttons.m)
456 			cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
457 		if (w->buttons.r)
458 			cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
459 		cairo_fill(cr);
460 	}
461 
462 	cairo_set_source_rgb(cr, 0, 0, 0);
463 	cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
464 	cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
465 	cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
466 	cairo_stroke(cr);
467 	cairo_restore(cr);
468 
469 	draw_other_button(w, cr);
470 }
471 
472 static inline void
draw_pad(struct window * w,cairo_t * cr)473 draw_pad(struct window *w, cairo_t *cr)
474 {
475 	double rx, ry;
476 	double pos;
477 	char number[3];
478 
479 	rx = w->width/2 - 200;
480 	ry = w->height/2 + 100;
481 
482 	cairo_save(cr);
483 	/* outer ring */
484 	cairo_set_source_rgb(cr, .7, .7, .0);
485 	cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
486 	cairo_fill(cr);
487 
488 	/* inner ring */
489 	cairo_set_source_rgb(cr, 1., 1., 1.);
490 	cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
491 	cairo_fill(cr);
492 
493 	/* marker */
494 	/* libinput has degrees and 0 is north, cairo has radians and 0 is
495 	 * east */
496 	if (w->pad.ring.position != -1) {
497 		pos = (w->pad.ring.position + 270) * M_PI/180.0;
498 		cairo_set_source_rgb(cr, .0, .0, .0);
499 		cairo_set_line_width(cr, 20);
500 		cairo_arc(cr, rx, ry, 40, pos - M_PI/8 , pos + M_PI/8);
501 		cairo_stroke(cr);
502 
503 		snprintf(number, sizeof(number), "%d", w->pad.ring.number);
504 		cairo_set_source_rgb(cr, .0, .0, .0);
505 		draw_text(cr, number, rx, ry);
506 
507 	}
508 
509 	cairo_restore(cr);
510 
511 	rx = w->width/2 - 300;
512 	ry = w->height/2 + 50;
513 
514 	cairo_save(cr);
515 	cairo_set_source_rgb(cr, .7, .7, .0);
516 	cairo_rectangle(cr, rx, ry, 20, 100);
517 	cairo_fill(cr);
518 
519 	if (w->pad.strip.position != -1) {
520 		pos = w->pad.strip.position * 80;
521 		cairo_set_source_rgb(cr, .0, .0, .0);
522 		cairo_rectangle(cr, rx, ry + pos, 20, 20);
523 		cairo_fill(cr);
524 
525 		snprintf(number, sizeof(number), "%d", w->pad.strip.number);
526 		cairo_set_source_rgb(cr, .0, .0, .0);
527 		draw_text(cr, number, rx + 10, ry - 10);
528 	}
529 
530 	cairo_restore(cr);
531 }
532 
533 static inline void
draw_tablet(struct window * w,cairo_t * cr)534 draw_tablet(struct window *w, cairo_t *cr)
535 {
536 	double x, y;
537 	int first, last;
538 	size_t mask;
539 	int rx, ry;
540 
541 	/* pressure/distance bars */
542 	rx = w->width/2 + 100;
543 	ry = w->height/2 + 50;
544 	cairo_save(cr);
545 	cairo_set_source_rgb(cr, .2, .6, .6);
546 	cairo_rectangle(cr, rx, ry, 20, 100);
547 	cairo_stroke(cr);
548 
549 	if (w->tool.distance > 0) {
550 		double pos = w->tool.distance * 100;
551 		cairo_rectangle(cr, rx, ry + 100 - pos, 20, 5);
552 		cairo_fill(cr);
553 	}
554 	if (w->tool.pressure > 0) {
555 		double pos = w->tool.pressure * 100;
556 		if (w->tool.is_down)
557 			cairo_rectangle(cr, rx + 25, ry + 95, 5, 5);
558 		cairo_rectangle(cr, rx, ry + 100 - pos, 20, pos);
559 		cairo_fill(cr);
560 	}
561 	cairo_restore(cr);
562 
563 
564 	/* tablet tool, square for prox-in location */
565 	cairo_save(cr);
566 	cairo_set_source_rgb(cr, .2, .6, .6);
567 	if (w->tool.x_in && w->tool.y_in) {
568 		cairo_rectangle(cr, w->tool.x_in - 15, w->tool.y_in - 15, 30, 30);
569 		cairo_stroke(cr);
570 	}
571 
572 	if (w->tool.x_down && w->tool.y_down) {
573 		cairo_rectangle(cr, w->tool.x_down - 10, w->tool.y_down - 10, 20, 20);
574 		cairo_stroke(cr);
575 	}
576 
577 	if (w->tool.x_up && w->tool.y_up) {
578 		cairo_rectangle(cr, w->tool.x_up - 10, w->tool.y_up - 10, 20, 20);
579 		cairo_stroke(cr);
580 	}
581 
582 	if (w->tool.pressure)
583 		cairo_set_source_rgb(cr, .2, .8, .8);
584 
585 	cairo_translate(cr, w->tool.x, w->tool.y);
586 	/* scale of 2.5 is large enough to make the marker visible around the
587 	   physical totem */
588 	cairo_scale(cr,
589 		    1.0 + w->tool.size_major * 2.5,
590 		    1.0 + w->tool.size_minor * 2.5);
591 	cairo_scale(cr, 1.0 + w->tool.tilt_x/30.0, 1.0 + w->tool.tilt_y/30.0);
592 	if (w->tool.rotation)
593 		cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
594 	if (w->tool.pressure)
595 		cairo_set_source_rgb(cr, .8, .8, .2);
596 	cairo_arc(cr, 0, 0,
597 		  1 + 10 * max(w->tool.pressure, w->tool.distance),
598 		  0, 2 * M_PI);
599 	cairo_fill(cr);
600 	cairo_restore(cr);
601 
602 	/* The line to indicate the origin */
603 	if (w->tool.size_major) {
604 		cairo_save(cr);
605 		cairo_scale(cr, 1.0, 1.0);
606 		cairo_translate(cr, w->tool.x, w->tool.y);
607 		if (w->tool.rotation)
608 			cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
609 		cairo_set_source_rgb(cr, .0, .0, .0);
610 		cairo_move_to(cr, 0, 0);
611 		cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
612 		cairo_stroke(cr);
613 		cairo_restore(cr);
614 	}
615 
616 	/* tablet deltas */
617 	mask = ARRAY_LENGTH(w->tool.deltas);
618 	first = max(w->tool.ndeltas + 1, mask) - mask;
619 	last = w->tool.ndeltas;
620 
621 	cairo_save(cr);
622 	cairo_set_source_rgb(cr, .8, .8, .2);
623 
624 	x = w->tool.deltas[first % mask].x;
625 	y = w->tool.deltas[first % mask].y;
626 	cairo_move_to(cr, x, y);
627 
628 	for (int i = first + 1; i < last; i++) {
629 		x = w->tool.deltas[i % mask].x;
630 		y = w->tool.deltas[i % mask].y;
631 		cairo_line_to(cr, x, y);
632 	}
633 
634 	cairo_stroke(cr);
635 	cairo_restore(cr);
636 }
637 
638 static inline void
draw_pointer(struct window * w,cairo_t * cr)639 draw_pointer(struct window *w, cairo_t *cr)
640 {
641 	double x, y;
642 	int first, last;
643 	size_t mask;
644 
645 	/* draw pointer sprite */
646 	cairo_set_source_rgb(cr, 0, 0, 0);
647 	cairo_save(cr);
648 	cairo_move_to(cr, w->x, w->y);
649 	cairo_rel_line_to(cr, 10, 15);
650 	cairo_rel_line_to(cr, -10, 0);
651 	cairo_rel_line_to(cr, 0, -15);
652 	cairo_fill(cr);
653 
654 	/* pointer deltas */
655 	mask = ARRAY_LENGTH(w->deltas);
656 	first = max(w->ndeltas + 1, mask) - mask;
657 	last = w->ndeltas;
658 
659 	cairo_set_source_rgb(cr, .8, .5, .2);
660 
661 	x = w->deltas[first % mask].x;
662 	y = w->deltas[first % mask].y;
663 	cairo_move_to(cr, x, y);
664 
665 	for (int i = first + 1; i < last; i++) {
666 		x = w->deltas[i % mask].x;
667 		y = w->deltas[i % mask].y;
668 		cairo_line_to(cr, x, y);
669 	}
670 
671 	cairo_stroke(cr);
672 	cairo_restore(cr);
673 }
674 
675 static inline void
draw_background(struct window * w,cairo_t * cr)676 draw_background(struct window *w, cairo_t *cr)
677 {
678 	int x1, x2, y1, y2, x3, y3, x4, y4;
679 	int cols;
680 
681 	/* 10px and 5px grids */
682 	cairo_save(cr);
683 	cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
684 	x1 = w->width/2 - 200;
685 	y1 = w->height/2 - 200;
686 	x2 = w->width/2 + 200;
687 	y2 = w->height/2 - 200;
688 	for (cols = 1; cols < 10; cols++) {
689 		cairo_move_to(cr, x1 + 10 * cols, y1);
690 		cairo_rel_line_to(cr, 0, 100);
691 		cairo_move_to(cr, x1, y1 + 10 * cols);
692 		cairo_rel_line_to(cr, 100, 0);
693 
694 		cairo_move_to(cr, x2 + 5 * cols, y2);
695 		cairo_rel_line_to(cr, 0, 50);
696 		cairo_move_to(cr, x2, y2 + 5 * cols);
697 		cairo_rel_line_to(cr, 50, 0);
698 	}
699 
700 	/* 3px horiz/vert bar codes */
701 	x3 = w->width/2 - 200;
702 	y3 = w->height/2 + 200;
703 	x4 = w->width/2 + 200;
704 	y4 = w->height/2 + 100;
705 	for (cols = 0; cols < 50; cols++) {
706 		cairo_move_to(cr, x3 + 3 * cols, y3);
707 		cairo_rel_line_to(cr, 0, 20);
708 
709 		cairo_move_to(cr, x4, y4 + 3 * cols);
710 		cairo_rel_line_to(cr, 20, 0);
711 	}
712 	cairo_stroke(cr);
713 
714 	/* round targets */
715 	for (int i = 0; i <= 3; i++) {
716 		x1 = w->width * i/4.0;
717 		x2 = w->width * i/4.0;
718 
719 		y1 = w->height * 1.0/4.0;
720 		y2 = w->height * 3.0/4.0;
721 
722 		cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
723 		cairo_stroke(cr);
724 		cairo_arc(cr, x2, y2, 10, 0, 2 * M_PI);
725 		cairo_stroke(cr);
726 	}
727 
728 	cairo_restore(cr);
729 }
730 
731 static gboolean
draw(GtkWidget * widget,cairo_t * cr,gpointer data)732 draw(GtkWidget *widget, cairo_t *cr, gpointer data)
733 {
734 	struct window *w = data;
735 
736 	cairo_set_source_rgb(cr, 1, 1, 1);
737 	cairo_rectangle(cr, 0, 0, w->width, w->height);
738 	cairo_fill(cr);
739 
740 	draw_background(w, cr);
741 	draw_evdev_rel(w, cr);
742 	draw_evdev_abs(w, cr);
743 
744 	draw_pad(w, cr);
745 	draw_tablet(w, cr);
746 	draw_gestures(w, cr);
747 	draw_scrollbars(w, cr);
748 	draw_touchpoints(w, cr);
749 	draw_abs_pointer(w, cr);
750 	draw_buttons(w, cr);
751 	draw_pointer(w, cr);
752 
753 	return TRUE;
754 }
755 
756 static void
map_event_cb(GtkWidget * widget,GdkEvent * event,gpointer data)757 map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
758 {
759 	struct window *w = data;
760 	GdkDisplay *display;
761 	GdkSeat *seat;
762 	GdkWindow *window;
763 
764 	gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
765 
766 	w->x = w->width/2;
767 	w->y = w->height/2;
768 
769 	w->scroll.vx = w->width/2;
770 	w->scroll.vy = w->height/2;
771 	w->scroll.hx = w->width/2;
772 	w->scroll.hy = w->height/2;
773 	w->scroll.vx_discrete = w->width/2;
774 	w->scroll.vy_discrete = w->height/2;
775 	w->scroll.hx_discrete = w->width/2;
776 	w->scroll.hy_discrete = w->height/2;
777 
778 	w->swipe.x = w->width/2;
779 	w->swipe.y = w->height/2;
780 
781 	w->pinch.scale = 1.0;
782 	w->pinch.x = w->width/2;
783 	w->pinch.y = w->height/2;
784 
785 	g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
786 
787 	window = gdk_event_get_window(event);
788 	display = gdk_window_get_display(window);
789 
790 	gdk_window_set_cursor(gtk_widget_get_window(w->win),
791 			      gdk_cursor_new_for_display(display,
792 							 GDK_BLANK_CURSOR));
793 
794 	seat = gdk_display_get_default_seat(display);
795 	gdk_seat_grab(seat,
796 		      window,
797 		      GDK_SEAT_CAPABILITY_ALL_POINTING,
798 		      FALSE, /* owner-events */
799 		      NULL, /* cursor */
800 		      NULL, /* triggering event */
801 		      NULL, /* prepare_func */
802 		      NULL /* prepare_func_data */
803 		     );
804 }
805 
806 static void
window_init(struct window * w)807 window_init(struct window *w)
808 {
809 	list_init(&w->evdev_devices);
810 
811 	w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
812 	if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
813 		gtk_window_iconify(GTK_WINDOW(w->win));
814 	gtk_widget_set_events(w->win, 0);
815 	gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
816 	gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
817 	gtk_window_maximize(GTK_WINDOW(w->win));
818 	gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
819 	gtk_widget_realize(w->win);
820 	g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
821 	g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(gtk_main_quit), NULL);
822 
823 	w->area = gtk_drawing_area_new();
824 	gtk_widget_set_events(w->area, 0);
825 	gtk_container_add(GTK_CONTAINER(w->win), w->area);
826 	gtk_widget_show_all(w->win);
827 
828 	w->pad.ring.position = -1;
829 	w->pad.strip.position = -1;
830 }
831 
832 static void
window_cleanup(struct window * w)833 window_cleanup(struct window *w)
834 {
835 	struct libinput_device **dev;
836 	ARRAY_FOR_EACH(w->devices, dev) {
837 		if (*dev)
838 			libinput_device_unref(*dev);
839 	}
840 }
841 
842 static void
change_ptraccel(struct window * w,double amount)843 change_ptraccel(struct window *w, double amount)
844 {
845 	struct libinput_device **dev;
846 
847 	ARRAY_FOR_EACH(w->devices, dev) {
848 		double speed;
849 		enum libinput_config_status status;
850 
851 		if (*dev == NULL)
852 			continue;
853 
854 		if (!libinput_device_config_accel_is_available(*dev))
855 			continue;
856 
857 		speed = libinput_device_config_accel_get_speed(*dev);
858 		speed = clip(speed + amount, -1, 1);
859 
860 		status = libinput_device_config_accel_set_speed(*dev, speed);
861 
862 		if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
863 			msg("%s: failed to change accel to %.2f (%s)\n",
864 			    libinput_device_get_name(*dev),
865 			    speed,
866 			    libinput_config_status_to_str(status));
867 		} else {
868 			printf("%s: speed is %.2f\n",
869 			       libinput_device_get_name(*dev),
870 			       speed);
871 		}
872 
873 	}
874 }
875 
876 static int
handle_event_evdev(GIOChannel * source,GIOCondition condition,gpointer data)877 handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)
878 {
879 	struct libinput_device *dev = data;
880 	struct libinput *li = libinput_device_get_context(dev);
881 	struct window *w = libinput_get_user_data(li);
882 	struct evdev_device *d,
883 			    *device = NULL;
884 	struct input_event e;
885 	int rc;
886 
887 	list_for_each(d, &w->evdev_devices, node) {
888 		if (d->libinput_device == dev) {
889 			device = d;
890 			break;
891 		}
892 	}
893 
894 	if (device == NULL) {
895 		msg("Unknown device: %s\n", libinput_device_get_name(dev));
896 		return FALSE;
897 	}
898 
899 	do {
900 		rc = libevdev_next_event(device->evdev,
901 					 LIBEVDEV_READ_FLAG_NORMAL,
902 					 &e);
903 		if (rc == -EAGAIN) {
904 			break;
905 		} else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
906 			msg("SYN_DROPPED received\n");
907 			goto out;
908 		} else if (rc != LIBEVDEV_READ_STATUS_SUCCESS) {
909 			msg("Error reading event: %s\n", strerror(-rc));
910 			goto out;
911 		}
912 
913 #define EVENT(t_, c_) (t_ << 16 | c_)
914 		switch (EVENT(e.type, e.code)) {
915 		case EVENT(EV_REL, REL_X):
916 			w->evdev.rel_x = e.value;
917 			break;
918 		case EVENT(EV_REL, REL_Y):
919 			w->evdev.rel_y = e.value;
920 			break;
921 		case EVENT(EV_ABS, ABS_MT_SLOT):
922 			w->evdev.slot = min((unsigned int)e.value,
923 					  ARRAY_LENGTH(w->evdev.slots) - 1);
924 			w->evdev.device = (uintptr_t)dev;
925 			break;
926 		case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
927 			w->evdev.slots[w->evdev.slot].active = (e.value != -1);
928 			w->evdev.device = (uintptr_t)dev;
929 			break;
930 		case EVENT(EV_ABS, ABS_X):
931 			w->evdev.x = e.value;
932 			w->evdev.device = (uintptr_t)dev;
933 			break;
934 		case EVENT(EV_ABS, ABS_Y):
935 			w->evdev.y = e.value;
936 			w->evdev.device = (uintptr_t)dev;
937 			break;
938 		case EVENT(EV_ABS, ABS_MT_POSITION_X):
939 			w->evdev.slots[w->evdev.slot].x = e.value;
940 			w->evdev.device = (uintptr_t)dev;
941 			break;
942 		case EVENT(EV_ABS, ABS_MT_POSITION_Y):
943 			w->evdev.slots[w->evdev.slot].y = e.value;
944 			w->evdev.device = (uintptr_t)dev;
945 			break;
946 		}
947 	} while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
948 
949 	gtk_widget_queue_draw(w->area);
950 out:
951 	return TRUE;
952 }
953 
954 static void
register_evdev_device(struct window * w,struct libinput_device * dev)955 register_evdev_device(struct window *w, struct libinput_device *dev)
956 {
957 	GIOChannel *c;
958 	struct udev_device *ud;
959 	struct libevdev *evdev;
960 	const char *device_node;
961 	int fd;
962 	struct evdev_device *d;
963 	struct device_user_data *data;
964 
965 	ud = libinput_device_get_udev_device(dev);
966 	device_node = udev_device_get_devnode(ud);
967 
968 	fd = open(device_node, O_RDONLY|O_NONBLOCK);
969 	if (fd == -1) {
970 		msg("failed to open %s, evdev events unavailable\n", device_node);
971 		goto out;
972 	}
973 
974 	if (libevdev_new_from_fd(fd, &evdev) != 0) {
975 		msg("failed to create context for %s, evdev events unavailable\n",
976 		    device_node);
977 		goto out;
978 	}
979 
980 	d = zalloc(sizeof *d);
981 	list_append(&w->evdev_devices, &d->node);
982 	d->fd = fd;
983 	d->evdev = evdev;
984 	d->libinput_device =libinput_device_ref(dev);
985 
986 	data = zalloc(sizeof *data);
987 	libinput_device_set_user_data(dev, data);
988 
989 	c = g_io_channel_unix_new(fd);
990 	g_io_channel_set_encoding(c, NULL, NULL);
991 	d->source_id = g_io_add_watch(c, G_IO_IN,
992 				      handle_event_evdev,
993 				      d->libinput_device);
994 	fd = -1;
995 out:
996 	close(fd);
997 	udev_device_unref(ud);
998 }
999 
1000 static void
unregister_evdev_device(struct window * w,struct libinput_device * dev)1001 unregister_evdev_device(struct window *w, struct libinput_device *dev)
1002 {
1003 	struct evdev_device *d;
1004 
1005 	list_for_each(d, &w->evdev_devices, node) {
1006 		if (d->libinput_device != dev)
1007 			continue;
1008 
1009 		list_remove(&d->node);
1010 		g_source_remove(d->source_id);
1011 		free(libinput_device_get_user_data(d->libinput_device));
1012 		libinput_device_unref(d->libinput_device);
1013 		libevdev_free(d->evdev);
1014 		close(d->fd);
1015 		free(d);
1016 		w->evdev.last_device = 0;
1017 		break;
1018 	}
1019 }
1020 
1021 static void
handle_event_device_notify(struct libinput_event * ev)1022 handle_event_device_notify(struct libinput_event *ev)
1023 {
1024 	struct libinput_device *dev = libinput_event_get_device(ev);
1025 	struct libinput *li;
1026 	struct window *w;
1027 	const char *type;
1028 	size_t i;
1029 
1030 	li = libinput_event_get_context(ev);
1031 	w = libinput_get_user_data(li);
1032 
1033 	if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
1034 		type = "added";
1035 		register_evdev_device(w, dev);
1036 		tools_device_apply_config(libinput_event_get_device(ev),
1037 					  &w->options);
1038 	} else {
1039 		type = "removed";
1040 		unregister_evdev_device(w, dev);
1041 	}
1042 
1043 	msg("%s %-30s %s\n",
1044 	    libinput_device_get_sysname(dev),
1045 	    libinput_device_get_name(dev),
1046 	    type);
1047 
1048 	if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
1049 		for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
1050 			if (w->devices[i] == NULL) {
1051 				w->devices[i] = libinput_device_ref(dev);
1052 				break;
1053 			}
1054 		}
1055 	} else  {
1056 		for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
1057 			if (w->devices[i] == dev) {
1058 				libinput_device_unref(w->devices[i]);
1059 				w->devices[i] = NULL;
1060 				break;
1061 			}
1062 		}
1063 	}
1064 }
1065 
1066 static void
handle_event_motion(struct libinput_event * ev,struct window * w)1067 handle_event_motion(struct libinput_event *ev, struct window *w)
1068 {
1069 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1070 	double dx = libinput_event_pointer_get_dx(p),
1071 	       dy = libinput_event_pointer_get_dy(p);
1072 	struct point point;
1073 	const int mask = ARRAY_LENGTH(w->deltas);
1074 	size_t idx;
1075 
1076 	w->x += dx;
1077 	w->y += dy;
1078 	w->x = clip(w->x, 0.0, w->width);
1079 	w->y = clip(w->y, 0.0, w->height);
1080 
1081 	idx = w->ndeltas % mask;
1082 	point = w->deltas[idx];
1083 	idx = (w->ndeltas + 1) % mask;
1084 	point.x += libinput_event_pointer_get_dx_unaccelerated(p);
1085 	point.y += libinput_event_pointer_get_dy_unaccelerated(p);
1086 	w->deltas[idx] = point;
1087 	w->ndeltas++;
1088 }
1089 
1090 static void
handle_event_absmotion(struct libinput_event * ev,struct window * w)1091 handle_event_absmotion(struct libinput_event *ev, struct window *w)
1092 {
1093 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1094 	double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
1095 	       y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
1096 
1097 	w->absx = x;
1098 	w->absy = y;
1099 }
1100 
1101 static void
handle_event_touch(struct libinput_event * ev,struct window * w)1102 handle_event_touch(struct libinput_event *ev, struct window *w)
1103 {
1104 	struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
1105 	int slot = libinput_event_touch_get_seat_slot(t);
1106 	struct touch *touch;
1107 	double x, y;
1108 
1109 	if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
1110 		return;
1111 
1112 	touch = &w->touches[slot];
1113 
1114 	switch (libinput_event_get_type(ev)) {
1115 	case LIBINPUT_EVENT_TOUCH_UP:
1116 		touch->state = TOUCH_ENDED;
1117 		return;
1118 	case LIBINPUT_EVENT_TOUCH_CANCEL:
1119 		touch->state = TOUCH_CANCELLED;
1120 		return;
1121 	default:
1122 		break;
1123 	}
1124 
1125 	x = libinput_event_touch_get_x_transformed(t, w->width),
1126 	y = libinput_event_touch_get_y_transformed(t, w->height);
1127 
1128 	touch->state = TOUCH_ACTIVE;
1129 	touch->x = (int)x;
1130 	touch->y = (int)y;
1131 }
1132 
1133 static void
handle_event_axis(struct libinput_event * ev,struct window * w)1134 handle_event_axis(struct libinput_event *ev, struct window *w)
1135 {
1136 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1137 	struct libinput_device *dev = libinput_event_get_device(ev);
1138 	struct device_user_data *data = libinput_device_get_user_data(dev);
1139 	double value;
1140 	int discrete;
1141 
1142 	assert(data);
1143 
1144 	if (libinput_event_pointer_has_axis(p,
1145 			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
1146 		value = libinput_event_pointer_get_axis_value(p,
1147 				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
1148 		w->scroll.vy += value;
1149 		w->scroll.vy = clip(w->scroll.vy, 0, w->height);
1150 		data->scroll_accumulated.y += value;
1151 
1152 		discrete = libinput_event_pointer_get_axis_value_discrete(p,
1153 				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
1154 		if (discrete) {
1155 			w->scroll.vy_discrete += data->scroll_accumulated.y;
1156 			w->scroll.vy_discrete = clip(w->scroll.vy_discrete, 0, w->height);
1157 			data->scroll_accumulated.y = 0;
1158 		}
1159 	}
1160 
1161 	if (libinput_event_pointer_has_axis(p,
1162 			LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
1163 		value = libinput_event_pointer_get_axis_value(p,
1164 				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
1165 		w->scroll.hx += value;
1166 		w->scroll.hx = clip(w->scroll.hx, 0, w->width);
1167 		data->scroll_accumulated.x += value;
1168 
1169 		discrete = libinput_event_pointer_get_axis_value_discrete(p,
1170 				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
1171 		if (discrete) {
1172 			w->scroll.hx_discrete += data->scroll_accumulated.x;
1173 			w->scroll.hx_discrete = clip(w->scroll.hx_discrete, 0, w->width);
1174 			data->scroll_accumulated.x = 0;
1175 		}
1176 	}
1177 }
1178 
1179 static int
handle_event_keyboard(struct libinput_event * ev,struct window * w)1180 handle_event_keyboard(struct libinput_event *ev, struct window *w)
1181 {
1182 	struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
1183 	unsigned int key = libinput_event_keyboard_get_key(k);
1184 
1185 	if (libinput_event_keyboard_get_key_state(k) ==
1186 	    LIBINPUT_KEY_STATE_RELEASED)
1187 		return 0;
1188 
1189 	switch(key) {
1190 	case KEY_ESC:
1191 		return 1;
1192 	case KEY_UP:
1193 		change_ptraccel(w, 0.1);
1194 		break;
1195 	case KEY_DOWN:
1196 		change_ptraccel(w, -0.1);
1197 		break;
1198 	default:
1199 		break;
1200 	}
1201 
1202 	return 0;
1203 }
1204 
1205 static void
handle_event_button(struct libinput_event * ev,struct window * w)1206 handle_event_button(struct libinput_event *ev, struct window *w)
1207 {
1208 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1209 	unsigned int button = libinput_event_pointer_get_button(p);
1210 	bool is_press;
1211 
1212 	is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
1213 
1214 	switch (button) {
1215 	case BTN_LEFT:
1216 		w->buttons.l = is_press;
1217 		break;
1218 	case BTN_RIGHT:
1219 		w->buttons.r = is_press;
1220 		break;
1221 	case BTN_MIDDLE:
1222 		w->buttons.m = is_press;
1223 		break;
1224 	default:
1225 		w->buttons.other = is_press;
1226 		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
1227 								     button);
1228 	}
1229 
1230 }
1231 
1232 static void
handle_event_swipe(struct libinput_event * ev,struct window * w)1233 handle_event_swipe(struct libinput_event *ev, struct window *w)
1234 {
1235 	struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
1236 	int nfingers;
1237 	double dx, dy;
1238 
1239 	nfingers = libinput_event_gesture_get_finger_count(g);
1240 
1241 	switch (libinput_event_get_type(ev)) {
1242 	case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
1243 		w->swipe.nfingers = nfingers;
1244 		w->swipe.x = w->width/2;
1245 		w->swipe.y = w->height/2;
1246 		break;
1247 	case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
1248 		dx = libinput_event_gesture_get_dx(g);
1249 		dy = libinput_event_gesture_get_dy(g);
1250 		w->swipe.x += dx;
1251 		w->swipe.y += dy;
1252 		break;
1253 	case LIBINPUT_EVENT_GESTURE_SWIPE_END:
1254 		w->swipe.nfingers = 0;
1255 		w->swipe.x = w->width/2;
1256 		w->swipe.y = w->height/2;
1257 		break;
1258 	default:
1259 		abort();
1260 	}
1261 }
1262 
1263 static void
handle_event_pinch(struct libinput_event * ev,struct window * w)1264 handle_event_pinch(struct libinput_event *ev, struct window *w)
1265 {
1266 	struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
1267 	int nfingers;
1268 	double dx, dy;
1269 
1270 	nfingers = libinput_event_gesture_get_finger_count(g);
1271 
1272 	switch (libinput_event_get_type(ev)) {
1273 	case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
1274 		w->pinch.nfingers = nfingers;
1275 		w->pinch.x = w->width/2;
1276 		w->pinch.y = w->height/2;
1277 		break;
1278 	case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
1279 		dx = libinput_event_gesture_get_dx(g);
1280 		dy = libinput_event_gesture_get_dy(g);
1281 		w->pinch.x += dx;
1282 		w->pinch.y += dy;
1283 		w->pinch.scale = libinput_event_gesture_get_scale(g);
1284 		w->pinch.angle += libinput_event_gesture_get_angle_delta(g);
1285 		break;
1286 	case LIBINPUT_EVENT_GESTURE_PINCH_END:
1287 		w->pinch.nfingers = 0;
1288 		w->pinch.x = w->width/2;
1289 		w->pinch.y = w->height/2;
1290 		w->pinch.angle = 0.0;
1291 		w->pinch.scale = 1.0;
1292 		break;
1293 	default:
1294 		abort();
1295 	}
1296 }
1297 
1298 static void
handle_event_tablet(struct libinput_event * ev,struct window * w)1299 handle_event_tablet(struct libinput_event *ev, struct window *w)
1300 {
1301 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
1302 	double x, y;
1303 	struct point point;
1304 	int idx;
1305 	const int mask = ARRAY_LENGTH(w->tool.deltas);
1306 	bool is_press;
1307 	unsigned int button;
1308 
1309 	x = libinput_event_tablet_tool_get_x_transformed(t, w->width);
1310 	y = libinput_event_tablet_tool_get_y_transformed(t, w->height);
1311 
1312 	switch (libinput_event_get_type(ev)) {
1313 	case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
1314 		if (libinput_event_tablet_tool_get_proximity_state(t) ==
1315 		    LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
1316 			w->tool.x_in = 0;
1317 			w->tool.y_in = 0;
1318 			w->tool.x_down = 0;
1319 			w->tool.y_down = 0;
1320 			w->tool.x_up = 0;
1321 			w->tool.y_up = 0;
1322 		} else {
1323 			w->tool.x_in = x;
1324 			w->tool.y_in = y;
1325 			w->tool.ndeltas = 0;
1326 			w->tool.deltas[0].x = w->width/2;
1327 			w->tool.deltas[0].y = w->height/2;
1328 		}
1329 		break;
1330 	case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1331 		w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
1332 		w->tool.distance = libinput_event_tablet_tool_get_distance(t);
1333 		w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
1334 		w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
1335 		if (libinput_event_tablet_tool_get_tip_state(t) ==
1336 		    LIBINPUT_TABLET_TOOL_TIP_DOWN) {
1337 			w->tool.x_down = x;
1338 			w->tool.y_down = y;
1339 			w->tool.is_down = true;
1340 		} else {
1341 			w->tool.x_up = x;
1342 			w->tool.y_up = y;
1343 			w->tool.is_down = false;
1344 		}
1345 		/* fallthrough */
1346 	case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1347 		w->tool.x = x;
1348 		w->tool.y = y;
1349 		w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
1350 		w->tool.distance = libinput_event_tablet_tool_get_distance(t);
1351 		w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
1352 		w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
1353 		w->tool.rotation = libinput_event_tablet_tool_get_rotation(t);
1354 		w->tool.size_major = libinput_event_tablet_tool_get_size_major(t);
1355 		w->tool.size_minor = libinput_event_tablet_tool_get_size_minor(t);
1356 
1357 		/* Add the delta to the last position and store them as abs
1358 		 * coordinates */
1359 		idx = w->tool.ndeltas % mask;
1360 		point = w->tool.deltas[idx];
1361 
1362 		idx = (w->tool.ndeltas + 1) % mask;
1363 		point.x += libinput_event_tablet_tool_get_dx(t);
1364 		point.y += libinput_event_tablet_tool_get_dy(t);
1365 		w->tool.deltas[idx] = point;
1366 		w->tool.ndeltas++;
1367 		break;
1368 	case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1369 		is_press = libinput_event_tablet_tool_get_button_state(t) == LIBINPUT_BUTTON_STATE_PRESSED;
1370 		button = libinput_event_tablet_tool_get_button(t);
1371 
1372 		w->buttons.other = is_press;
1373 		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
1374 								     button);
1375 		break;
1376 	default:
1377 		abort();
1378 	}
1379 }
1380 
1381 static void
handle_event_tablet_pad(struct libinput_event * ev,struct window * w)1382 handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
1383 {
1384 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
1385 	bool is_press;
1386 	unsigned int button;
1387 	static const char *pad_buttons[] = {
1388 		"Pad 0", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5",
1389 		"Pad 6", "Pad 7", "Pad 8", "Pad 9", "Pad >= 10"
1390 	};
1391 	double position;
1392 	double number;
1393 
1394 	switch (libinput_event_get_type(ev)) {
1395 	case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1396 		is_press = libinput_event_tablet_pad_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
1397 		button = libinput_event_tablet_pad_get_button_number(p);
1398 		w->buttons.other = is_press;
1399 		w->buttons.other_name = pad_buttons[min(button, 10)];
1400 		break;
1401 	case LIBINPUT_EVENT_TABLET_PAD_RING:
1402 		position = libinput_event_tablet_pad_get_ring_position(p);
1403 		number = libinput_event_tablet_pad_get_ring_number(p);
1404 		w->pad.ring.number = number;
1405 		w->pad.ring.position = position;
1406 		break;
1407 	case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1408 		position = libinput_event_tablet_pad_get_strip_position(p);
1409 		number = libinput_event_tablet_pad_get_strip_number(p);
1410 		w->pad.strip.number = number;
1411 		w->pad.strip.position = position;
1412 		break;
1413 	default:
1414 		abort();
1415 	}
1416 }
1417 
1418 static gboolean
handle_event_libinput(GIOChannel * source,GIOCondition condition,gpointer data)1419 handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
1420 {
1421 	struct libinput *li = data;
1422 	struct window *w = libinput_get_user_data(li);
1423 	struct libinput_event *ev;
1424 
1425 	libinput_dispatch(li);
1426 
1427 	while ((ev = libinput_get_event(li))) {
1428 		switch (libinput_event_get_type(ev)) {
1429 		case LIBINPUT_EVENT_NONE:
1430 			abort();
1431 		case LIBINPUT_EVENT_DEVICE_ADDED:
1432 		case LIBINPUT_EVENT_DEVICE_REMOVED:
1433 			handle_event_device_notify(ev);
1434 			break;
1435 		case LIBINPUT_EVENT_POINTER_MOTION:
1436 			handle_event_motion(ev, w);
1437 			break;
1438 		case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
1439 			handle_event_absmotion(ev, w);
1440 			break;
1441 		case LIBINPUT_EVENT_TOUCH_DOWN:
1442 		case LIBINPUT_EVENT_TOUCH_MOTION:
1443 		case LIBINPUT_EVENT_TOUCH_UP:
1444 		case LIBINPUT_EVENT_TOUCH_CANCEL:
1445 			handle_event_touch(ev, w);
1446 			break;
1447 		case LIBINPUT_EVENT_TOUCH_FRAME:
1448 			break;
1449 		case LIBINPUT_EVENT_POINTER_AXIS:
1450 			handle_event_axis(ev, w);
1451 			break;
1452 		case LIBINPUT_EVENT_POINTER_BUTTON:
1453 			handle_event_button(ev, w);
1454 			break;
1455 		case LIBINPUT_EVENT_KEYBOARD_KEY:
1456 			if (handle_event_keyboard(ev, w)) {
1457 				libinput_event_destroy(ev);
1458 				gtk_main_quit();
1459 				return FALSE;
1460 			}
1461 			break;
1462 		case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
1463 		case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
1464 		case LIBINPUT_EVENT_GESTURE_SWIPE_END:
1465 			handle_event_swipe(ev, w);
1466 			break;
1467 		case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
1468 		case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
1469 		case LIBINPUT_EVENT_GESTURE_PINCH_END:
1470 			handle_event_pinch(ev, w);
1471 			break;
1472 		case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1473 		case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
1474 		case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1475 		case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1476 			handle_event_tablet(ev, w);
1477 			break;
1478 		case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1479 		case LIBINPUT_EVENT_TABLET_PAD_RING:
1480 		case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1481 			handle_event_tablet_pad(ev, w);
1482 			break;
1483 		case LIBINPUT_EVENT_TABLET_PAD_KEY:
1484 			break;
1485 		case LIBINPUT_EVENT_SWITCH_TOGGLE:
1486 			break;
1487 		}
1488 
1489 		libinput_event_destroy(ev);
1490 		libinput_dispatch(li);
1491 	}
1492 	gtk_widget_queue_draw(w->area);
1493 
1494 	return TRUE;
1495 }
1496 
1497 static void
sockets_init(struct libinput * li)1498 sockets_init(struct libinput *li)
1499 {
1500 	GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
1501 
1502 	g_io_channel_set_encoding(c, NULL, NULL);
1503 	g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
1504 }
1505 
1506 static void
usage(void)1507 usage(void) {
1508 	printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
1509 }
1510 
1511 static gboolean
signal_handler(void * data)1512 signal_handler(void *data)
1513 {
1514 	gtk_main_quit();
1515 
1516 	return FALSE;
1517 }
1518 
1519 int
main(int argc,char ** argv)1520 main(int argc, char **argv)
1521 {
1522 	struct window w = {0};
1523 	struct tools_options options;
1524 	struct libinput *li;
1525 	enum tools_backend backend = BACKEND_NONE;
1526 	const char *seat_or_device[2] = {"seat0", NULL};
1527 	bool verbose = false;
1528 
1529 	if (!gtk_init_check(&argc, &argv))
1530 		return 77;
1531 
1532 	g_unix_signal_add(SIGINT, signal_handler, NULL);
1533 
1534 	tools_init_options(&options);
1535 
1536 	while (1) {
1537 		int c;
1538 		int option_index = 0;
1539 		enum {
1540 			OPT_DEVICE = 1,
1541 			OPT_UDEV,
1542 			OPT_GRAB,
1543 			OPT_VERBOSE,
1544 		};
1545 		static struct option opts[] = {
1546 			CONFIGURATION_OPTIONS,
1547 			{ "help",                      no_argument,       0, 'h' },
1548 			{ "device",                    required_argument, 0, OPT_DEVICE },
1549 			{ "udev",                      required_argument, 0, OPT_UDEV },
1550 			{ "grab",                      no_argument,       0, OPT_GRAB },
1551 			{ "verbose",                   no_argument,       0, OPT_VERBOSE },
1552 			{ 0, 0, 0, 0}
1553 		};
1554 
1555 		c = getopt_long(argc, argv, "h", opts, &option_index);
1556 		if (c == -1)
1557 			break;
1558 
1559 		switch(c) {
1560 		case '?':
1561 			exit(EXIT_INVALID_USAGE);
1562 			break;
1563 		case 'h':
1564 			usage();
1565 			exit(0);
1566 			break;
1567 		case OPT_DEVICE:
1568 			backend = BACKEND_DEVICE;
1569 			seat_or_device[0] = optarg;
1570 			break;
1571 		case OPT_UDEV:
1572 			backend = BACKEND_UDEV;
1573 			seat_or_device[0] = optarg;
1574 			break;
1575 		case OPT_GRAB:
1576 			w.grab = true;
1577 			break;
1578 		case OPT_VERBOSE:
1579 			verbose = true;
1580 			break;
1581 		default:
1582 			if (tools_parse_option(c, optarg, &options) != 0) {
1583 				usage();
1584 				return EXIT_INVALID_USAGE;
1585 			}
1586 			break;
1587 		}
1588 
1589 	}
1590 
1591 	if (optind < argc) {
1592 		if (optind < argc - 1 || backend != BACKEND_NONE) {
1593 			usage();
1594 			return EXIT_INVALID_USAGE;
1595 		}
1596 		backend = BACKEND_DEVICE;
1597 		seat_or_device[0] = argv[optind];
1598 	} else if (backend == BACKEND_NONE) {
1599 		backend = BACKEND_UDEV;
1600 	}
1601 
1602 	li = tools_open_backend(backend, seat_or_device, verbose, &w.grab);
1603 	if (!li)
1604 		return EXIT_FAILURE;
1605 
1606 	libinput_set_user_data(li, &w);
1607 
1608 	window_init(&w);
1609 	w.options = options;
1610 	sockets_init(li);
1611 	handle_event_libinput(NULL, 0, li);
1612 
1613 	gtk_main();
1614 
1615 	window_cleanup(&w);
1616 	libinput_unref(li);
1617 
1618 	return EXIT_SUCCESS;
1619 }
1620