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