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 #if HAVE_GTK_WAYLAND
52 #include <wayland-client.h>
53 #include "pointer-constraints-unstable-v1-client-protocol.h"
54 #if HAVE_GTK4
55 #include <gdk/wayland/gdkwayland.h>
56 #else
57 #include <gdk/gdkwayland.h>
58 #endif
59 #endif
60
61 #if HAVE_GTK_X11
62 #include <X11/X.h>
63 #include <X11/Xlib.h>
64 #if HAVE_GTK4
65 #include <gdk/x11/gdkx.h>
66 #else
67 #include <gdk/gdkx.h>
68 #endif
69 #endif
70
71 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
72
73 enum touch_state {
74 TOUCH_ACTIVE,
75 TOUCH_ENDED,
76 TOUCH_CANCELLED,
77 };
78
79 struct touch {
80 enum touch_state state;
81 int x, y;
82 };
83
84 struct point {
85 double x, y;
86 };
87
88 struct evdev_device {
89 struct list node;
90 struct libevdev *evdev;
91 struct libinput_device *libinput_device;
92 int fd;
93 guint source_id;
94 };
95
96 struct window {
97 bool grab;
98 struct tools_options options;
99 struct list evdev_devices;
100
101 GMainLoop *event_loop;
102
103 GtkWidget *win;
104 GtkWidget *area;
105 int width, height; /* of window */
106
107 /* sprite position */
108 struct point pointer;
109 struct point unaccelerated;
110
111 /* these are for the delta coordinates, but they're not
112 * deltas, they are converted into abs positions */
113 size_t ndeltas;
114 struct point deltas[64];
115
116 /* abs position */
117 struct point abs;
118
119 /* Wayland and X11 pointer locking */
120 struct {
121 bool locked;
122
123 #if HAVE_GTK_WAYLAND
124 struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
125 struct zwp_locked_pointer_v1 *wayland_locked_pointer;
126 #endif
127 } lock_pointer;
128
129 /* scroll bar positions */
130 struct {
131 double vx, vy;
132 double hx, hy;
133
134 double vx_discrete, vy_discrete;
135 double hx_discrete, hy_discrete;
136 } scroll;
137
138 /* touch positions */
139 struct touch touches[32];
140
141 /* l/m/r mouse buttons */
142 struct {
143 bool l, m, r;
144 bool other;
145 const char *other_name;
146 } buttons;
147
148 /* touchpad swipe */
149 struct {
150 int nfingers;
151 double x, y;
152 } swipe;
153
154 struct {
155 int nfingers;
156 double scale;
157 double angle;
158 double x, y;
159 } pinch;
160
161 struct {
162 int nfingers;
163 bool active;
164 } hold;
165
166 struct {
167 double x, y;
168 double x_in, y_in;
169 double x_down, y_down;
170 double x_up, y_up;
171 double pressure;
172 double distance;
173 double tilt_x, tilt_y;
174 double rotation;
175 double size_major, size_minor;
176 bool is_down;
177
178 /* these are for the delta coordinates, but they're not
179 * deltas, they are converted into abs positions */
180 size_t ndeltas;
181 struct point deltas[64];
182 } tool;
183
184 struct {
185 struct {
186 double position;
187 int number;
188 } ring;
189 struct {
190 double position;
191 int number;
192 } strip;
193 } pad;
194
195 struct {
196 int rel_x, rel_y; /* REL_X/Y */
197 int x, y; /* ABS_X/Y */
198 struct {
199 int x, y; /* ABS_MT_POSITION_X/Y */
200 bool active;
201 } slots[16];
202 unsigned int slot; /* ABS_MT_SLOT */
203 /* So we know when to re-fetch the abs axes */
204 uintptr_t device, last_device;
205 } evdev;
206
207 struct libinput_device *devices[50];
208 };
209
210 #if HAVE_GTK_WAYLAND
211 static void
wayland_registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version)212 wayland_registry_global(void *data,
213 struct wl_registry *registry,
214 uint32_t name,
215 const char *interface,
216 uint32_t version)
217 {
218 struct window *w = data;
219
220 if (!g_strcmp0(interface, "zwp_pointer_constraints_v1")) {
221 w->lock_pointer.wayland_pointer_constraints =
222 wl_registry_bind(registry,
223 name,
224 &zwp_pointer_constraints_v1_interface,
225 1);
226 }
227 }
228
229 static void
wayland_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)230 wayland_registry_global_remove(void *data,
231 struct wl_registry *wl_registry,
232 uint32_t name)
233 {
234
235 }
236
237 struct wl_registry_listener registry_listener = {
238 wayland_registry_global,
239 wayland_registry_global_remove
240 };
241
242 static bool
wayland_lock_pointer(struct window *w)243 wayland_lock_pointer(struct window *w)
244 {
245 GdkDisplay *gdk_display;
246 GdkSeat *gdk_seat;
247 GdkDevice *gdk_device;
248 struct wl_display *display;
249 struct wl_registry *registry;
250 struct wl_pointer *wayland_pointer;
251 struct wl_surface *surface;
252
253 w->lock_pointer.wayland_pointer_constraints = NULL;
254
255 gdk_display = gdk_display_get_default();
256 display = gdk_wayland_display_get_wl_display(gdk_display);
257
258 gdk_seat = gdk_display_get_default_seat(gdk_display);
259 gdk_device = gdk_seat_get_pointer(gdk_seat);
260 wayland_pointer = gdk_wayland_device_get_wl_pointer(gdk_device);
261
262 registry = wl_display_get_registry(display);
263 wl_registry_add_listener(registry, ®istry_listener, w);
264 wl_display_roundtrip(display);
265
266 if (!w->lock_pointer.wayland_pointer_constraints)
267 return false;
268
269 #if HAVE_GTK4
270 GtkNative *window = gtk_widget_get_native(w->win);
271 GdkSurface *gdk_surface = gtk_native_get_surface(window);
272 surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
273 #else
274 GdkWindow *window = gtk_widget_get_window(w->win);
275 surface = gdk_wayland_window_get_wl_surface(window);
276 #endif
277
278 w->lock_pointer.wayland_locked_pointer =
279 zwp_pointer_constraints_v1_lock_pointer(w->lock_pointer.wayland_pointer_constraints,
280 surface,
281 wayland_pointer,
282 NULL,
283 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
284
285 return true;
286 }
287
288 static void
wayland_unlock_pointer(struct window *w)289 wayland_unlock_pointer(struct window *w)
290 {
291 w->lock_pointer.wayland_pointer_constraints = NULL;
292 zwp_locked_pointer_v1_destroy(w->lock_pointer.wayland_locked_pointer);
293 }
294
295 static inline bool
backend_is_wayland(void)296 backend_is_wayland(void)
297 {
298 return GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
299 }
300 #endif /* HAVE_GTK_WAYLAND */
301
302 #if HAVE_GTK_X11
303 static bool
x_lock_pointer(struct window *w)304 x_lock_pointer(struct window *w)
305 {
306 Display *x_display;
307 Window x_win;
308 int result;
309
310 x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
311
312 #if HAVE_GTK4
313 GtkNative *window = gtk_widget_get_native(w->win);
314 GdkSurface *surface = gtk_native_get_surface(window);
315 x_win = GDK_SURFACE_XID(surface);
316 #else
317 GdkWindow *window = gtk_widget_get_window(w->win);
318 x_win = GDK_WINDOW_XID(window);
319 #endif
320
321 result = XGrabPointer(x_display, x_win,
322 False, NoEventMask,
323 GrabModeAsync, GrabModeAsync,
324 x_win,
325 None,
326 CurrentTime);
327 return (result == GrabSuccess);
328 }
329
330 static void
x_unlock_pointer(struct window *w)331 x_unlock_pointer(struct window *w)
332 {
333 Display *x_display;
334
335 x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
336
337 XUngrabPointer(x_display, CurrentTime);
338 }
339
340 static inline bool
backend_is_x11(void)341 backend_is_x11(void)
342 {
343 return GDK_IS_X11_DISPLAY(gdk_display_get_default());
344 }
345 #endif /* HAVE_GTK_X11 */
346
347 static bool
window_lock_pointer(struct window *w)348 window_lock_pointer(struct window *w)
349 {
350 if (w->lock_pointer.locked)
351 return true;
352
353 #if HAVE_GTK_WAYLAND
354 if (backend_is_wayland())
355 w->lock_pointer.locked = wayland_lock_pointer(w);
356 #endif
357
358 #if HAVE_GTK_X11
359 if (backend_is_x11())
360 w->lock_pointer.locked = x_lock_pointer(w);
361 #endif
362
363 return w->lock_pointer.locked;
364 }
365
366 static void
window_unlock_pointer(struct window *w)367 window_unlock_pointer(struct window *w)
368 {
369 if (!w->lock_pointer.locked)
370 return;
371
372 w->lock_pointer.locked = false;
373
374 #if HAVE_GTK_WAYLAND
375 if (backend_is_wayland())
376 wayland_unlock_pointer(w);
377 #endif
378
379 #if HAVE_GTK_X11
380 if (backend_is_x11())
381 x_unlock_pointer(w);
382 #endif
383 }
384
385 LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
386 static inline void
msg(const char *fmt, ...)387 msg(const char *fmt, ...)
388 {
389 va_list args;
390 printf("info: ");
391
392 va_start(args, fmt);
393 vprintf(fmt, args);
394 va_end(args);
395 }
396
397 static inline void
draw_evdev_rel(struct window *w, cairo_t *cr)398 draw_evdev_rel(struct window *w, cairo_t *cr)
399 {
400 int center_x, center_y;
401
402 cairo_save(cr);
403 cairo_set_source_rgb(cr, .2, .2, .8);
404 center_x = w->width/2 - 400;
405 center_y = w->height/2;
406
407 cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
408 cairo_stroke(cr);
409
410 if (w->evdev.rel_x) {
411 int dir = w->evdev.rel_x > 0 ? 1 : -1;
412 for (int i = 0; i < abs(w->evdev.rel_x); i++) {
413 cairo_move_to(cr,
414 center_x + (i + 1) * 20 * dir,
415 center_y - 20);
416 cairo_rel_line_to(cr, 0, 40);
417 cairo_rel_line_to(cr, 20 * dir, -20);
418 cairo_rel_line_to(cr, -20 * dir, -20);
419 cairo_fill(cr);
420 }
421 }
422
423 if (w->evdev.rel_y) {
424 int dir = w->evdev.rel_y > 0 ? 1 : -1;
425 for (int i = 0; i < abs(w->evdev.rel_y); i++) {
426 cairo_move_to(cr,
427 center_x - 20,
428 center_y + (i + 1) * 20 * dir);
429 cairo_rel_line_to(cr, 40, 0);
430 cairo_rel_line_to(cr, -20, 20 * dir);
431 cairo_rel_line_to(cr, -20, -20 * dir);
432 cairo_fill(cr);
433 }
434 }
435
436 cairo_restore(cr);
437 }
438
439 static inline void
draw_evdev_abs(struct window *w, cairo_t *cr)440 draw_evdev_abs(struct window *w, cairo_t *cr)
441 {
442 static const struct input_absinfo *ax = NULL, *ay = NULL;
443 const int normalized_width = 200;
444 int outline_width = normalized_width,
445 outline_height = normalized_width * 0.75;
446 int center_x, center_y;
447 int width, height;
448 int x, y;
449
450 cairo_save(cr);
451
452 center_x = w->width/2 + 400;
453 center_y = w->height/2;
454
455 /* Always the outline even if we didn't get any abs events yet so it
456 * doesn't just appear out of nowhere */
457 if (w->evdev.device == 0)
458 goto draw_outline;
459
460 /* device has changed, so the abs proportions/dimensions have
461 * changed. */
462 if (w->evdev.device != w->evdev.last_device) {
463 struct evdev_device *d;
464
465 ax = NULL;
466 ay = NULL;
467
468 list_for_each(d, &w->evdev_devices, node) {
469 if ((uintptr_t)d->libinput_device != w->evdev.device)
470 continue;
471
472 ax = libevdev_get_abs_info(d->evdev, ABS_X);
473 ay = libevdev_get_abs_info(d->evdev, ABS_Y);
474 w->evdev.last_device = w->evdev.device;
475 }
476
477 }
478 if (ax == NULL || ay == NULL)
479 goto draw_outline;
480
481 width = ax->maximum - ax->minimum;
482 height = ay->maximum - ay->minimum;
483 outline_height = 1.0 * height/width * normalized_width;
484 outline_width = normalized_width;
485
486 x = 1.0 * (w->evdev.x - ax->minimum)/width * outline_width;
487 y = 1.0 * (w->evdev.y - ay->minimum)/height * outline_height;
488 x += center_x - outline_width/2;
489 y += center_y - outline_height/2;
490 cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
491 cairo_fill(cr);
492
493 for (size_t i = 0; i < ARRAY_LENGTH(w->evdev.slots); i++) {
494 if (!w->evdev.slots[i].active)
495 continue;
496
497 cairo_set_source_rgb(cr, .2, .2, .8);
498 x = w->evdev.slots[i].x;
499 y = w->evdev.slots[i].y;
500 x = 1.0 * (x - ax->minimum)/width * outline_width;
501 y = 1.0 * (y - ay->minimum)/height * outline_height;
502 x += center_x - outline_width/2;
503 y += center_y - outline_height/2;
504 cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
505 cairo_fill(cr);
506
507 char finger_text[3];
508 cairo_text_extents_t finger_text_extents;
509 snprintf(finger_text, 3, "%zu", i);
510 cairo_set_source_rgb(cr, 1.f, 1.f, 1.f);
511 cairo_set_font_size(cr, 12.0);
512 cairo_text_extents(cr, finger_text, &finger_text_extents);
513 cairo_move_to(cr, x - finger_text_extents.width/2,
514 y + finger_text_extents.height/2);
515 cairo_show_text(cr, finger_text);
516 }
517
518 draw_outline:
519 /* The touchpad outline */
520 cairo_set_source_rgb(cr, .2, .2, .8);
521 cairo_rectangle(cr,
522 center_x - outline_width/2,
523 center_y - outline_height/2,
524 outline_width,
525 outline_height);
526 cairo_stroke(cr);
527 cairo_restore(cr);
528 }
529
530 static inline void
draw_gestures(struct window *w, cairo_t *cr)531 draw_gestures(struct window *w, cairo_t *cr)
532 {
533 int offset;
534
535 /* swipe */
536 cairo_save(cr);
537 cairo_translate(cr, w->swipe.x, w->swipe.y);
538 for (int i = 0; i < w->swipe.nfingers; i++) {
539 cairo_set_source_rgb(cr, .8, .8, .4);
540 cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
541 cairo_fill(cr);
542 }
543
544 for (int i = 0; i < 4; i++) { /* 4 fg max */
545 cairo_set_source_rgb(cr, 0, 0, 0);
546 cairo_arc(cr, (i - 2) * 40, 0, 20, 0, 2 * M_PI);
547 cairo_stroke(cr);
548 }
549 cairo_restore(cr);
550
551 /* pinch */
552 cairo_save(cr);
553 offset = w->pinch.scale * 100;
554 cairo_translate(cr, w->pinch.x, w->pinch.y);
555 cairo_rotate(cr, w->pinch.angle * M_PI/180.0);
556 if (w->pinch.nfingers > 0) {
557 cairo_set_source_rgb(cr, .4, .4, .8);
558 cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
559 cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
560 cairo_fill(cr);
561 }
562
563 cairo_set_source_rgb(cr, 0, 0, 0);
564 cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
565 cairo_stroke(cr);
566 cairo_arc(cr, -offset, offset, 20, 0, 2 * M_PI);
567 cairo_stroke(cr);
568
569 cairo_restore(cr);
570
571 /* hold */
572 cairo_save(cr);
573 cairo_translate(cr, w->width/2, w->height/2 + 100);
574
575 for (int i = 4; i > 0; i--) { /* 4 fg max */
576 double r, g, b, hold_alpha;
577
578 r = .4 + .2 * (i % 2);
579 g = .2;
580 b = .2;
581 hold_alpha = (w->hold.active && i <= w->hold.nfingers) ? 1 : .5;
582
583 cairo_set_source_rgba(cr, r, g, b, hold_alpha);
584 cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
585 cairo_fill(cr);
586
587 cairo_set_source_rgba(cr, 0, 0, 0, hold_alpha);
588 cairo_arc(cr, 0, 0, 20 * i, 0, 2 * M_PI);
589 cairo_stroke(cr);
590 }
591
592 cairo_restore(cr);
593 }
594
595 static inline void
draw_scrollbars(struct window *w, cairo_t *cr)596 draw_scrollbars(struct window *w, cairo_t *cr)
597 {
598
599 /* normal scrollbars */
600 cairo_save(cr);
601 cairo_set_source_rgb(cr, .4, .8, 0);
602 cairo_rectangle(cr, w->scroll.vx - 10, w->scroll.vy - 20, 20, 40);
603 cairo_rectangle(cr, w->scroll.hx - 20, w->scroll.hy - 10, 40, 20);
604 cairo_fill(cr);
605
606 /* discrete scrollbars */
607 cairo_set_source_rgb(cr, .8, .4, 0);
608 cairo_rectangle(cr, w->scroll.vx_discrete - 5, w->scroll.vy_discrete - 10, 10, 20);
609 cairo_rectangle(cr, w->scroll.hx_discrete - 10, w->scroll.hy_discrete - 5, 20, 10);
610 cairo_fill(cr);
611
612 cairo_restore(cr);
613 }
614
615 static inline void
draw_touchpoints(struct window *w, cairo_t *cr)616 draw_touchpoints(struct window *w, cairo_t *cr)
617 {
618 cairo_save(cr);
619 ARRAY_FOR_EACH(w->touches, t) {
620 if (t->state == TOUCH_ACTIVE)
621 cairo_set_source_rgb(cr, .8, .2, .2);
622 else
623 cairo_set_source_rgb(cr, .8, .4, .4);
624 cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
625 if (t->state == TOUCH_CANCELLED)
626 cairo_stroke(cr);
627 else
628 cairo_fill(cr);
629 }
630 cairo_restore(cr);
631 }
632
633 static inline void
draw_abs_pointer(struct window *w, cairo_t *cr)634 draw_abs_pointer(struct window *w, cairo_t *cr)
635 {
636
637 cairo_save(cr);
638 cairo_set_source_rgb(cr, .2, .4, .8);
639 cairo_arc(cr, w->abs.x, w->abs.y, 10, 0, 2 * M_PI);
640 cairo_fill(cr);
641 cairo_restore(cr);
642 }
643
644 static inline void
draw_text(cairo_t *cr, const char *text, double x, double y)645 draw_text(cairo_t *cr, const char *text, double x, double y)
646 {
647 cairo_text_extents_t te;
648 cairo_font_extents_t fe;
649
650 cairo_text_extents(cr, text, &te);
651 cairo_font_extents(cr, &fe);
652 /* center of the rectangle */
653 cairo_move_to(cr, x, y);
654 cairo_rel_move_to(cr, -te.width/2, -fe.descent + te.height/2);
655 cairo_show_text(cr, text);
656 }
657
658 static inline void
draw_other_button(struct window *w, cairo_t *cr)659 draw_other_button (struct window *w, cairo_t *cr)
660 {
661 const char *name = w->buttons.other_name;
662
663 cairo_save(cr);
664
665 if (!w->buttons.other)
666 goto outline;
667
668 if (!name)
669 name = "undefined";
670
671 cairo_set_source_rgb(cr, .2, .8, .8);
672 cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
673 cairo_fill(cr);
674
675 cairo_set_source_rgb(cr, 0, 0, 0);
676
677 draw_text(cr, name, w->width/2, w->height - 150 + 15);
678
679 outline:
680 cairo_set_source_rgb(cr, 0, 0, 0);
681 cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
682 cairo_stroke(cr);
683 cairo_restore(cr);
684 }
685
686 static inline void
draw_buttons(struct window *w, cairo_t *cr)687 draw_buttons(struct window *w, cairo_t *cr)
688 {
689 cairo_save(cr);
690
691 if (w->buttons.l || w->buttons.m || w->buttons.r) {
692 cairo_set_source_rgb(cr, .2, .8, .8);
693 if (w->buttons.l)
694 cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
695 if (w->buttons.m)
696 cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
697 if (w->buttons.r)
698 cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
699 cairo_fill(cr);
700 }
701
702 cairo_set_source_rgb(cr, 0, 0, 0);
703 cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
704 cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
705 cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
706 cairo_stroke(cr);
707 cairo_restore(cr);
708
709 draw_other_button(w, cr);
710 }
711
712 static inline void
draw_pad(struct window *w, cairo_t *cr)713 draw_pad(struct window *w, cairo_t *cr)
714 {
715 double rx, ry;
716 double pos;
717 char number[3];
718
719 rx = w->width/2 - 200;
720 ry = w->height/2 + 100;
721
722 cairo_save(cr);
723 /* outer ring */
724 cairo_set_source_rgb(cr, .7, .7, .0);
725 cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
726 cairo_fill(cr);
727
728 /* inner ring */
729 cairo_set_source_rgb(cr, 1., 1., 1.);
730 cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
731 cairo_fill(cr);
732
733 /* marker */
734 /* libinput has degrees and 0 is north, cairo has radians and 0 is
735 * east */
736 if (w->pad.ring.position != -1) {
737 pos = (w->pad.ring.position + 270) * M_PI/180.0;
738 cairo_set_source_rgb(cr, .0, .0, .0);
739 cairo_set_line_width(cr, 20);
740 cairo_arc(cr, rx, ry, 40, pos - M_PI/8 , pos + M_PI/8);
741 cairo_stroke(cr);
742
743 snprintf(number, sizeof(number), "%d", w->pad.ring.number);
744 cairo_set_source_rgb(cr, .0, .0, .0);
745 draw_text(cr, number, rx, ry);
746
747 }
748
749 cairo_restore(cr);
750
751 rx = w->width/2 - 300;
752 ry = w->height/2 + 50;
753
754 cairo_save(cr);
755 cairo_set_source_rgb(cr, .7, .7, .0);
756 cairo_rectangle(cr, rx, ry, 20, 100);
757 cairo_fill(cr);
758
759 if (w->pad.strip.position != -1) {
760 pos = w->pad.strip.position * 80;
761 cairo_set_source_rgb(cr, .0, .0, .0);
762 cairo_rectangle(cr, rx, ry + pos, 20, 20);
763 cairo_fill(cr);
764
765 snprintf(number, sizeof(number), "%d", w->pad.strip.number);
766 cairo_set_source_rgb(cr, .0, .0, .0);
767 draw_text(cr, number, rx + 10, ry - 10);
768 }
769
770 cairo_restore(cr);
771 }
772
773 static inline void
draw_tablet(struct window *w, cairo_t *cr)774 draw_tablet(struct window *w, cairo_t *cr)
775 {
776 double x, y;
777 int first, last;
778 size_t mask;
779 int rx, ry;
780
781 /* pressure/distance bars */
782 rx = w->width/2 + 100;
783 ry = w->height/2 + 50;
784 cairo_save(cr);
785 cairo_set_source_rgb(cr, .2, .6, .6);
786 cairo_rectangle(cr, rx, ry, 20, 100);
787 cairo_stroke(cr);
788
789 if (w->tool.distance > 0) {
790 double pos = w->tool.distance * 100;
791 cairo_rectangle(cr, rx, ry + 100 - pos, 20, 5);
792 cairo_fill(cr);
793 }
794 if (w->tool.pressure > 0) {
795 double pos = w->tool.pressure * 100;
796 if (w->tool.is_down)
797 cairo_rectangle(cr, rx + 25, ry + 95, 5, 5);
798 cairo_rectangle(cr, rx, ry + 100 - pos, 20, pos);
799 cairo_fill(cr);
800 }
801 cairo_restore(cr);
802
803 /* tablet tool, square for prox-in location */
804 cairo_save(cr);
805 cairo_set_source_rgb(cr, .2, .6, .6);
806 if (w->tool.x_in && w->tool.y_in) {
807 cairo_rectangle(cr, w->tool.x_in - 15, w->tool.y_in - 15, 30, 30);
808 cairo_stroke(cr);
809 }
810
811 if (w->tool.x_down && w->tool.y_down) {
812 cairo_rectangle(cr, w->tool.x_down - 10, w->tool.y_down - 10, 20, 20);
813 cairo_stroke(cr);
814 }
815
816 if (w->tool.x_up && w->tool.y_up) {
817 cairo_rectangle(cr, w->tool.x_up - 10, w->tool.y_up - 10, 20, 20);
818 cairo_stroke(cr);
819 }
820
821 if (w->tool.pressure)
822 cairo_set_source_rgb(cr, .2, .8, .8);
823
824 cairo_translate(cr, w->tool.x, w->tool.y);
825 /* scale of 2.5 is large enough to make the marker visible around the
826 physical totem */
827 cairo_scale(cr,
828 1.0 + w->tool.size_major * 2.5,
829 1.0 + w->tool.size_minor * 2.5);
830 cairo_scale(cr, 1.0 + w->tool.tilt_x/30.0, 1.0 + w->tool.tilt_y/30.0);
831 if (w->tool.rotation)
832 cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
833 if (w->tool.pressure)
834 cairo_set_source_rgb(cr, .8, .8, .2);
835 cairo_arc(cr, 0, 0,
836 1 + 10 * max(w->tool.pressure, w->tool.distance),
837 0, 2 * M_PI);
838 cairo_fill(cr);
839 cairo_restore(cr);
840
841 /* The line to indicate the origin */
842 if (w->tool.size_major) {
843 cairo_save(cr);
844 cairo_scale(cr, 1.0, 1.0);
845 cairo_translate(cr, w->tool.x, w->tool.y);
846 if (w->tool.rotation)
847 cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
848 cairo_set_source_rgb(cr, .0, .0, .0);
849 cairo_move_to(cr, 0, 0);
850 cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
851 cairo_stroke(cr);
852 cairo_restore(cr);
853 }
854
855 /* tablet deltas */
856 mask = ARRAY_LENGTH(w->tool.deltas);
857 first = max(w->tool.ndeltas + 1, mask) - mask;
858 last = w->tool.ndeltas;
859
860 cairo_save(cr);
861 cairo_set_source_rgb(cr, .8, .8, .2);
862
863 x = w->tool.deltas[first % mask].x;
864 y = w->tool.deltas[first % mask].y;
865 cairo_move_to(cr, x, y);
866
867 for (int i = first + 1; i < last; i++) {
868 x = w->tool.deltas[i % mask].x;
869 y = w->tool.deltas[i % mask].y;
870 cairo_line_to(cr, x, y);
871 }
872
873 cairo_stroke(cr);
874 cairo_restore(cr);
875 }
876
877 static inline void
draw_pointer(struct window *w, cairo_t *cr)878 draw_pointer(struct window *w, cairo_t *cr)
879 {
880 double x, y;
881 int first, last;
882 size_t mask;
883
884 /* draw pointer sprite */
885 cairo_set_source_rgb(cr, 0, 0, 0);
886 cairo_save(cr);
887 cairo_move_to(cr, w->pointer.x, w->pointer.y);
888 cairo_rel_line_to(cr, 10, 15);
889 cairo_rel_line_to(cr, -10, 0);
890 cairo_rel_line_to(cr, 0, -15);
891 cairo_fill(cr);
892
893 /* draw unaccelerated sprite */
894 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
895 cairo_save(cr);
896 cairo_move_to(cr, w->unaccelerated.x, w->unaccelerated.y);
897 cairo_rel_line_to(cr, -5, -10);
898 cairo_rel_line_to(cr, 10, 0);
899 cairo_rel_line_to(cr, -5, 10);
900 cairo_fill(cr);
901
902 /* pointer deltas */
903 mask = ARRAY_LENGTH(w->deltas);
904 first = max(w->ndeltas + 1, mask) - mask;
905 last = w->ndeltas;
906
907 cairo_set_source_rgb(cr, .8, .5, .2);
908
909 x = w->deltas[first % mask].x;
910 y = w->deltas[first % mask].y;
911 cairo_move_to(cr, x, y);
912
913 for (int i = first + 1; i < last; i++) {
914 x = w->deltas[i % mask].x;
915 y = w->deltas[i % mask].y;
916 cairo_line_to(cr, x, y);
917 }
918
919 cairo_stroke(cr);
920 cairo_restore(cr);
921 }
922
923 static inline void
draw_background(struct window *w, cairo_t *cr)924 draw_background(struct window *w, cairo_t *cr)
925 {
926 int x1, x2, y1, y2, x3, y3, x4, y4;
927 int cols;
928
929 /* 10px and 5px grids */
930 cairo_save(cr);
931 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
932 x1 = w->width/2 - 200;
933 y1 = w->height/2 - 200;
934 x2 = w->width/2 + 200;
935 y2 = w->height/2 - 200;
936 for (cols = 1; cols < 10; cols++) {
937 cairo_move_to(cr, x1 + 10 * cols, y1);
938 cairo_rel_line_to(cr, 0, 100);
939 cairo_move_to(cr, x1, y1 + 10 * cols);
940 cairo_rel_line_to(cr, 100, 0);
941
942 cairo_move_to(cr, x2 + 5 * cols, y2);
943 cairo_rel_line_to(cr, 0, 50);
944 cairo_move_to(cr, x2, y2 + 5 * cols);
945 cairo_rel_line_to(cr, 50, 0);
946 }
947
948 /* 3px horiz/vert bar codes */
949 x3 = w->width/2 - 200;
950 y3 = w->height/2 + 200;
951 x4 = w->width/2 + 200;
952 y4 = w->height/2 + 100;
953 for (cols = 0; cols < 50; cols++) {
954 cairo_move_to(cr, x3 + 3 * cols, y3);
955 cairo_rel_line_to(cr, 0, 20);
956
957 cairo_move_to(cr, x4, y4 + 3 * cols);
958 cairo_rel_line_to(cr, 20, 0);
959 }
960 cairo_stroke(cr);
961
962 /* round targets */
963 for (int i = 0; i <= 3; i++) {
964 x1 = w->width * i/4.0;
965 x2 = w->width * i/4.0;
966
967 y1 = w->height * 1.0/4.0;
968 y2 = w->height * 3.0/4.0;
969
970 cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
971 cairo_stroke(cr);
972 cairo_arc(cr, x2, y2, 10, 0, 2 * M_PI);
973 cairo_stroke(cr);
974 }
975
976 cairo_restore(cr);
977 }
978
979 static gboolean
draw(GtkWidget *widget, cairo_t *cr, gpointer data)980 draw(GtkWidget *widget, cairo_t *cr, gpointer data)
981 {
982 struct window *w = data;
983
984 cairo_set_font_size(cr, 12.0);
985 cairo_set_source_rgb(cr, 1, 1, 1);
986 cairo_rectangle(cr, 0, 0, w->width, w->height);
987 cairo_fill(cr);
988
989 draw_background(w, cr);
990 draw_evdev_rel(w, cr);
991 draw_evdev_abs(w, cr);
992
993 draw_pad(w, cr);
994 draw_tablet(w, cr);
995 draw_gestures(w, cr);
996 draw_scrollbars(w, cr);
997 draw_touchpoints(w, cr);
998 draw_abs_pointer(w, cr);
999 draw_buttons(w, cr);
1000 draw_pointer(w, cr);
1001
1002 return TRUE;
1003 }
1004
1005 #if HAVE_GTK4
1006 static void
draw_gtk4(GtkDrawingArea *widget, cairo_t *cr, int width, int height, gpointer data)1007 draw_gtk4(GtkDrawingArea *widget,
1008 cairo_t *cr,
1009 int width,
1010 int height,
1011 gpointer data)
1012 {
1013 draw(GTK_WIDGET(widget), cr, data);
1014 }
1015 #endif
1016
1017 static void
window_place_ui_elements(GtkWidget *widget, struct window *w)1018 window_place_ui_elements(GtkWidget *widget, struct window *w)
1019 {
1020 #if HAVE_GTK4
1021 w->width = gtk_widget_get_width(w->area);
1022 w->height = gtk_widget_get_height(w->area);
1023 #else
1024 gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
1025 #endif
1026
1027 w->pointer.x = w->width/2;
1028 w->pointer.y = w->height/2;
1029 w->unaccelerated.x = w->width/2;
1030 w->unaccelerated.y = w->height/2;
1031 w->deltas[0].x = w->pointer.x;
1032 w->deltas[0].y = w->pointer.y;
1033
1034 w->scroll.vx = w->width/2;
1035 w->scroll.vy = w->height/2;
1036 w->scroll.hx = w->width/2;
1037 w->scroll.hy = w->height/2;
1038 w->scroll.vx_discrete = w->width/2;
1039 w->scroll.vy_discrete = w->height/2;
1040 w->scroll.hx_discrete = w->width/2;
1041 w->scroll.hy_discrete = w->height/2;
1042
1043 w->swipe.x = w->width/2;
1044 w->swipe.y = w->height/2;
1045
1046 w->pinch.scale = 1.0;
1047 w->pinch.x = w->width/2;
1048 w->pinch.y = w->height/2;
1049 }
1050
1051 #if HAVE_GTK4
1052 static void
map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)1053 map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
1054 {
1055 struct window *w = data;
1056
1057 window_place_ui_elements(GTK_WIDGET(widget), w);
1058
1059 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area),
1060 draw_gtk4,
1061 w,
1062 NULL);
1063
1064 gtk_widget_set_cursor_from_name(w->win, "none");
1065
1066 window_lock_pointer(w);
1067 }
1068 #else
1069 static void
map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)1070 map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
1071 {
1072 struct window *w = data;
1073 GdkDisplay *display;
1074 GdkWindow *window;
1075
1076 window_place_ui_elements(widget, w);
1077
1078 g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
1079
1080 window = gdk_event_get_window(event);
1081 display = gdk_window_get_display(window);
1082
1083 gdk_window_set_cursor(gtk_widget_get_window(w->win),
1084 gdk_cursor_new_for_display(display,
1085 GDK_BLANK_CURSOR));
1086
1087 window_lock_pointer(w);
1088 }
1089 #endif
1090
1091 static void
window_quit(struct window *w)1092 window_quit(struct window *w)
1093 {
1094 g_main_loop_quit(w->event_loop);
1095 }
1096
1097 #if HAVE_GTK4
1098 static gboolean
window_delete_event_cb(GtkWindow *window, gpointer data)1099 window_delete_event_cb(GtkWindow *window, gpointer data)
1100 {
1101 struct window *w = data;
1102
1103 window_quit(w);
1104
1105 return TRUE;
1106 }
1107 #else
1108 static void
window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)1109 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
1110 {
1111 struct window *w = data;
1112
1113 window_quit(w);
1114 }
1115 #endif
1116
1117 static void
window_init(struct window *w)1118 window_init(struct window *w)
1119 {
1120 list_init(&w->evdev_devices);
1121
1122 #if HAVE_GTK4
1123 w->win = gtk_window_new();
1124 #else
1125 w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1126 #endif
1127
1128 if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) {
1129 #if HAVE_GTK4
1130 gtk_window_minimize(GTK_WINDOW(w->win));
1131 #else
1132 gtk_window_iconify(GTK_WINDOW(w->win));
1133 #endif
1134 }
1135
1136 gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
1137 gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
1138 gtk_window_maximize(GTK_WINDOW(w->win));
1139 gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
1140 gtk_widget_realize(w->win);
1141
1142 w->area = gtk_drawing_area_new();
1143
1144 #if HAVE_GTK4
1145 g_signal_connect(G_OBJECT(w->area), "resize", G_CALLBACK(map_event_cb), w);
1146 g_signal_connect(G_OBJECT(w->win), "close-request", G_CALLBACK(window_delete_event_cb), w);
1147
1148 gtk_window_set_child(GTK_WINDOW(w->win), w->area);
1149 gtk_widget_set_visible(w->win, TRUE);
1150 #else
1151 g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
1152 g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(window_delete_event_cb), w);
1153
1154 gtk_widget_set_events(w->win, 0);
1155 gtk_widget_set_events(w->area, 0);
1156
1157 gtk_container_add(GTK_CONTAINER(w->win), w->area);
1158 gtk_widget_show_all(w->win);
1159 #endif
1160
1161 w->pad.ring.position = -1;
1162 w->pad.strip.position = -1;
1163 }
1164
1165 static void
window_cleanup(struct window *w)1166 window_cleanup(struct window *w)
1167 {
1168 ARRAY_FOR_EACH(w->devices, dev) {
1169 if (*dev)
1170 libinput_device_unref(*dev);
1171 }
1172 }
1173
1174 static void
change_ptraccel(struct window *w, double amount)1175 change_ptraccel(struct window *w, double amount)
1176 {
1177 ARRAY_FOR_EACH(w->devices, dev) {
1178 double speed;
1179 enum libinput_config_status status;
1180
1181 if (*dev == NULL)
1182 continue;
1183
1184 if (!libinput_device_config_accel_is_available(*dev))
1185 continue;
1186
1187 speed = libinput_device_config_accel_get_speed(*dev);
1188 speed = clip(speed + amount, -1, 1);
1189
1190 status = libinput_device_config_accel_set_speed(*dev, speed);
1191
1192 if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
1193 msg("%s: failed to change accel to %.2f (%s)\n",
1194 libinput_device_get_name(*dev),
1195 speed,
1196 libinput_config_status_to_str(status));
1197 } else {
1198 printf("%s: speed is %.2f\n",
1199 libinput_device_get_name(*dev),
1200 speed);
1201 }
1202
1203 }
1204 }
1205
1206 static int
handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)1207 handle_event_evdev(GIOChannel *source, GIOCondition condition, gpointer data)
1208 {
1209 struct libinput_device *dev = data;
1210 struct libinput *li = libinput_device_get_context(dev);
1211 struct window *w = libinput_get_user_data(li);
1212 struct evdev_device *d,
1213 *device = NULL;
1214 struct input_event e;
1215 int rc;
1216
1217 list_for_each(d, &w->evdev_devices, node) {
1218 if (d->libinput_device == dev) {
1219 device = d;
1220 break;
1221 }
1222 }
1223
1224 if (device == NULL) {
1225 msg("Unknown device: %s\n", libinput_device_get_name(dev));
1226 return FALSE;
1227 }
1228
1229 do {
1230 rc = libevdev_next_event(device->evdev,
1231 LIBEVDEV_READ_FLAG_NORMAL,
1232 &e);
1233 if (rc == -EAGAIN) {
1234 break;
1235 } else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
1236 msg("SYN_DROPPED received\n");
1237 goto out;
1238 } else if (rc != LIBEVDEV_READ_STATUS_SUCCESS) {
1239 msg("Error reading event: %s\n", strerror(-rc));
1240 goto out;
1241 }
1242
1243 #define EVENT(t_, c_) (t_ << 16 | c_)
1244 switch (EVENT(e.type, e.code)) {
1245 case EVENT(EV_REL, REL_X):
1246 w->evdev.rel_x = e.value;
1247 break;
1248 case EVENT(EV_REL, REL_Y):
1249 w->evdev.rel_y = e.value;
1250 break;
1251 case EVENT(EV_ABS, ABS_MT_SLOT):
1252 w->evdev.slot = min((unsigned int)e.value,
1253 ARRAY_LENGTH(w->evdev.slots) - 1);
1254 w->evdev.device = (uintptr_t)dev;
1255 break;
1256 case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
1257 w->evdev.slots[w->evdev.slot].active = (e.value != -1);
1258 w->evdev.device = (uintptr_t)dev;
1259 break;
1260 case EVENT(EV_ABS, ABS_X):
1261 w->evdev.x = e.value;
1262 w->evdev.device = (uintptr_t)dev;
1263 break;
1264 case EVENT(EV_ABS, ABS_Y):
1265 w->evdev.y = e.value;
1266 w->evdev.device = (uintptr_t)dev;
1267 break;
1268 case EVENT(EV_ABS, ABS_MT_POSITION_X):
1269 w->evdev.slots[w->evdev.slot].x = e.value;
1270 w->evdev.device = (uintptr_t)dev;
1271 break;
1272 case EVENT(EV_ABS, ABS_MT_POSITION_Y):
1273 w->evdev.slots[w->evdev.slot].y = e.value;
1274 w->evdev.device = (uintptr_t)dev;
1275 break;
1276 }
1277 } while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
1278
1279 gtk_widget_queue_draw(w->area);
1280 out:
1281 return TRUE;
1282 }
1283
1284 static void
register_evdev_device(struct window *w, struct libinput_device *dev)1285 register_evdev_device(struct window *w, struct libinput_device *dev)
1286 {
1287 GIOChannel *c;
1288 struct udev_device *ud;
1289 struct libevdev *evdev;
1290 const char *device_node;
1291 int fd;
1292 struct evdev_device *d;
1293
1294 ud = libinput_device_get_udev_device(dev);
1295 device_node = udev_device_get_devnode(ud);
1296
1297 fd = open(device_node, O_RDONLY|O_NONBLOCK);
1298 if (fd == -1) {
1299 msg("failed to open %s, evdev events unavailable\n", device_node);
1300 goto out;
1301 }
1302
1303 if (libevdev_new_from_fd(fd, &evdev) != 0) {
1304 msg("failed to create context for %s, evdev events unavailable\n",
1305 device_node);
1306 goto out;
1307 }
1308
1309 d = zalloc(sizeof *d);
1310 list_append(&w->evdev_devices, &d->node);
1311 d->fd = fd;
1312 d->evdev = evdev;
1313 d->libinput_device =libinput_device_ref(dev);
1314
1315 c = g_io_channel_unix_new(fd);
1316 g_io_channel_set_encoding(c, NULL, NULL);
1317 d->source_id = g_io_add_watch(c, G_IO_IN,
1318 handle_event_evdev,
1319 d->libinput_device);
1320 fd = -1;
1321 out:
1322 close(fd);
1323 udev_device_unref(ud);
1324 }
1325
1326 static void
unregister_evdev_device(struct window *w, struct libinput_device *dev)1327 unregister_evdev_device(struct window *w, struct libinput_device *dev)
1328 {
1329 struct evdev_device *d;
1330
1331 list_for_each_safe(d, &w->evdev_devices, node) {
1332 if (d->libinput_device != dev)
1333 continue;
1334
1335 list_remove(&d->node);
1336 g_source_remove(d->source_id);
1337 free(libinput_device_get_user_data(d->libinput_device));
1338 libinput_device_unref(d->libinput_device);
1339 libevdev_free(d->evdev);
1340 close(d->fd);
1341 free(d);
1342 w->evdev.last_device = 0;
1343 break;
1344 }
1345 }
1346
1347 static void
handle_event_device_notify(struct libinput_event *ev)1348 handle_event_device_notify(struct libinput_event *ev)
1349 {
1350 struct libinput_device *dev = libinput_event_get_device(ev);
1351 struct libinput *li;
1352 struct window *w;
1353 const char *type;
1354
1355 li = libinput_event_get_context(ev);
1356 w = libinput_get_user_data(li);
1357
1358 if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
1359 type = "added";
1360 register_evdev_device(w, dev);
1361 tools_device_apply_config(libinput_event_get_device(ev),
1362 &w->options);
1363 } else {
1364 type = "removed";
1365 unregister_evdev_device(w, dev);
1366 }
1367
1368 msg("%s %-30s %s\n",
1369 libinput_device_get_sysname(dev),
1370 libinput_device_get_name(dev),
1371 type);
1372
1373 if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
1374 ARRAY_FOR_EACH(w->devices, device) {
1375 if (*device == NULL) {
1376 *device = libinput_device_ref(dev);
1377 break;
1378 }
1379 }
1380 } else {
1381 ARRAY_FOR_EACH(w->devices, device) {
1382 if (*device == dev) {
1383 libinput_device_unref(*device);
1384 *device = NULL;
1385 break;
1386 }
1387 }
1388 }
1389 }
1390
1391 static void
handle_event_motion(struct libinput_event *ev, struct window *w)1392 handle_event_motion(struct libinput_event *ev, struct window *w)
1393 {
1394 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1395 double dx = libinput_event_pointer_get_dx(p),
1396 dy = libinput_event_pointer_get_dy(p);
1397 double dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(p),
1398 dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(p);
1399 struct point point;
1400 const int mask = ARRAY_LENGTH(w->deltas);
1401 size_t idx;
1402
1403 w->pointer.x = clip(w->pointer.x + dx, 0.0, w->width);
1404 w->pointer.y = clip(w->pointer.y + dy, 0.0, w->height);
1405 w->unaccelerated.x = clip(w->unaccelerated.x + dx_unaccel, 0.0, w->width);
1406 w->unaccelerated.y = clip(w->unaccelerated.y + dy_unaccel, 0.0, w->height);
1407
1408 idx = w->ndeltas % mask;
1409 point = w->deltas[idx];
1410 idx = (w->ndeltas + 1) % mask;
1411 point.x += dx_unaccel;
1412 point.y += dy_unaccel;
1413 w->deltas[idx] = point;
1414 w->ndeltas++;
1415 }
1416
1417 static void
handle_event_absmotion(struct libinput_event *ev, struct window *w)1418 handle_event_absmotion(struct libinput_event *ev, struct window *w)
1419 {
1420 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1421 double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
1422 y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
1423
1424 w->abs.x = x;
1425 w->abs.y = y;
1426 }
1427
1428 static void
handle_event_touch(struct libinput_event *ev, struct window *w)1429 handle_event_touch(struct libinput_event *ev, struct window *w)
1430 {
1431 struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
1432 int slot = libinput_event_touch_get_seat_slot(t);
1433 struct touch *touch;
1434 double x, y;
1435
1436 if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
1437 return;
1438
1439 touch = &w->touches[slot];
1440
1441 switch (libinput_event_get_type(ev)) {
1442 case LIBINPUT_EVENT_TOUCH_UP:
1443 touch->state = TOUCH_ENDED;
1444 return;
1445 case LIBINPUT_EVENT_TOUCH_CANCEL:
1446 touch->state = TOUCH_CANCELLED;
1447 return;
1448 default:
1449 break;
1450 }
1451
1452 x = libinput_event_touch_get_x_transformed(t, w->width),
1453 y = libinput_event_touch_get_y_transformed(t, w->height);
1454
1455 touch->state = TOUCH_ACTIVE;
1456 touch->x = (int)x;
1457 touch->y = (int)y;
1458 }
1459
1460 static void
handle_event_axis(struct libinput_event *ev, struct window *w)1461 handle_event_axis(struct libinput_event *ev, struct window *w)
1462 {
1463 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1464 double value;
1465 enum libinput_pointer_axis axis;
1466 enum libinput_event_type type;
1467
1468 type = libinput_event_get_type(ev);
1469
1470 axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
1471 if (libinput_event_pointer_has_axis(p, axis)) {
1472 value = libinput_event_pointer_get_scroll_value(p, axis);
1473 w->scroll.vy += value;
1474 w->scroll.vy = clip(w->scroll.vy, 0, w->height);
1475
1476 if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
1477 w->scroll.vy_discrete += value;
1478 w->scroll.vy_discrete = clip(w->scroll.vy_discrete, 0, w->height);
1479 }
1480 }
1481
1482 axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
1483 if (libinput_event_pointer_has_axis(p, axis)) {
1484 value = libinput_event_pointer_get_scroll_value(p, axis);
1485 w->scroll.hx += value;
1486 w->scroll.hx = clip(w->scroll.hx, 0, w->width);
1487
1488 if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
1489 w->scroll.hx_discrete += value;
1490 w->scroll.hx_discrete = clip(w->scroll.hx_discrete, 0, w->width);
1491 }
1492 }
1493 }
1494
1495 static int
handle_event_keyboard(struct libinput_event *ev, struct window *w)1496 handle_event_keyboard(struct libinput_event *ev, struct window *w)
1497 {
1498 struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
1499 unsigned int key = libinput_event_keyboard_get_key(k);
1500
1501 if (libinput_event_keyboard_get_key_state(k) ==
1502 LIBINPUT_KEY_STATE_RELEASED)
1503 return 0;
1504
1505 switch(key) {
1506 case KEY_ESC:
1507 return 1;
1508 case KEY_UP:
1509 change_ptraccel(w, 0.1);
1510 break;
1511 case KEY_DOWN:
1512 change_ptraccel(w, -0.1);
1513 break;
1514 default:
1515 break;
1516 }
1517
1518 return 0;
1519 }
1520
1521 static void
handle_event_button(struct libinput_event *ev, struct window *w)1522 handle_event_button(struct libinput_event *ev, struct window *w)
1523 {
1524 struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
1525 unsigned int button = libinput_event_pointer_get_button(p);
1526 bool is_press;
1527
1528 is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
1529
1530 switch (button) {
1531 case BTN_LEFT:
1532 w->buttons.l = is_press;
1533 break;
1534 case BTN_RIGHT:
1535 w->buttons.r = is_press;
1536 break;
1537 case BTN_MIDDLE:
1538 w->buttons.m = is_press;
1539 break;
1540 default:
1541 w->buttons.other = is_press;
1542 w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
1543 button);
1544 }
1545
1546 }
1547
1548 static void
handle_event_swipe(struct libinput_event *ev, struct window *w)1549 handle_event_swipe(struct libinput_event *ev, struct window *w)
1550 {
1551 struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
1552 int nfingers;
1553 double dx, dy;
1554
1555 nfingers = libinput_event_gesture_get_finger_count(g);
1556
1557 switch (libinput_event_get_type(ev)) {
1558 case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
1559 w->swipe.nfingers = nfingers;
1560 w->swipe.x = w->width/2;
1561 w->swipe.y = w->height/2;
1562 break;
1563 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
1564 dx = libinput_event_gesture_get_dx(g);
1565 dy = libinput_event_gesture_get_dy(g);
1566 w->swipe.x += dx;
1567 w->swipe.y += dy;
1568 break;
1569 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
1570 w->swipe.nfingers = 0;
1571 w->swipe.x = w->width/2;
1572 w->swipe.y = w->height/2;
1573 break;
1574 default:
1575 abort();
1576 }
1577 }
1578
1579 static void
handle_event_pinch(struct libinput_event *ev, struct window *w)1580 handle_event_pinch(struct libinput_event *ev, struct window *w)
1581 {
1582 struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
1583 int nfingers;
1584 double dx, dy;
1585
1586 nfingers = libinput_event_gesture_get_finger_count(g);
1587
1588 switch (libinput_event_get_type(ev)) {
1589 case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
1590 w->pinch.nfingers = nfingers;
1591 w->pinch.x = w->width/2;
1592 w->pinch.y = w->height/2;
1593 break;
1594 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
1595 dx = libinput_event_gesture_get_dx(g);
1596 dy = libinput_event_gesture_get_dy(g);
1597 w->pinch.x += dx;
1598 w->pinch.y += dy;
1599 w->pinch.scale = libinput_event_gesture_get_scale(g);
1600 w->pinch.angle += libinput_event_gesture_get_angle_delta(g);
1601 break;
1602 case LIBINPUT_EVENT_GESTURE_PINCH_END:
1603 w->pinch.nfingers = 0;
1604 w->pinch.x = w->width/2;
1605 w->pinch.y = w->height/2;
1606 w->pinch.angle = 0.0;
1607 w->pinch.scale = 1.0;
1608 break;
1609 default:
1610 abort();
1611 }
1612 }
1613
1614 static void
handle_event_hold(struct libinput_event *ev, struct window *w)1615 handle_event_hold(struct libinput_event *ev, struct window *w)
1616 {
1617 struct libinput_event_gesture *g = libinput_event_get_gesture_event(ev);
1618 int nfingers;
1619
1620 nfingers = libinput_event_gesture_get_finger_count(g);
1621
1622 switch (libinput_event_get_type(ev)) {
1623 case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
1624 w->hold.nfingers = nfingers;
1625 w->hold.active = true;
1626 break;
1627 case LIBINPUT_EVENT_GESTURE_HOLD_END:
1628 w->hold.nfingers = nfingers;
1629 w->hold.active = false;
1630 break;
1631 default:
1632 abort();
1633 }
1634 }
1635
1636 static void
handle_event_tablet(struct libinput_event *ev, struct window *w)1637 handle_event_tablet(struct libinput_event *ev, struct window *w)
1638 {
1639 struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
1640 double x, y;
1641 struct point point;
1642 int idx;
1643 const int mask = ARRAY_LENGTH(w->tool.deltas);
1644 bool is_press;
1645 unsigned int button;
1646
1647 x = libinput_event_tablet_tool_get_x_transformed(t, w->width);
1648 y = libinput_event_tablet_tool_get_y_transformed(t, w->height);
1649
1650 switch (libinput_event_get_type(ev)) {
1651 case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
1652 if (libinput_event_tablet_tool_get_proximity_state(t) ==
1653 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
1654 w->tool.x_in = 0;
1655 w->tool.y_in = 0;
1656 w->tool.x_down = 0;
1657 w->tool.y_down = 0;
1658 w->tool.x_up = 0;
1659 w->tool.y_up = 0;
1660 } else {
1661 w->tool.x_in = x;
1662 w->tool.y_in = y;
1663 w->tool.ndeltas = 0;
1664 w->tool.deltas[0].x = w->width/2;
1665 w->tool.deltas[0].y = w->height/2;
1666 }
1667 break;
1668 case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1669 w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
1670 w->tool.distance = libinput_event_tablet_tool_get_distance(t);
1671 w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
1672 w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
1673 if (libinput_event_tablet_tool_get_tip_state(t) ==
1674 LIBINPUT_TABLET_TOOL_TIP_DOWN) {
1675 w->tool.x_down = x;
1676 w->tool.y_down = y;
1677 w->tool.is_down = true;
1678 } else {
1679 w->tool.x_up = x;
1680 w->tool.y_up = y;
1681 w->tool.is_down = false;
1682 }
1683 _fallthrough_;
1684 case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1685 w->tool.x = x;
1686 w->tool.y = y;
1687 w->tool.pressure = libinput_event_tablet_tool_get_pressure(t);
1688 w->tool.distance = libinput_event_tablet_tool_get_distance(t);
1689 w->tool.tilt_x = libinput_event_tablet_tool_get_tilt_x(t);
1690 w->tool.tilt_y = libinput_event_tablet_tool_get_tilt_y(t);
1691 w->tool.rotation = libinput_event_tablet_tool_get_rotation(t);
1692 w->tool.size_major = libinput_event_tablet_tool_get_size_major(t);
1693 w->tool.size_minor = libinput_event_tablet_tool_get_size_minor(t);
1694
1695 /* Add the delta to the last position and store them as abs
1696 * coordinates */
1697 idx = w->tool.ndeltas % mask;
1698 point = w->tool.deltas[idx];
1699
1700 idx = (w->tool.ndeltas + 1) % mask;
1701 point.x += libinput_event_tablet_tool_get_dx(t);
1702 point.y += libinput_event_tablet_tool_get_dy(t);
1703 w->tool.deltas[idx] = point;
1704 w->tool.ndeltas++;
1705 break;
1706 case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1707 is_press = libinput_event_tablet_tool_get_button_state(t) == LIBINPUT_BUTTON_STATE_PRESSED;
1708 button = libinput_event_tablet_tool_get_button(t);
1709
1710 w->buttons.other = is_press;
1711 w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
1712 button);
1713 break;
1714 default:
1715 abort();
1716 }
1717 }
1718
1719 static void
handle_event_tablet_pad(struct libinput_event *ev, struct window *w)1720 handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
1721 {
1722 struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
1723 bool is_press;
1724 unsigned int button;
1725 static const char *pad_buttons[] = {
1726 "Pad 0", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5",
1727 "Pad 6", "Pad 7", "Pad 8", "Pad 9", "Pad >= 10"
1728 };
1729 double position;
1730 double number;
1731
1732 switch (libinput_event_get_type(ev)) {
1733 case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1734 is_press = libinput_event_tablet_pad_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
1735 button = libinput_event_tablet_pad_get_button_number(p);
1736 w->buttons.other = is_press;
1737 w->buttons.other_name = pad_buttons[min(button, 10)];
1738 break;
1739 case LIBINPUT_EVENT_TABLET_PAD_RING:
1740 position = libinput_event_tablet_pad_get_ring_position(p);
1741 number = libinput_event_tablet_pad_get_ring_number(p);
1742 w->pad.ring.number = number;
1743 w->pad.ring.position = position;
1744 break;
1745 case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1746 position = libinput_event_tablet_pad_get_strip_position(p);
1747 number = libinput_event_tablet_pad_get_strip_number(p);
1748 w->pad.strip.number = number;
1749 w->pad.strip.position = position;
1750 break;
1751 default:
1752 abort();
1753 }
1754 }
1755
1756 static gboolean
handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)1757 handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
1758 {
1759 struct libinput *li = data;
1760 struct window *w = libinput_get_user_data(li);
1761 struct libinput_event *ev;
1762
1763 tools_dispatch(li);
1764
1765 while ((ev = libinput_get_event(li))) {
1766 switch (libinput_event_get_type(ev)) {
1767 case LIBINPUT_EVENT_NONE:
1768 abort();
1769 case LIBINPUT_EVENT_DEVICE_ADDED:
1770 case LIBINPUT_EVENT_DEVICE_REMOVED:
1771 handle_event_device_notify(ev);
1772 break;
1773 case LIBINPUT_EVENT_POINTER_MOTION:
1774 handle_event_motion(ev, w);
1775 break;
1776 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
1777 handle_event_absmotion(ev, w);
1778 break;
1779 case LIBINPUT_EVENT_TOUCH_DOWN:
1780 case LIBINPUT_EVENT_TOUCH_MOTION:
1781 case LIBINPUT_EVENT_TOUCH_UP:
1782 case LIBINPUT_EVENT_TOUCH_CANCEL:
1783 handle_event_touch(ev, w);
1784 break;
1785 case LIBINPUT_EVENT_TOUCH_FRAME:
1786 break;
1787 case LIBINPUT_EVENT_POINTER_AXIS:
1788 /* ignore */
1789 break;
1790 case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
1791 case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
1792 case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
1793 handle_event_axis(ev, w);
1794 break;
1795 case LIBINPUT_EVENT_POINTER_BUTTON:
1796 handle_event_button(ev, w);
1797 break;
1798 case LIBINPUT_EVENT_KEYBOARD_KEY:
1799 if (handle_event_keyboard(ev, w)) {
1800 libinput_event_destroy(ev);
1801 window_quit(w);
1802 return FALSE;
1803 }
1804 break;
1805 case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
1806 case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
1807 case LIBINPUT_EVENT_GESTURE_SWIPE_END:
1808 handle_event_swipe(ev, w);
1809 break;
1810 case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
1811 case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
1812 case LIBINPUT_EVENT_GESTURE_PINCH_END:
1813 handle_event_pinch(ev, w);
1814 break;
1815 case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
1816 case LIBINPUT_EVENT_GESTURE_HOLD_END:
1817 handle_event_hold(ev, w);
1818 break;
1819 case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
1820 case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
1821 case LIBINPUT_EVENT_TABLET_TOOL_TIP:
1822 case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
1823 handle_event_tablet(ev, w);
1824 break;
1825 case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
1826 case LIBINPUT_EVENT_TABLET_PAD_RING:
1827 case LIBINPUT_EVENT_TABLET_PAD_STRIP:
1828 handle_event_tablet_pad(ev, w);
1829 break;
1830 case LIBINPUT_EVENT_TABLET_PAD_KEY:
1831 break;
1832 case LIBINPUT_EVENT_SWITCH_TOGGLE:
1833 break;
1834 }
1835
1836 libinput_event_destroy(ev);
1837 }
1838 gtk_widget_queue_draw(w->area);
1839
1840 return TRUE;
1841 }
1842
1843 static void
sockets_init(struct libinput *li)1844 sockets_init(struct libinput *li)
1845 {
1846 GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
1847
1848 g_io_channel_set_encoding(c, NULL, NULL);
1849 g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
1850 }
1851
1852 static void
usage(void)1853 usage(void) {
1854 printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
1855 }
1856
1857 static gboolean
signal_handler(void *data)1858 signal_handler(void *data)
1859 {
1860 struct libinput *li = data;
1861 struct window *w = libinput_get_user_data(li);
1862
1863 window_quit(w);
1864
1865 return FALSE;
1866 }
1867
1868 int
main(int argc, char **argv)1869 main(int argc, char **argv)
1870 {
1871 struct window w = {0};
1872 struct tools_options options;
1873 struct libinput *li;
1874 enum tools_backend backend = BACKEND_NONE;
1875 const char *seat_or_device[2] = {"seat0", NULL};
1876 bool verbose = false;
1877 bool gtk_init = false;
1878
1879 #if HAVE_GTK4
1880 gtk_init = gtk_init_check();
1881 #else
1882 gtk_init = gtk_init_check(&argc, &argv);
1883 #endif
1884
1885 if (!gtk_init)
1886 return 77;
1887
1888 tools_init_options(&options);
1889
1890 while (1) {
1891 int c;
1892 int option_index = 0;
1893 enum {
1894 OPT_DEVICE = 1,
1895 OPT_UDEV,
1896 OPT_GRAB,
1897 OPT_VERBOSE,
1898 };
1899 static struct option opts[] = {
1900 CONFIGURATION_OPTIONS,
1901 { "help", no_argument, 0, 'h' },
1902 { "device", required_argument, 0, OPT_DEVICE },
1903 { "udev", required_argument, 0, OPT_UDEV },
1904 { "grab", no_argument, 0, OPT_GRAB },
1905 { "verbose", no_argument, 0, OPT_VERBOSE },
1906 { 0, 0, 0, 0}
1907 };
1908
1909 c = getopt_long(argc, argv, "h", opts, &option_index);
1910 if (c == -1)
1911 break;
1912
1913 switch(c) {
1914 case '?':
1915 exit(EXIT_INVALID_USAGE);
1916 break;
1917 case 'h':
1918 usage();
1919 exit(0);
1920 break;
1921 case OPT_DEVICE:
1922 backend = BACKEND_DEVICE;
1923 seat_or_device[0] = optarg;
1924 break;
1925 case OPT_UDEV:
1926 backend = BACKEND_UDEV;
1927 seat_or_device[0] = optarg;
1928 break;
1929 case OPT_GRAB:
1930 w.grab = true;
1931 break;
1932 case OPT_VERBOSE:
1933 verbose = true;
1934 break;
1935 default:
1936 if (tools_parse_option(c, optarg, &options) != 0) {
1937 usage();
1938 return EXIT_INVALID_USAGE;
1939 }
1940 break;
1941 }
1942
1943 }
1944
1945 if (optind < argc) {
1946 if (optind < argc - 1 || backend != BACKEND_NONE) {
1947 usage();
1948 return EXIT_INVALID_USAGE;
1949 }
1950 backend = BACKEND_DEVICE;
1951 seat_or_device[0] = argv[optind];
1952 } else if (backend == BACKEND_NONE) {
1953 backend = BACKEND_UDEV;
1954 }
1955
1956 li = tools_open_backend(backend, seat_or_device, verbose, &w.grab);
1957 if (!li)
1958 return EXIT_FAILURE;
1959
1960 libinput_set_user_data(li, &w);
1961
1962 g_unix_signal_add(SIGINT, signal_handler, li);
1963
1964 window_init(&w);
1965 w.options = options;
1966 sockets_init(li);
1967 handle_event_libinput(NULL, 0, li);
1968
1969 w.event_loop = g_main_loop_new(NULL, FALSE);
1970 g_main_loop_run(w.event_loop);
1971
1972 window_unlock_pointer(&w);
1973 window_cleanup(&w);
1974 libinput_unref(li);
1975
1976 return EXIT_SUCCESS;
1977 }
1978