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