state: allow different modes for calculating consumed modifiers
[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                                  XKB_CONSUMED_MODE_XKB);
365
366         /* Exit on ESC. */
367         if (xkb_state_key_get_one_sym(seat->state, key + 8) == XKB_KEY_Escape)
368                 terminate = true;
369 }
370
371 static void
372 kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
373               uint32_t mods_depressed, uint32_t mods_latched,
374               uint32_t mods_locked, uint32_t group)
375 {
376         struct interactive_seat *seat = data;
377
378         xkb_state_update_mask(seat->state, mods_depressed, mods_latched,
379                               mods_locked, 0, 0, group);
380 }
381
382 static void
383 kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
384                 int32_t delay)
385 {
386 }
387
388 static const struct wl_keyboard_listener kbd_listener = {
389         kbd_keymap,
390         kbd_enter,
391         kbd_leave,
392         kbd_key,
393         kbd_modifiers,
394         kbd_repeat_info
395 };
396
397 static void
398 pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
399               struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy)
400 {
401 }
402
403 static void
404 pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
405               struct wl_surface *surf)
406 {
407 }
408
409 static void
410 pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
411                wl_fixed_t fsx, wl_fixed_t fsy)
412 {
413 }
414
415 static void
416 pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
417                uint32_t time, uint32_t button, uint32_t state)
418 {
419         struct interactive_seat *seat = data;
420
421         xdg_surface_move(seat->inter->xdg_surf, seat->wl_seat, serial);
422 }
423
424 static void
425 pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
426              uint32_t axis, wl_fixed_t value)
427 {
428 }
429
430 static void
431 pointer_frame(void *data, struct wl_pointer *wl_pointer)
432 {
433 }
434
435 static void
436 pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source)
437 {
438 }
439
440 static void
441 pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time,
442                   uint32_t axis)
443 {
444 }
445
446 static void
447 pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time,
448                       int32_t discrete)
449 {
450 }
451
452 static const struct wl_pointer_listener pointer_listener = {
453         pointer_enter,
454         pointer_leave,
455         pointer_motion,
456         pointer_button,
457         pointer_axis,
458         pointer_frame,
459         pointer_axis_source,
460         pointer_axis_stop,
461         pointer_axis_discrete
462 };
463
464 static void
465 seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
466 {
467         struct interactive_seat *seat = data;
468
469         if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
470                 seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
471                 wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
472         }
473         else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
474                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
475                         wl_keyboard_release(seat->wl_kbd);
476                 else
477                         wl_keyboard_destroy(seat->wl_kbd);
478
479                 xkb_state_unref(seat->state);
480                 xkb_keymap_unref(seat->keymap);
481
482                 seat->state = NULL;
483                 seat->keymap = NULL;
484                 seat->wl_kbd = NULL;
485         }
486
487         if (!seat->wl_pointer && (caps & WL_SEAT_CAPABILITY_POINTER)) {
488                 seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
489                 wl_pointer_add_listener(seat->wl_pointer, &pointer_listener,
490                                         seat);
491         }
492         else if (seat->wl_pointer && !(caps & WL_SEAT_CAPABILITY_POINTER)) {
493                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
494                         wl_pointer_release(seat->wl_pointer);
495                 else
496                         wl_pointer_destroy(seat->wl_pointer);
497                 seat->wl_pointer = NULL;
498         }
499 }
500
501 static void
502 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
503 {
504         struct interactive_seat *seat = data;
505
506         free(seat->name_str);
507         seat->name_str = strdup(name);
508 }
509
510 static const struct wl_seat_listener seat_listener = {
511         seat_capabilities,
512         seat_name
513 };
514
515 static void
516 seat_create(struct interactive_dpy *inter, struct wl_registry *registry,
517             uint32_t name, uint32_t version)
518 {
519         int ret;
520         struct interactive_seat *seat = calloc(1, sizeof(*seat));
521
522         seat->global_name = name;
523         seat->inter = inter;
524         seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface,
525                                          MAX(version, 5));
526         wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
527         ret = asprintf(&seat->name_str, "seat:%d",
528                        wl_proxy_get_id((struct wl_proxy *) seat->wl_seat));
529         assert(ret >= 0);
530         wl_list_insert(&inter->seats, &seat->link);
531 }
532
533 static void
534 seat_destroy(struct interactive_seat *seat)
535 {
536         if (seat->wl_kbd) {
537                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
538                         wl_keyboard_release(seat->wl_kbd);
539                 else
540                         wl_keyboard_destroy(seat->wl_kbd);
541
542                 xkb_state_unref(seat->state);
543                 xkb_keymap_unref(seat->keymap);
544         }
545
546         if (seat->wl_pointer) {
547                 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
548                         wl_pointer_release(seat->wl_pointer);
549                 else
550                         wl_pointer_destroy(seat->wl_pointer);
551         }
552
553         if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
554                 wl_seat_release(seat->wl_seat);
555         else
556                 wl_seat_destroy(seat->wl_seat);
557
558         free(seat->name_str);
559         wl_list_remove(&seat->link);
560         free(seat);
561 }
562
563 static void
564 registry_global(void *data, struct wl_registry *registry, uint32_t name,
565                 const char *interface, uint32_t version)
566 {
567         struct interactive_dpy *inter = data;
568
569         if (strcmp(interface, "wl_seat") == 0) {
570                 seat_create(inter, registry, name, version);
571         }
572         else if (strcmp(interface, "xdg_shell") == 0) {
573                 inter->shell = wl_registry_bind(registry, name,
574                                                 &xdg_shell_interface,
575                                                 MAX(version, 1));
576                 xdg_shell_add_listener(inter->shell, &shell_listener, inter);
577                 xdg_shell_use_unstable_version(inter->shell, 5);
578         }
579         else if (strcmp(interface, "wl_compositor") == 0) {
580                 inter->compositor = wl_registry_bind(registry, name,
581                                                      &wl_compositor_interface,
582                                                      MAX(version, 1));
583         }
584         else if (strcmp(interface, "wl_shm") == 0) {
585                 inter->shm = wl_registry_bind(registry, name, &wl_shm_interface,
586                                               MAX(version, 1));
587         }
588 }
589
590 static void
591 registry_delete(void *data, struct wl_registry *registry, uint32_t name)
592 {
593         struct interactive_dpy *inter = data;
594         struct interactive_seat *seat, *tmp;
595
596         wl_list_for_each_safe(seat, tmp, &inter->seats, link) {
597                 if (seat->global_name != name)
598                         continue;
599
600                 seat_destroy(seat);
601         }
602 }
603
604 static const struct wl_registry_listener registry_listener = {
605         registry_global,
606         registry_delete
607 };
608
609 static void
610 dpy_disconnect(struct interactive_dpy *inter)
611 {
612         struct interactive_seat *seat, *tmp;
613
614         wl_list_for_each_safe(seat, tmp, &inter->seats, link)
615                 seat_destroy(seat);
616
617         if (inter->xdg_surf)
618                 xdg_surface_destroy(inter->xdg_surf);
619         if (inter->wl_surf)
620                 wl_surface_destroy(inter->wl_surf);
621         if (inter->shell)
622                 xdg_shell_destroy(inter->shell);
623         if (inter->compositor)
624                 wl_compositor_destroy(inter->compositor);
625         if (inter->shm)
626                 wl_shm_destroy(inter->shm);
627
628         /* Do one last roundtrip to try to destroy our wl_buffer. */
629         wl_display_roundtrip(inter->dpy);
630
631         xkb_context_unref(inter->ctx);
632         wl_display_disconnect(inter->dpy);
633 }
634
635 int
636 main(int argc, char *argv[])
637 {
638         int ret;
639         struct interactive_dpy inter;
640         struct wl_registry *registry;
641
642         setlocale(LC_ALL, "");
643
644         memset(&inter, 0, sizeof(inter));
645         wl_list_init(&inter.seats);
646
647         inter.dpy = wl_display_connect(NULL);
648         if (!inter.dpy) {
649                 fprintf(stderr, "Couldn't connect to Wayland server\n");
650                 ret = -1;
651                 goto err_out;
652         }
653
654         inter.ctx = test_get_context(0);
655         if (!inter.ctx) {
656                 ret = -1;
657                 fprintf(stderr, "Couldn't create xkb context\n");
658                 goto err_out;
659         }
660
661         registry = wl_display_get_registry(inter.dpy);
662         wl_registry_add_listener(registry, &registry_listener, &inter);
663
664         /* The first roundtrip gets the list of advertised globals. */
665         wl_display_roundtrip(inter.dpy);
666
667         /* The second roundtrip dispatches the events sent after binding, e.g.
668          * after binding to wl_seat globals in the first roundtrip, we will get
669          * the wl_seat::capabilities event in this roundtrip. */
670         wl_display_roundtrip(inter.dpy);
671
672         if (!inter.shell || !inter.shm || !inter.compositor) {
673                 fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n",
674                         (inter.shell) ? "" : "xdg_shell ",
675                         (inter.shm) ? "" : "wl_shm",
676                         (inter.compositor) ? "" : "wl_compositor");
677                 goto err_conn;
678         }
679
680         surface_create(&inter);
681
682         test_disable_stdin_echo();
683         do {
684                 ret = wl_display_dispatch(inter.dpy);
685         } while (ret >= 0 && !terminate);
686         test_enable_stdin_echo();
687
688         wl_registry_destroy(registry);
689 err_conn:
690         dpy_disconnect(&inter);
691 err_out:
692         exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);
693 }