From 6f6e3371c92ac63bf29d5010b2933659e3f8dbef Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Wed, 26 Jun 2019 20:55:53 -0400 Subject: [PATCH] cube: Port Wayland impl from wl-shell to xdg-shell 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 --- cmake/FindWaylandProtocols.cmake | 13 +++++ cmake/FindWaylandScanner.cmake | 14 ++++++ cube/CMakeLists.txt | 52 +++++++++++++++++++- cube/cube.c | 100 +++++++++++++++++++++++++++++--------- cube/cube.cpp | 101 ++++++++++++++++++++++++++++++++------- 5 files changed, 239 insertions(+), 41 deletions(-) create mode 100644 cmake/FindWaylandProtocols.cmake create mode 100644 cmake/FindWaylandScanner.cmake diff --git a/cmake/FindWaylandProtocols.cmake b/cmake/FindWaylandProtocols.cmake new file mode 100644 index 0000000..17859ed --- /dev/null +++ b/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() diff --git a/cmake/FindWaylandScanner.cmake b/cmake/FindWaylandScanner.cmake new file mode 100644 index 0000000..cd93d90 --- /dev/null +++ b/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() diff --git a/cube/CMakeLists.txt b/cube/CMakeLists.txt index 451303f..4fc0034 100644 --- a/cube/CMakeLists.txt +++ b/cube/CMakeLists.txt @@ -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() @@ -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() @@ -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) @@ -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() diff --git a/cube/cube.c b/cube/cube.c index 09a243a..5382cd0 100644 --- a/cube/cube.c +++ b/cube/cube.c @@ -37,6 +37,8 @@ #include #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) #include +#include "xdg-shell-client-header.h" +#include "xdg-decoration-client-header.h" #endif #ifdef _WIN32 @@ -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; @@ -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); @@ -2737,18 +2748,39 @@ 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"); @@ -2756,15 +2788,29 @@ static void demo_create_window(struct demo *demo) { 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) { @@ -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); } } @@ -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); } } diff --git a/cube/cube.cpp b/cube/cube.cpp index 87b24f9..abe80a8 100644 --- a/cube/cube.cpp +++ b/cube/cube.cpp @@ -22,6 +22,8 @@ #include #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) #include +#include "xdg-shell-client-header.h" +#include "xdg-decoration-client-header.h" #endif #include @@ -289,8 +291,12 @@ struct Demo { wl_registry *registry; wl_compositor *compositor; wl_surface *window; - wl_shell *shell; - wl_shell_surface *shell_surface; + xdg_wm_base *wm_base; + zxdg_decoration_manager_v1 *xdg_decoration_mgr; + zxdg_toplevel_decoration_v1 *toplevel_decoration; + xdg_surface *window_surface; + bool xdg_surface_has_been_configured; + xdg_toplevel *window_toplevel; wl_seat *seat; wl_pointer *pointer; wl_keyboard *keyboard; @@ -418,7 +424,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin uint32_t state) { Demo *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->window_toplevel, demo->seat, serial); } } @@ -486,16 +492,24 @@ static const wl_seat_listener seat_listener = { seat_handle_capabilities, }; +static void wm_base_ping(void *data, 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, wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { Demo *demo = (Demo *)data; // pickup wayland objects when they appear - if (strcmp(interface, "wl_compositor") == 0) { + if (strcmp(interface, wl_compositor_interface.name) == 0) { demo->compositor = (wl_compositor *)wl_registry_bind(registry, id, &wl_compositor_interface, 1); - } else if (strcmp(interface, "wl_shell") == 0) { - demo->shell = (wl_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->wm_base = (xdg_wm_base *)wl_registry_bind(registry, id, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(demo->wm_base, &wm_base_listener, nullptr); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { demo->seat = (wl_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 = + (zxdg_decoration_manager_v1 *)wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); } } @@ -525,8 +539,12 @@ Demo::Demo() registry{nullptr}, compositor{nullptr}, window{nullptr}, - shell{nullptr}, - shell_surface{nullptr}, + wm_base{nullptr}, + xdg_decoration_mgr{nullptr}, + toplevel_decoration{nullptr}, + window_surface{nullptr}, + xdg_surface_has_been_configured{false}, + window_toplevel{nullptr}, seat{nullptr}, pointer{nullptr}, keyboard{nullptr}, @@ -669,9 +687,14 @@ void Demo::cleanup() { wl_keyboard_destroy(keyboard); wl_pointer_destroy(pointer); wl_seat_destroy(seat); - wl_shell_surface_destroy(shell_surface); + xdg_toplevel_destroy(window_toplevel); + xdg_surface_destroy(window_surface); wl_surface_destroy(window); - wl_shell_destroy(shell); + xdg_wm_base_destroy(wm_base); + if (xdg_decoration_mgr) { + zxdg_toplevel_decoration_v1_destroy(toplevel_decoration); + zxdg_decoration_manager_v1_destroy(xdg_decoration_mgr); + } wl_compositor_destroy(compositor); wl_registry_destroy(registry); wl_display_disconnect(display); @@ -2690,7 +2713,39 @@ void Demo::run() { } } +static void handle_surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) { + Demo *demo = (Demo *)data; + xdg_surface_ack_configure(xdg_surface, serial); + if (demo->xdg_surface_has_been_configured) { + demo->resize(); + } + demo->xdg_surface_has_been_configured = true; +} + +static const xdg_surface_listener surface_listener = {handle_surface_configure}; + +static void handle_toplevel_configure(void *data, xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, + struct wl_array *states) { + Demo *demo = (Demo *)data; + demo->width = width; + demo->height = height; + // This will be followed by a surface configure +} + +static void handle_toplevel_close(void *data, xdg_toplevel *xdg_toplevel) { + Demo *demo = (Demo *)data; + demo->quit = true; +} + +static const xdg_toplevel_listener toplevel_listener = {handle_toplevel_configure, handle_toplevel_close}; + void Demo::create_window() { + if (!wm_base) { + printf("Compositor did not provide the standard protocol xdg-wm-base\n"); + fflush(stdout); + exit(1); + } + window = wl_compositor_create_surface(compositor); if (!window) { printf("Can not create wayland_surface from compositor!\n"); @@ -2698,16 +2753,28 @@ void Demo::create_window() { exit(1); } - shell_surface = wl_shell_get_shell_surface(shell, window); - if (!shell_surface) { - printf("Can not get shell_surface from wayland_surface!\n"); + window_surface = xdg_wm_base_get_xdg_surface(wm_base, window); + if (!window_surface) { + printf("Can not get xdg_surface from wayland_surface!\n"); fflush(stdout); exit(1); } + window_toplevel = xdg_surface_get_toplevel(window_surface); + if (!window_toplevel) { + printf("Can not allocate xdg_toplevel for xdg_surface!\n"); + fflush(stdout); + exit(1); + } + xdg_surface_add_listener(window_surface, &surface_listener, this); + xdg_toplevel_add_listener(window_toplevel, &toplevel_listener, this); + xdg_toplevel_set_title(window_toplevel, APP_SHORT_NAME); + if (xdg_decoration_mgr) { + // if supported, let the compositor render titlebars for us + toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(xdg_decoration_mgr, window_toplevel); + zxdg_toplevel_decoration_v1_set_mode(toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } - wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this); - wl_shell_surface_set_toplevel(shell_surface); - wl_shell_surface_set_title(shell_surface, APP_SHORT_NAME); + wl_surface_commit(window); } #elif defined(VK_USE_PLATFORM_MACOS_MVK) void Demo::run() { -- 2.7.4