Skip to content

Commit

Permalink
cube: Port Wayland impl from wl-shell to xdg-shell
Browse files Browse the repository at this point in the history
This change ports vkcube in both C and C++ versions to use the stable
xdg-shell protocol for Wayland compositors. The original wl-shell
protocol has been deprecated and is being removed from major compositor
libraries; wlroots has already dropped support.

New cmake modules have been added to look for Wayland-Protocols
(containing XML descriptions of all common protocols) and
`wayland-scanner`, the tool used to convert the XML files to a usable C
interface.

The change also adds support for the xdg-decoration protocol, which for
some compositors is provided and needed to let them know that they
should draw a titlebar, borders with resize controls, and other standard
features.

Change-Id: I39bedda93a7c5d0aeeb59c68023552723b413567
  • Loading branch information
mstoeckl authored and TonyBarbour committed Nov 25, 2019
1 parent 39b3005 commit 6f6e337
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 41 deletions.
13 changes: 13 additions & 0 deletions cmake/FindWaylandProtocols.cmake
@@ -0,0 +1,13 @@
# Wayland protocols Defines:
#
# * WAYLAND_PROTOCOLS_FOUND True if the wayland-protocols data path is found
# * WAYLAND_PROTOCOLS_PATH Path to the wayland-scanner executable
#

if(NOT WIN32)
find_package(PkgConfig)
pkg_check_modules(PKG_WAYLAND_PROTOCOLS QUIET wayland-protocols)
set(WAYLAND_PROTOCOLS_PATH ${PKG_WAYLAND_PROTOCOLS_PREFIX}/share/wayland-protocols)
find_package_handle_standard_args(WAYLAND DEFAULT_MSG WAYLAND_PROTOCOLS_PATH)
mark_as_advanced(WAYLAND_PROTOCOLS_PATH)
endif()
14 changes: 14 additions & 0 deletions cmake/FindWaylandScanner.cmake
@@ -0,0 +1,14 @@
# Wayland scanner Defines:
#
# * WAYLAND_SCANNER_FOUND True if wayland-scanner is found
# * WAYLAND_SCANNER_EXECUTABLE Path to the wayland-scanner executable
#

if(NOT WIN32)
# Delegate to pkg-config for our first guess
find_package(PkgConfig)
pkg_check_modules(PKG_WAYLAND_SCANNER QUIET wayland-scanner)
find_program(WAYLAND_SCANNER_EXECUTABLE wayland-scanner ${PKG_WAYLAND_SCANNER_PREFIX}/bin/wayland-scanner)
find_package_handle_standard_args(WAYLAND DEFAULT_MSG WAYLAND_SCANNER_EXECUTABLE)
mark_as_advanced(WAYLAND_SCANNER_EXECUTABLE)
endif()
52 changes: 50 additions & 2 deletions cube/CMakeLists.txt
Expand Up @@ -58,6 +58,8 @@ if(UNIX AND NOT APPLE) # i.e. Linux

if(BUILD_WSI_WAYLAND_SUPPORT)
find_package(Wayland REQUIRED)
find_package(WaylandScanner REQUIRED)
find_package(WaylandProtocols REQUIRED)
include_directories(${WAYLAND_CLIENT_INCLUDE_DIR})
endif()
endif()
Expand Down Expand Up @@ -101,6 +103,50 @@ elseif(UNIX AND NOT APPLE) # i.e. Linux
set(CUBE_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIR} ${CUBE_INCLUDE_DIRS})
link_libraries(${WAYLAND_CLIENT_LIBRARIES})
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)

set(XDG_SHELL_PROTOCOL ${WAYLAND_PROTOCOLS_PATH}/stable/xdg-shell/xdg-shell.xml)
add_custom_command(COMMENT "Generating xdg-shell protocol dispatch data"
OUTPUT xdg-shell-code.c
COMMAND ${WAYLAND_SCANNER_EXECUTABLE}
private-code
${XDG_SHELL_PROTOCOL}
${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-code.c
MAIN_DEPENDENCY ${XDG_SHELL_PROTOCOL}
DEPENDS ${XDG_SHELL_PROTOCOL} ${WAYLAND_SCANNER_EXECUTABLE})
add_custom_command(COMMENT "Generating xdg-shell protocol header"
OUTPUT xdg-shell-client-header.h
COMMAND ${WAYLAND_SCANNER_EXECUTABLE}
client-header
${XDG_SHELL_PROTOCOL}
${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-header.h
MAIN_DEPENDENCY ${XDG_SHELL_PROTOCOL}
DEPENDS ${XDG_SHELL_PROTOCOL} ${WAYLAND_SCANNER_EXECUTABLE})

set(XDG_DECORATION_PROTOCOL ${WAYLAND_PROTOCOLS_PATH}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml)
add_custom_command(COMMENT "Generating xdg-decoration protocol dispatch data"
OUTPUT xdg-decoration-code.c
COMMAND ${WAYLAND_SCANNER_EXECUTABLE}
private-code
${XDG_DECORATION_PROTOCOL}
${CMAKE_CURRENT_BINARY_DIR}/xdg-decoration-code.c
MAIN_DEPENDENCY ${XDG_DECORATION_PROTOCOL}
DEPENDS ${XDG_DECORATION_PROTOCOL} ${WAYLAND_SCANNER_EXECUTABLE})
add_custom_command(COMMENT "Generating xdg-decoration protocol header"
OUTPUT xdg-decoration-client-header.h
COMMAND ${WAYLAND_SCANNER_EXECUTABLE}
client-header
${XDG_DECORATION_PROTOCOL}
${CMAKE_CURRENT_BINARY_DIR}/xdg-decoration-client-header.h
MAIN_DEPENDENCY ${XDG_DECORATION_PROTOCOL}
DEPENDS ${XDG_DECORATION_PROTOCOL} ${WAYLAND_SCANNER_EXECUTABLE})

set(OPTIONAL_WAYLAND_DATA_FILES
${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-code.c
${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-header.h
${CMAKE_CURRENT_BINARY_DIR}/xdg-decoration-code.c
${CMAKE_CURRENT_BINARY_DIR}/xdg-decoration-client-header.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})

elseif(CUBE_WSI_SELECTION STREQUAL "DISPLAY")
add_definitions(-DVK_USE_PLATFORM_DISPLAY_KHR)
else()
Expand Down Expand Up @@ -172,7 +218,8 @@ elseif(NOT WIN32)
${PROJECT_SOURCE_DIR}/cube/cube.vert
${PROJECT_SOURCE_DIR}/cube/cube.frag
cube.vert.inc
cube.frag.inc)
cube.frag.inc
${OPTIONAL_WAYLAND_DATA_FILES})
target_link_libraries(vkcube Vulkan::Vulkan)
CHECK_LIBRARY_EXISTS("rt" clock_gettime "" NEED_RT)
if (NEED_RT)
Expand Down Expand Up @@ -221,7 +268,8 @@ elseif(NOT WIN32)
${PROJECT_SOURCE_DIR}/cube/cube.vert
${PROJECT_SOURCE_DIR}/cube/cube.frag
cube.vert.inc
cube.frag.inc)
cube.frag.inc
${OPTIONAL_WAYLAND_DATA_FILES})
target_link_libraries(vkcubepp Vulkan::Vulkan)
endif()
else()
Expand Down
100 changes: 78 additions & 22 deletions cube/cube.c
Expand Up @@ -37,6 +37,8 @@
#include <X11/Xutil.h>
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
#include <linux/input.h>
#include "xdg-shell-client-header.h"
#include "xdg-decoration-client-header.h"
#endif

#ifdef _WIN32
Expand Down Expand Up @@ -321,8 +323,12 @@ struct demo {
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_surface *window;
struct wl_shell *shell;
struct wl_shell_surface *shell_surface;
struct xdg_wm_base *xdg_wm_base;
struct zxdg_decoration_manager_v1 *xdg_decoration_mgr;
struct zxdg_toplevel_decoration_v1 *toplevel_decoration;
struct xdg_surface *xdg_surface;
int xdg_surface_has_been_configured;
struct xdg_toplevel *xdg_toplevel;
struct wl_seat *seat;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
Expand Down Expand Up @@ -2359,9 +2365,14 @@ static void demo_cleanup(struct demo *demo) {
wl_keyboard_destroy(demo->keyboard);
wl_pointer_destroy(demo->pointer);
wl_seat_destroy(demo->seat);
wl_shell_surface_destroy(demo->shell_surface);
xdg_toplevel_destroy(demo->xdg_toplevel);
xdg_surface_destroy(demo->xdg_surface);
wl_surface_destroy(demo->window);
wl_shell_destroy(demo->shell);
xdg_wm_base_destroy(demo->xdg_wm_base);
if (demo->xdg_decoration_mgr) {
zxdg_toplevel_decoration_v1_destroy(demo->toplevel_decoration);
zxdg_decoration_manager_v1_destroy(demo->xdg_decoration_mgr);
}
wl_compositor_destroy(demo->compositor);
wl_registry_destroy(demo->registry);
wl_display_disconnect(demo->display);
Expand Down Expand Up @@ -2737,34 +2748,69 @@ static void demo_run(struct demo *demo) {
}
}

static void handle_ping(void *data UNUSED, struct wl_shell_surface *shell_surface, uint32_t serial) {
wl_shell_surface_pong(shell_surface, serial);
static void handle_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
struct demo *demo = (struct demo *)data;
xdg_surface_ack_configure(xdg_surface, serial);
if (demo->xdg_surface_has_been_configured) {
demo_resize(demo);
}
demo->xdg_surface_has_been_configured = 1;
}

static void handle_configure(void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED, uint32_t edges UNUSED,
int32_t width UNUSED, int32_t height UNUSED) {}
static const struct xdg_surface_listener xdg_surface_listener = {handle_surface_configure};

static void handle_popup_done(void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED) {}
static void handle_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel UNUSED, int32_t width, int32_t height,
struct wl_array *states UNUSED) {
struct demo *demo = (struct demo *)data;
demo->width = width;
demo->height = height;
/* This should be followed by a surface configure */
}

static const struct wl_shell_surface_listener shell_surface_listener = {handle_ping, handle_configure, handle_popup_done};
static void handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel UNUSED) {
struct demo *demo = (struct demo *)data;
demo->quit = true;
}

static const struct xdg_toplevel_listener xdg_toplevel_listener = {handle_toplevel_configure, handle_toplevel_close};

static void demo_create_window(struct demo *demo) {
if (!demo->xdg_wm_base) {
printf("Compositor did not provide the standard protocol xdg-wm-base\n");
fflush(stdout);
exit(1);
}

demo->window = wl_compositor_create_surface(demo->compositor);
if (!demo->window) {
printf("Can not create wayland_surface from compositor!\n");
fflush(stdout);
exit(1);
}

demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->window);
if (!demo->shell_surface) {
printf("Can not get shell_surface from wayland_surface!\n");
demo->xdg_surface = xdg_wm_base_get_xdg_surface(demo->xdg_wm_base, demo->window);
if (!demo->xdg_surface) {
printf("Can not get xdg_surface from wayland_surface!\n");
fflush(stdout);
exit(1);
}
wl_shell_surface_add_listener(demo->shell_surface, &shell_surface_listener, demo);
wl_shell_surface_set_toplevel(demo->shell_surface);
wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME);
demo->xdg_toplevel = xdg_surface_get_toplevel(demo->xdg_surface);
if (!demo->xdg_toplevel) {
printf("Can not allocate xdg_toplevel for xdg_surface!\n");
fflush(stdout);
exit(1);
}
xdg_surface_add_listener(demo->xdg_surface, &xdg_surface_listener, demo);
xdg_toplevel_add_listener(demo->xdg_toplevel, &xdg_toplevel_listener, demo);
xdg_toplevel_set_title(demo->xdg_toplevel, APP_SHORT_NAME);
if (demo->xdg_decoration_mgr) {
// if supported, let the compositor render titlebars for us
demo->toplevel_decoration =
zxdg_decoration_manager_v1_get_toplevel_decoration(demo->xdg_decoration_mgr, demo->xdg_toplevel);
zxdg_toplevel_decoration_v1_set_mode(demo->toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}

wl_surface_commit(demo->window);
}
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
static void demo_run(struct demo *demo) {
Expand Down Expand Up @@ -3568,7 +3614,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
uint32_t state) {
struct demo *demo = data;
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
wl_shell_surface_move(demo->shell_surface, demo->seat, serial);
xdg_toplevel_move(demo->xdg_toplevel, demo->seat, serial);
}
}

Expand Down Expand Up @@ -3636,21 +3682,31 @@ static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
};

static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) {
static void wm_base_ping(void *data UNUSED, struct xdg_wm_base *xdg_wm_base, uint32_t serial) {
xdg_wm_base_pong(xdg_wm_base, serial);
}

static const struct xdg_wm_base_listener wm_base_listener = {wm_base_ping};

static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface,
uint32_t version UNUSED) {
struct demo *demo = data;
// pickup wayland objects when they appear
if (strcmp(interface, "wl_compositor") == 0) {
if (strcmp(interface, wl_compositor_interface.name) == 0) {
uint32_t minVersion = version < 4 ? version : 4;
demo->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, minVersion);
if (demo->VK_KHR_incremental_present_enabled && minVersion < 4) {
fprintf(stderr, "Wayland compositor doesn't support VK_KHR_incremental_present, disabling.\n");
demo->VK_KHR_incremental_present_enabled = false;
}
} else if (strcmp(interface, "wl_shell") == 0) {
demo->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
} else if (strcmp(interface, "wl_seat") == 0) {
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
demo->xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(demo->xdg_wm_base, &wm_base_listener, NULL);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
demo->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
wl_seat_add_listener(demo->seat, &seat_listener, demo);
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
demo->xdg_decoration_mgr = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
}
}

Expand Down

0 comments on commit 6f6e337

Please sign in to comment.