Module: wine Branch: master Commit: 36ecb876dbc10151d62e35d009a310d59fed07e6 URL: https://gitlab.winehq.org/wine/wine/-/commit/36ecb876dbc10151d62e35d009a310d...
Author: Alexandros Frantzis alexandros.frantzis@collabora.com Date: Mon Sep 18 09:57:12 2023 +0300
winewayland.drv: Handle xdg_toplevel maximized state.
A request for the maximized state has two potential origins:
1. The compositor, through an xdg_toplevel configure event. In this case we update the window state and size accordingly.
2. The application or Wine itself, by changing the window style. When we detect such a style, we make a request to the compositor to set the maximized state. The compositor will then eventually reply with a configure event, and we continue with case (1). Note that the compositor may deny our request, in which case we will also sync the window style accordingly.
An acknowledged maximized state imposes very strict constraints on the size of surface content we can present. We are careful to not violate these constraints, since otherwise the compositor will disconnect us.
---
dlls/winewayland.drv/wayland_surface.c | 88 +++++++++++++++++++++++++++++----- dlls/winewayland.drv/waylanddrv.h | 6 +++ dlls/winewayland.drv/window.c | 41 +++++++++++++++- 3 files changed, 123 insertions(+), 12 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 0004e51fe0f..9514106d0d1 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -78,8 +78,22 @@ static void xdg_toplevel_handle_configure(void *data, { struct wayland_surface *surface; HWND hwnd = data; + uint32_t *state; + enum wayland_surface_config_state config_state = 0;
- TRACE("hwnd=%p %dx%d\n", hwnd, width, height); + wl_array_for_each(state, states) + { + switch(*state) + { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + config_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + break; + default: + break; + } + } + + TRACE("hwnd=%p %dx%d,%#x\n", hwnd, width, height, config_state);
if (!(surface = wayland_surface_lock_hwnd(hwnd))) return;
@@ -87,6 +101,7 @@ static void xdg_toplevel_handle_configure(void *data, { surface->pending.width = width; surface->pending.height = height; + surface->pending.state = config_state; }
pthread_mutex_unlock(&surface->mutex); @@ -291,6 +306,32 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, } }
+/********************************************************************** + * wayland_surface_configure_is_compatible + * + * Checks whether a wayland_surface_configure object is compatible with the + * the provided arguments. + */ +static BOOL wayland_surface_configure_is_compatible(struct wayland_surface_config *conf, + int width, int height, + enum wayland_surface_config_state state) +{ + static enum wayland_surface_config_state mask = + WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + + /* We require the same state. */ + if ((state & mask) != (conf->state & mask)) return FALSE; + + /* The maximized state requires the configured size. */ + if ((conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + (width != conf->width || height != conf->height)) + { + return FALSE; + } + + return TRUE; +} + /********************************************************************** * wayland_surface_reconfigure * @@ -299,27 +340,52 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, */ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) { - if (!surface->xdg_toplevel) return TRUE; + RECT window_rect; + int width, height; + enum wayland_surface_config_state window_state;
- TRACE("hwnd=%p\n", surface->hwnd); - - /* Acknowledge any processed config. */ - if (surface->processing.serial && surface->processing.processed) + if (!surface->xdg_toplevel) return TRUE; + if (!NtUserGetWindowRect(surface->hwnd, &window_rect)) return FALSE; + + width = window_rect.right - window_rect.left; + height = window_rect.bottom - window_rect.top; + + window_state = + (NtUserGetWindowLongW(surface->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? + WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0; + + TRACE("hwnd=%p window=%dx%d,%#x processing=%dx%d,%#x current=%dx%d,%#x\n", + surface->hwnd, width, height, window_state, + surface->processing.width, surface->processing.height, + surface->processing.state, surface->current.width, + surface->current.height, surface->current.state); + + /* Acknowledge any compatible processed config. */ + if (surface->processing.serial && surface->processing.processed && + wayland_surface_configure_is_compatible(&surface->processing, + width, height, + window_state)) { surface->current = surface->processing; memset(&surface->processing, 0, sizeof(surface->processing)); xdg_surface_ack_configure(surface->xdg_surface, surface->current.serial); } - /* If this is the initial configure, and we have a requested config, - * use that, in order to draw windows that don't go through the message - * loop (e.g., some splash screens). */ - else if (!surface->current.serial && surface->requested.serial) + /* If this is the initial configure, and we have a compatible requested + * config, use that, in order to draw windows that don't go through the + * message loop (e.g., some splash screens). */ + else if (!surface->current.serial && surface->requested.serial && + wayland_surface_configure_is_compatible(&surface->requested, + width, height, + window_state)) { surface->current = surface->requested; memset(&surface->requested, 0, sizeof(surface->requested)); xdg_surface_ack_configure(surface->xdg_surface, surface->current.serial); } - else if (!surface->current.serial) + else if (!surface->current.serial || + !wayland_surface_configure_is_compatible(&surface->current, + width, height, + window_state)) { return FALSE; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 5246d3ba9cb..51e958c85f8 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -57,6 +57,11 @@ enum wayland_window_message WM_WAYLAND_CONFIGURE = 0x80001001 };
+enum wayland_surface_config_state +{ + WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED = (1 << 0) +}; + struct wayland_cursor { struct wayland_shm_buffer *shm_buffer; @@ -121,6 +126,7 @@ struct wayland_output struct wayland_surface_config { int32_t width, height; + enum wayland_surface_config_state state; uint32_t serial; BOOL processed; }; diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 86f4e30b136..194568e0228 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -195,15 +195,38 @@ out: static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) { struct wayland_surface *surface = data->wayland_surface; + uint32_t window_state; + struct wayland_surface_config *conf;
pthread_mutex_lock(&surface->mutex);
if (!surface->xdg_toplevel) goto out;
- if (surface->processing.serial) surface->processing.processed = TRUE; + window_state = + (NtUserGetWindowLongW(surface->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? + WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0; + + conf = surface->processing.serial ? &surface->processing : &surface->current; + + TRACE("hwnd=%p window_state=%#x conf->state=%#x\n", + data->hwnd, window_state, conf->state); + + if ((window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + !(conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + { + xdg_toplevel_set_maximized(surface->xdg_toplevel); + } + else if (!(window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + (conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + { + xdg_toplevel_unset_maximized(surface->xdg_toplevel); + } + + conf->processed = TRUE;
out: pthread_mutex_unlock(&surface->mutex); + wl_display_flush(process_wayland.wl_display); }
/*********************************************************************** @@ -310,6 +333,8 @@ static void wayland_configure_window(HWND hwnd) struct wayland_surface *surface; INT width, height; UINT flags; + uint32_t state; + DWORD style;
if (!(surface = wayland_surface_lock_hwnd(hwnd))) return;
@@ -332,12 +357,26 @@ static void wayland_configure_window(HWND hwnd)
width = surface->processing.width; height = surface->processing.height; + state = surface->processing.state;
pthread_mutex_unlock(&surface->mutex);
+ TRACE("processing=%dx%d,%#x\n", width, height, state); + flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE; if (width == 0 || height == 0) flags |= SWP_NOSIZE;
+ style = NtUserGetWindowLongW(hwnd, GWL_STYLE); + if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE)) + { + NtUserSetWindowLong(hwnd, GWL_STYLE, style ^ WS_MAXIMIZE, FALSE); + flags |= SWP_FRAMECHANGED; + } + + /* The Wayland maximized state is very strict about surface size, so don't + * let the application override it. */ + if (state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) flags |= SWP_NOSENDCHANGING; + NtUserSetWindowPos(hwnd, 0, 0, 0, width, height, flags); }