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