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.
33 #include "xkbcommon/xkbcommon.h"
36 #include <wayland-client.h>
37 #include "xdg-shell-unstable-v5-client-protocol.h"
38 #include <wayland-util.h>
40 struct interactive_dpy {
41 struct wl_display *dpy;
42 struct wl_compositor *compositor;
43 struct xdg_shell *shell;
47 struct xkb_context *ctx;
49 struct wl_surface *wl_surf;
50 struct xdg_surface *xdg_surf;
55 struct interactive_seat {
56 struct interactive_dpy *inter;
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 */
65 struct xkb_keymap *keymap;
66 struct xkb_state *state;
71 static bool terminate;
73 /* The following utility functions are taken from Weston's
74 * shared/os-compatibility.c. */
76 os_fd_set_cloexec(int fd)
83 flags = fcntl(fd, F_GETFD);
87 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
94 set_cloexec_or_close(int fd)
96 if (os_fd_set_cloexec(fd) != 0) {
104 create_tmpfile_cloexec(char *tmpname)
109 fd = mkostemp(tmpname, O_CLOEXEC);
113 fd = mkstemp(tmpname);
115 fd = set_cloexec_or_close(fd);
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.
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.
132 * The file name is deleted from the file system.
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.
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.
145 os_create_anonymous_file(off_t size)
147 static const char template[] = "/weston-shared-XXXXXX";
153 path = getenv("XDG_RUNTIME_DIR");
159 name = malloc(strlen(path) + sizeof(template));
164 strcat(name, template);
166 fd = create_tmpfile_cloexec(name);
173 #ifdef HAVE_POSIX_FALLOCATE
174 ret = posix_fallocate(fd, 0, size);
181 ret = ftruncate(fd, size);
192 buffer_release(void *data, struct wl_buffer *buffer)
194 wl_buffer_destroy(buffer);
197 static const struct wl_buffer_listener buffer_listener = {
202 buffer_create(struct interactive_dpy *inter, uint32_t width, uint32_t height)
204 struct wl_shm_pool *pool;
205 struct wl_buffer *buf;
206 struct wl_region *opaque;
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:
219 case WL_SHM_FORMAT_RGB565:
220 case WL_SHM_FORMAT_BGR565:
225 size = stride * height;
226 fd = os_create_anonymous_file(size);
228 fprintf(stderr, "Couldn't create surface buffer\n");
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");
237 memset(map, 0xff, size);
240 pool = wl_shm_create_pool(inter->shm, fd, size);
241 buf = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
244 wl_surface_attach(inter->wl_surf, buf, 0, 0);
245 wl_surface_damage(inter->wl_surf, 0, 0, width, height);
247 opaque = wl_compositor_create_region(inter->compositor);
248 wl_region_add(opaque, 0, 0, width, height);
249 wl_surface_set_opaque_region(inter->wl_surf, opaque);
250 wl_region_destroy(opaque);
252 wl_shm_pool_destroy(pool);
257 surface_configure(void *data, struct xdg_surface *surface,
258 int32_t width, int32_t height, struct wl_array *states,
261 struct interactive_dpy *inter = data;
263 if (width == 0 || height == 0) {
264 xdg_surface_ack_configure(inter->xdg_surf, serial);
268 buffer_create(inter, width, height);
269 xdg_surface_ack_configure(inter->xdg_surf, serial);
270 wl_surface_commit(inter->wl_surf);
274 surface_close(void *data, struct xdg_surface *surface)
279 static const struct xdg_surface_listener surface_listener = {
284 static int surface_create(struct interactive_dpy *inter)
286 int width = 200, height = 200;
288 inter->wl_surf = wl_compositor_create_surface(inter->compositor);
289 inter->xdg_surf = xdg_shell_get_xdg_surface(inter->shell,
291 xdg_surface_add_listener(inter->xdg_surf, &surface_listener, inter);
292 xdg_surface_set_title(inter->xdg_surf, "xkbcommon event tester");
293 xdg_surface_set_app_id(inter->xdg_surf,
294 "org.xkbcommon.test.interactive-wayland");
296 buffer_create(inter, width, height);
297 wl_surface_commit(inter->wl_surf);
301 shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
303 xdg_shell_pong(shell, serial);
306 static const struct xdg_shell_listener shell_listener = {
311 kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
312 int fd, uint32_t size)
314 struct interactive_seat *seat = data;
317 buf = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
318 if (buf == MAP_FAILED) {
319 fprintf(stderr, "Failed to mmap keymap: %d\n", errno);
324 seat->keymap = xkb_keymap_new_from_buffer(seat->inter->ctx, buf, size - 1,
325 XKB_KEYMAP_FORMAT_TEXT_V1, 0);
329 fprintf(stderr, "Failed to compile keymap!\n");
333 seat->state = xkb_state_new(seat->keymap);
335 fprintf(stderr, "Failed to create XKB state!\n");
341 kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
342 struct wl_surface *surf, struct wl_array *keys)
347 kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
348 struct wl_surface *surf)
353 kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
354 uint32_t key, uint32_t state)
356 struct interactive_seat *seat = data;
358 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
361 printf("%s: ", seat->name_str);
362 test_print_keycode_state(seat->state, NULL, key + 8);
365 if (xkb_state_key_get_one_sym(seat->state, key + 8) == XKB_KEY_Escape)
370 kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
371 uint32_t mods_depressed, uint32_t mods_latched,
372 uint32_t mods_locked, uint32_t group)
374 struct interactive_seat *seat = data;
376 xkb_state_update_mask(seat->state, mods_depressed, mods_latched,
377 mods_locked, 0, 0, group);
381 kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
386 static const struct wl_keyboard_listener kbd_listener = {
396 pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
397 struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy)
402 pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
403 struct wl_surface *surf)
408 pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
409 wl_fixed_t fsx, wl_fixed_t fsy)
414 pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
415 uint32_t time, uint32_t button, uint32_t state)
417 struct interactive_seat *seat = data;
419 xdg_surface_move(seat->inter->xdg_surf, seat->wl_seat, serial);
423 pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
424 uint32_t axis, wl_fixed_t value)
429 pointer_frame(void *data, struct wl_pointer *wl_pointer)
434 pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source)
439 pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time,
445 pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time,
450 static const struct wl_pointer_listener pointer_listener = {
459 pointer_axis_discrete
463 seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
465 struct interactive_seat *seat = data;
467 if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
468 seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
469 wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
471 else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
472 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
473 wl_keyboard_release(seat->wl_kbd);
475 wl_keyboard_destroy(seat->wl_kbd);
478 xkb_state_unref(seat->state);
480 xkb_keymap_unref(seat->keymap);
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,
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);
496 wl_pointer_destroy(seat->wl_pointer);
497 seat->wl_pointer = NULL;
502 seat_name(void *data, struct wl_seat *wl_seat, const char *name)
504 struct interactive_seat *seat = data;
506 free(seat->name_str);
507 seat->name_str = strdup(name);
510 static const struct wl_seat_listener seat_listener = {
516 seat_create(struct interactive_dpy *inter, struct wl_registry *registry,
517 uint32_t name, uint32_t version)
519 struct interactive_seat *seat = calloc(1, sizeof(*seat));
521 seat->global_name = name;
523 seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface,
525 wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
526 asprintf(&seat->name_str, "seat:%d",
527 wl_proxy_get_id((struct wl_proxy *) seat->wl_seat));
531 seat_destroy(struct interactive_seat *seat)
534 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
535 wl_keyboard_release(seat->wl_kbd);
537 wl_keyboard_destroy(seat->wl_kbd);
540 xkb_state_unref(seat->state);
542 xkb_keymap_unref(seat->keymap);
545 if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
546 wl_seat_release(seat->wl_seat);
548 wl_seat_destroy(seat->wl_seat);
550 wl_list_remove(&seat->link);
555 registry_global(void *data, struct wl_registry *registry, uint32_t name,
556 const char *interface, uint32_t version)
558 struct interactive_dpy *inter = data;
560 if (strcmp(interface, "wl_seat") == 0) {
561 seat_create(inter, registry, name, version);
563 else if (strcmp(interface, "xdg_shell") == 0) {
564 inter->shell = wl_registry_bind(registry, name,
565 &xdg_shell_interface,
567 xdg_shell_add_listener(inter->shell, &shell_listener, inter);
568 xdg_shell_use_unstable_version(inter->shell, 5);
570 else if (strcmp(interface, "wl_compositor") == 0) {
571 inter->compositor = wl_registry_bind(registry, name,
572 &wl_compositor_interface,
575 else if (strcmp(interface, "wl_shm") == 0) {
576 inter->shm = wl_registry_bind(registry, name, &wl_shm_interface,
582 registry_delete(void *data, struct wl_registry *registry, uint32_t name)
584 struct interactive_dpy *inter = data;
585 struct interactive_seat *seat, *tmp;
587 wl_list_for_each_safe(seat, tmp, &inter->seats, link) {
588 if (seat->global_name != name)
595 static const struct wl_registry_listener registry_listener = {
601 dpy_disconnect(struct interactive_dpy *inter)
603 struct interactive_seat *seat, *tmp;
605 wl_list_for_each_safe(seat, tmp, &inter->seats, link)
609 xdg_surface_destroy(inter->xdg_surf);
611 wl_surface_destroy(inter->wl_surf);
613 xdg_shell_destroy(inter->shell);
615 xkb_context_unref(inter->ctx);
616 wl_display_disconnect(inter->dpy);
620 main(int argc, char *argv[])
623 struct interactive_dpy inter;
624 struct wl_registry *registry;
626 setlocale(LC_ALL, "");
628 memset(&inter, 0, sizeof(inter));
629 wl_list_init(&inter.seats);
631 inter.dpy = wl_display_connect(NULL);
633 fprintf(stderr, "Couldn't connect to Wayland server\n");
638 inter.ctx = test_get_context(0);
641 fprintf(stderr, "Couldn't create xkb context\n");
645 registry = wl_display_get_registry(inter.dpy);
646 wl_registry_add_listener(registry, ®istry_listener, &inter);
648 /* The first roundtrip gets the list of advertised globals. */
649 wl_display_roundtrip(inter.dpy);
651 /* The second roundtrip dispatches the events sent after binding, e.g.
652 * after binding to wl_seat globals in the first roundtrip, we will get
653 * the wl_seat::capabilities event in this roundtrip. */
654 wl_display_roundtrip(inter.dpy);
656 if (!inter.shell || !inter.shm || !inter.compositor) {
657 fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n",
658 (inter.shell) ? "" : "xdg_shell ",
659 (inter.shm) ? "" : "wl_shm",
660 (inter.compositor) ? "" : "wl_compositor");
664 ret = surface_create(&inter);
666 fprintf(stderr, "Couldn't create a capture window\n");
670 (void) system("stty -echo");
672 ret = wl_display_dispatch(inter.dpy);
673 } while (ret >= 0 && !terminate);
674 (void) system("stty echo");
677 dpy_disconnect(&inter);
679 exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);