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