Merge pull request #45 from fooishbar/xdg-shell-v6
[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 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         }
225
226         size = stride * height;
227         fd = os_create_anonymous_file(size);
228         if (fd < 0) {
229                 fprintf(stderr, "Couldn't create surface buffer\n");
230                 exit(1);
231         }
232
233         map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
234         if (map == MAP_FAILED) {
235                 fprintf(stderr, "Couldn't mmap surface buffer\n");
236                 exit(1);
237         }
238         memset(map, 0xff, size);
239         munmap(map, size);
240
241         pool = wl_shm_create_pool(inter->shm, fd, size);
242         buf = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
243                                         inter->shm_format);
244         wl_buffer_add_listener(buf, &buffer_listener, inter);
245
246         wl_surface_attach(inter->wl_surf, buf, 0, 0);
247         wl_surface_damage(inter->wl_surf, 0, 0, width, height);
248
249         opaque = wl_compositor_create_region(inter->compositor);
250         wl_region_add(opaque, 0, 0, width, height);
251         wl_surface_set_opaque_region(inter->wl_surf, opaque);
252         wl_region_destroy(opaque);
253
254         wl_shm_pool_destroy(pool);
255         close(fd);
256 }
257
258 static void
259 surface_configure(void *data, struct zxdg_surface_v6 *surface,
260                   uint32_t serial)
261 {
262         struct interactive_dpy *inter = data;
263
264         zxdg_surface_v6_ack_configure(inter->xdg_surf, serial);
265         wl_surface_commit(inter->wl_surf);
266 }
267
268 static const struct zxdg_surface_v6_listener surface_listener = {
269         surface_configure,
270 };
271
272 static void
273 toplevel_configure(void *data, struct zxdg_toplevel_v6 *toplevel,
274                    int32_t width, int32_t height, struct wl_array *states)
275 {
276         struct interactive_dpy *inter = data;
277
278         if (width == 0)
279                 width = 200;
280         if (height == 0)
281                 height = 200;
282
283         buffer_create(inter, width, height);
284 }
285
286 static void
287 toplevel_close(void *data, struct zxdg_toplevel_v6 *toplevel)
288 {
289         terminate = true;
290 }
291
292 static const struct zxdg_toplevel_v6_listener toplevel_listener = {
293         toplevel_configure,
294         toplevel_close
295 };
296
297 static void surface_create(struct interactive_dpy *inter)
298 {
299         inter->wl_surf = wl_compositor_create_surface(inter->compositor);
300         inter->xdg_surf = zxdg_shell_v6_get_xdg_surface(inter->shell,
301                                                         inter->wl_surf);
302         zxdg_surface_v6_add_listener(inter->xdg_surf, &surface_listener, inter);
303         inter->xdg_top = zxdg_surface_v6_get_toplevel(inter->xdg_surf);
304         zxdg_toplevel_v6_add_listener(inter->xdg_top, &toplevel_listener, inter);
305         zxdg_toplevel_v6_set_title(inter->xdg_top, "xkbcommon event tester");
306         zxdg_toplevel_v6_set_app_id(inter->xdg_top,
307                                     "org.xkbcommon.test.interactive-wayland");
308         wl_surface_commit(inter->wl_surf);
309 }
310
311 static void
312 shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
313 {
314         zxdg_shell_v6_pong(shell, serial);
315 }
316
317 static const struct zxdg_shell_v6_listener shell_listener = {
318         shell_ping
319 };
320
321 static void
322 kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
323            int fd, uint32_t size)
324 {
325         struct interactive_seat *seat = data;
326         void *buf;
327
328         buf = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
329         if (buf == MAP_FAILED) {
330                 fprintf(stderr, "Failed to mmap keymap: %d\n", errno);
331                 close(fd);
332                 return;
333         }
334
335         seat->keymap = xkb_keymap_new_from_buffer(seat->inter->ctx, buf, size - 1,
336                                                   XKB_KEYMAP_FORMAT_TEXT_V1, 0);
337         munmap(buf, size);
338         close(fd);
339         if (!seat->keymap) {
340                 fprintf(stderr, "Failed to compile keymap!\n");
341                 return;
342         }
343
344         seat->state = xkb_state_new(seat->keymap);
345         if (!seat->keymap) {
346                 fprintf(stderr, "Failed to create XKB state!\n");
347                 return;
348         }
349 }
350
351 static void
352 kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
353           struct wl_surface *surf, struct wl_array *keys)
354 {
355 }
356
357 static void
358 kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
359           struct wl_surface *surf)
360 {
361 }
362
363 static void
364 kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
365         uint32_t key, uint32_t state)
366 {
367         struct interactive_seat *seat = data;
368
369         if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
370                 return;
371
372         printf("%s: ", seat->name_str);
373         test_print_keycode_state(seat->state, NULL, key + 8,
374                                  XKB_CONSUMED_MODE_XKB);
375
376         /* Exit on ESC. */
377         if (xkb_state_key_get_one_sym(seat->state, key + 8) == XKB_KEY_Escape)
378                 terminate = true;
379 }
380
381 static void
382 kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
383               uint32_t mods_depressed, uint32_t mods_latched,
384               uint32_t mods_locked, uint32_t group)
385 {
386         struct interactive_seat *seat = data;
387
388         xkb_state_update_mask(seat->state, mods_depressed, mods_latched,
389                               mods_locked, 0, 0, group);
390 }
391
392 static void
393 kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
394                 int32_t delay)
395 {
396 }
397
398 static const struct wl_keyboard_listener kbd_listener = {
399         kbd_keymap,
400         kbd_enter,
401         kbd_leave,
402         kbd_key,
403         kbd_modifiers,
404         kbd_repeat_info
405 };
406
407 static void
408 pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
409               struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy)
410 {
411 }
412
413 static void
414 pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
415               struct wl_surface *surf)
416 {
417 }
418
419 static void
420 pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
421                wl_fixed_t fsx, wl_fixed_t fsy)
422 {
423 }
424
425 static void
426 pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
427                uint32_t time, uint32_t button, uint32_t state)
428 {
429         struct interactive_seat *seat = data;
430
431         zxdg_toplevel_v6_move(seat->inter->xdg_top, seat->wl_seat, serial);
432 }
433
434 static void
435 pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
436              uint32_t axis, wl_fixed_t value)
437 {
438 }
439
440 static void
441 pointer_frame(void *data, struct wl_pointer *wl_pointer)
442 {
443 }
444
445 static void
446 pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source)
447 {
448 }
449
450 static void
451 pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time,
452                   uint32_t axis)
453 {
454 }
455
456 static void
457 pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time,
458                       int32_t discrete)
459 {
460 }
461
462 static const struct wl_pointer_listener pointer_listener = {
463         pointer_enter,
464         pointer_leave,
465         pointer_motion,
466         pointer_button,
467         pointer_axis,
468         pointer_frame,
469         pointer_axis_source,
470         pointer_axis_stop,
471         pointer_axis_discrete
472 };
473
474 static void
475 seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
476 {
477         struct interactive_seat *seat = data;
478
479         if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
480                 seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
481                 wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
482         }
483         else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
484                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
485                         wl_keyboard_release(seat->wl_kbd);
486                 else
487                         wl_keyboard_destroy(seat->wl_kbd);
488
489                 xkb_state_unref(seat->state);
490                 xkb_keymap_unref(seat->keymap);
491
492                 seat->state = NULL;
493                 seat->keymap = NULL;
494                 seat->wl_kbd = NULL;
495         }
496
497         if (!seat->wl_pointer && (caps & WL_SEAT_CAPABILITY_POINTER)) {
498                 seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
499                 wl_pointer_add_listener(seat->wl_pointer, &pointer_listener,
500                                         seat);
501         }
502         else if (seat->wl_pointer && !(caps & WL_SEAT_CAPABILITY_POINTER)) {
503                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
504                         wl_pointer_release(seat->wl_pointer);
505                 else
506                         wl_pointer_destroy(seat->wl_pointer);
507                 seat->wl_pointer = NULL;
508         }
509 }
510
511 static void
512 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
513 {
514         struct interactive_seat *seat = data;
515
516         free(seat->name_str);
517         seat->name_str = strdup(name);
518 }
519
520 static const struct wl_seat_listener seat_listener = {
521         seat_capabilities,
522         seat_name
523 };
524
525 static void
526 seat_create(struct interactive_dpy *inter, struct wl_registry *registry,
527             uint32_t name, uint32_t version)
528 {
529         int ret;
530         struct interactive_seat *seat = calloc(1, sizeof(*seat));
531
532         seat->global_name = name;
533         seat->inter = inter;
534         seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface,
535                                          MAX(version, 5));
536         wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
537         ret = asprintf(&seat->name_str, "seat:%d",
538                        wl_proxy_get_id((struct wl_proxy *) seat->wl_seat));
539         assert(ret >= 0);
540         wl_list_insert(&inter->seats, &seat->link);
541 }
542
543 static void
544 seat_destroy(struct interactive_seat *seat)
545 {
546         if (seat->wl_kbd) {
547                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
548                         wl_keyboard_release(seat->wl_kbd);
549                 else
550                         wl_keyboard_destroy(seat->wl_kbd);
551
552                 xkb_state_unref(seat->state);
553                 xkb_keymap_unref(seat->keymap);
554         }
555
556         if (seat->wl_pointer) {
557                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
558                         wl_pointer_release(seat->wl_pointer);
559                 else
560                         wl_pointer_destroy(seat->wl_pointer);
561         }
562
563         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
564                 wl_seat_release(seat->wl_seat);
565         else
566                 wl_seat_destroy(seat->wl_seat);
567
568         free(seat->name_str);
569         wl_list_remove(&seat->link);
570         free(seat);
571 }
572
573 static void
574 registry_global(void *data, struct wl_registry *registry, uint32_t name,
575                 const char *interface, uint32_t version)
576 {
577         struct interactive_dpy *inter = data;
578
579         if (strcmp(interface, "wl_seat") == 0) {
580                 seat_create(inter, registry, name, version);
581         }
582         else if (strcmp(interface, "zxdg_shell_v6") == 0) {
583                 inter->shell = wl_registry_bind(registry, name,
584                                                 &zxdg_shell_v6_interface,
585                                                 MAX(version, 1));
586                 zxdg_shell_v6_add_listener(inter->shell, &shell_listener, inter);
587         }
588         else if (strcmp(interface, "wl_compositor") == 0) {
589                 inter->compositor = wl_registry_bind(registry, name,
590                                                      &wl_compositor_interface,
591                                                      MAX(version, 1));
592         }
593         else if (strcmp(interface, "wl_shm") == 0) {
594                 inter->shm = wl_registry_bind(registry, name, &wl_shm_interface,
595                                               MAX(version, 1));
596         }
597 }
598
599 static void
600 registry_delete(void *data, struct wl_registry *registry, uint32_t name)
601 {
602         struct interactive_dpy *inter = data;
603         struct interactive_seat *seat, *tmp;
604
605         wl_list_for_each_safe(seat, tmp, &inter->seats, link) {
606                 if (seat->global_name != name)
607                         continue;
608
609                 seat_destroy(seat);
610         }
611 }
612
613 static const struct wl_registry_listener registry_listener = {
614         registry_global,
615         registry_delete
616 };
617
618 static void
619 dpy_disconnect(struct interactive_dpy *inter)
620 {
621         struct interactive_seat *seat, *tmp;
622
623         wl_list_for_each_safe(seat, tmp, &inter->seats, link)
624                 seat_destroy(seat);
625
626         if (inter->xdg_surf)
627                 zxdg_surface_v6_destroy(inter->xdg_surf);
628         if (inter->xdg_top)
629                 zxdg_toplevel_v6_destroy(inter->xdg_top);
630         if (inter->wl_surf)
631                 wl_surface_destroy(inter->wl_surf);
632         if (inter->shell)
633                 zxdg_shell_v6_destroy(inter->shell);
634         if (inter->compositor)
635                 wl_compositor_destroy(inter->compositor);
636         if (inter->shm)
637                 wl_shm_destroy(inter->shm);
638
639         /* Do one last roundtrip to try to destroy our wl_buffer. */
640         wl_display_roundtrip(inter->dpy);
641
642         xkb_context_unref(inter->ctx);
643         wl_display_disconnect(inter->dpy);
644 }
645
646 int
647 main(int argc, char *argv[])
648 {
649         int ret;
650         struct interactive_dpy inter;
651         struct wl_registry *registry;
652
653         setlocale(LC_ALL, "");
654
655         memset(&inter, 0, sizeof(inter));
656         wl_list_init(&inter.seats);
657
658         inter.dpy = wl_display_connect(NULL);
659         if (!inter.dpy) {
660                 fprintf(stderr, "Couldn't connect to Wayland server\n");
661                 ret = -1;
662                 goto err_out;
663         }
664
665         inter.ctx = test_get_context(0);
666         if (!inter.ctx) {
667                 ret = -1;
668                 fprintf(stderr, "Couldn't create xkb context\n");
669                 goto err_out;
670         }
671
672         registry = wl_display_get_registry(inter.dpy);
673         wl_registry_add_listener(registry, &registry_listener, &inter);
674
675         /* The first roundtrip gets the list of advertised globals. */
676         wl_display_roundtrip(inter.dpy);
677
678         /* The second roundtrip dispatches the events sent after binding, e.g.
679          * after binding to wl_seat globals in the first roundtrip, we will get
680          * the wl_seat::capabilities event in this roundtrip. */
681         wl_display_roundtrip(inter.dpy);
682
683         if (!inter.shell || !inter.shm || !inter.compositor) {
684                 fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n",
685                         (inter.shell) ? "" : "xdg_shell ",
686                         (inter.shm) ? "" : "wl_shm",
687                         (inter.compositor) ? "" : "wl_compositor");
688                 goto err_conn;
689         }
690
691         surface_create(&inter);
692
693         test_disable_stdin_echo();
694         do {
695                 ret = wl_display_dispatch(inter.dpy);
696         } while (ret >= 0 && !terminate);
697         test_enable_stdin_echo();
698
699         wl_registry_destroy(registry);
700 err_conn:
701         dpy_disconnect(&inter);
702 err_out:
703         exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);
704 }