tools/event-gui: Silence a couple of compiler warnings
[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
72 static int
73 error(const char *fmt, ...)
74 {
75         va_list args;
76         fprintf(stderr, "error: ");
77
78         va_start(args, fmt);
79         vfprintf(stderr, fmt, args);
80         va_end(args);
81
82         return EXIT_FAILURE;
83 }
84
85 static void
86 msg(const char *fmt, ...)
87 {
88         va_list args;
89         printf("info: ");
90
91         va_start(args, fmt);
92         vprintf(fmt, args);
93         va_end(args);
94 }
95
96 static void
97 usage(void)
98 {
99         printf("%s [path/to/device]\n", program_invocation_short_name);
100 }
101
102 static gboolean
103 draw(GtkWidget *widget, cairo_t *cr, gpointer data)
104 {
105         struct window *w = data;
106         struct touch *t;
107
108         cairo_set_source_rgb(cr, 1, 1, 1);
109         cairo_rectangle(cr, 0, 0, w->width, w->height);
110         cairo_fill(cr);
111
112         /* draw pointer sprite */
113         cairo_set_source_rgb(cr, 0, 0, 0);
114         cairo_save(cr);
115         cairo_move_to(cr, w->x, w->y);
116         cairo_rel_line_to(cr, 10, 15);
117         cairo_rel_line_to(cr, -10, 0);
118         cairo_rel_line_to(cr, 0, -15);
119         cairo_fill(cr);
120         cairo_restore(cr);
121
122         /* draw scroll bars */
123         cairo_set_source_rgb(cr, .4, .8, 0);
124
125         cairo_save(cr);
126         cairo_rectangle(cr, w->vx - 10, w->vy - 20, 20, 40);
127         cairo_rectangle(cr, w->hx - 20, w->hy - 10, 40, 20);
128         cairo_fill(cr);
129         cairo_restore(cr);
130
131         /* touch points */
132         cairo_set_source_rgb(cr, .8, .2, .2);
133
134         ARRAY_FOR_EACH(w->touches, t) {
135                 cairo_save(cr);
136                 cairo_arc(cr, t->x, t->y, 10, 0, 2 * M_PI);
137                 cairo_fill(cr);
138                 cairo_restore(cr);
139         }
140
141         /* abs position */
142         cairo_set_source_rgb(cr, .2, .4, .8);
143
144         cairo_save(cr);
145         cairo_arc(cr, w->absx, w->absy, 10, 0, 2 * M_PI);
146         cairo_fill(cr);
147         cairo_restore(cr);
148
149         /* lmr buttons */
150         cairo_save(cr);
151         if (w->l || w->m || w->r) {
152                 cairo_set_source_rgb(cr, .2, .8, .8);
153                 if (w->l)
154                         cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
155                 if (w->m)
156                         cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
157                 if (w->r)
158                         cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
159                 cairo_fill(cr);
160         }
161
162         cairo_set_source_rgb(cr, 0, 0, 0);
163         cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
164         cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
165         cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
166         cairo_stroke(cr);
167         cairo_restore(cr);
168
169         return TRUE;
170 }
171
172 static void
173 map_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
174 {
175         struct window *w = data;
176
177         gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
178
179         w->x = w->width/2;
180         w->y = w->height/2;
181
182         w->vx = w->width/2;
183         w->vy = w->height/2;
184         w->hx = w->width/2;
185         w->hy = w->height/2;
186
187         g_signal_connect(G_OBJECT(w->area), "draw", G_CALLBACK(draw), w);
188
189         gdk_window_set_cursor(gtk_widget_get_window(w->win),
190                               gdk_cursor_new(GDK_BLANK_CURSOR));
191 }
192
193 static void
194 window_init(struct window *w)
195 {
196         memset(w, 0, sizeof(*w));
197
198         w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
199         gtk_widget_set_events(w->win, 0);
200         gtk_window_set_title(GTK_WINDOW(w->win), "libinput debugging tool");
201         gtk_window_set_default_size(GTK_WINDOW(w->win), 1024, 768);
202         gtk_window_maximize(GTK_WINDOW(w->win));
203         gtk_window_set_resizable(GTK_WINDOW(w->win), TRUE);
204         gtk_widget_realize(w->win);
205         g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
206         g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(gtk_main_quit), NULL);
207
208         w->area = gtk_drawing_area_new();
209         gtk_widget_set_events(w->area, 0);
210         gtk_container_add(GTK_CONTAINER(w->win), w->area);
211         gtk_widget_show_all(w->win);
212 }
213
214 static void
215 handle_event_device_notify(struct libinput_event *ev)
216 {
217         struct libinput_device *dev = libinput_event_get_device(ev);
218         const char *type;
219
220         if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
221                 type = "added";
222         else
223                 type = "removed";
224
225         msg("%s %s\n", libinput_device_get_sysname(dev), type);
226 }
227
228 static void
229 handle_event_motion(struct libinput_event *ev, struct window *w)
230 {
231         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
232         double dx = libinput_event_pointer_get_dx(p),
233                dy = libinput_event_pointer_get_dy(p);
234
235         w->x += dx;
236         w->y += dy;
237         w->x = clip(w->x, 0.0, w->width);
238         w->y = clip(w->y, 0.0, w->height);
239 }
240
241 static void
242 handle_event_absmotion(struct libinput_event *ev, struct window *w)
243 {
244         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
245         double x = libinput_event_pointer_get_absolute_x_transformed(p, w->width),
246                y = libinput_event_pointer_get_absolute_y_transformed(p, w->height);
247
248         w->absx = x;
249         w->absy = y;
250 }
251
252 static void
253 handle_event_touch(struct libinput_event *ev, struct window *w)
254 {
255         struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
256         int slot = libinput_event_touch_get_seat_slot(t);
257         struct touch *touch;
258         double x, y;
259
260         if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
261                 return;
262
263         touch = &w->touches[slot];
264
265         if (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_UP) {
266                 touch->active = 0;
267                 return;
268         }
269
270         x = libinput_event_touch_get_x_transformed(t, w->width),
271         y = libinput_event_touch_get_y_transformed(t, w->height);
272
273         touch->active = 1;
274         touch->x = (int)x;
275         touch->y = (int)y;
276 }
277
278 static void
279 handle_event_axis(struct libinput_event *ev, struct window *w)
280 {
281         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
282         enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p);
283         double v = libinput_event_pointer_get_axis_value(p);
284
285         switch (axis) {
286         case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
287                 w->vy += (int)v;
288                 w->vy = clip(w->vy, 0, w->height);
289                 break;
290         case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
291                 w->hx += (int)v;
292                 w->hx = clip(w->hx, 0, w->width);
293                 break;
294         default:
295                 abort();
296         }
297 }
298
299 static int
300 handle_event_keyboard(struct libinput_event *ev, struct window *w)
301 {
302         struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
303
304         if (libinput_event_keyboard_get_key(k) == KEY_ESC)
305                 return 1;
306
307         return 0;
308 }
309
310 static void
311 handle_event_button(struct libinput_event *ev, struct window *w)
312 {
313         struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
314         unsigned int button = libinput_event_pointer_get_button(p);
315         int is_press;
316
317         is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
318
319         switch (button) {
320         case BTN_LEFT:
321                 w->l = is_press;
322                 break;
323         case BTN_RIGHT:
324                 w->r = is_press;
325                 break;
326         case BTN_MIDDLE:
327                 w->m = is_press;
328                 break;
329         }
330
331 }
332
333 static gboolean
334 handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
335 {
336         struct libinput *li = data;
337         struct window *w = libinput_get_user_data(li);
338         struct libinput_event *ev;
339
340         libinput_dispatch(li);
341
342         while ((ev = libinput_get_event(li))) {
343                 switch (libinput_event_get_type(ev)) {
344                 case LIBINPUT_EVENT_NONE:
345                         abort();
346                 case LIBINPUT_EVENT_DEVICE_ADDED:
347                 case LIBINPUT_EVENT_DEVICE_REMOVED:
348                         handle_event_device_notify(ev);
349                         break;
350                 case LIBINPUT_EVENT_POINTER_MOTION:
351                         handle_event_motion(ev, w);
352                         break;
353                 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
354                         handle_event_absmotion(ev, w);
355                         break;
356                 case LIBINPUT_EVENT_TOUCH_DOWN:
357                 case LIBINPUT_EVENT_TOUCH_MOTION:
358                 case LIBINPUT_EVENT_TOUCH_UP:
359                         handle_event_touch(ev, w);
360                         break;
361                 case LIBINPUT_EVENT_POINTER_AXIS:
362                         handle_event_axis(ev, w);
363                         break;
364                 case LIBINPUT_EVENT_TOUCH_CANCEL:
365                 case LIBINPUT_EVENT_TOUCH_FRAME:
366                         break;
367                 case LIBINPUT_EVENT_POINTER_BUTTON:
368                         handle_event_button(ev, w);
369                         break;
370                 case LIBINPUT_EVENT_KEYBOARD_KEY:
371                         if (handle_event_keyboard(ev, w)) {
372                                 libinput_event_destroy(ev);
373                                 gtk_main_quit();
374                                 return FALSE;
375                         }
376                         break;
377                 }
378
379                 libinput_event_destroy(ev);
380                 libinput_dispatch(li);
381         }
382         gtk_widget_queue_draw(w->area);
383
384         return TRUE;
385 }
386
387 static void
388 sockets_init(struct libinput *li)
389 {
390         GIOChannel *c = g_io_channel_unix_new(libinput_get_fd(li));
391
392         g_io_channel_set_encoding(c, NULL, NULL);
393         g_io_add_watch(c, G_IO_IN, handle_event_libinput, li);
394 }
395
396 static int
397 parse_opts(int argc, char *argv[])
398 {
399         while (1) {
400                 static struct option long_options[] = {
401                         { "help", no_argument, 0, 'h' },
402                 };
403
404                 int option_index = 0;
405                 int c;
406
407                 c = getopt_long(argc, argv, "h", long_options,
408                                 &option_index);
409                 if (c == -1)
410                         break;
411
412                 switch(c) {
413                 case 'h':
414                         usage();
415                         return 0;
416                 default:
417                         usage();
418                         return 1;
419                 }
420         }
421
422         return 0;
423 }
424
425
426 static int
427 open_restricted(const char *path, int flags, void *user_data)
428 {
429         int fd = open(path, flags);
430         return fd < 0 ? -errno : fd;
431 }
432
433 static void
434 close_restricted(int fd, void *user_data)
435 {
436         close(fd);
437 }
438
439 static const struct libinput_interface interface = {
440         .open_restricted = open_restricted,
441         .close_restricted = close_restricted,
442 };
443
444 int
445 main(int argc, char *argv[])
446 {
447         struct window w;
448         struct libinput *li;
449         struct udev *udev;
450
451         gtk_init(&argc, &argv);
452
453         if (parse_opts(argc, argv) != 0)
454                 return 1;
455
456         udev = udev_new();
457         if (!udev)
458                 error("Failed to initialize udev\n");
459
460         li = libinput_udev_create_context(&interface, &w, udev);
461         if (!li || libinput_udev_assign_seat(li, "seat0") != 0)
462                 error("Failed to initialize context from udev\n");
463
464         window_init(&w);
465         sockets_init(li);
466
467         gtk_main();
468
469         libinput_unref(li);
470         udev_unref(udev);
471
472         return 0;
473 }