Fix documentation for libinput_log_set_handler
[platform/upstream/libinput.git] / tools / event-gui.c
1 /*
2  * Copyright © 2014 Red Hat, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 #define _GNU_SOURCE
23 #include <config.h>
24
25 #include <linux/input.h>
26
27 #include <cairo.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include <gtk/gtk.h>
38 #include <glib.h>
39
40 #include <libinput.h>
41 #include <libinput-util.h>
42
43 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
44
45 struct touch {
46         int active;
47         int x, y;
48 };
49
50 struct window {
51         GtkWidget *win;
52         GtkWidget *area;
53         int width, height; /* of window */
54
55         /* sprite position */
56         double x, y;
57
58         /* abs position */
59         int absx, absy;
60
61         /* scroll bar positions */
62         int vx, vy;
63         int hx, hy;
64
65         /* touch positions */
66         struct touch touches[32];
67
68         /* l/m/r mouse buttons */
69         int l, m, r;
70
71         struct libinput_device *devices[50];
72 };
73
74 static int
75 error(const char *fmt, ...)
76 {
77         va_list args;
78         fprintf(stderr, "error: ");
79
80         va_start(args, fmt);
81         vfprintf(stderr, fmt, args);
82         va_end(args);
83
84         return EXIT_FAILURE;
85 }
86
87 static void
88 msg(const char *fmt, ...)
89 {
90         va_list args;
91         printf("info: ");
92
93         va_start(args, fmt);
94         vprintf(fmt, args);
95         va_end(args);
96 }
97
98 static void
99 usage(void)
100 {
101         printf("%s [path/to/device]\n", program_invocation_short_name);
102 }
103
104 static gboolean
105 draw(GtkWidget *widget, cairo_t *cr, gpointer data)
106 {
107         struct window *w = data;
108         struct touch *t;
109
110         cairo_set_source_rgb(cr, 1, 1, 1);
111         cairo_rectangle(cr, 0, 0, w->width, w->height);
112         cairo_fill(cr);
113
114         /* draw pointer sprite */
115         cairo_set_source_rgb(cr, 0, 0, 0);
116         cairo_save(cr);
117         cairo_move_to(cr, w->x, w->y);
118         cairo_rel_line_to(cr, 10, 15);
119         cairo_rel_line_to(cr, -10, 0);
120         cairo_rel_line_to(cr, 0, -15);
121         cairo_fill(cr);
122         cairo_restore(cr);
123
124         /* draw scroll bars */
125         cairo_set_source_rgb(cr, .4, .8, 0);
126
127         cairo_save(cr);
128         cairo_rectangle(cr, w->vx - 10, w->vy - 20, 20, 40);
129         cairo_rectangle(cr, w->hx - 20, w->hy - 10, 40, 20);
130         cairo_fill(cr);
131         cairo_restore(cr);
132
133         /* touch points */
134         cairo_set_source_rgb(cr, .8, .2, .2);
135
136         ARRAY_FOR_EACH(w->touches, t) {
137                 cairo_save(cr);
138                 cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
139                 cairo_fill(cr);
140                 cairo_restore(cr);
141         }
142
143         /* abs position */
144         cairo_set_source_rgb(cr, .2, .4, .8);
145
146         cairo_save(cr);
147         cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI);
148         cairo_fill(cr);
149         cairo_restore(cr);
150
151         /* lmr buttons */
152         cairo_save(cr);
153         if (w->l || w->m || w->r) {
154                 cairo_set_source_rgb(cr, .2, .8, .8);
155                 if (w->l)
156                         cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
157                 if (w->m)
158                         cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
159                 if (w->r)
160                         cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
161                 cairo_fill(cr);
162         }
163
164         cairo_set_source_rgb(cr, 0, 0, 0);
165         cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
166         cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
167         cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
168         cairo_stroke(cr);
169         cairo_restore(cr);
170
171         return TRUE;
172 }
173
174 static void
175 map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
176 {
177         struct window *w = data;
178
179         gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
180
181         w->x = w->width/2;
182         w->y = w->height/2;
183
184         w->vx = w->width/2;
185         w->vy = w->height/2;
186         w->hx = w->width/2;
187         w->hy = w->height/2;
188
189         g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
190
191         gdk_window_set_cursor(gtk_widget_get_window(w->win),
192                               gdk_cursor_new(GDK_BLANK_CURSOR));
193 }
194
195 static void
196 window_init(struct window *w)
197 {
198         memset(w, 0, sizeof(*w));
199
200         w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
201         gtk_widget_set_events(w->win, 0);
202         gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
203         gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
204         gtk_window_maximize(GTK_WINDOW(w->win));
205         gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
206         gtk_widget_realize(w->win);
207         g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
208         g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(gtk_main_quit), NULL);
209
210         w->area = gtk_drawing_area_new();
211         gtk_widget_set_events(w->area, 0);
212         gtk_container_add(GTK_CONTAINER(w->win), w->area);
213         gtk_widget_show_all(w->win);
214 }
215
216 static void
217 window_cleanup(struct window *w)
218 {
219         struct libinput_device **dev;
220         ARRAY_FOR_EACH(w->devices, dev) {
221                 if (*dev)
222                         libinput_device_unref(*dev);
223         }
224 }
225
226 static void
227 change_ptraccel(struct window *w, double amount)
228 {
229         struct libinput_device **dev;
230
231         ARRAY_FOR_EACH(w->devices, dev) {
232                 double speed;
233                 enum libinput_config_status status;
234
235                 if (*dev == NULL)
236                         continue;
237
238                 if (!libinput_device_config_accel_is_available(*dev))
239                         continue;
240
241                 speed = libinput_device_config_accel_get_speed(*dev);
242                 speed = clip(speed + amount, -1, 1);
243
244                 status = libinput_device_config_accel_set_speed(*dev, speed);
245
246                 if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
247                         msg("%s: failed to change accel to %.2f (%s)\n",
248                             libinput_device_get_name(*dev),
249                             speed,
250                             libinput_config_status_to_str(status));
251                 } else {
252                         printf("%s: speed is %.2f\n",
253                                libinput_device_get_name(*dev),
254                                speed);
255                 }
256
257         }
258 }
259
260
261 static void
262 handle_event_device_notify(struct libinput_event *ev)
263 {
264         struct libinput_device *dev = libinput_event_get_device(ev);
265         struct libinput *li;
266         struct window *w;
267         const char *type;
268         int i;
269
270         if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
271                 type = "added";
272         else
273                 type = "removed";
274
275         msg("%s %s\n", libinput_device_get_sysname(dev), type);
276
277         if (libinput_device_config_tap_get_finger_count(dev) > 0) {
278                 enum libinput_config_status status;
279                 status = libinput_device_config_tap_set_enabled(dev,
280                                                                 LIBINPUT_CONFIG_TAP_ENABLED);
281                 if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
282                         error("%s: Failed to enable tapping\n",
283                               libinput_device_get_sysname(dev));
284         }
285
286         li = libinput_event_get_context(ev);
287         w = libinput_get_user_data(li);
288
289         if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
290                 for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
291                         if (w->devices[i] == NULL) {
292                                 w->devices[i] = libinput_device_ref(dev);
293                                 break;
294                         }
295                 }
296         } else  {
297                 for (i = 0; i < ARRAY_LENGTH(w->devices); i++) {
298                         if (w->devices[i] == dev) {
299                                 libinput_device_unref(w->devices[i]);
300                                 w->devices[i] = NULL;
301                                 break;
302                         }
303                 }
304         }
305 }
306
307 static void
308 handle_event_motion(struct libinput_event *ev, struct window *w)
309 {
310         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
311         double dx = libinput_event_pointer_get_dx(p),
312                dy = libinput_event_pointer_get_dy(p);
313
314         w->x += dx;
315         w->y += dy;
316         w->x = clip(w->x, 0.0, w->width);
317         w->y = clip(w->y, 0.0, w->height);
318 }
319
320 static void
321 handle_event_absmotion(struct libinput_event *ev, struct window *w)
322 {
323         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
324         double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
325                y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
326
327         w->absx = x;
328         w->absy = y;
329 }
330
331 static void
332 handle_event_touch(struct libinput_event *ev, struct window *w)
333 {
334         struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
335         int slot = libinput_event_touch_get_seat_slot(t);
336         struct touch *touch;
337         double x, y;
338
339         if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
340                 return;
341
342         touch = &w->touches[slot];
343
344         if (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_UP) {
345                 touch->active = 0;
346                 return;
347         }
348
349         x = libinput_event_touch_get_x_transformed(t, w->width),
350         y = libinput_event_touch_get_y_transformed(t, w->height);
351
352         touch->active = 1;
353         touch->x = (int)x;
354         touch->y = (int)y;
355 }
356
357 static void
358 handle_event_axis(struct libinput_event *ev, struct window *w)
359 {
360         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
361         enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p);
362         double v = libinput_event_pointer_get_axis_value(p);
363
364         switch (axis) {
365         case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
366                 w->vy += (int)v;
367                 w->vy = clip(w->vy, 0, w->height);
368                 break;
369         case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
370                 w->hx += (int)v;
371                 w->hx = clip(w->hx, 0, w->width);
372                 break;
373         default:
374                 abort();
375         }
376 }
377
378 static int
379 handle_event_keyboard(struct libinput_event *ev, struct window *w)
380 {
381         struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
382         unsigned int key = libinput_event_keyboard_get_key(k);
383
384         if (libinput_event_keyboard_get_key_state(k) ==
385             LIBINPUT_KEY_STATE_RELEASED)
386                 return 0;
387
388         switch(key) {
389         case KEY_ESC:
390                 return 1;
391         case KEY_UP:
392                 change_ptraccel(w, 0.1);
393                 break;
394         case KEY_DOWN:
395                 change_ptraccel(w, -0.1);
396                 break;
397         default:
398                 break;
399         }
400
401         return 0;
402 }
403
404 static void
405 handle_event_button(struct libinput_event *ev, struct window *w)
406 {
407         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
408         unsigned int button = libinput_event_pointer_get_button(p);
409         int is_press;
410
411         is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
412
413         switch (button) {
414         case BTN_LEFT:
415                 w->l = is_press;
416                 break;
417         case BTN_RIGHT:
418                 w->r = is_press;
419                 break;
420         case BTN_MIDDLE:
421                 w->m = is_press;
422                 break;
423         }
424
425 }
426
427 static gboolean
428 handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
429 {
430         struct libinput *li = data;
431         struct window *w = libinput_get_user_data(li);
432         struct libinput_event *ev;
433
434         libinput_dispatch(li);
435
436         while ((ev = libinput_get_event(li))) {
437                 switch (libinput_event_get_type(ev)) {
438                 case LIBINPUT_EVENT_NONE:
439                         abort();
440                 case LIBINPUT_EVENT_DEVICE_ADDED:
441                 case LIBINPUT_EVENT_DEVICE_REMOVED:
442                         handle_event_device_notify(ev);
443                         break;
444                 case LIBINPUT_EVENT_POINTER_MOTION:
445                         handle_event_motion(ev, w);
446                         break;
447                 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
448                         handle_event_absmotion(ev, w);
449                         break;
450                 case LIBINPUT_EVENT_TOUCH_DOWN:
451                 case LIBINPUT_EVENT_TOUCH_MOTION:
452                 case LIBINPUT_EVENT_TOUCH_UP:
453                         handle_event_touch(ev, w);
454                         break;
455                 case LIBINPUT_EVENT_POINTER_AXIS:
456                         handle_event_axis(ev, w);
457                         break;
458                 case LIBINPUT_EVENT_TOUCH_CANCEL:
459                 case LIBINPUT_EVENT_TOUCH_FRAME:
460                         break;
461                 case LIBINPUT_EVENT_POINTER_BUTTON:
462                         handle_event_button(ev, w);
463                         break;
464                 case LIBINPUT_EVENT_KEYBOARD_KEY:
465                         if (handle_event_keyboard(ev, w)) {
466                                 libinput_event_destroy(ev);
467                                 gtk_main_quit();
468                                 return FALSE;
469                         }
470                         break;
471                 }
472
473                 libinput_event_destroy(ev);
474                 libinput_dispatch(li);
475         }
476         gtk_widget_queue_draw(w->area);
477
478         return TRUE;
479 }
480
481 static void
482 sockets_init(struct libinput *li)
483 {
484         GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
485
486         g_io_channel_set_encoding(c, NULL, NULL);
487         g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
488 }
489
490 static int
491 parse_opts(int argc, char *argv[])
492 {
493         while (1) {
494                 static struct option long_options[] = {
495                         { "help", no_argument, 0, 'h' },
496                 };
497
498                 int option_index = 0;
499                 int c;
500
501                 c = getopt_long(argc, argv, "h", long_options,
502                                 &option_index);
503                 if (c == -1)
504                         break;
505
506                 switch(c) {
507                 case 'h':
508                         usage();
509                         return 0;
510                 default:
511                         usage();
512                         return 1;
513                 }
514         }
515
516         return 0;
517 }
518
519 static int
520 open_restricted(const char *path, int flags, void *user_data)
521 {
522         int fd = open(path, flags);
523         return fd < 0 ? -errno : fd;
524 }
525
526 static void
527 close_restricted(int fd, void *user_data)
528 {
529         close(fd);
530 }
531
532 static const struct libinput_interface interface = {
533         .open_restricted = open_restricted,
534         .close_restricted = close_restricted,
535 };
536
537 int
538 main(int argc, char *argv[])
539 {
540         struct window w;
541         struct libinput *li;
542         struct udev *udev;
543
544         gtk_init(&argc, &argv);
545
546         if (parse_opts(argc, argv) != 0)
547                 return 1;
548
549         udev = udev_new();
550         if (!udev)
551                 error("Failed to initialize udev\n");
552
553         li = libinput_udev_create_context(&interface, &w, udev);
554         if (!li || libinput_udev_assign_seat(li, "seat0") != 0)
555                 error("Failed to initialize context from udev\n");
556
557         window_init(&w);
558         sockets_init(li);
559
560         gtk_main();
561
562         window_cleanup(&w);
563         libinput_unref(li);
564         udev_unref(udev);
565
566         return 0;
567 }