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 <daniels@collabora.com>master
parent
03f5d36b44
commit
7e123a10b6
2
LICENSE
2
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 <ran234@gmail.com>
|
||||
Copyright © 2010, 2012 Intel Corporation
|
||||
Copyright © 2008, 2009 Dan Nicholson
|
||||
|
|
36
Makefile.am
36
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)
|
||||
|
||||
|
|
20
configure.ac
20
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}
|
||||
|
|
|
@ -17,6 +17,7 @@ print-compiled-keymap
|
|||
atom
|
||||
x11
|
||||
interactive-x11
|
||||
interactive-wayland
|
||||
utf8
|
||||
x11comp
|
||||
compose
|
||||
|
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Ran Benita <ran234@gmail.com>
|
||||
* Copyright © 2016 Daniel Stone <daniel@fooishbar.org>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "xkbcommon/xkbcommon.h"
|
||||
#include "test.h"
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-shell-unstable-v5-client-protocol.h"
|
||||
#include <wayland-util.h>
|
||||
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue