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