From 9e6fcbe72c3550d2007d54bcdab1178f90803eda Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Thu, 11 Jun 2020 22:10:28 +0100 Subject: [PATCH] wayland: client-side decoration --- CMakeLists.txt | 8 + cmake/sdlchecks.cmake | 9 + configure.ac | 38 ++++ include/SDL_config.h.cmake | 1 + include/SDL_config.h.in | 2 + src/video/wayland/SDL_waylanddyn.c | 6 +- src/video/wayland/SDL_waylanddyn.h | 44 +++++ src/video/wayland/SDL_waylandevents.c | 40 +++++ src/video/wayland/SDL_waylandsym.h | 53 ++++++ src/video/wayland/SDL_waylandvideo.c | 50 +++++- src/video/wayland/SDL_waylandvideo.h | 3 + src/video/wayland/SDL_waylandwindow.c | 240 +++++++++++++++++++++++++- src/video/wayland/SDL_waylandwindow.h | 12 ++ 13 files changed, 499 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 665bca6e8..6df265618 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -388,6 +388,7 @@ set_option(CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" O set_option(VIDEO_X11 "Use X11 video driver" ${UNIX_SYS}) set_option(VIDEO_WAYLAND "Use Wayland video driver" ${UNIX_SYS}) dep_option(WAYLAND_SHARED "Dynamically load Wayland support" ON "VIDEO_WAYLAND" OFF) +dep_option(WAYLAND_LIBDECOR "Use client-side window decorations on Wayland" ON "VIDEO_WAYLAND" ON) dep_option(VIDEO_WAYLAND_QT_TOUCH "QtWayland server support for Wayland video driver" ON "VIDEO_WAYLAND" OFF) set_option(VIDEO_RPI "Use Raspberry Pi video driver" ${UNIX_SYS}) dep_option(X11_SHARED "Dynamically load X11 support" ON "VIDEO_X11" OFF) @@ -2593,6 +2594,9 @@ if(SDL_SHARED) set_property(TARGET SDL2 APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") target_compile_definitions(SDL2 PRIVATE IOS_DYLIB=1) endif() + if(WAYLAND_LIBDECOR) + target_include_directories(SDL2 PRIVATE "${libdecor_INCLUDE_DIRS}") + endif() endif() if(ANDROID) @@ -2644,6 +2648,10 @@ if(SDL_STATIC) if(IOS OR TVOS) set_property(TARGET SDL2-static APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") endif() + if(WAYLAND_LIBDECOR) + target_include_directories(SDL2-static PRIVATE "${libdecor_INCLUDE_DIRS}") + target_link_libraries(SDL2-static "${libdecor_LIBRARIES}") + endif() endif() ##### Tests ##### diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index d3f6cec1f..8df592549 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -697,6 +697,15 @@ macro(CheckWayland) set(EXTRA_LIBS ${WAYLAND_LIBRARIES} ${EXTRA_LIBS}) endif() + if(WAYLAND_LIBDECOR) + pkg_check_modules(libdecor REQUIRED libdecor-0) + + FindLibraryAndSONAME(decor-0) + set(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR "\"${DECOR_0_LIB_SONAME}\"") + + add_definitions(-DHAVE_LIBDECOR_H) + endif() + set(SDL_VIDEO_DRIVER_WAYLAND 1) endif() endif() diff --git a/configure.ac b/configure.ac index 10879ee8d..615048e41 100644 --- a/configure.ac +++ b/configure.ac @@ -2777,6 +2777,43 @@ CheckFcitx() fi } +dnl See if libdecor is available +CheckLibDecor() +{ + AC_ARG_ENABLE(libdecor, +[AS_HELP_STRING([--enable-libdecor], [use libdecor for Wayland client-side decorations [default=yes]])], + , enable_libdecor=yes) + if test x$enable_libdecor = xyes; then + AC_MSG_CHECKING(for libdecor support) + AS_IF([$PKG_CONFIG --exists libdecor-0], + [video_libdecor=yes], + [video_libdecor=no]) + AC_MSG_RESULT($video_libdecor) + if test x$video_libdecor = xyes; then + EXTRA_CFLAGS="$EXTRA_CFLAGS `$PKG_CONFIG --cflags libdecor-0`" + AC_DEFINE(HAVE_LIBDECOR_H, 1, [ ]) + + AC_ARG_ENABLE(libdecor-shared, +[AS_HELP_STRING([--enable-libdecor-shared], [dynamically load libdecor [default=yes]])], + , enable_libdecor_shared=yes) + + decor_lib=[`find_lib "libdecor-0.so.*" "" | sed 's/.*\/\(.*\)/\1/; q'`] + + if test x$have_loadso != xyes && \ + test x$enable_libdecor_shared = xyes; then + AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic libdecor loading]) + fi + if test x$have_loadso = xyes && \ + test x$enable_libdecor_shared = xyes && test x$decor_lib != x; then + echo "-- dynamic libdecor -> $decor_lib" + AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR, "$decor_lib", [ ]) + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS `$PKG_CONFIG --libs libdecor-0`" + fi + fi + fi +} + dnl Check to see if GameController framework support is desired CheckJoystickMFI() { @@ -3572,6 +3609,7 @@ case "$host" in CheckInotify CheckIBus CheckFcitx + CheckLibDecor case $ARCH in linux) CheckInputKD diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index 40c5a7ed8..897d06ac8 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -387,6 +387,7 @@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR@ #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON@ +#cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR@ #cmakedefine SDL_VIDEO_DRIVER_X11 @SDL_VIDEO_DRIVER_X11@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC @SDL_VIDEO_DRIVER_X11_DYNAMIC@ diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 9ddd932e3..ea877237c 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -225,6 +225,7 @@ #undef HAVE_IMMINTRIN_H #undef HAVE_LIBUDEV_H #undef HAVE_LIBSAMPLERATE_H +#undef HAVE_LIBDECOR_H #undef HAVE_DDRAW_H #undef HAVE_DINPUT_H @@ -365,6 +366,7 @@ #undef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL #undef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR #undef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON +#undef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR #undef SDL_VIDEO_DRIVER_X11 #undef SDL_VIDEO_DRIVER_RPI #undef SDL_VIDEO_DRIVER_KMSDRM diff --git a/src/video/wayland/SDL_waylanddyn.c b/src/video/wayland/SDL_waylanddyn.c index e72986188..2bbc49efe 100644 --- a/src/video/wayland/SDL_waylanddyn.c +++ b/src/video/wayland/SDL_waylanddyn.c @@ -47,12 +47,16 @@ typedef struct #ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON #define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON NULL #endif +#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR +#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR NULL +#endif static waylanddynlib waylandlibs[] = { {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC}, {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL}, {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR}, - {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON} + {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON}, + {NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR} }; static void * diff --git a/src/video/wayland/SDL_waylanddyn.h b/src/video/wayland/SDL_waylanddyn.h index 55ac1ebf7..eef24a6dd 100644 --- a/src/video/wayland/SDL_waylanddyn.h +++ b/src/video/wayland/SDL_waylanddyn.h @@ -34,6 +34,19 @@ struct wl_display; struct wl_surface; struct wl_shm; +/* We also need some for libdecor */ +struct wl_seat; +struct wl_output; +struct libdecor; +struct libdecor_frame; +struct libdecor_state; +struct libdecor_configuration; +struct libdecor_interface; +struct libdecor_frame_interface; +enum libdecor_resize_edge; +enum libdecor_capabilities; +enum libdecor_window_state; + #include #include "wayland-cursor.h" #include "wayland-util.h" @@ -82,6 +95,8 @@ void SDL_WAYLAND_UnloadSymbols(void); #define wl_proxy_add_listener (*WAYLAND_wl_proxy_add_listener) #define wl_proxy_marshal_constructor (*WAYLAND_wl_proxy_marshal_constructor) #define wl_proxy_marshal_constructor_versioned (*WAYLAND_wl_proxy_marshal_constructor_versioned) +#define wl_proxy_set_tag (*WAYLAND_wl_proxy_set_tag) +#define wl_proxy_get_tag (*WAYLAND_wl_proxy_get_tag) #define wl_seat_interface (*WAYLAND_wl_seat_interface) #define wl_surface_interface (*WAYLAND_wl_surface_interface) @@ -101,6 +116,35 @@ void SDL_WAYLAND_UnloadSymbols(void); #define wl_data_source_interface (*WAYLAND_wl_data_source_interface) #define wl_data_device_manager_interface (*WAYLAND_wl_data_device_manager_interface) +#ifdef HAVE_LIBDECOR_H +/* Must be included before our defines */ +#include + +#define libdecor_unref (*WAYLAND_libdecor_unref) +#define libdecor_new (*WAYLAND_libdecor_new) +#define libdecor_decorate (*WAYLAND_libdecor_decorate) +#define libdecor_frame_unref (*WAYLAND_libdecor_frame_unref) +#define libdecor_frame_set_title (*WAYLAND_libdecor_frame_set_title) +#define libdecor_frame_set_app_id (*WAYLAND_libdecor_frame_set_app_id) +#define libdecor_frame_set_max_content_size (*WAYLAND_libdecor_frame_set_max_content_size) +#define libdecor_frame_set_min_content_size (*WAYLAND_libdecor_frame_set_min_content_size) +#define libdecor_frame_resize (*WAYLAND_libdecor_frame_resize) +#define libdecor_frame_move (*WAYLAND_libdecor_frame_move) +#define libdecor_frame_commit (*WAYLAND_libdecor_frame_commit) +#define libdecor_frame_set_minimized (*WAYLAND_libdecor_frame_set_minimized) +#define libdecor_frame_set_maximized (*WAYLAND_libdecor_frame_set_maximized) +#define libdecor_frame_unset_maximized (*WAYLAND_libdecor_frame_unset_maximized) +#define libdecor_frame_set_fullscreen (*WAYLAND_libdecor_frame_set_fullscreen) +#define libdecor_frame_unset_fullscreen (*WAYLAND_libdecor_frame_unset_fullscreen) +#define libdecor_frame_set_capabilities (*WAYLAND_libdecor_frame_set_capabilities) +#define libdecor_frame_unset_capabilities (*WAYLAND_libdecor_frame_unset_capabilities) +#define libdecor_frame_map (*WAYLAND_libdecor_frame_map) +#define libdecor_state_new (*WAYLAND_libdecor_state_new) +#define libdecor_state_free (*WAYLAND_libdecor_state_free) +#define libdecor_configuration_get_content_size (*WAYLAND_libdecor_configuration_get_content_size) +#define libdecor_configuration_get_window_state (*WAYLAND_libdecor_configuration_get_window_state) +#endif + #endif /* SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC */ #include "wayland-client-protocol.h" diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index b69171ab6..d0c681173 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -43,6 +43,10 @@ #include "xdg-shell-unstable-v6-client-protocol.h" #include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" +#ifdef HAVE_LIBDECOR_H +#include +#endif + #ifdef SDL_INPUT_LINUXEV #include #else @@ -272,6 +276,11 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, return; } + /* check that this surface belongs to one of the SDL windows */ + if (!SDL_WAYLAND_own_surface(surface)) { + return; + } + /* This handler will be called twice in Wayland 1.4 * Once for the window surface which has valid user data * and again for the mouse cursor surface which does not have valid user data @@ -301,6 +310,10 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer, { struct SDL_WaylandInput *input = data; + if (!surface || !SDL_WAYLAND_own_surface(surface)) { + return; + } + if (input->pointer_focus) { SDL_SetMouseFocus(NULL); input->pointer_focus = NULL; @@ -328,8 +341,20 @@ ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial) WL_SHELL_SURFACE_RESIZE_*), but the values are the same. */ const uint32_t *directions_zxdg = directions_wl; +#ifdef HAVE_LIBDECOR_H + /* ditto for libdecor. */ + const uint32_t *directions_libdecor = directions_wl; +#endif + switch (rc) { case SDL_HITTEST_DRAGGABLE: +#ifdef HAVE_LIBDECOR_H + if (input->display->shell.libdecor) { + if (window_data->shell_surface.libdecor.frame) { + libdecor_frame_move(window_data->shell_surface.libdecor.frame, input->seat, serial); + } + } else +#endif if (input->display->shell.xdg) { if (window_data->shell_surface.xdg.roleobj.toplevel) { xdg_toplevel_move(window_data->shell_surface.xdg.roleobj.toplevel, @@ -357,6 +382,13 @@ ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial) case SDL_HITTEST_RESIZE_BOTTOM: case SDL_HITTEST_RESIZE_BOTTOMLEFT: case SDL_HITTEST_RESIZE_LEFT: +#ifdef HAVE_LIBDECOR_H + if (input->display->shell.libdecor) { + if (window_data->shell_surface.libdecor.frame) { + libdecor_frame_resize(window_data->shell_surface.libdecor.frame, input->seat, serial, directions_libdecor[rc - SDL_HITTEST_RESIZE_TOPLEFT]); + } + } else +#endif if (input->display->shell.xdg) { if (window_data->shell_surface.xdg.roleobj.toplevel) { xdg_toplevel_resize(window_data->shell_surface.xdg.roleobj.toplevel, @@ -723,6 +755,10 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, return; } + if (!SDL_WAYLAND_own_surface(surface)) { + return; + } + window = wl_surface_get_user_data(surface); if (window) { @@ -741,6 +777,10 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, { struct SDL_WaylandInput *input = data; + if (!surface || !SDL_WAYLAND_own_surface(surface)) { + return; + } + /* Stop key repeat before clearing keyboard focus */ keyboard_repeat_clear(&input->keyboard_repeat); diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 278c4ff17..76b9c276f 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h @@ -33,6 +33,8 @@ #define SDL_WAYLAND_INTERFACE(iface) #endif +#include + SDL_WAYLAND_MODULE(WAYLAND_CLIENT) SDL_WAYLAND_SYM(void, wl_proxy_marshal, (struct wl_proxy *, uint32_t, ...)) SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_create, (struct wl_proxy *, const struct wl_interface *)) @@ -71,6 +73,10 @@ SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_constructor, (struct wl_prox SDL_WAYLAND_MODULE(WAYLAND_CLIENT_1_10) SDL_WAYLAND_SYM(struct wl_proxy *, wl_proxy_marshal_constructor_versioned, (struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...)) +SDL_WAYLAND_MODULE(WAYLAND_CLIENT_1_18) +SDL_WAYLAND_SYM(void, wl_proxy_set_tag, (struct wl_proxy *, const char * const *)) +SDL_WAYLAND_SYM(const char * const *, wl_proxy_get_tag, (struct wl_proxy *)) + SDL_WAYLAND_INTERFACE(wl_seat_interface) SDL_WAYLAND_INTERFACE(wl_surface_interface) SDL_WAYLAND_INTERFACE(wl_shm_pool_interface) @@ -134,6 +140,53 @@ SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, const xkb_keysym_t **) ) SDL_WAYLAND_SYM(uint32_t, xkb_keysym_to_utf32, (xkb_keysym_t) ) +#ifdef HAVE_LIBDECOR_H +SDL_WAYLAND_MODULE(WAYLAND_LIBDECOR) +SDL_WAYLAND_SYM(void, libdecor_unref, (struct libdecor *)) +SDL_WAYLAND_SYM(struct libdecor *, libdecor_new, (struct wl_display *, struct libdecor_interface *)) +SDL_WAYLAND_SYM(struct libdecor_frame *, libdecor_decorate, (struct libdecor *,\ + struct wl_surface *,\ + struct libdecor_frame_interface *,\ + void *)) +SDL_WAYLAND_SYM(void, libdecor_frame_unref, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_title, (struct libdecor_frame *, const char *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_app_id, (struct libdecor_frame *, const char *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_max_content_size, (struct libdecor_frame *frame,\ + int content_width,\ + int content_height)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_min_content_size, (struct libdecor_frame *frame,\ + int content_width,\ + int content_height)) +SDL_WAYLAND_SYM(void, libdecor_frame_resize, (struct libdecor_frame *,\ + struct wl_seat *,\ + uint32_t,\ + enum libdecor_resize_edge)) +SDL_WAYLAND_SYM(void, libdecor_frame_move, (struct libdecor_frame *,\ + struct wl_seat *,\ + uint32_t)) +SDL_WAYLAND_SYM(void, libdecor_frame_commit, (struct libdecor_frame *,\ + struct libdecor_state *,\ + struct libdecor_configuration *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_minimized, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_maximized, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_unset_maximized, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_fullscreen, (struct libdecor_frame *, struct wl_output *)) +SDL_WAYLAND_SYM(void, libdecor_frame_unset_fullscreen, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_set_capabilities, (struct libdecor_frame *, \ + enum libdecor_capabilities)) +SDL_WAYLAND_SYM(void, libdecor_frame_unset_capabilities, (struct libdecor_frame *, \ + enum libdecor_capabilities)) +SDL_WAYLAND_SYM(void, libdecor_frame_map, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(struct libdecor_state *, libdecor_state_new, (int, int)) +SDL_WAYLAND_SYM(void, libdecor_state_free, (struct libdecor_state *)) +SDL_WAYLAND_SYM(bool, libdecor_configuration_get_content_size, (struct libdecor_configuration *,\ + struct libdecor_frame *,\ + int *,\ + int *)) +SDL_WAYLAND_SYM(bool, libdecor_configuration_get_window_state, (struct libdecor_configuration *,\ + enum libdecor_window_state *)) +#endif + #undef SDL_WAYLAND_MODULE #undef SDL_WAYLAND_SYM #undef SDL_WAYLAND_INTERFACE diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 44b216361..ba5dc2376 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -53,6 +53,10 @@ #include "idle-inhibit-unstable-v1-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" +#ifdef HAVE_LIBDECOR_H +#include +#endif + #define WAYLANDVID_DRIVER_NAME "wayland" /* Initialization/Query functions */ @@ -433,6 +437,21 @@ static const struct xdg_wm_base_listener shell_listener_xdg = { }; +#ifdef HAVE_LIBDECOR_H +static void +libdecor_error(struct libdecor *context, + enum libdecor_error error, + const char *message) +{ + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "libdecor error (%d): %s\n", error, message); +} + +static struct libdecor_interface libdecor_interface = { + libdecor_error, +}; +#endif + + static void display_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) @@ -447,13 +466,25 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, Wayland_add_display(d, id); } else if (SDL_strcmp(interface, "wl_seat") == 0) { Wayland_display_add_input(d, id, version); - } else if (SDL_strcmp(interface, "xdg_wm_base") == 0) { + } else if ( +#ifdef HAVE_LIBDECOR_H + !d->shell.libdecor && +#endif + SDL_strcmp(interface, "xdg_wm_base") == 0) { d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL); - } else if (SDL_strcmp(interface, "zxdg_shell_v6") == 0) { + } else if ( +#ifdef HAVE_LIBDECOR_H + !d->shell.libdecor && +#endif + SDL_strcmp(interface, "zxdg_shell_v6") == 0) { d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1); zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL); - } else if (SDL_strcmp(interface, "wl_shell") == 0) { + } else if ( +#ifdef HAVE_LIBDECOR_H + !d->shell.libdecor && +#endif + SDL_strcmp(interface, "wl_shell") == 0) { d->shell.wl = wl_registry_bind(d->registry, id, &wl_shell_interface, 1); } else if (SDL_strcmp(interface, "wl_shm") == 0) { d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); @@ -510,6 +541,12 @@ Wayland_VideoInit(_THIS) return SDL_SetError("Failed to get the Wayland registry"); } +#ifdef HAVE_LIBDECOR_H + if (SDL_WAYLAND_HAVE_WAYLAND_LIBDECOR) { + data->shell.libdecor = libdecor_new(data->display, &libdecor_interface); + } +#endif + wl_registry_add_listener(data->registry, ®istry_listener, data); // First roundtrip to receive all registry objects. @@ -632,6 +669,13 @@ Wayland_VideoQuit(_THIS) if (data->decoration_manager) zxdg_decoration_manager_v1_destroy(data->decoration_manager); +#ifdef HAVE_LIBDECOR_H + if (data->shell.libdecor) { + libdecor_unref(data->shell.libdecor); + data->shell.libdecor = NULL; + } +#endif + if (data->compositor) wl_compositor_destroy(data->compositor); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 0c6957f00..54201e226 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -62,6 +62,9 @@ typedef struct { struct xdg_wm_base *xdg; struct zxdg_shell_v6 *zxdg; struct wl_shell *wl; +#ifdef HAVE_LIBDECOR_H + struct libdecor *libdecor; +#endif } shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index c18e159e8..cc843c718 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -39,6 +39,20 @@ #include "idle-inhibit-unstable-v1-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" +#ifdef HAVE_LIBDECOR_H +#include +#endif + +static const char *SDL_WAYLAND_surface_tag = "sdl-window"; + +SDL_bool SDL_WAYLAND_own_surface(struct wl_surface *surface) +{ + if (SDL_WAYLAND_HAVE_WAYLAND_CLIENT_1_18) { + return wl_proxy_get_tag((struct wl_proxy *) surface) == &SDL_WAYLAND_surface_tag; + } + return SDL_TRUE; /* For older clients we have to assume this is us... */ +} + static float get_window_scale_factor(SDL_Window *window) { return ((SDL_WindowData*)window->driverdata)->scale_factor; } @@ -67,6 +81,19 @@ CommitMinMaxDimensions(SDL_Window *window) max_height = window->windowed.h; } +#ifdef HAVE_LIBDECOR_H + if (data->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + libdecor_frame_set_min_content_size(wind->shell_surface.libdecor.frame, + min_width, + min_height); + libdecor_frame_set_max_content_size(wind->shell_surface.libdecor.frame, + max_width, + max_height); + } else +#endif if (data->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ @@ -77,6 +104,7 @@ CommitMinMaxDimensions(SDL_Window *window) xdg_toplevel_set_max_size(wind->shell_surface.xdg.roleobj.toplevel, max_width, max_height); + wl_surface_commit(wind->surface); } else if (data->shell.zxdg) { if (wind->shell_surface.zxdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ @@ -87,9 +115,8 @@ CommitMinMaxDimensions(SDL_Window *window) zxdg_toplevel_v6_set_max_size(wind->shell_surface.zxdg.roleobj.toplevel, max_width, max_height); + wl_surface_commit(wind->surface); } - - wl_surface_commit(wind->surface); } static void @@ -103,6 +130,18 @@ SetFullscreen(SDL_Window *window, struct wl_output *output) */ CommitMinMaxDimensions(window); +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + if (output) { + libdecor_frame_set_fullscreen(wind->shell_surface.libdecor.frame, output); + } else { + libdecor_frame_unset_fullscreen(wind->shell_surface.libdecor.frame); + } + } else +#endif if (viddata->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ @@ -481,8 +520,97 @@ static const struct xdg_toplevel_listener toplevel_listener_xdg = { handle_close_xdg_toplevel }; +#ifdef HAVE_LIBDECOR_H +static void +decoration_frame_configure(struct libdecor_frame *frame, + struct libdecor_configuration *configuration, + void *user_data) +{ + SDL_WindowData *wind = user_data; + SDL_Window *window = wind->sdlwindow; + int width, height; + enum libdecor_window_state window_state; + struct libdecor_state *state; + /* window size */ + if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { + width = window->w; + height = window->h; + } + wind->resize.width = width; + wind->resize.height = height; + + wind->resize.pending = SDL_TRUE; + wind->resize.configure = SDL_TRUE; + Wayland_HandlePendingResize(window); + wind->shell_surface.libdecor.initial_configure_seen = SDL_TRUE; + + window->w = wind->resize.width; + window->h = wind->resize.height; + + /* window state */ + if (!libdecor_configuration_get_window_state(configuration, &window_state)) { + window_state = LIBDECOR_WINDOW_STATE_NONE; + } + + if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) { + window->flags |= SDL_WINDOW_MAXIMIZED; + } + else { + window->flags &= ~SDL_WINDOW_MAXIMIZED; + } + + if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) { + window->flags |= SDL_WINDOW_INPUT_FOCUS; + } + else { + window->flags &= ~SDL_WINDOW_INPUT_FOCUS; + } + + /* The fullscreen flag is already set my some other entity when 'SDL_SetWindowFullscreen' + * is called, but we will set it here again in case the compositor requests fullscreen. + */ + if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) { + window->flags |= SDL_WINDOW_FULLSCREEN; + } + else { + window->flags &= ~SDL_WINDOW_FULLSCREEN; + } + + /* commit frame state */ + state = libdecor_state_new(width, height); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + /* Update the resize capability. Since this will change the capabilities and + * commit a new frame state with the last known content dimension, this has + * to be called after the new state has been commited and the new content + * dimensions were updated. */ + Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, + window->flags & SDL_WINDOW_RESIZABLE); +} + +static void +decoration_frame_close(struct libdecor_frame *frame, void *user_data) +{ + SDL_SendWindowEvent(((SDL_WindowData *)user_data)->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0); +} + +static void +decoration_frame_commit(struct libdecor_frame *frame, void *user_data) +{ + SDL_WindowData *wind = user_data; + + SDL_SendWindowEvent(wind->sdlwindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); +} + +static struct libdecor_frame_interface libdecor_frame_interface = { + decoration_frame_configure, + decoration_frame_close, + decoration_frame_commit, +}; +#endif #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH static void @@ -706,6 +834,20 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window) wl_surface_commit(data->surface); /* Create the shell surface and map the toplevel */ +#ifdef HAVE_LIBDECOR_H + if (c->shell.libdecor) { + data->shell_surface.libdecor.frame = libdecor_decorate(c->shell.libdecor, + data->surface, + &libdecor_frame_interface, + data); + if (data->shell_surface.libdecor.frame == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Failed to create libdecor frame!"); + } else { + libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, c->classname); + libdecor_frame_map(data->shell_surface.libdecor.frame); + } + } else +#endif if (c->shell.xdg) { data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface); xdg_surface_set_user_data(data->shell_surface.xdg.surface, data); @@ -744,6 +886,16 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window) /* We have to wait until the surface gets a "configure" event, or use of * this surface will fail. This is a new rule for xdg_shell. */ +#ifdef HAVE_LIBDECOR_H + if (c->shell.libdecor) { + if (data->shell_surface.libdecor.frame) { + while (!data->shell_surface.libdecor.initial_configure_seen) { + WAYLAND_wl_display_flush(c->display); + WAYLAND_wl_display_dispatch(c->display); + } + } + } else +#endif if (c->shell.xdg) { if (data->shell_surface.xdg.surface) { while (!data->shell_surface.xdg.initial_configure_seen) { @@ -801,6 +953,14 @@ void Wayland_HideWindow(_THIS, SDL_Window *window) wind->server_decoration = NULL; } +#ifdef HAVE_LIBDECOR_H + if (data->shell.libdecor) { + if (wind->shell_surface.libdecor.frame) { + libdecor_frame_unref(wind->shell_surface.libdecor.frame); + wind->shell_surface.libdecor.frame = NULL; + } + } else +#endif if (data->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel) { xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel); @@ -1027,6 +1187,14 @@ Wayland_RestoreWindow(_THIS, SDL_Window * window) */ window->flags &= ~SDL_WINDOW_MAXIMIZED; +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame); + } else +#endif /* Note that xdg-shell does NOT provide a way to unset minimize! */ if (viddata->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { @@ -1053,6 +1221,11 @@ Wayland_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) { SDL_WindowData *wind = window->driverdata; const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata; +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + SDL_SetError("FIXME libdecor: Implement toggling decorations"); + } else +#endif if ((viddata->decoration_manager) && (wind->server_decoration)) { const enum zxdg_toplevel_decoration_v1_mode mode = bordered ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; zxdg_toplevel_decoration_v1_set_mode(wind->server_decoration, mode); @@ -1062,7 +1235,24 @@ Wayland_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) void Wayland_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) { - CommitMinMaxDimensions(window); +#ifdef HAVE_LIBDECOR_H + SDL_VideoData *data = _this->driverdata; + const SDL_WindowData *wind = window->driverdata; + + if (data->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + if (resizable) { + libdecor_frame_set_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE); + } else { + libdecor_frame_unset_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE); + } + } else +#endif + { + CommitMinMaxDimensions(window); + } } void @@ -1080,6 +1270,14 @@ Wayland_MaximizeWindow(_THIS, SDL_Window * window) */ window->flags |= SDL_WINDOW_MAXIMIZED; +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame); + } else +#endif if (viddata->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ @@ -1106,6 +1304,14 @@ Wayland_MinimizeWindow(_THIS, SDL_Window * window) SDL_WindowData *wind = window->driverdata; SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + libdecor_frame_set_minimized(wind->shell_surface.libdecor.frame); + } else +#endif if (viddata->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ @@ -1199,6 +1405,10 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) wl_compositor_create_surface(c->compositor); wl_surface_add_listener(data->surface, &surface_listener, data); + if (SDL_WAYLAND_HAVE_WAYLAND_CLIENT_1_18) { + wl_proxy_set_tag((struct wl_proxy *)data->surface, &SDL_WAYLAND_surface_tag); + } + /* Fire a callback when the compositor wants a new frame rendered. * Right now this only matters for OpenGL; we use this callback to add a * wait timeout that avoids getting deadlocked by the compositor when the @@ -1278,6 +1488,11 @@ Wayland_HandlePendingResize(SDL_Window *window) } if (data->resize.configure) { +#ifdef HAVE_LIBDECOR_H + if (data->waylandData->shell.libdecor) { + /* this has already been acknowledged in the the frames's 'configure' callback */ + } else +#endif if (data->waylandData->shell.xdg) { xdg_surface_ack_configure(data->shell_surface.xdg.surface, data->resize.serial); } else if (data->waylandData->shell.zxdg) { @@ -1312,6 +1527,9 @@ void Wayland_SetWindowSize(_THIS, SDL_Window * window) SDL_VideoData *data = _this->driverdata; SDL_WindowData *wind = window->driverdata; struct wl_region *region; +#ifdef HAVE_LIBDECOR_H + struct libdecor_state *state; +#endif wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window)); @@ -1319,6 +1537,14 @@ void Wayland_SetWindowSize(_THIS, SDL_Window * window) WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0); } +#ifdef HAVE_LIBDECOR_H + if (data->shell.libdecor && wind->shell_surface.libdecor.frame) { + state = libdecor_state_new(window->w, window->h); + libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL); + libdecor_state_free(state); + } +#endif + region = wl_compositor_create_region(data->compositor); wl_region_add(region, 0, 0, window->w, window->h); wl_surface_set_opaque_region(wind->surface, region); @@ -1331,6 +1557,14 @@ void Wayland_SetWindowTitle(_THIS, SDL_Window * window) SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; if (window->title != NULL) { +#ifdef HAVE_LIBDECOR_H + if (viddata->shell.libdecor) { + if (wind->shell_surface.libdecor.frame == NULL) { + return; /* Can't do anything yet, wait for ShowWindow */ + } + libdecor_frame_set_title(wind->shell_surface.libdecor.frame, window->title); + } else +#endif if (viddata->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel == NULL) { return; /* Can't do anything yet, wait for ShowWindow */ diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 5956cf352..493b50f03 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -50,6 +50,13 @@ typedef struct { SDL_bool initial_configure_seen; } SDL_xdg_shell_surface; +#ifdef HAVE_LIBDECOR_H +typedef struct { + struct libdecor_frame *frame; + SDL_bool initial_configure_seen; +} SDL_libdecor_surface; +#endif + typedef struct { SDL_Window *sdlwindow; SDL_VideoData *waylandData; @@ -58,6 +65,9 @@ typedef struct { union { SDL_xdg_shell_surface xdg; SDL_zxdg_shell_surface zxdg; +#ifdef HAVE_LIBDECOR_H + SDL_libdecor_surface libdecor; +#endif struct wl_shell_surface *wl; } shell_surface; struct wl_egl_window *egl_window; @@ -117,6 +127,8 @@ extern int Wayland_FlashWindow(_THIS, SDL_Window * window, SDL_FlashOperation op extern void Wayland_HandlePendingResize(SDL_Window *window); +extern SDL_bool SDL_WAYLAND_own_surface(struct wl_surface *surface); + #endif /* SDL_waylandwindow_h_ */ /* vi: set ts=4 sw=4 expandtab: */