interactive-wayland: Fix interface versioning
[platform/upstream/libxkbcommon.git] / tools / interactive-wayland.c
1 /*
2  * Copyright © 2012 Collabora, Ltd.
3  * Copyright © 2013 Ran Benita <ran234@gmail.com>
4  * Copyright © 2016 Daniel Stone <daniel@fooishbar.org>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25
26 #include "config.h"
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <locale.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/mman.h>
36 #include <unistd.h>
37
38 #include "xkbcommon/xkbcommon.h"
39 #include "tools-common.h"
40
41 #include <wayland-client.h>
42 #include "xdg-shell-client-protocol.h"
43 #include <wayland-util.h>
44
45 #define MIN(a, b) ((a) < (b) ? (a) : (b))
46
47 struct interactive_dpy {
48     struct wl_display *dpy;
49     struct wl_compositor *compositor;
50     struct xdg_wm_base *shell;
51     struct wl_shm *shm;
52     uint32_t shm_format;
53
54     struct xkb_context *ctx;
55
56     struct wl_surface *wl_surf;
57     struct xdg_surface *xdg_surf;
58     struct xdg_toplevel *xdg_top;
59
60     struct wl_list seats;
61 };
62
63 struct interactive_seat {
64     struct interactive_dpy *inter;
65
66     struct wl_seat *wl_seat;
67     struct wl_keyboard *wl_kbd;
68     struct wl_pointer *wl_pointer;
69     uint32_t version; /* ... of wl_seat */
70     uint32_t global_name; /* an ID of sorts */
71     char *name_str; /* a descriptor */
72
73     struct xkb_keymap *keymap;
74     struct xkb_state *state;
75
76     struct wl_list link;
77 };
78
79 static bool terminate;
80
81 #ifdef HAVE_MKOSTEMP
82 static int
83 create_tmpfile_cloexec(char *tmpname)
84 {
85     int fd = mkostemp(tmpname, O_CLOEXEC);
86     if (fd >= 0)
87         unlink(tmpname);
88     return fd;
89 }
90 #else
91 /* The following utility functions are taken from Weston's
92  * shared/os-compatibility.c. */
93 static int
94 os_fd_set_cloexec(int fd)
95 {
96     long flags;
97
98     if (fd == -1)
99         return -1;
100
101     flags = fcntl(fd, F_GETFD);
102     if (flags == -1)
103         return -1;
104
105     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
106         return -1;
107
108     return 0;
109 }
110
111 static int
112 set_cloexec_or_close(int fd)
113 {
114     if (os_fd_set_cloexec(fd) != 0) {
115         close(fd);
116         return -1;
117     }
118     return fd;
119 }
120
121 static int
122 create_tmpfile_cloexec(char *tmpname)
123 {
124     int fd = mkstemp(tmpname);
125     if (fd >= 0) {
126         fd = set_cloexec_or_close(fd);
127         unlink(tmpname);
128     }
129     return fd;
130 }
131 #endif
132
133 static int
134 os_resize_anonymous_file(int fd, off_t size)
135 {
136     int ret;
137 #ifdef HAVE_POSIX_FALLOCATE
138     ret = posix_fallocate(fd, 0, size);
139     if (ret == 0)
140         return 0;
141     /*
142      * Filesystems that do support fallocate will return EINVAL
143      * or EOPNOTSUPP, fallback to ftruncate() then.
144      */
145     if (ret != EINVAL && ret != EOPNOTSUPP)
146         return ret;
147 #endif
148     ret = ftruncate(fd, size);
149     if (ret != 0)
150         return errno;
151     return 0;
152 }
153
154 /*
155  * Create a new, unique, anonymous file of the given size, and
156  * return the file descriptor for it. The file descriptor is set
157  * CLOEXEC. The file is immediately suitable for mmap()'ing
158  * the given size at offset zero.
159  *
160  * The file should not have a permanent backing store like a disk,
161  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
162  *
163  * The file name is deleted from the file system.
164  *
165  * The file is suitable for buffer sharing between processes by
166  * transmitting the file descriptor over Unix sockets using the
167  * SCM_RIGHTS methods.
168  *
169  * If the C library implements posix_fallocate(), it is used to
170  * guarantee that disk space is available for the file at the
171  * given size. If disk space is insufficent, errno is set to ENOSPC.
172  * If posix_fallocate() is not supported, program will fallback
173  * to ftruncate() instead.
174  */
175 static int
176 os_create_anonymous_file(off_t size)
177 {
178     static const char template[] = "/weston-shared-XXXXXX";
179     const char *path;
180     char *name;
181     int fd;
182     int ret;
183
184     path = getenv("XDG_RUNTIME_DIR");
185     if (!path) {
186         errno = ENOENT;
187         return -1;
188     }
189
190     name = malloc(strlen(path) + sizeof(template));
191     if (!name)
192         return -1;
193
194     strcpy(name, path);
195     strcat(name, template);
196
197     fd = create_tmpfile_cloexec(name);
198
199     free(name);
200
201     if (fd < 0)
202         return -1;
203
204     ret = os_resize_anonymous_file(fd, size);
205     if (ret != 0) {
206         close(fd);
207         errno = ret;
208         return -1;
209     }
210
211     return fd;
212 }
213
214 static void
215 buffer_release(void *data, struct wl_buffer *buffer)
216 {
217     wl_buffer_destroy(buffer);
218 }
219
220 static const struct wl_buffer_listener buffer_listener = {
221     buffer_release
222 };
223
224 static void
225 buffer_create(struct interactive_dpy *inter, uint32_t width, uint32_t height)
226 {
227     struct wl_shm_pool *pool;
228     struct wl_buffer *buf;
229     struct wl_region *opaque;
230     uint32_t stride;
231     size_t size;
232     void *map;
233     int fd;
234
235     switch (inter->shm_format) {
236     case WL_SHM_FORMAT_ARGB8888:
237     case WL_SHM_FORMAT_XRGB8888:
238     case WL_SHM_FORMAT_ABGR8888:
239     case WL_SHM_FORMAT_XBGR8888:
240         stride = width * 4;
241         break;
242     case WL_SHM_FORMAT_RGB565:
243     case WL_SHM_FORMAT_BGR565:
244         stride = width * 2;
245         break;
246     default:
247         fprintf(stderr, "Unsupported SHM format %d\n", inter->shm_format);
248         exit(1);
249     }
250
251     size = stride * height;
252     fd = os_create_anonymous_file(size);
253     if (fd < 0) {
254         fprintf(stderr, "Couldn't create surface buffer\n");
255         exit(1);
256     }
257
258     map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
259     if (map == MAP_FAILED) {
260         fprintf(stderr, "Couldn't mmap surface buffer\n");
261         exit(1);
262     }
263     memset(map, 0xff, size);
264     munmap(map, size);
265
266     pool = wl_shm_create_pool(inter->shm, fd, size);
267     buf = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
268                                     inter->shm_format);
269     wl_buffer_add_listener(buf, &buffer_listener, inter);
270
271     wl_surface_attach(inter->wl_surf, buf, 0, 0);
272     wl_surface_damage(inter->wl_surf, 0, 0, width, height);
273
274     opaque = wl_compositor_create_region(inter->compositor);
275     wl_region_add(opaque, 0, 0, width, height);
276     wl_surface_set_opaque_region(inter->wl_surf, opaque);
277     wl_region_destroy(opaque);
278
279     wl_shm_pool_destroy(pool);
280     close(fd);
281 }
282
283 static void
284 surface_configure(void *data, struct xdg_surface *surface,
285                   uint32_t serial)
286 {
287     struct interactive_dpy *inter = data;
288
289     xdg_surface_ack_configure(inter->xdg_surf, serial);
290     wl_surface_commit(inter->wl_surf);
291 }
292
293 static const struct xdg_surface_listener surface_listener = {
294     surface_configure,
295 };
296
297 static void
298 toplevel_configure(void *data, struct xdg_toplevel *toplevel,
299                    int32_t width, int32_t height, struct wl_array *states)
300 {
301     struct interactive_dpy *inter = data;
302
303     if (width == 0)
304         width = 200;
305     if (height == 0)
306         height = 200;
307
308     buffer_create(inter, width, height);
309 }
310
311 static void
312 toplevel_close(void *data, struct xdg_toplevel *toplevel)
313 {
314     terminate = true;
315 }
316
317 static const struct xdg_toplevel_listener toplevel_listener = {
318     toplevel_configure,
319     toplevel_close
320 };
321
322 static void surface_create(struct interactive_dpy *inter)
323 {
324     inter->wl_surf = wl_compositor_create_surface(inter->compositor);
325     inter->xdg_surf = xdg_wm_base_get_xdg_surface(inter->shell, inter->wl_surf);
326     xdg_surface_add_listener(inter->xdg_surf, &surface_listener, inter);
327     inter->xdg_top = xdg_surface_get_toplevel(inter->xdg_surf);
328     xdg_toplevel_add_listener(inter->xdg_top, &toplevel_listener, inter);
329     xdg_toplevel_set_title(inter->xdg_top, "xkbcommon event tester");
330     xdg_toplevel_set_app_id(inter->xdg_top,
331                             "org.xkbcommon.test.interactive-wayland");
332     wl_surface_commit(inter->wl_surf);
333 }
334
335 static void
336 shell_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
337 {
338     xdg_wm_base_pong(shell, serial);
339 }
340
341 static const struct xdg_wm_base_listener shell_listener = {
342     shell_ping
343 };
344
345 static void
346 kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
347            int fd, uint32_t size)
348 {
349     struct interactive_seat *seat = data;
350     void *buf;
351
352     buf = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
353     if (buf == MAP_FAILED) {
354         fprintf(stderr, "Failed to mmap keymap: %d\n", errno);
355         close(fd);
356         return;
357     }
358
359     seat->keymap = xkb_keymap_new_from_buffer(seat->inter->ctx, buf, size - 1,
360                                               XKB_KEYMAP_FORMAT_TEXT_V1,
361                                               XKB_KEYMAP_COMPILE_NO_FLAGS);
362     munmap(buf, size);
363     close(fd);
364     if (!seat->keymap) {
365         fprintf(stderr, "Failed to compile keymap!\n");
366         return;
367     }
368
369     seat->state = xkb_state_new(seat->keymap);
370     if (!seat->state) {
371         fprintf(stderr, "Failed to create XKB state!\n");
372         return;
373     }
374 }
375
376 static void
377 kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
378           struct wl_surface *surf, struct wl_array *keys)
379 {
380 }
381
382 static void
383 kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
384           struct wl_surface *surf)
385 {
386 }
387
388 static void
389 kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
390         uint32_t key, uint32_t state)
391 {
392     struct interactive_seat *seat = data;
393
394     if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
395         return;
396
397     printf("%s: ", seat->name_str);
398     tools_print_keycode_state(seat->state, NULL, key + 8,
399                               XKB_CONSUMED_MODE_XKB);
400
401     /* Exit on ESC. */
402     if (xkb_state_key_get_one_sym(seat->state, key + 8) == XKB_KEY_Escape)
403         terminate = true;
404 }
405
406 static void
407 kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
408               uint32_t mods_depressed, uint32_t mods_latched,
409               uint32_t mods_locked, uint32_t group)
410 {
411     struct interactive_seat *seat = data;
412
413     xkb_state_update_mask(seat->state, mods_depressed, mods_latched,
414                           mods_locked, 0, 0, group);
415 }
416
417 static void
418 kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
419                 int32_t delay)
420 {
421 }
422
423 static const struct wl_keyboard_listener kbd_listener = {
424     kbd_keymap,
425     kbd_enter,
426     kbd_leave,
427     kbd_key,
428     kbd_modifiers,
429     kbd_repeat_info
430 };
431
432 static void
433 pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
434               struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy)
435 {
436 }
437
438 static void
439 pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
440               struct wl_surface *surf)
441 {
442 }
443
444 static void
445 pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
446                wl_fixed_t fsx, wl_fixed_t fsy)
447 {
448 }
449
450 static void
451 pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
452                uint32_t time, uint32_t button, uint32_t state)
453 {
454     struct interactive_seat *seat = data;
455
456     xdg_toplevel_move(seat->inter->xdg_top, seat->wl_seat, serial);
457 }
458
459 static void
460 pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
461              uint32_t axis, wl_fixed_t value)
462 {
463 }
464
465 static void
466 pointer_frame(void *data, struct wl_pointer *wl_pointer)
467 {
468 }
469
470 static void
471 pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source)
472 {
473 }
474
475 static void
476 pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time,
477                   uint32_t axis)
478 {
479 }
480
481 static void
482 pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time,
483                       int32_t discrete)
484 {
485 }
486
487 static const struct wl_pointer_listener pointer_listener = {
488     pointer_enter,
489     pointer_leave,
490     pointer_motion,
491     pointer_button,
492     pointer_axis,
493     pointer_frame,
494     pointer_axis_source,
495     pointer_axis_stop,
496     pointer_axis_discrete
497 };
498
499 static void
500 seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
501 {
502     struct interactive_seat *seat = data;
503
504     if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
505         seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
506         wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
507     }
508     else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
509         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
510             wl_keyboard_release(seat->wl_kbd);
511         else
512             wl_keyboard_destroy(seat->wl_kbd);
513
514         xkb_state_unref(seat->state);
515         xkb_keymap_unref(seat->keymap);
516
517         seat->state = NULL;
518         seat->keymap = NULL;
519         seat->wl_kbd = NULL;
520     }
521
522     if (!seat->wl_pointer && (caps & WL_SEAT_CAPABILITY_POINTER)) {
523         seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
524         wl_pointer_add_listener(seat->wl_pointer, &pointer_listener,
525                                 seat);
526     }
527     else if (seat->wl_pointer && !(caps & WL_SEAT_CAPABILITY_POINTER)) {
528         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
529             wl_pointer_release(seat->wl_pointer);
530         else
531             wl_pointer_destroy(seat->wl_pointer);
532         seat->wl_pointer = NULL;
533     }
534 }
535
536 static void
537 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
538 {
539     struct interactive_seat *seat = data;
540
541     free(seat->name_str);
542     seat->name_str = strdup(name);
543 }
544
545 static const struct wl_seat_listener seat_listener = {
546     seat_capabilities,
547     seat_name
548 };
549
550 static void
551 seat_create(struct interactive_dpy *inter, struct wl_registry *registry,
552             uint32_t name, uint32_t version)
553 {
554     int ret;
555     struct interactive_seat *seat = calloc(1, sizeof(*seat));
556
557     seat->global_name = name;
558     seat->inter = inter;
559     seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface,
560                                      MIN(version, 5));
561     wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
562     ret = asprintf(&seat->name_str, "seat:%d",
563                    wl_proxy_get_id((struct wl_proxy *) seat->wl_seat));
564     assert(ret >= 0);
565     wl_list_insert(&inter->seats, &seat->link);
566 }
567
568 static void
569 seat_destroy(struct interactive_seat *seat)
570 {
571     if (seat->wl_kbd) {
572         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
573             wl_keyboard_release(seat->wl_kbd);
574         else
575             wl_keyboard_destroy(seat->wl_kbd);
576
577         xkb_state_unref(seat->state);
578         xkb_keymap_unref(seat->keymap);
579     }
580
581     if (seat->wl_pointer) {
582         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
583             wl_pointer_release(seat->wl_pointer);
584         else
585             wl_pointer_destroy(seat->wl_pointer);
586     }
587
588     if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
589         wl_seat_release(seat->wl_seat);
590     else
591         wl_seat_destroy(seat->wl_seat);
592
593     free(seat->name_str);
594     wl_list_remove(&seat->link);
595     free(seat);
596 }
597
598 static void
599 registry_global(void *data, struct wl_registry *registry, uint32_t name,
600                 const char *interface, uint32_t version)
601 {
602     struct interactive_dpy *inter = data;
603
604     if (strcmp(interface, "wl_seat") == 0) {
605         seat_create(inter, registry, name, version);
606     }
607     else if (strcmp(interface, "xdg_wm_base") == 0) {
608         inter->shell = wl_registry_bind(registry, name,
609                                         &xdg_wm_base_interface,
610                                         MIN(version, 2));
611         xdg_wm_base_add_listener(inter->shell, &shell_listener, inter);
612     }
613     else if (strcmp(interface, "wl_compositor") == 0) {
614         inter->compositor = wl_registry_bind(registry, name,
615                                              &wl_compositor_interface,
616                                              MIN(version, 1));
617     }
618     else if (strcmp(interface, "wl_shm") == 0) {
619         inter->shm = wl_registry_bind(registry, name, &wl_shm_interface,
620                                       MIN(version, 1));
621     }
622 }
623
624 static void
625 registry_delete(void *data, struct wl_registry *registry, uint32_t name)
626 {
627     struct interactive_dpy *inter = data;
628     struct interactive_seat *seat, *tmp;
629
630     wl_list_for_each_safe(seat, tmp, &inter->seats, link) {
631         if (seat->global_name != name)
632             continue;
633
634         seat_destroy(seat);
635     }
636 }
637
638 static const struct wl_registry_listener registry_listener = {
639     registry_global,
640     registry_delete
641 };
642
643 static void
644 dpy_disconnect(struct interactive_dpy *inter)
645 {
646     struct interactive_seat *seat, *tmp;
647
648     wl_list_for_each_safe(seat, tmp, &inter->seats, link)
649         seat_destroy(seat);
650
651     if (inter->xdg_surf)
652         xdg_surface_destroy(inter->xdg_surf);
653     if (inter->xdg_top)
654         xdg_toplevel_destroy(inter->xdg_top);
655     if (inter->wl_surf)
656         wl_surface_destroy(inter->wl_surf);
657     if (inter->shell)
658         xdg_wm_base_destroy(inter->shell);
659     if (inter->compositor)
660         wl_compositor_destroy(inter->compositor);
661     if (inter->shm)
662         wl_shm_destroy(inter->shm);
663
664     /* Do one last roundtrip to try to destroy our wl_buffer. */
665     wl_display_roundtrip(inter->dpy);
666
667     xkb_context_unref(inter->ctx);
668     wl_display_disconnect(inter->dpy);
669 }
670
671 int
672 main(int argc, char *argv[])
673 {
674     int ret;
675     struct interactive_dpy inter;
676     struct wl_registry *registry;
677
678     if (argc != 1) {
679         ret = strcmp(argv[1], "--help");
680         fprintf(ret ? stderr : stdout, "Usage: %s [--help]\n", argv[0]);
681         if (ret)
682             fprintf(stderr, "unrecognized option: %s\n", argv[1]);
683         return ret ? EXIT_INVALID_USAGE : EXIT_SUCCESS;
684     }
685
686     setlocale(LC_ALL, "");
687
688     memset(&inter, 0, sizeof(inter));
689     wl_list_init(&inter.seats);
690
691     inter.dpy = wl_display_connect(NULL);
692     if (!inter.dpy) {
693         fprintf(stderr, "Couldn't connect to Wayland server\n");
694         ret = -1;
695         goto err_out;
696     }
697
698     inter.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
699     if (!inter.ctx) {
700         ret = -1;
701         fprintf(stderr, "Couldn't create xkb context\n");
702         goto err_out;
703     }
704
705     registry = wl_display_get_registry(inter.dpy);
706     wl_registry_add_listener(registry, &registry_listener, &inter);
707
708     /* The first roundtrip gets the list of advertised globals. */
709     wl_display_roundtrip(inter.dpy);
710
711     /* The second roundtrip dispatches the events sent after binding, e.g.
712      * after binding to wl_seat globals in the first roundtrip, we will get
713      * the wl_seat::capabilities event in this roundtrip. */
714     wl_display_roundtrip(inter.dpy);
715
716     if (!inter.shell || !inter.shm || !inter.compositor) {
717         fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n",
718                 (inter.shell) ? "" : "xdg_shell ",
719                 (inter.shm) ? "" : "wl_shm",
720                 (inter.compositor) ? "" : "wl_compositor");
721         ret = -1;
722         goto err_conn;
723     }
724
725     surface_create(&inter);
726
727     tools_disable_stdin_echo();
728     do {
729         ret = wl_display_dispatch(inter.dpy);
730     } while (ret >= 0 && !terminate);
731     tools_enable_stdin_echo();
732
733     wl_registry_destroy(registry);
734 err_conn:
735     dpy_disconnect(&inter);
736 err_out:
737     exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);
738 }