wayland: Always commit window constraints before entering fullscreen XDG-toplevel min/max size values are double-buffered data and must be committed before entering the fullscreen state, or a max window size value smaller than the display dimensions may cause the compositor to incorrectly configure the fullscreen window size. This fixes windowed->fullscreen transitions on GNOME, where, previously, certain combinations of window flags and min/max size values could cause entering fullscreen mode to fail with odd window sizes and/or offsets due to the new max size values not being committed before entering fullscreen, causing the compositor to clamp to the old values. In the case of libdecor, it has its own layer of buffering on top of the xdg-toplevel surface for the min/max window dimensions, so both a frame commit and surface commit are required to set the state properly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 8be9cd9..cfd835b 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -351,6 +351,13 @@ SetMinMaxDimensions(SDL_Window *window, SDL_bool commit)
libdecor_frame_set_max_content_size(wind->shell_surface.libdecor.frame,
max_width,
max_height);
+
+ if (commit) {
+ struct libdecor_state *state = libdecor_state_new(GetWindowWidth(window), GetWindowHeight(window));
+ libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL);
+ libdecor_state_free(state);
+ wl_surface_commit(wind->surface);
+ }
} else
#endif
if (viddata->shell.xdg) {
@@ -396,17 +403,31 @@ SetFullscreen(SDL_Window *window, struct wl_output *output, SDL_bool commit)
}
if (output) {
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
- /* ensure that window is resizable before going into fullscreen */
+ /* Ensure that window is resizable before going into fullscreen.
+ * This triggers a frame commit internally, so a separate one is not necessary.
+ */
libdecor_frame_set_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE);
wl_surface_commit(wind->surface);
+ } else if (commit) {
+ struct libdecor_state *state = libdecor_state_new(GetWindowWidth(window), GetWindowHeight(window));
+ libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL);
+ libdecor_state_free(state);
+ wl_surface_commit(wind->surface);
}
+
libdecor_frame_set_fullscreen(wind->shell_surface.libdecor.frame, output);
} else {
libdecor_frame_unset_fullscreen(wind->shell_surface.libdecor.frame);
+
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
/* restore previous RESIZE capability */
libdecor_frame_unset_capabilities(wind->shell_surface.libdecor.frame, LIBDECOR_ACTION_RESIZE);
wl_surface_commit(wind->surface);
+ } else if (commit) {
+ struct libdecor_state *state = libdecor_state_new(GetWindowWidth(window), GetWindowHeight(window));
+ libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL);
+ libdecor_state_free(state);
+ wl_surface_commit(wind->surface);
}
}
} else
@@ -415,14 +436,14 @@ SetFullscreen(SDL_Window *window, struct wl_output *output, SDL_bool commit)
if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
return; /* Can't do anything yet, wait for ShowWindow */
}
+ if (commit) {
+ wl_surface_commit(wind->surface);
+ }
if (output) {
xdg_toplevel_set_fullscreen(wind->shell_surface.xdg.roleobj.toplevel, output);
} else {
xdg_toplevel_unset_fullscreen(wind->shell_surface.xdg.roleobj.toplevel);
}
- if (commit) {
- wl_surface_commit(wind->surface);
- }
}
}