From 7e123a10b6bd29d48d5f3c18287b3f533fcd3f80 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Apr 2016 12:03:32 +0100 Subject: [PATCH] test: Add interactive-wayland interactive-wayland is very similar to x11/xev, and dumps out as much state as possible. It provides no titlebar and a completely random cursor, but such is life. Signed-off-by: Daniel Stone --- LICENSE | 2 +- Makefile.am | 36 ++ configure.ac | 20 ++ test/.gitignore | 1 + test/interactive-wayland.c | 680 +++++++++++++++++++++++++++++++++++++ 5 files changed, 738 insertions(+), 1 deletion(-) create mode 100644 test/interactive-wayland.c diff --git a/LICENSE b/LICENSE index d96c428..3dcd039 100644 --- a/LICENSE +++ b/LICENSE @@ -20,7 +20,7 @@ BEGINNING OF SOFTWARE COPYRIGHT/LICENSE STATEMENTS: ------------------------------------------------------------------------------- -Copyright © 2009-2012 Daniel Stone +Copyright © 2009-2012, 2016 Daniel Stone Copyright © 2012 Ran Benita Copyright © 2010, 2012 Intel Corporation Copyright © 2008, 2009 Dan Nicholson diff --git a/Makefile.am b/Makefile.am index e91c484..1f8e219 100644 --- a/Makefile.am +++ b/Makefile.am @@ -254,6 +254,42 @@ test_interactive_x11_LDADD = $(TESTS_X11_LDADD) test_interactive_x11_CFLAGS = $(TESTS_X11_CFLAGS) endif ENABLE_X11 +if ENABLE_WAYLAND +build_only_tests += \ + test/interactive-wayland + +TESTS_WAYLAND_LDADD = $(WAYLAND_LIBS) $(TESTS_LDADD) +TESTS_WAYLAND_CFLAGS = $(WAYLAND_CFLAGS) + +.SECONDEXPANSION: + +define protostability +$(if $(findstring unstable,$1),unstable,stable) +endef + +define protoname +$(shell echo $1 | sed 's/\([a-z\-]\+\)-[a-z]\+-v[0-9]\+/\1/') +endef + +%-protocol.c: $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ +%-client-protocol.h: $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header < $< > $@ + +INTERACTIVE_WL_XDG_SHELL_SRCS = \ + xdg-shell-unstable-v5-protocol.c \ + xdg-shell-unstable-v5-client-protocol.h + +test_interactive_wayland_SOURCES = \ + test/interactive-wayland.c \ + $(INTERACTIVE_WL_XDG_SHELL_SRCS) +test_interactive_wayland_LDADD = $(TESTS_WAYLAND_LDADD) +test_interactive_wayland_CFLAGS = $(TESTS_WAYLAND_CFLAGS) + +BUILT_SOURCES += \ + $(INTERACTIVE_WL_XDG_SHELL_SRCS) +endif + check_PROGRAMS = $(build_run_tests) $(build_only_tests) TESTS = $(build_run_tests) $(run_only_tests) diff --git a/configure.ac b/configure.ac index cbaf51f..3f8de97 100644 --- a/configure.ac +++ b/configure.ac @@ -186,6 +186,25 @@ You can disable X11 support with --disable-x11.])]) ], [enable_x11=no]) AM_CONDITIONAL([ENABLE_X11], [test "x$enable_x11" = xyes]) +WAYLAND_PKGS="wayland-client >= 1.2.0 wayland-protocols >= 1.0 wayland-scanner" +AC_ARG_ENABLE([wayland], + [AS_HELP_STRING([--disable-wayland], + [Disable support for Wayland utility programs (default: auto)])], + [], [enable_wayland=auto]) +AS_IF([test "x$enable_wayland" = xauto], [ + PKG_CHECK_EXISTS($WAYLAND_PKGS, [enable_wayland=yes], [enable_wayland=no])], + []) +AS_IF([test "x$enable_wayland" = xyes], [ + PKG_CHECK_MODULES([WAYLAND], $WAYLAND_PKGS, [], + [AC_MSG_ERROR([Wayland utilities require libwayland-client >= 1.2 which was not found. \ +You can disable Wayland support with --disable-wayland.])]) + AC_PATH_PROG([wayland_scanner], [wayland-scanner]) + wayland_scanner=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner` + ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols` + AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir) +], [enable_wayland=no]) +AM_CONDITIONAL([ENABLE_WAYLAND], [test "x$enable_wayland" = xyes]) + AC_CONFIG_FILES([ Makefile xkbcommon-uninstalled.pc @@ -201,6 +220,7 @@ AC_MSG_RESULT([ libxkbcommon: yes libxkbcommon-x11: ${enable_x11} + Wayland utilities: ${enable_wayland} documentation: ${build_docs} default XKB rules: ${DEFAULT_XKB_RULES} diff --git a/test/.gitignore b/test/.gitignore index 626eb19..1be56ad 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -17,6 +17,7 @@ print-compiled-keymap atom x11 interactive-x11 +interactive-wayland utf8 x11comp compose diff --git a/test/interactive-wayland.c b/test/interactive-wayland.c new file mode 100644 index 0000000..7919ff7 --- /dev/null +++ b/test/interactive-wayland.c @@ -0,0 +1,680 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * Copyright © 2013 Ran Benita + * Copyright © 2016 Daniel Stone + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "xkbcommon/xkbcommon.h" +#include "test.h" + +#include +#include "xdg-shell-unstable-v5-client-protocol.h" +#include + +struct interactive_dpy { + struct wl_display *dpy; + struct wl_compositor *compositor; + struct xdg_shell *shell; + struct wl_shm *shm; + uint32_t shm_format; + + struct xkb_context *ctx; + + struct wl_surface *wl_surf; + struct xdg_surface *xdg_surf; + + struct wl_list seats; +}; + +struct interactive_seat { + struct interactive_dpy *inter; + + struct wl_seat *wl_seat; + struct wl_keyboard *wl_kbd; + struct wl_pointer *wl_pointer; + uint32_t version; /* ... of wl_seat */ + uint32_t global_name; /* an ID of sorts */ + char *name_str; /* a descriptor */ + + struct xkb_keymap *keymap; + struct xkb_state *state; + + struct wl_list link; +}; + +static bool terminate; + +/* The following utility functions are taken from Weston's + * shared/os-compatibility.c. */ +static int +os_fd_set_cloexec(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + + return 0; +} + +static int +set_cloexec_or_close(int fd) +{ + if (os_fd_set_cloexec(fd) != 0) { + close(fd); + return -1; + } + return fd; +} + +static int +create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * If the C library implements posix_fallocate(), it is used to + * guarantee that disk space is available for the file at the + * given size. If disk space is insufficent, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. + */ +int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/weston-shared-XXXXXX"; + const char *path; + char *name; + int fd; + int ret; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + +#ifdef HAVE_POSIX_FALLOCATE + ret = posix_fallocate(fd, 0, size); + if (ret != 0) { + close(fd); + errno = ret; + return -1; + } +#else + ret = ftruncate(fd, size); + if (ret < 0) { + close(fd); + return -1; + } +#endif + + return fd; +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + wl_buffer_destroy(buffer); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static void +buffer_create(struct interactive_dpy *inter, uint32_t width, uint32_t height) +{ + struct wl_shm_pool *pool; + struct wl_buffer *buf; + struct wl_region *opaque; + uint32_t stride; + size_t size; + void *map; + int fd; + + switch (inter->shm_format) { + case WL_SHM_FORMAT_ARGB8888: + case WL_SHM_FORMAT_XRGB8888: + case WL_SHM_FORMAT_ABGR8888: + case WL_SHM_FORMAT_XBGR8888: + stride = width * 4; + break; + case WL_SHM_FORMAT_RGB565: + case WL_SHM_FORMAT_BGR565: + stride = width * 2; + break; + } + + size = stride * height; + fd = os_create_anonymous_file(size); + if (fd < 0) { + fprintf(stderr, "Couldn't create surface buffer\n"); + exit(1); + } + + map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, "Couldn't mmap surface buffer\n"); + exit(1); + } + memset(map, 0xff, size); + munmap(map, size); + + pool = wl_shm_create_pool(inter->shm, fd, size); + buf = wl_shm_pool_create_buffer(pool, 0, width, height, stride, + inter->shm_format); + + wl_surface_attach(inter->wl_surf, buf, 0, 0); + wl_surface_damage(inter->wl_surf, 0, 0, width, height); + + opaque = wl_compositor_create_region(inter->compositor); + wl_region_add(opaque, 0, 0, width, height); + wl_surface_set_opaque_region(inter->wl_surf, opaque); + wl_region_destroy(opaque); + + wl_shm_pool_destroy(pool); + close(fd); +} + +static void +surface_configure(void *data, struct xdg_surface *surface, + int32_t width, int32_t height, struct wl_array *states, + uint32_t serial) +{ + struct interactive_dpy *inter = data; + + if (width == 0 || height == 0) { + xdg_surface_ack_configure(inter->xdg_surf, serial); + return; + } + + buffer_create(inter, width, height); + xdg_surface_ack_configure(inter->xdg_surf, serial); + wl_surface_commit(inter->wl_surf); +} + +static void +surface_close(void *data, struct xdg_surface *surface) +{ + terminate = true; +} + +static const struct xdg_surface_listener surface_listener = { + surface_configure, + surface_close +}; + +static int surface_create(struct interactive_dpy *inter) +{ + int width = 200, height = 200; + + inter->wl_surf = wl_compositor_create_surface(inter->compositor); + inter->xdg_surf = xdg_shell_get_xdg_surface(inter->shell, + inter->wl_surf); + xdg_surface_add_listener(inter->xdg_surf, &surface_listener, inter); + xdg_surface_set_title(inter->xdg_surf, "xkbcommon event tester"); + xdg_surface_set_app_id(inter->xdg_surf, + "org.xkbcommon.test.interactive-wayland"); + + buffer_create(inter, width, height); + wl_surface_commit(inter->wl_surf); +} + +static void +shell_ping(void *data, struct xdg_shell *shell, uint32_t serial) +{ + xdg_shell_pong(shell, serial); +} + +static const struct xdg_shell_listener shell_listener = { + shell_ping +}; + +static void +kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format, + int fd, uint32_t size) +{ + struct interactive_seat *seat = data; + void *buf; + + buf = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (buf == MAP_FAILED) { + fprintf(stderr, "Failed to mmap keymap: %d\n", errno); + close(fd); + return; + } + + seat->keymap = xkb_keymap_new_from_buffer(seat->inter->ctx, buf, size - 1, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(buf, size); + close(fd); + if (!seat->keymap) { + fprintf(stderr, "Failed to compile keymap!\n"); + return; + } + + seat->state = xkb_state_new(seat->keymap); + if (!seat->keymap) { + fprintf(stderr, "Failed to create XKB state!\n"); + return; + } +} + +static void +kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + struct wl_surface *surf, struct wl_array *keys) +{ +} + +static void +kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + struct wl_surface *surf) +{ +} + +static void +kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ + struct interactive_seat *seat = data; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + printf("%s: ", seat->name_str); + test_print_keycode_state(seat->state, NULL, key + 8); + + /* Exit on ESC. */ + if (xkb_state_key_get_one_sym(seat->state, key + 8) == XKB_KEY_Escape) + terminate = true; +} + +static void +kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct interactive_seat *seat = data; + + xkb_state_update_mask(seat->state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); +} + +static void +kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate, + int32_t delay) +{ +} + +static const struct wl_keyboard_listener kbd_listener = { + kbd_keymap, + kbd_enter, + kbd_leave, + kbd_key, + kbd_modifiers, + kbd_repeat_info +}; + +static void +pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surf, wl_fixed_t fsx, wl_fixed_t fsy) +{ +} + +static void +pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surf) +{ +} + +static void +pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, + wl_fixed_t fsx, wl_fixed_t fsy) +{ +} + +static void +pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + struct interactive_seat *seat = data; + + xdg_surface_move(seat->inter->xdg_surf, seat->wl_seat, serial); +} + +static void +pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ +} + +static void +pointer_frame(void *data, struct wl_pointer *wl_pointer) +{ +} + +static void +pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t source) +{ +} + +static void +pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, + uint32_t axis) +{ +} + +static void +pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t time, + int32_t discrete) +{ +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_enter, + pointer_leave, + pointer_motion, + pointer_button, + pointer_axis, + pointer_frame, + pointer_axis_source, + pointer_axis_stop, + pointer_axis_discrete +}; + +static void +seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps) +{ + struct interactive_seat *seat = data; + + if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat); + wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat); + } + else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_keyboard_release(seat->wl_kbd); + else + wl_keyboard_destroy(seat->wl_kbd); + + if (seat->state) + xkb_state_unref(seat->state); + if (seat->keymap) + xkb_keymap_unref(seat->keymap); + + seat->state = NULL; + seat->keymap = NULL; + seat->wl_kbd = NULL; + } + + if (!seat->wl_pointer && (caps & WL_SEAT_CAPABILITY_POINTER)) { + seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat); + wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, + seat); + } + else if (seat->wl_pointer && !(caps & WL_SEAT_CAPABILITY_POINTER)) { + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_pointer_release(seat->wl_pointer); + else + wl_pointer_destroy(seat->wl_pointer); + seat->wl_pointer = NULL; + } +} + +static void +seat_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + struct interactive_seat *seat = data; + + free(seat->name_str); + seat->name_str = strdup(name); +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name +}; + +static void +seat_create(struct interactive_dpy *inter, struct wl_registry *registry, + uint32_t name, uint32_t version) +{ + struct interactive_seat *seat = calloc(1, sizeof(*seat)); + + seat->global_name = name; + seat->inter = inter; + seat->wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, + MAX(version, 5)); + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + asprintf(&seat->name_str, "seat:%d", + wl_proxy_get_id((struct wl_proxy *) seat->wl_seat)); +} + +static void +seat_destroy(struct interactive_seat *seat) +{ + if (seat->wl_kbd) { + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_keyboard_release(seat->wl_kbd); + else + wl_keyboard_destroy(seat->wl_kbd); + + if (seat->state) + xkb_state_unref(seat->state); + if (seat->keymap) + xkb_keymap_unref(seat->keymap); + } + + if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION) + wl_seat_release(seat->wl_seat); + else + wl_seat_destroy(seat->wl_seat); + + wl_list_remove(&seat->link); + free(seat); +} + +static void +registry_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + struct interactive_dpy *inter = data; + + if (strcmp(interface, "wl_seat") == 0) { + seat_create(inter, registry, name, version); + } + else if (strcmp(interface, "xdg_shell") == 0) { + inter->shell = wl_registry_bind(registry, name, + &xdg_shell_interface, + MAX(version, 1)); + xdg_shell_add_listener(inter->shell, &shell_listener, inter); + xdg_shell_use_unstable_version(inter->shell, 5); + } + else if (strcmp(interface, "wl_compositor") == 0) { + inter->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, + MAX(version, 1)); + } + else if (strcmp(interface, "wl_shm") == 0) { + inter->shm = wl_registry_bind(registry, name, &wl_shm_interface, + MAX(version, 1)); + } +} + +static void +registry_delete(void *data, struct wl_registry *registry, uint32_t name) +{ + struct interactive_dpy *inter = data; + struct interactive_seat *seat, *tmp; + + wl_list_for_each_safe(seat, tmp, &inter->seats, link) { + if (seat->global_name != name) + continue; + + seat_destroy(seat); + } +} + +static const struct wl_registry_listener registry_listener = { + registry_global, + registry_delete +}; + +static void +dpy_disconnect(struct interactive_dpy *inter) +{ + struct interactive_seat *seat, *tmp; + + wl_list_for_each_safe(seat, tmp, &inter->seats, link) + seat_destroy(seat); + + if (inter->xdg_surf) + xdg_surface_destroy(inter->xdg_surf); + if (inter->wl_surf) + wl_surface_destroy(inter->wl_surf); + if (inter->shell) + xdg_shell_destroy(inter->shell); + + xkb_context_unref(inter->ctx); + wl_display_disconnect(inter->dpy); +} + +int +main(int argc, char *argv[]) +{ + int ret; + struct interactive_dpy inter; + struct wl_registry *registry; + + setlocale(LC_ALL, ""); + + memset(&inter, 0, sizeof(inter)); + wl_list_init(&inter.seats); + + inter.dpy = wl_display_connect(NULL); + if (!inter.dpy) { + fprintf(stderr, "Couldn't connect to Wayland server\n"); + ret = -1; + goto err_out; + } + + inter.ctx = test_get_context(0); + if (!inter.ctx) { + ret = -1; + fprintf(stderr, "Couldn't create xkb context\n"); + goto err_out; + } + + registry = wl_display_get_registry(inter.dpy); + wl_registry_add_listener(registry, ®istry_listener, &inter); + + /* The first roundtrip gets the list of advertised globals. */ + wl_display_roundtrip(inter.dpy); + + /* The second roundtrip dispatches the events sent after binding, e.g. + * after binding to wl_seat globals in the first roundtrip, we will get + * the wl_seat::capabilities event in this roundtrip. */ + wl_display_roundtrip(inter.dpy); + + if (!inter.shell || !inter.shm || !inter.compositor) { + fprintf(stderr, "Required Wayland interfaces %s%s%s unsupported\n", + (inter.shell) ? "" : "xdg_shell ", + (inter.shm) ? "" : "wl_shm", + (inter.compositor) ? "" : "wl_compositor"); + goto err_conn; + } + + ret = surface_create(&inter); + if (ret) { + fprintf(stderr, "Couldn't create a capture window\n"); + goto err_conn; + } + + (void) system("stty -echo"); + do { + ret = wl_display_dispatch(inter.dpy); + } while (ret >= 0 && !terminate); + (void) system("stty echo"); + +err_conn: + dpy_disconnect(&inter); +err_out: + exit(ret >= 0 ? EXIT_SUCCESS : EXIT_FAILURE); +}