This MR improves window management related behavior and adds support for more scenarios.
Some highlights (please see the full commit list and messages for more info):
1. Better support for tiled states. 2. Detect and handle fullscreen windows. 3. Increased robustness against state disagreements between Wine and the Wayland compositor. 4. Move top-level windows to (0,0) (using that fixed point for now, we will later generalize this mechanism for multiple monitors), in order to: a. maximize the accessible mouse input region (which may be clipped in Windows virtual screen space, but still accessible in Wayland space). b. introduce the basic mechanism to allow windows applications to (very roughly!) track which output(s) they are really on (to be continued in the full multi-monitor form).
Although (4) is not a perfect solution for the lack of absolute positioning, it has worked well in practice in its full, multi-monitor implementation (i.e., in the experimental branch). I wonder if for (4a) in particular we can do better, e.g., by being able to emit input that circumvents the normal virtual screen clipping. Is there a way to do this already? If not, do you think that such an approach would be a feasible and acceptable way forward, or is virtual screen input clipping fundamentally baked in the current core design and assumptions?
Thanks!
-- v4: winewayland.drv: Avoid resizing fullscreen windows. winewayland.drv: Rename wayland_surface_configure_is_compatible for consistency. winewayland.drv: Use surface geometry to satisfy state size constraints. winewayland.drv: Handle application-initiated fullscreen state. winewayland.drv: Store and use the latest window config for a wayland_surface.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
If we are processing a config request by the compositor, consider that config as authoritative for the window. Otherwise use the window state to determine the new Wayland state to apply. --- dlls/winewayland.drv/window.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 5079d820929..09a4125ff22 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -196,7 +196,7 @@ 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; + BOOL processing_config;
pthread_mutex_lock(&surface->mutex);
@@ -206,24 +206,34 @@ static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) (NtUserGetWindowLongW(surface->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0;
- conf = surface->processing.serial ? &surface->processing : &surface->current; + processing_config = surface->processing.serial && + !surface->processing.processed;
- TRACE("hwnd=%p window_state=%#x conf->state=%#x\n", - data->hwnd, window_state, conf->state); + TRACE("hwnd=%p window_state=%#x %s->state=%#x\n", + data->hwnd, window_state, + processing_config ? "processing" : "current", + processing_config ? surface->processing.state : surface->current.state);
- if ((window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && - !(conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + /* If we are not processing a compositor requested config, use the + * window state to determine and update the Wayland state. */ + if (!processing_config) { - xdg_toplevel_set_maximized(surface->xdg_toplevel); + if ((window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + { + xdg_toplevel_set_maximized(surface->xdg_toplevel); + } + else if (!(window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + { + xdg_toplevel_unset_maximized(surface->xdg_toplevel); + } } - else if (!(window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && - (conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + else { - xdg_toplevel_unset_maximized(surface->xdg_toplevel); + surface->processing.processed = TRUE; }
- conf->processed = TRUE; - out: pthread_mutex_unlock(&surface->mutex); wl_display_flush(process_wayland.wl_display);
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Tiled states don't place strict constraints on the surface size, but they indicate a strong size preference, so try to respect it. --- dlls/winewayland.drv/wayland_surface.c | 6 ++++++ dlls/winewayland.drv/waylanddrv.h | 3 ++- dlls/winewayland.drv/window.c | 9 +++++++-- 3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 8551ad98eeb..ddb10be6e06 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -91,6 +91,12 @@ static void xdg_toplevel_handle_configure(void *data, case XDG_TOPLEVEL_STATE_RESIZING: config_state |= WAYLAND_SURFACE_CONFIG_STATE_RESIZING; break; + case XDG_TOPLEVEL_STATE_TILED_LEFT: + case XDG_TOPLEVEL_STATE_TILED_RIGHT: + case XDG_TOPLEVEL_STATE_TILED_TOP: + case XDG_TOPLEVEL_STATE_TILED_BOTTOM: + config_state |= WAYLAND_SURFACE_CONFIG_STATE_TILED; + break; default: break; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index faff529940d..fcfc187925d 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -60,7 +60,8 @@ enum wayland_window_message enum wayland_surface_config_state { WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED = (1 << 0), - WAYLAND_SURFACE_CONFIG_STATE_RESIZING = (1 << 1) + WAYLAND_SURFACE_CONFIG_STATE_RESIZING = (1 << 1), + WAYLAND_SURFACE_CONFIG_STATE_TILED = (1 << 2) };
struct wayland_cursor diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 09a4125ff22..23f4c89ea0b 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -401,8 +401,13 @@ static void wayland_configure_window(HWND hwnd) }
/* 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; + * let the application override it. The tiled state is not as strict, + * but it indicates a strong size preference, so try to respect it. */ + if (state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED | + WAYLAND_SURFACE_CONFIG_STATE_TILED)) + { + flags |= SWP_NOSENDCHANGING; + }
NtUserSetWindowPos(hwnd, 0, 0, 0, width, height, flags); }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Instead of querying the window config for a wayland_surface on demand, query it and store it only when it is applied through SetWindowPos. In addition to being more efficient, this prevents deadlocks by avoiding calling NtUser functions that require the user lock, while holding the window_surface lock. --- dlls/winewayland.drv/wayland_surface.c | 20 +++++-------- dlls/winewayland.drv/waylanddrv.h | 7 +++++ dlls/winewayland.drv/window.c | 39 +++++++++++++++++--------- 3 files changed, 39 insertions(+), 27 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index ddb10be6e06..5bb8c959537 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -349,22 +349,16 @@ static BOOL wayland_surface_configure_is_compatible(struct wayland_surface_confi */ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) { - RECT window_rect; + struct wayland_window_config *window = &surface->window; int width, height; - enum wayland_surface_config_state window_state;
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; + width = surface->window.rect.right - surface->window.rect.left; + height = surface->window.rect.bottom - surface->window.rect.top;
TRACE("hwnd=%p window=%dx%d,%#x processing=%dx%d,%#x current=%dx%d,%#x\n", - surface->hwnd, width, height, window_state, + surface->hwnd, width, height, window->state, surface->processing.width, surface->processing.height, surface->processing.state, surface->current.width, surface->current.height, surface->current.state); @@ -373,7 +367,7 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) if (surface->processing.serial && surface->processing.processed && wayland_surface_configure_is_compatible(&surface->processing, width, height, - window_state)) + window->state)) { surface->current = surface->processing; memset(&surface->processing, 0, sizeof(surface->processing)); @@ -385,7 +379,7 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) else if (!surface->current.serial && surface->requested.serial && wayland_surface_configure_is_compatible(&surface->requested, width, height, - window_state)) + window->state)) { surface->current = surface->requested; memset(&surface->requested, 0, sizeof(surface->requested)); @@ -394,7 +388,7 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) else if (!surface->current.serial || !wayland_surface_configure_is_compatible(&surface->current, width, height, - window_state)) + window->state)) { return FALSE; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index fcfc187925d..4b74b674ea6 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -141,6 +141,12 @@ struct wayland_surface_config BOOL processed; };
+struct wayland_window_config +{ + RECT rect; + enum wayland_surface_config_state state; +}; + struct wayland_surface { HWND hwnd; @@ -151,6 +157,7 @@ struct wayland_surface struct wayland_surface_config pending, requested, processing, current; struct wayland_shm_buffer *latest_window_buffer; BOOL resizing; + struct wayland_window_config window; };
struct wayland_shm_buffer diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 23f4c89ea0b..78e716a493f 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -43,6 +43,8 @@ struct wayland_win_data struct wayland_surface *wayland_surface; /* wine window_surface backing this window */ struct window_surface *window_surface; + /* USER window rectangle relative to win32 parent window client area */ + RECT window_rect; };
static int wayland_win_data_cmp_rb(const void *key, @@ -65,7 +67,8 @@ static struct rb_tree win_data_rb = { wayland_win_data_cmp_rb }; * * Create a data window structure for an existing window. */ -static struct wayland_win_data *wayland_win_data_create(HWND hwnd) +static struct wayland_win_data *wayland_win_data_create(HWND hwnd, + const RECT *window_rect) { struct wayland_win_data *data; struct rb_entry *rb_entry; @@ -79,6 +82,7 @@ static struct wayland_win_data *wayland_win_data_create(HWND hwnd) if (!(data = calloc(1, sizeof(*data)))) return NULL;
data->hwnd = hwnd; + data->window_rect = *window_rect;
pthread_mutex_lock(&win_data_mutex);
@@ -146,6 +150,14 @@ static void wayland_win_data_release(struct wayland_win_data *data) pthread_mutex_unlock(&win_data_mutex); }
+static void wayland_win_data_get_config(struct wayland_win_data *data, + struct wayland_window_config *conf) +{ + conf->rect = data->window_rect; + conf->state = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? + WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0; +} + static void wayland_win_data_update_wayland_surface(struct wayland_win_data *data) { struct wayland_surface *surface = data->wayland_surface; @@ -170,20 +182,22 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE; xdg_visible = surface->xdg_toplevel != NULL;
+ pthread_mutex_lock(&surface->mutex); + if (visible != xdg_visible) { - pthread_mutex_lock(&surface->mutex); - /* If we have a pre-existing surface ensure it has no role. */ if (data->wayland_surface) wayland_surface_clear_role(surface); /* If the window is a visible toplevel make it a wayland * xdg_toplevel. Otherwise keep it role-less to avoid polluting the * compositor with empty xdg_toplevels. */ if (visible) wayland_surface_make_toplevel(surface); - - pthread_mutex_unlock(&surface->mutex); }
+ wayland_win_data_get_config(data, &surface->window); + + pthread_mutex_unlock(&surface->mutex); + if (data->window_surface) wayland_window_surface_update_wayland_surface(data->window_surface, surface);
@@ -195,22 +209,17 @@ 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; BOOL processing_config;
pthread_mutex_lock(&surface->mutex);
if (!surface->xdg_toplevel) goto out;
- window_state = - (NtUserGetWindowLongW(surface->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? - WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0; - processing_config = surface->processing.serial && !surface->processing.processed;
TRACE("hwnd=%p window_state=%#x %s->state=%#x\n", - data->hwnd, window_state, + data->hwnd, surface->window.state, processing_config ? "processing" : "current", processing_config ? surface->processing.state : surface->current.state);
@@ -218,12 +227,12 @@ static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) * window state to determine and update the Wayland state. */ if (!processing_config) { - if ((window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) { xdg_toplevel_set_maximized(surface->xdg_toplevel); } - else if (!(window_state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + else if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) { xdg_toplevel_unset_maximized(surface->xdg_toplevel); @@ -268,7 +277,7 @@ BOOL WAYLAND_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags, hwnd, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect), wine_dbgstr_rect(visible_rect), insert_after, swp_flags);
- if (!data && !(data = wayland_win_data_create(hwnd))) return TRUE; + if (!data && !(data = wayland_win_data_create(hwnd, window_rect))) return TRUE;
/* Release the dummy surface wine provides for toplevels. */ if (*surface) window_surface_release(*surface); @@ -318,6 +327,8 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
if (!data) return;
+ data->window_rect = *window_rect; + if (surface) window_surface_add_ref(surface); if (data->window_surface) window_surface_release(data->window_surface); data->window_surface = surface;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Detect when an window wants to become fullscreen and request the Wayland fullscreen state from the compositor. The fullscreen state for windows is inferred by their size and position relative to the monitor that contains their largest region.
Since there is no explicit fullscreen state for windows, compositor initiated fullscreen requests may lead to a size change, but will not directly affect other aspects of the window state. --- dlls/winewayland.drv/wayland_surface.c | 10 ++++ dlls/winewayland.drv/waylanddrv.h | 3 +- dlls/winewayland.drv/window.c | 65 ++++++++++++++++++++------ 3 files changed, 64 insertions(+), 14 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 5bb8c959537..124d15821bf 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -97,6 +97,9 @@ static void xdg_toplevel_handle_configure(void *data, case XDG_TOPLEVEL_STATE_TILED_BOTTOM: config_state |= WAYLAND_SURFACE_CONFIG_STATE_TILED; break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + config_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN; + break; default: break; } @@ -338,6 +341,13 @@ static BOOL wayland_surface_configure_is_compatible(struct wayland_surface_confi return FALSE; }
+ /* The fullscreen state requires at most the configured size. */ + if ((conf->state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + (width > conf->width || height > conf->height)) + { + return FALSE; + } + return TRUE; }
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 4b74b674ea6..e1dc58bdc80 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -61,7 +61,8 @@ enum wayland_surface_config_state { WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED = (1 << 0), WAYLAND_SURFACE_CONFIG_STATE_RESIZING = (1 << 1), - WAYLAND_SURFACE_CONFIG_STATE_TILED = (1 << 2) + WAYLAND_SURFACE_CONFIG_STATE_TILED = (1 << 2), + WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN = (1 << 3) };
struct wayland_cursor diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 78e716a493f..7fc0d2acdd6 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -153,9 +153,28 @@ static void wayland_win_data_release(struct wayland_win_data *data) static void wayland_win_data_get_config(struct wayland_win_data *data, struct wayland_window_config *conf) { + enum wayland_surface_config_state window_state = 0; + DWORD style; + conf->rect = data->window_rect; - conf->state = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_MAXIMIZE) ? - WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED : 0; + style = NtUserGetWindowLongW(data->hwnd, GWL_STYLE); + + TRACE("window=%s style=%#lx\n", wine_dbgstr_rect(&conf->rect), (long)style); + + /* The fullscreen state is implied by the window position and style. */ + if (NtUserIsWindowRectFullScreen(&conf->rect)) + { + if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) + window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + else if (!(style & WS_MINIMIZE)) + window_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN; + } + else if (style & WS_MAXIMIZE) + { + window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + } + + conf->state = window_state; }
static void wayland_win_data_update_wayland_surface(struct wayland_win_data *data) @@ -227,15 +246,28 @@ static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) * window state to determine and update the Wayland state. */ if (!processing_config) { + /* First do all state unsettings, before setting new state. Some + * Wayland compositors misbehave if the order is reversed. */ + if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + { + xdg_toplevel_unset_maximized(surface->xdg_toplevel); + } + if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) + { + xdg_toplevel_unset_fullscreen(surface->xdg_toplevel); + } + if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) { xdg_toplevel_set_maximized(surface->xdg_toplevel); } - else if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && - (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) { - xdg_toplevel_unset_maximized(surface->xdg_toplevel); + xdg_toplevel_set_fullscreen(surface->xdg_toplevel, NULL); } } else @@ -353,7 +385,7 @@ static void wayland_configure_window(HWND hwnd) { struct wayland_surface *surface; INT width, height; - UINT flags; + UINT flags = 0; uint32_t state; DWORD style; BOOL needs_enter_size_move = FALSE; @@ -394,6 +426,14 @@ static void wayland_configure_window(HWND hwnd) needs_exit_size_move = TRUE; }
+ /* Transitions between normal/max/fullscreen may entail a frame change. */ + if ((state ^ surface->current.state) & + (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED | + WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) + { + flags |= SWP_FRAMECHANGED; + } + pthread_mutex_unlock(&surface->mutex);
TRACE("processing=%dx%d,%#x\n", width, height, state); @@ -401,20 +441,19 @@ static void wayland_configure_window(HWND hwnd) if (needs_enter_size_move) send_message(hwnd, WM_ENTERSIZEMOVE, 0, 0); if (needs_exit_size_move) send_message(hwnd, WM_EXITSIZEMOVE, 0, 0);
- flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE; + 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. The tiled state is not as strict, - * but it indicates a strong size preference, so try to respect it. */ + /* The Wayland maximized and fullscreen states are very strict about + * surface size, so don't let the application override it. The tiled state + * is not as strict, but it indicates a strong size preference, so try to + * respect it. */ if (state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED | + WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN | WAYLAND_SURFACE_CONFIG_STATE_TILED)) { flags |= SWP_NOSENDCHANGING;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
The maximized and fullscreen states disallow surface contents that are larger than the configured size. However, if we have a larger surface, and only part of the surface contains useful client data, we can inform the compositor using xdg_surface_set_geometry, so that only the specified region is considered for compositor-side positioning and state constraints.
We use the geometry to support the presentation of windows that insist on particular positions and sizes which may not match the compositor's requested size (e.g., fullscreen but may overshoot a few pixels outside the monitor boundary). --- dlls/winewayland.drv/wayland_surface.c | 92 ++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 7 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 124d15821bf..b581cec1b46 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -334,21 +334,97 @@ static BOOL wayland_surface_configure_is_compatible(struct wayland_surface_confi /* We require the same state. */ if ((state & mask) != (conf->state & mask)) return FALSE;
- /* The maximized state requires the configured size. */ + /* The maximized state requires the configured size. During surface + * reconfiguration we can use surface geometry to provide smaller areas + * from larger sizes, so only smaller sizes are incompatible. */ if ((conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && - (width != conf->width || height != conf->height)) + (width < conf->width || height < conf->height)) { return FALSE; }
- /* The fullscreen state requires at most the configured size. */ - if ((conf->state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && - (width > conf->width || height > conf->height)) + /* The fullscreen state requires a size smaller or equal to the configured + * size. If we have a larger size, we can use surface geometry during + * surface reconfiguration to provide the smaller size, so we are always + * compatible with a fullscreen state. */ + + return TRUE; +} + +/********************************************************************** + * wayland_surface_get_rect_in_monitor + * + * Gets the largest rectangle within a surface's window (in window coordinates) + * that is visible in a monitor. + */ +static void wayland_surface_get_rect_in_monitor(struct wayland_surface *surface, + RECT *rect) +{ + HMONITOR hmonitor; + MONITORINFO mi; + + mi.cbSize = sizeof(mi); + if (!(hmonitor = NtUserMonitorFromRect(&surface->window.rect, 0)) || + !NtUserGetMonitorInfo(hmonitor, (MONITORINFO *)&mi)) { - return FALSE; + SetRectEmpty(rect); + return; }
- return TRUE; + intersect_rect(rect, &mi.rcMonitor, &surface->window.rect); + OffsetRect(rect, -surface->window.rect.left, -surface->window.rect.top); +} + +/********************************************************************** + * wayland_surface_reconfigure_geometry + * + * Sets the xdg_surface geometry + */ +static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface) +{ + int width, height; + RECT rect; + + width = surface->window.rect.right - surface->window.rect.left; + height = surface->window.rect.bottom - surface->window.rect.top; + + /* If the window size is bigger than the current state accepts, use the + * largest visible (from Windows' perspective) subregion of the window. */ + if ((surface->current.state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED | + WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) && + (width > surface->current.width || height > surface->current.height)) + { + wayland_surface_get_rect_in_monitor(surface, &rect); + + /* If the window rect in the monitor is smaller than required, + * fall back to an appropriately sized rect at the top-left. */ + if ((surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && + (rect.right - rect.left < surface->current.width || + rect.bottom - rect.top < surface->current.height)) + { + SetRect(&rect, 0, 0, surface->current.width, surface->current.height); + } + else + { + rect.right = min(rect.right, rect.left + surface->current.width); + rect.bottom = min(rect.bottom, rect.top + surface->current.height); + } + TRACE("Window is too large for Wayland state, using subregion\n"); + } + else + { + SetRect(&rect, 0, 0, width, height); + } + + TRACE("hwnd=%p geometry=%s\n", surface->hwnd, wine_dbgstr_rect(&rect)); + + if (!IsRectEmpty(&rect)) + { + xdg_surface_set_window_geometry(surface->xdg_surface, + rect.left, rect.top, + rect.right - rect.left, + rect.bottom - rect.top); + } }
/********************************************************************** @@ -403,6 +479,8 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) return FALSE; }
+ wayland_surface_reconfigure_geometry(surface); + return TRUE; }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
If the window is already fullscreen and its size is compatible with what the compositor is requesting, don't force a resize, since some applications are very insistent on a particular fullscreen size (which may not match the monitor size). --- dlls/winewayland.drv/wayland_surface.c | 6 +++--- dlls/winewayland.drv/waylanddrv.h | 3 +++ dlls/winewayland.drv/window.c | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 17d1fffc29b..ae4812ebb08 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -324,9 +324,9 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, * Checks whether a wayland_surface_config object is compatible with the * the provided arguments. */ -static BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, - int width, int height, - enum wayland_surface_config_state state) +BOOL wayland_surface_config_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; diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index e1dc58bdc80..4bcd9e6706e 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -201,6 +201,9 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, HRGN surface_damage_region) DECLSPEC_HIDDEN; struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd) DECLSPEC_HIDDEN; BOOL wayland_surface_reconfigure(struct wayland_surface *surface) DECLSPEC_HIDDEN; +BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, + int width, int height, + enum wayland_surface_config_state state) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland SHM buffer diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 7fc0d2acdd6..c95a4d78ee0 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -434,6 +434,21 @@ static void wayland_configure_window(HWND hwnd) flags |= SWP_FRAMECHANGED; }
+ /* If the window is already fullscreen and its size is compatible with what + * the compositor is requesting, don't force a resize, since some applications + * are very insistent on a particular fullscreen size (which may not match + * the monitor size). */ + if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + wayland_surface_config_is_compatible(&surface->processing, + surface->window.rect.right - + surface->window.rect.left, + surface->window.rect.bottom - + surface->window.rect.top, + surface->window.state)) + { + flags |= SWP_NOSIZE; + } + pthread_mutex_unlock(&surface->mutex);
TRACE("processing=%dx%d,%#x\n", width, height, state);
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Rename the function wayland_surface_configure_is_compatible to wayland_surface_config_is_compatible to match the associated struct name. --- dlls/winewayland.drv/wayland_surface.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index b581cec1b46..17d1fffc29b 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -319,14 +319,14 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, }
/********************************************************************** - * wayland_surface_configure_is_compatible + * wayland_surface_config_is_compatible * - * Checks whether a wayland_surface_configure object is compatible with the + * Checks whether a wayland_surface_config 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 BOOL wayland_surface_config_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; @@ -451,9 +451,9 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface)
/* Acknowledge any compatible processed config. */ if (surface->processing.serial && surface->processing.processed && - wayland_surface_configure_is_compatible(&surface->processing, - width, height, - window->state)) + wayland_surface_config_is_compatible(&surface->processing, + width, height, + window->state)) { surface->current = surface->processing; memset(&surface->processing, 0, sizeof(surface->processing)); @@ -463,18 +463,18 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) * 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)) + wayland_surface_config_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 || - !wayland_surface_configure_is_compatible(&surface->current, - width, height, - window->state)) + !wayland_surface_config_is_compatible(&surface->current, + width, height, + window->state)) { return FALSE; }
On Fri Oct 13 11:07:32 2023 +0000, Alexandros Frantzis wrote:
changed this line in [version 4 of the diff](/wine/wine/-/merge_requests/4014/diffs?diff_id=75933&start_sha=520dcf0ce312fd5ffcb0bb003eae9cadc43e23ff#be6b1a6381414c8dae6f91e46942652a04a69f08_47_46)
Ack, done.
This merge request was approved by Rémi Bernon.
On Tue Oct 10 19:01:55 2023 +0000, Vixea wrote:
That would have been me, maybe others but yea I was using the proprietary drivers. It wasn't working at first but I got it to work only for it to break and start working again, something something Nvidia bad 🤷♂️
@Vixea anyway if it works again for your in the future please tag me I would be more than happy to be an alpha tester
I just want copy pasting to work btw