wayland: client-side decoration

This commit is contained in:
Christian Rauch 2020-06-11 22:10:28 +01:00 committed by Sam Lantinga
parent f4f9c6bc55
commit 9e6fcbe72c
13 changed files with 499 additions and 7 deletions

View file

@ -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 #####

View file

@ -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()

View file

@ -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

View file

@ -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@

View file

@ -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

View file

@ -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 *

View file

@ -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 <stdint.h>
#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 <libdecor.h>
#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"

View file

@ -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 <libdecor.h>
#endif
#ifdef SDL_INPUT_LINUXEV
#include <linux/input.h>
#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);

View file

@ -33,6 +33,8 @@
#define SDL_WAYLAND_INTERFACE(iface)
#endif
#include <stdbool.h>
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

View file

@ -53,6 +53,10 @@
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
#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, &registry_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);

View file

@ -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;

View file

@ -39,6 +39,20 @@
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
#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 */

View file

@ -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: */