Fix #144919: Opening windows hangs on GNOME-49 with Wayland
Opening "normal" windows (non-maximized) windows was hanging. Blender was waiting for a "configure" event with a valid size, which stopped being sent in GNOME-49. Even though GNOME-49.1 will resolve the problem, according to the GNOME developers Blender's use of Wayland was incorrect. Resolve the issue with the following changes: - Don't wait for the `xdg_toplevel` to be when creating new windows. Instead, defer setting the window state using logic that was already used for Vulkan. - Set new window's pending size - used if no size is received from LIBDECOR's "configure" callback. - When the window is "configured" always set the window "state" even if the size is not yet known. Ref !148104
This commit is contained in:
@@ -110,6 +110,10 @@ static libdecor_configuration *ghost_wl_libdecor_configuration_copy(
|
||||
static void ghost_wl_libdecor_configuration_free(libdecor_configuration *configuration);
|
||||
#endif
|
||||
|
||||
static bool gwl_window_state_set_for_xdg(xdg_toplevel *toplevel,
|
||||
const GHOST_TWindowState state,
|
||||
const GHOST_TWindowState state_current);
|
||||
|
||||
static const xdg_activation_token_v1_listener *xdg_activation_listener_get();
|
||||
|
||||
static constexpr size_t base_dpi = 96;
|
||||
@@ -173,6 +177,16 @@ static void gwl_libdecor_window_destroy(GWL_LibDecor_Window *decor)
|
||||
libdecor_frame_unref(decor->frame);
|
||||
delete decor;
|
||||
}
|
||||
|
||||
static void gwl_libdecor_window_initial_configure_state_set(GWL_LibDecor_Window *decor,
|
||||
const GHOST_TWindowState state_current)
|
||||
{
|
||||
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(decor->frame);
|
||||
GHOST_ASSERT(toplevel, "Expected to be valid!");
|
||||
gwl_window_state_set_for_xdg(toplevel, decor->initial_configure_state.value(), state_current);
|
||||
decor->initial_configure_state = std::nullopt;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
|
||||
|
||||
struct GWL_XDG_Decor_Window {
|
||||
@@ -1019,12 +1033,8 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
|
||||
|
||||
if (decor.initial_configure_seen == false) {
|
||||
decor.initial_configure_seen = true;
|
||||
|
||||
if (decor.initial_configure_state) {
|
||||
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(decor.frame);
|
||||
gwl_window_state_set_for_xdg(
|
||||
toplevel, decor.initial_configure_state.value(), gwl_window_state_get(win));
|
||||
decor.initial_configure_state = std::nullopt;
|
||||
gwl_libdecor_window_initial_configure_state_set(&decor, gwl_window_state_get(win));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,6 +1431,9 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
|
||||
/* Set the size. */
|
||||
int size_next[2] = {0, 0};
|
||||
|
||||
/* Perform a "final" commit. */
|
||||
bool surface_needs_commit_finally = false;
|
||||
|
||||
{
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
const int fractional_scale = win->frame.fractional_scale;
|
||||
@@ -1521,6 +1534,19 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
|
||||
decor.pending.configuration_needs_free = false;
|
||||
/* Wait until we have a valid size. */
|
||||
decor.pending.ack_configure = false;
|
||||
|
||||
/* It's important to set this when `ack_configure` is disabled,
|
||||
* otherwise it's possible the window is never called with a valid size `ack_configure`
|
||||
* is never set to true and the `decor.initial_configure_state` is never applied.
|
||||
*
|
||||
* So set the state here, and commit the surface.
|
||||
* Then LIBDECOR is responsible for applying the state. */
|
||||
if (decor.initial_configure_state) {
|
||||
gwl_libdecor_window_initial_configure_state_set(&decor, gwl_window_state_get(win));
|
||||
/* Without the final commit, popup windows such as the preferences wont
|
||||
* update the window frame and the window wont be clickable. */
|
||||
surface_needs_commit_finally = true;
|
||||
}
|
||||
}
|
||||
# endif /* USE_LIBDECOR_CONFIG_COPY_QUEUE */
|
||||
}
|
||||
@@ -1538,6 +1564,19 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
|
||||
gwl_window_frame_update_from_pending_no_lock(win);
|
||||
}
|
||||
}
|
||||
|
||||
if (surface_needs_commit_finally) {
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
# ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
if (!is_main_thread) {
|
||||
gwl_window_pending_actions_tag(win, PENDING_WINDOW_SURFACE_COMMIT);
|
||||
}
|
||||
else
|
||||
# endif
|
||||
{
|
||||
wl_surface_commit(win->wl.surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void libdecor_frame_handle_close(libdecor_frame * /*frame*/, void *data)
|
||||
@@ -2001,14 +2040,6 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
# ifdef WITH_VULKAN_BACKEND
|
||||
const bool libdecor_wait_for_window_init = (type == GHOST_kDrawingContextTypeVulkan);
|
||||
# else
|
||||
const bool libdecor_wait_for_window_init = false;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Drawing context. */
|
||||
if (setDrawingContextType(type) == GHOST_kFailure) {
|
||||
/* This can happen when repeatedly creating windows, see #123096.
|
||||
@@ -2025,7 +2056,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
||||
}
|
||||
else
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor && libdecor_wait_for_window_init)
|
||||
if (use_libdecor)
|
||||
{
|
||||
/* Ensuring the XDG window has been created is *not* supported by VULKAN.
|
||||
*
|
||||
@@ -2043,43 +2074,21 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
||||
* This can be removed if CSD are implemented, see: #113795. */
|
||||
GWL_LibDecor_Window &decor = *window_->libdecor;
|
||||
decor.initial_configure_state = state;
|
||||
}
|
||||
else if (use_libdecor) {
|
||||
/* Commit needed so the top-level callbacks run (and `toplevel` can be accessed). */
|
||||
wl_surface_commit(window_->wl.surface);
|
||||
GWL_LibDecor_Window &decor = *window_->libdecor;
|
||||
|
||||
/* Additional round-trip is needed to ensure `xdg_toplevel` is set. */
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
/* NOTE: LIBDECOR requires the window to be created & configured before the state can be set.
|
||||
* Workaround this by using the underlying `xdg_toplevel` */
|
||||
|
||||
/* Failure exits with an error, simply prevent an eternal loop. */
|
||||
while (!decor.initial_configure_seen && !ghost_wl_display_report_error_if_set(display)) {
|
||||
wl_display_flush(display);
|
||||
wl_display_dispatch(display);
|
||||
}
|
||||
|
||||
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(decor.frame);
|
||||
gwl_window_state_set_for_xdg(toplevel, state, gwl_window_state_get(window_));
|
||||
|
||||
/* NOTE(@ideasman42): Round trips are necessary with LIBDECOR on GNOME
|
||||
* because resizing later on and redrawing does *not* update as it should, see #119871.
|
||||
/* Set the pending size now, this is an imperfect solution,
|
||||
* it's needed for the following reasons.
|
||||
*
|
||||
* Without the round-trip here:
|
||||
* - The window will be created and this function will return using the requested buffer size,
|
||||
* instead of the window size which ends up being used (causing a visible flicker).
|
||||
* This has the down side that Blender's internal window state has the outdated size
|
||||
* which then gets immediately resized, causing a noticeable glitch.
|
||||
* - The window decorations will be displayed at the wrong size before refreshing
|
||||
* at the new size.
|
||||
* - On GNOME-Shell 46 shows the previous buffer-size under some conditions.
|
||||
* - New windows won't apply their configuration
|
||||
* (when #GWL_LibDecor_Window::ack_configure is true)
|
||||
* *unless* there is a valid size.
|
||||
* - In some cases (GNOME 49.0 maybe other versions) the window never gets a valid size.
|
||||
*
|
||||
* In principle this could be used with XDG too however it causes problems with KDE
|
||||
* and some WLROOTS based compositors.
|
||||
*/
|
||||
wl_display_roundtrip(display);
|
||||
* So set a size here, it will be used if the window is configured without a size.
|
||||
* Note that this may not match the size used by LIBDECOR, showing a visible
|
||||
* difference between the window and it's frame. This mainly happens when attempting
|
||||
* to use small window sizes (which may be clamped to a larger size). */
|
||||
decor.pending.size[0] = window_->frame.size[0] / window_->frame.buffer_scale;
|
||||
decor.pending.size[1] = window_->frame.size[1] / window_->frame.buffer_scale;
|
||||
}
|
||||
else
|
||||
#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
|
||||
|
||||
Reference in New Issue
Block a user