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