wayland: client-side decoration
This commit is contained in:
parent
f4f9c6bc55
commit
9e6fcbe72c
13 changed files with 499 additions and 7 deletions
|
@ -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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue