2 * Copyright © 2012 Collabora, Ltd.
3 * Copyright © 2013 Ran Benita <ran234@gmail.com>
4 * Copyright © 2016 Daniel Stone <daniel@fooishbar.org>
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:
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
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.
38 #include "xkbcommon/xkbcommon.h"
39 #include "tools-common.h"
41 #include <wayland-client.h>
42 #include "xdg-shell-client-protocol.h"
43 #include <wayland-util.h>
45 #define MIN(a, b) ((a) < (b) ? (a) : (b))
47 /* Offset between evdev keycodes (where KEY_ESCAPE is 1), and the evdev XKB
48 * keycode set (where ESC is 9). */
49 #define EVDEV_OFFSET 8
51 struct interactive_dpy {
52 struct wl_display *dpy;
53 struct wl_compositor *compositor;
54 struct xdg_wm_base *shell;
58 struct xkb_context *ctx;
60 struct wl_surface *wl_surf;
61 struct xdg_surface *xdg_surf;
62 struct xdg_toplevel *xdg_top;
67 struct interactive_seat {
68 struct interactive_dpy *inter;
70 struct wl_seat *wl_seat;
71 struct wl_keyboard *wl_kbd;
72 struct wl_pointer *wl_pointer;
73 uint32_t version; /* ... of wl_seat */
74 uint32_t global_name; /* an ID of sorts */
75 char *name_str; /* a descriptor */
77 struct xkb_keymap *keymap;
78 struct xkb_state *state;
83 static bool terminate;
87 create_tmpfile_cloexec(char *tmpname)
89 int fd = mkostemp(tmpname, O_CLOEXEC);
95 /* The following utility functions are taken from Weston's
96 * shared/os-compatibility.c. */
98 os_fd_set_cloexec(int fd)
105 flags = fcntl(fd, F_GETFD);
109 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
116 set_cloexec_or_close(int fd)
118 if (os_fd_set_cloexec(fd) != 0) {
126 create_tmpfile_cloexec(char *tmpname)
128 int fd = mkstemp(tmpname);
130 fd = set_cloexec_or_close(fd);
138 os_resize_anonymous_file(int fd, off_t size)
141 #ifdef HAVE_POSIX_FALLOCATE
142 ret = posix_fallocate(fd, 0, size);
146 * Filesystems that do support fallocate will return EINVAL
147 * or EOPNOTSUPP, fallback to ftruncate() then.
149 if (ret != EINVAL && ret != EOPNOTSUPP)
152 ret = ftruncate(fd, size);
159 * Create a new, unique, anonymous file of the given size, and
160 * return the file descriptor for it. The file descriptor is set
161 * CLOEXEC. The file is immediately suitable for mmap()'ing
162 * the given size at offset zero.
164 * The file should not have a permanent backing store like a disk,
165 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
167 * The file name is deleted from the file system.
169 * The file is suitable for buffer sharing between processes by
170 * transmitting the file descriptor over Unix sockets using the
171 * SCM_RIGHTS methods.
173 * If the C library implements posix_fallocate(), it is used to
174 * guarantee that disk space is available for the file at the
175 * given size. If disk space is insufficent, errno is set to ENOSPC.
176 * If posix_fallocate() is not supported, program will fallback
177 * to ftruncate() instead.
180 os_create_anonymous_file(off_t size)
182 static const char template[] = "/weston-shared-XXXXXX";
188 path = getenv("XDG_RUNTIME_DIR");
194 name = malloc(strlen(path) + sizeof(template));
199 strcat(name, template);
201 fd = create_tmpfile_cloexec(name);
208 ret = os_resize_anonymous_file(fd, size);
219 buffer_release(void *data, struct wl_buffer *buffer)
221 wl_buffer_destroy(buffer);
224 static const struct wl_buffer_listener buffer_listener = {
229 buffer_create(struct interactive_dpy *inter, uint32_t width, uint32_t height)
231 struct wl_shm_pool *pool;
232 struct wl_buffer *buf;
233 struct wl_region *opaque;
239 switch (inter->shm_format) {
240 case WL_SHM_FORMAT_ARGB8888:
241 case WL_SHM_FORMAT_XRGB8888:
242 case WL_SHM_FORMAT_ABGR8888:
243 case WL_SHM_FORMAT_XBGR8888:
246 case WL_SHM_FORMAT_RGB565:
247 case WL_SHM_FORMAT_BGR565:
251 fprintf(stderr, "Unsupported SHM format %d\n", inter->shm_format);
255 size = stride * height;
256 fd = os_create_anonymous_file(size);
258 fprintf(stderr, "Couldn't create surface buffer\n");
262 map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
263 if (map == MAP_FAILED) {
264 fprintf(stderr, "Couldn't mmap surface buffer\n");
267 memset(map, 0xff, size);
270 pool = wl_shm_create_pool(inter->shm, fd, size);
271 buf = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
273 wl_buffer_add_listener(buf, &buffer_listener, inter);
275 wl_surface_attach(inter->wl_surf, buf, 0, 0);
276 wl_surface_damage(inter->wl_surf, 0, 0, width, height);
278 opaque = wl_compositor_create_region(inter->compositor);
279 wl_region_add(opaque, 0, 0, width, height);
280 wl_surface_set_opaque_region(inter->wl_surf, opaque);
281 wl_region_destroy(opaque);
283 wl_shm_pool_destroy(pool);
288 surface_configure(void *data, struct xdg_surface *surface,
291 struct interactive_dpy *inter = data;
293 xdg_surface_ack_configure(inter->xdg_surf, serial);
294 wl_surface_commit(inter->wl_surf);
297 static const struct xdg_surface_listener surface_listener = {
302 toplevel_configure(void *data, struct xdg_toplevel *toplevel,
303 int32_t width, int32_t height, struct wl_array *states)
305 struct interactive_dpy *inter = data;
312 buffer_create(inter, width, height);
316 toplevel_close(void *data, struct xdg_toplevel *toplevel)
321 static const struct xdg_toplevel_listener toplevel_listener = {
326 static void surface_create(struct interactive_dpy *inter)
328 inter->wl_surf = wl_compositor_create_surface(inter->compositor);
329 inter->xdg_surf = xdg_wm_base_get_xdg_surface(inter->shell, inter->wl_surf);
330 xdg_surface_add_listener(inter->xdg_surf, &surface_listener, inter);
331 inter->xdg_top = xdg_surface_get_toplevel(inter->xdg_surf);
332 xdg_toplevel_add_listener(inter->xdg_top, &toplevel_listener, inter);
333 xdg_toplevel_set_title(inter->xdg_top, "xkbcommon event tester");
334 xdg_toplevel_set_app_id(inter->xdg_top,
335 "org.xkbcommon.test.interactive-wayland");
336 wl_surface_commit(inter->wl_surf);
340 shell_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
342 xdg_wm_base_pong(shell, serial);
345 static const struct xdg_wm_base_listener shell_listener = {
350 kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
351 int fd, uint32_t size)
353 struct interactive_seat *seat = data;
356 buf = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
357 if (buf == MAP_FAILED) {
358 fprintf(stderr, "Failed to mmap keymap: %d\n", errno);
363 seat->keymap = xkb_keymap_new_from_buffer(seat->inter->ctx, buf, size - 1,
364 XKB_KEYMAP_FORMAT_TEXT_V1,
365 XKB_KEYMAP_COMPILE_NO_FLAGS);
369 fprintf(stderr, "Failed to compile keymap!\n");
373 seat->state = xkb_state_new(seat->keymap);
375 fprintf(stderr, "Failed to create XKB state!\n");
381 kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
382 struct wl_surface *surf, struct wl_array *keys)
387 kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
388 struct wl_surface *surf)
393 kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
394 uint32_t key, uint32_t state)
396 struct interactive_seat *seat = data;
398 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
401 printf("%s: ", seat->name_str);
402 tools_print_keycode_state(seat->state, NULL, key + EVDEV_OFFSET,
403 XKB_CONSUMED_MODE_XKB);
406 if (xkb_state_key_get_one_sym(seat->state, key + EVDEV_OFFSET) == XKB_KEY_Escape)
411 kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
412 uint32_t mods_depressed, uint32_t mods_latched,
413 uint32_t mods_locked, uint32_t group)
415 struct interactive_seat *seat = data;
417 xkb_state_update_mask(seat->state, mods_depressed, mods_latched,
418 mods_locked, 0, 0, group);
422 kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
427 static const struct wl_keyboard_listener kbd_listener = {
437 pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
438 struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy)
443 pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
444 struct wl_surface *surf)
449 pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
450 wl_fixed_t fsx, wl_fixed_t fsy)
455 pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
456 uint32_t time, uint32_t button, uint32_t state)
458 struct interactive_seat *seat = data;
460 xdg_toplevel_move(seat->inter->xdg_top, seat->wl_seat, serial);
464 pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
465 uint32_t axis, wl_fixed_t value)
470 pointer_frame(void *data, struct wl_pointer *wl_pointer)
475 pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source)
480 pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time,
486 pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time,
491 static const struct wl_pointer_listener pointer_listener = {
500 pointer_axis_discrete
504 seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
506 struct interactive_seat *seat = data;
508 if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
509 seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
510 wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
512 else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
513 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
514 wl_keyboard_release(seat->wl_kbd);
516 wl_keyboard_destroy(seat->wl_kbd);
518 xkb_state_unref(seat->state);
519 xkb_keymap_unref(seat->keymap);
526 if (!seat->wl_pointer && (caps & WL_SEAT_CAPABILITY_POINTER)) {
527 seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
528 wl_pointer_add_listener(seat->wl_pointer, &pointer_listener,
531 else if (seat->wl_pointer && !(caps & WL_SEAT_CAPABILITY_POINTER)) {
532 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
533 wl_pointer_release(seat->wl_pointer);
535 wl_pointer_destroy(seat->wl_pointer);
536 seat->wl_pointer = NULL;
541 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
543 struct interactive_seat *seat = data;
545 free(seat->name_str);
546 seat->name_str = strdup(name);
549 static const struct wl_seat_listener seat_listener = {
555 seat_create(struct interactive_dpy *inter, struct wl_registry *registry,
556 uint32_t name, uint32_t version)
559 struct interactive_seat *seat = calloc(1, sizeof(*seat));
561 seat->global_name = name;
563 seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface,
565 wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
566 ret = asprintf(&seat->name_str, "seat:%d",
567 wl_proxy_get_id((struct wl_proxy *) seat->wl_seat));
569 wl_list_insert(&inter->seats, &seat->link);
573 seat_destroy(struct interactive_seat *seat)
576 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
577 wl_keyboard_release(seat->wl_kbd);
579 wl_keyboard_destroy(seat->wl_kbd);
581 xkb_state_unref(seat->state);
582 xkb_keymap_unref(seat->keymap);
585 if (seat->wl_pointer) {
586 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
587 wl_pointer_release(seat->wl_pointer);
589 wl_pointer_destroy(seat->wl_pointer);
592 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
593 wl_seat_release(seat->wl_seat);
595 wl_seat_destroy(seat->wl_seat);
597 free(seat->name_str);
598 wl_list_remove(&seat->link);
603 registry_global(void *data, struct wl_registry *registry, uint32_t name,
604 const char *interface, uint32_t version)
606 struct interactive_dpy *inter = data;
608 if (strcmp(interface, "wl_seat") == 0) {
609 seat_create(inter, registry, name, version);
611 else if (strcmp(interface, "xdg_wm_base") == 0) {
612 inter->shell = wl_registry_bind(registry, name,
613 &xdg_wm_base_interface,
615 xdg_wm_base_add_listener(inter->shell, &shell_listener, inter);
617 else if (strcmp(interface, "wl_compositor") == 0) {
618 inter->compositor = wl_registry_bind(registry, name,
619 &wl_compositor_interface,
622 else if (strcmp(interface, "wl_shm") == 0) {
623 inter->shm = wl_registry_bind(registry, name, &wl_shm_interface,
629 registry_delete(void *data, struct wl_registry *registry, uint32_t name)
631 struct interactive_dpy *inter = data;
632 struct interactive_seat *seat, *tmp;
634 wl_list_for_each_safe(seat, tmp, &inter->seats, link) {
635 if (seat->global_name != name)
642 static const struct wl_registry_listener registry_listener = {
648 dpy_disconnect(struct interactive_dpy *inter)
650 struct interactive_seat *seat, *tmp;
652 wl_list_for_each_safe(seat, tmp, &inter->seats, link)
656 xdg_surface_destroy(inter->xdg_surf);
658 xdg_toplevel_destroy(inter->xdg_top);
660 wl_surface_destroy(inter->wl_surf);
662 xdg_wm_base_destroy(inter->shell);
663 if (inter->compositor)
664 wl_compositor_destroy(inter->compositor);
666 wl_shm_destroy(inter->shm);
668 /* Do one last roundtrip to try to destroy our wl_buffer. */
669 wl_display_roundtrip(inter->dpy);
671 xkb_context_unref(inter->ctx);
672 wl_display_disconnect(inter->dpy);
676 main(int argc, char *argv[])
679 struct interactive_dpy inter;
680 struct wl_registry *registry;
683 ret = strcmp(argv[1], "--help");
684 fprintf(ret ? stderr : stdout, "Usage: %s [--help]\n", argv[0]);
686 fprintf(stderr, "unrecognized option: %s\n", argv[1]);
687 return ret ? EXIT_INVALID_USAGE : EXIT_SUCCESS;
690 setlocale(LC_ALL, "");
692 memset(&inter, 0, sizeof(inter));
693 wl_list_init(&inter.seats);
695 inter.dpy = wl_display_connect(NULL);
697 fprintf(stderr, "Couldn't connect to Wayland server\n");
702 inter.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
705 fprintf(stderr, "Couldn't create xkb context\n");
709 registry = wl_display_get_registry(inter.dpy);
710 wl_registry_add_listener(registry, ®istry_listener, &inter);
712 /* The first roundtrip gets the list of advertised globals. */
713 wl_display_roundtrip(inter.dpy);
715 /* The second roundtrip dispatches the events sent after binding, e.g.
716 * after binding to wl_seat globals in the first roundtrip, we will get
717 * the wl_seat::capabilities event in this roundtrip. */
718 wl_display_roundtrip(inter.dpy);
720 if (!inter.shell || !inter.shm || !inter.compositor) {
721 fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n",
722 (inter.shell) ? "" : "xdg_shell ",
723 (inter.shm) ? "" : "wl_shm",
724 (inter.compositor) ? "" : "wl_compositor");
729 surface_create(&inter);
731 tools_disable_stdin_echo();
733 ret = wl_display_dispatch(inter.dpy);
734 } while (ret >= 0 && !terminate);
735 tools_enable_stdin_echo();
737 wl_registry_destroy(registry);
739 dpy_disconnect(&inter);
741 exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);