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