[PATCH v3 0/7] MR11321: Draft: winewayland: Implement xdg-popup for unmanaged windows instead of subsurfaces
This MR fixes race conditions in the fractional scaling implementation by moving the scale value to the surface config. In addition to avoiding the aforementioned race conditions, it also enables us to store the win32u unscaled rect within the surface config (that is not within this MR, though). It is based on some of the other refactoring present in !11248 This approach makes winewayland ignore the scale event until the next xdg_surface::configure. Since the scale factor is just a suggestion from the compositor to the client, we don't need to follow it strictly. I need to test this with more compositors, but I don't see why they wouldn't send fractional scale events (as needed) with configure events. Draft because still need to figure out how to deal with subsurface scaling using this approach -- v3: winewayland: Use xdg-popup for unmanaged windows. winewayland: Store the unscaled rect in the surface config. https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Etaash Mathamsetty <etaash.mathamsetty@gmail.com> --- dlls/winewayland.drv/wayland_surface.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index c2b4c4be891..f7f0f094645 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -638,19 +638,15 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface } TRACE("Window is too large for Wayland state, using subregion\n"); } - else - { - OffsetRect(&rect, -rect.left, -rect.top); - } TRACE("hwnd=%p geometry=%s\n", surface->hwnd, wine_dbgstr_rect(&rect)); if (!IsRectEmpty(&rect)) { int width = rect.right - rect.left, height = rect.bottom - rect.top; - xdg_surface_set_window_geometry(surface->xdg_surface, - rect.left, rect.top, - width, height); + + xdg_surface_set_window_geometry(surface->xdg_surface, 0, 0, width, height); + if (surface->window.resizeable) { xdg_toplevel_set_min_size(surface->xdg_toplevel, 0, 0); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winewayland.drv/wayland_surface.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index f7f0f094645..d64c21b30dd 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -543,6 +543,16 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, surface->content_height = win_height; } +static BOOL is_rect_bigger(RECT a, RECT b) +{ + return a.right - a.left > b.right - b.left || a.bottom - a.top > b.bottom - b.top; +} + +static BOOL is_rect_smaller(RECT a, RECT b) +{ + return a.right - a.left < b.right - b.left || a.bottom - a.top < b.bottom - b.top; +} + /********************************************************************** * wayland_surface_config_is_compatible * @@ -569,12 +579,7 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, R /* 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) && - (rect.right - rect.left < conf->rect.right - conf->rect.left || - rect.bottom - rect.top < conf->rect.bottom - conf->rect.top)) - { - return FALSE; - } + if ((conf->state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && is_rect_smaller(rect, conf->rect)) return FALSE; return TRUE; } @@ -614,8 +619,7 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface * largest visible (from Windows' perspective) subregion of the window. */ if ((surface->current.state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED | WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) && - (rect.right - rect.left > surface->current.rect.right - surface->current.rect.left || - rect.bottom - rect.top > surface->current.rect.bottom - surface->current.rect.top)) + is_rect_bigger(rect, surface->current.rect)) { wayland_surface_get_rect_in_monitor(surface, &rect); @@ -625,8 +629,7 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface * fall back to an appropriately sized rect at the top-left. */ if ((surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && - (rect.right - rect.left < surface->current.rect.right - surface->current.rect.left || - rect.bottom - rect.top < surface->current.rect.bottom - surface->current.rect.top)) + is_rect_smaller(rect, surface->current.rect)) { SetRect(&rect, 0, 0, surface->current.rect.right - surface->current.rect.left, surface->current.rect.bottom - surface->current.rect.top); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winewayland.drv/wayland_surface.c | 44 +++++++++++--------------- dlls/winewayland.drv/waylanddrv.h | 3 +- dlls/winewayland.drv/window.c | 7 ++-- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index d64c21b30dd..f51fcd97142 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -559,11 +559,9 @@ static BOOL is_rect_smaller(RECT a, RECT b) * Checks whether a wayland_surface_config object is compatible with the * the provided arguments. */ -BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, RECT rect, - enum wayland_surface_config_state state) +BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct wayland_surface_config *conf) { - static enum wayland_surface_config_state mask = - WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + RECT rect = map_rect_to_surface(surface, surface->window.rect); /* 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 @@ -574,7 +572,7 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, R return TRUE; /* We require the same state. */ - if ((state & mask) != (conf->state & mask)) return FALSE; + if ((surface->window.state ^ conf->state) & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) return FALSE; /* The maximized state requires the configured size. During surface * reconfiguration we can use surface geometry to provide smaller areas @@ -613,8 +611,10 @@ static void wayland_surface_get_rect_in_monitor(struct wayland_surface *surface, * * Sets the xdg_surface geometry */ -static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface, RECT rect) +static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface) { + RECT rect = map_rect_to_surface(surface, surface->window.rect); + /* 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 | @@ -668,10 +668,12 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface * * Sets the surface size with viewporter */ -static void wayland_surface_reconfigure_size(struct wayland_surface *surface, - int width, int height) +static void wayland_surface_reconfigure_size(struct wayland_surface *surface) { - TRACE("hwnd=%p size=%dx%d\n", surface->hwnd, width, height); + RECT rect = map_rect_to_surface(surface, surface->window.rect); + int width = rect.right - rect.left, height = rect.bottom - rect.top; + + TRACE("hwnd=%p rect=%s\n", surface->hwnd, wine_dbgstr_rect(&rect)); if (width != 0 && height != 0) wp_viewport_set_destination(surface->wp_viewport, width, height); @@ -719,14 +721,11 @@ static void wayland_surface_reconfigure_client(struct wayland_surface *surface, * Reconfigures the xdg surface as needed to match the latest requested * state. */ -static BOOL wayland_surface_reconfigure_xdg(struct wayland_surface *surface, RECT rect) +static BOOL wayland_surface_reconfigure_xdg(struct wayland_surface *surface) { - struct wayland_window_config *window = &surface->window; - /* Acknowledge any compatible processed config. */ if (surface->processing.serial && surface->processing.processed && - wayland_surface_config_is_compatible(&surface->processing, rect, - window->state)) + wayland_surface_config_is_compatible(surface, &surface->processing)) { surface->current = surface->processing; memset(&surface->processing, 0, sizeof(surface->processing)); @@ -736,21 +735,19 @@ static BOOL wayland_surface_reconfigure_xdg(struct wayland_surface *surface, REC * 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_config_is_compatible(&surface->requested, rect, - window->state)) + wayland_surface_config_is_compatible(surface, &surface->requested)) { 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_config_is_compatible(&surface->current, rect, - window->state)) + !wayland_surface_config_is_compatible(surface, &surface->current)) { return FALSE; } - wayland_surface_reconfigure_geometry(surface, rect); + wayland_surface_reconfigure_geometry(surface); return TRUE; } @@ -799,11 +796,8 @@ static void wayland_surface_reconfigure_subsurface(struct wayland_surface *surfa */ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) { - struct wayland_window_config *window = &surface->window; - RECT rect = map_rect_to_surface(surface, surface->window.rect); - TRACE("hwnd=%p window=%s,%#x processing=%s,%#x current=%s,%#x\n", - surface->hwnd, wine_dbgstr_rect(&rect), window->state, + surface->hwnd, wine_dbgstr_rect(&surface->window.rect), surface->window.state, wine_dbgstr_rect(&surface->processing.rect), surface->processing.state, wine_dbgstr_rect(&surface->current.rect), surface->current.state); @@ -813,7 +807,7 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) break; case WAYLAND_SURFACE_ROLE_TOPLEVEL: if (!surface->xdg_surface) break; /* surface role has been cleared */ - if (!wayland_surface_reconfigure_xdg(surface, rect)) return FALSE; + if (!wayland_surface_reconfigure_xdg(surface)) return FALSE; break; case WAYLAND_SURFACE_ROLE_SUBSURFACE: if (!surface->wl_subsurface) break; /* surface role has been cleared */ @@ -821,7 +815,7 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) break; } - wayland_surface_reconfigure_size(surface, rect.right - rect.left, rect.bottom - rect.top); + wayland_surface_reconfigure_size(surface); return TRUE; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index da166c939d4..1aae646d2a9 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -332,8 +332,7 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, HRGN surface_damage_region); BOOL wayland_surface_reconfigure(struct wayland_surface *surface); -BOOL wayland_surface_config_is_compatible(struct wayland_surface_config *conf, RECT rect, - enum wayland_surface_config_state state); +BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct wayland_surface_config *conf); RECT map_rect_to_surface(struct wayland_surface *surface, RECT rect); POINT map_point_to_surface(struct wayland_surface *surface, POINT point); RECT map_rect_from_surface(struct wayland_surface *surface, RECT rect); diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 55385eb3772..2f6f8adf3ca 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -481,7 +481,7 @@ static void wayland_configure_window(HWND hwnd) BOOL needs_exit_size_move = FALSE; BOOL restoring_from_minimize = FALSE; struct wayland_win_data *data; - RECT rect, surface_rect; + RECT rect; if (!(data = wayland_win_data_get(hwnd))) return; if (!(surface = data->wayland_surface)) @@ -540,15 +540,12 @@ static void wayland_configure_window(HWND hwnd) flags |= SWP_FRAMECHANGED; } - surface_rect = map_rect_to_surface(surface, surface->window.rect); - /* 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_rect, - surface->window.state)) + wayland_surface_config_is_compatible(surface, &surface->processing)) { flags |= SWP_NOSIZE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winewayland.drv/window.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 2f6f8adf3ca..000f8001710 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -473,8 +473,7 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN static void wayland_configure_window(HWND hwnd) { struct wayland_surface *surface; - INT width, height; - UINT flags = 0; + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE; uint32_t state; DWORD style; BOOL needs_enter_size_move = FALSE; @@ -507,18 +506,12 @@ static void wayland_configure_window(HWND hwnd) surface->processing = surface->requested; memset(&surface->requested, 0, sizeof(surface->requested)); + rect = map_rect_from_surface(surface, surface->processing.rect); state = surface->processing.state; + /* Ignore size hints if we don't have a state that requires strict * size adherence, in order to avoid spurious resizes. */ - if (state) - { - width = surface->processing.rect.right - surface->processing.rect.left; - height = surface->processing.rect.bottom - surface->processing.rect.top; - } - else - { - width = height = 0; - } + if (!state) flags |= SWP_NOSIZE; if ((state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && !surface->resizing) { @@ -572,20 +565,13 @@ static void wayland_configure_window(HWND hwnd) return; } - SetRect(&rect, 0, 0, width, height); - rect = map_rect_from_surface(surface, rect); - OffsetRect(&rect, data->rects.window.left, data->rects.window.top); - wayland_win_data_release(data); - TRACE("processing=%dx%d,%#x\n", width, height, state); + TRACE("processing rect=%s state=%#x flags=%#x\n", wine_dbgstr_rect(&rect), state, flags); 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; - if (rect.left == rect.right || rect.bottom == rect.top) flags |= SWP_NOSIZE; - style = NtUserGetWindowLongW(hwnd, GWL_STYLE); if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE) && !(state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winewayland.drv/wayland_surface.c | 33 ++++++++++--------- dlls/winewayland.drv/waylanddrv.h | 30 ++++++++++-------- dlls/winewayland.drv/window.c | 44 +++++++++++++------------- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index f51fcd97142..7ccdf2998d9 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -83,43 +83,46 @@ static void xdg_toplevel_handle_configure(void *private, int32_t width, int32_t height, struct wl_array *states) { + RECT rect; + enum surface_state config_state = 0; struct wayland_surface *surface; HWND hwnd = private; uint32_t *state; - enum wayland_surface_config_state config_state = 0; struct wayland_win_data *data; + SetRect(&rect, 0, 0, width, height); + wl_array_for_each(state, states) { switch(*state) { case XDG_TOPLEVEL_STATE_MAXIMIZED: - config_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + config_state |= SURFACE_STATE_MAXIMIZED; break; case XDG_TOPLEVEL_STATE_RESIZING: - config_state |= WAYLAND_SURFACE_CONFIG_STATE_RESIZING; + config_state |= SURFACE_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; + config_state |= SURFACE_STATE_TILED; break; case XDG_TOPLEVEL_STATE_FULLSCREEN: - config_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN; + config_state |= SURFACE_STATE_FULLSCREEN; break; default: break; } } - TRACE("hwnd=%p %dx%d,%#x\n", hwnd, width, height, config_state); + TRACE("hwnd=%p rect=%s state=%#x\n", hwnd, wine_dbgstr_rect(&rect), config_state); if (!(data = wayland_win_data_get(hwnd))) return; if ((surface = data->wayland_surface) && wayland_surface_is_toplevel(surface)) { - SetRect(&surface->pending.rect, 0, 0, width, height); + surface->pending.rect = rect; surface->pending.state = config_state; } @@ -559,7 +562,7 @@ static BOOL is_rect_smaller(RECT a, RECT b) * Checks whether a wayland_surface_config object is compatible with the * the provided arguments. */ -BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct wayland_surface_config *conf) +BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct surface_config *conf) { RECT rect = map_rect_to_surface(surface, surface->window.rect); @@ -568,16 +571,16 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struc * surface reconfiguration to provide the smaller size, so we are always * compatible with a fullscreen state. * NOTE: Fullscreen combined with maximized is the same as fullscreen. */ - if (conf->state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) + if (conf->state & SURFACE_STATE_FULLSCREEN) return TRUE; /* We require the same state. */ - if ((surface->window.state ^ conf->state) & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) return FALSE; + if ((surface->window.state ^ conf->state) & SURFACE_STATE_MAXIMIZED) return FALSE; /* 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) && is_rect_smaller(rect, conf->rect)) return FALSE; + if ((conf->state & SURFACE_STATE_MAXIMIZED) && is_rect_smaller(rect, conf->rect)) return FALSE; return TRUE; } @@ -617,8 +620,8 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface /* 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)) && + if ((surface->current.state & (SURFACE_STATE_MAXIMIZED | + SURFACE_STATE_FULLSCREEN)) && is_rect_bigger(rect, surface->current.rect)) { wayland_surface_get_rect_in_monitor(surface, &rect); @@ -627,8 +630,8 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface /* 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) && - !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + if ((surface->current.state & SURFACE_STATE_MAXIMIZED) && + !(surface->current.state & SURFACE_STATE_FULLSCREEN) && is_rect_smaller(rect, surface->current.rect)) { SetRect(&rect, 0, 0, surface->current.rect.right - surface->current.rect.left, diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 1aae646d2a9..551f14e76e9 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -72,12 +72,12 @@ enum wayland_window_message WM_WAYLAND_SET_FOREGROUND, }; -enum wayland_surface_config_state +enum surface_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_FULLSCREEN = (1 << 3) + SURFACE_STATE_MAXIMIZED = (1 << 0), + SURFACE_STATE_RESIZING = (1 << 1), + SURFACE_STATE_TILED = (1 << 2), + SURFACE_STATE_FULLSCREEN = (1 << 3) }; enum wayland_surface_role @@ -224,19 +224,19 @@ struct wayland_output struct wayland_output_state current; }; -struct wayland_surface_config +struct surface_config { - RECT rect; - enum wayland_surface_config_state state; - uint32_t serial; - BOOL processed; + RECT rect; /* rect of the compositor surface (in surface coordinates) */ + enum surface_state state; /* state of the compositor surface */ + uint32_t serial; /* serial of the corresponding xdg_surface_configure event */ + BOOL processed; /* config has been fully applied to the surface win32 window */ }; struct wayland_window_config { RECT rect; RECT client_rect; - enum wayland_surface_config_state state; + enum surface_state state; /* The scale (i.e., normalized dpi) the window is rendering at. */ double scale; BOOL visible; @@ -297,7 +297,11 @@ struct wayland_surface }; struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1; - struct wayland_surface_config pending, requested, processing, current; + struct surface_config pending; /* incomplete surface config being received from the compositor */ + struct surface_config requested; /* latest complete surface config received from the compositor */ + struct surface_config processing; /* surface config being applied to the surface win32 window */ + struct surface_config current; /* latest config that has been applied to the surface win32 window */ + BOOL resizing; struct wayland_window_config window; int content_width, content_height; @@ -332,7 +336,7 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, HRGN surface_damage_region); BOOL wayland_surface_reconfigure(struct wayland_surface *surface); -BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct wayland_surface_config *conf); +BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct surface_config *conf); RECT map_rect_to_surface(struct wayland_surface *surface, RECT rect); POINT map_point_to_surface(struct wayland_surface *surface, POINT point); RECT map_rect_from_surface(struct wayland_surface *surface, RECT rect); diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 000f8001710..b3cc1e135aa 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -135,7 +135,7 @@ 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; + enum surface_state window_state = 0; DWORD style; conf->rect = data->rects.window; @@ -150,13 +150,13 @@ static void wayland_win_data_get_config(struct wayland_win_data *data, if (data->is_fullscreen) { if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) - window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + window_state |= SURFACE_STATE_MAXIMIZED; else if (!(style & WS_MINIMIZE)) - window_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN; + window_state |= SURFACE_STATE_FULLSCREEN; } else if (style & WS_MAXIMIZE) { - window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED; + window_state |= SURFACE_STATE_MAXIMIZED; } conf->resizeable = data->resizeable; @@ -254,26 +254,26 @@ static void wayland_surface_update_state_toplevel(struct wayland_surface *surfac { /* 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) && + if (!(surface->window.state & SURFACE_STATE_MAXIMIZED) && + (surface->current.state & SURFACE_STATE_MAXIMIZED) && !surface->window.minimized) { xdg_toplevel_unset_maximized(surface->xdg_toplevel); } - if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && - (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && + if (!(surface->window.state & SURFACE_STATE_FULLSCREEN) && + (surface->current.state & SURFACE_STATE_FULLSCREEN) && !surface->window.minimized) { xdg_toplevel_unset_fullscreen(surface->xdg_toplevel); } - if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) && - !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED)) + if ((surface->window.state & SURFACE_STATE_MAXIMIZED) && + !(surface->current.state & SURFACE_STATE_MAXIMIZED)) { xdg_toplevel_set_maximized(surface->xdg_toplevel); } - if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) && - !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) + if ((surface->window.state & SURFACE_STATE_FULLSCREEN) && + !(surface->current.state & SURFACE_STATE_FULLSCREEN)) { xdg_toplevel_set_fullscreen(surface->xdg_toplevel, NULL); } @@ -513,13 +513,13 @@ static void wayland_configure_window(HWND hwnd) * size adherence, in order to avoid spurious resizes. */ if (!state) flags |= SWP_NOSIZE; - if ((state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && !surface->resizing) + if ((state & SURFACE_STATE_RESIZING) && !surface->resizing) { surface->resizing = TRUE; needs_enter_size_move = TRUE; } - if (!(state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && surface->resizing) + if (!(state & SURFACE_STATE_RESIZING) && surface->resizing) { surface->resizing = FALSE; needs_exit_size_move = TRUE; @@ -527,8 +527,8 @@ static void wayland_configure_window(HWND hwnd) /* 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)) + (SURFACE_STATE_MAXIMIZED | + SURFACE_STATE_FULLSCREEN)) { flags |= SWP_FRAMECHANGED; } @@ -537,7 +537,7 @@ static void wayland_configure_window(HWND hwnd) * 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) && + if ((surface->window.state & SURFACE_STATE_FULLSCREEN) && wayland_surface_config_is_compatible(surface, &surface->processing)) { flags |= SWP_NOSIZE; @@ -573,17 +573,17 @@ static void wayland_configure_window(HWND hwnd) if (needs_exit_size_move) send_message(hwnd, WM_EXITSIZEMOVE, 0, 0); style = NtUserGetWindowLongW(hwnd, GWL_STYLE); - if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE) - && !(state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN)) + if (!(state & SURFACE_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE) + && !(state & SURFACE_STATE_FULLSCREEN)) NtUserSetWindowLong(hwnd, GWL_STYLE, style ^ WS_MAXIMIZE, FALSE); /* 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)) + if (state & (SURFACE_STATE_MAXIMIZED | + SURFACE_STATE_FULLSCREEN | + SURFACE_STATE_TILED)) { flags |= SWP_NOSENDCHANGING; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Etaash Mathamsetty <etaash.mathamsetty@gmail.com> --- dlls/winewayland.drv/wayland_surface.c | 14 ++++++-------- dlls/winewayland.drv/waylanddrv.h | 2 +- dlls/winewayland.drv/window.c | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 7ccdf2998d9..b5fd69194be 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -116,17 +116,17 @@ static void xdg_toplevel_handle_configure(void *private, } } - TRACE("hwnd=%p rect=%s state=%#x\n", hwnd, wine_dbgstr_rect(&rect), config_state); - if (!(data = wayland_win_data_get(hwnd))) return; if ((surface = data->wayland_surface) && wayland_surface_is_toplevel(surface)) { - surface->pending.rect = rect; + surface->pending.rect = rect = map_rect_from_surface(surface, rect); surface->pending.state = config_state; } wayland_win_data_release(data); + + TRACE("hwnd=%p rect=%s state=%#x\n", hwnd, wine_dbgstr_rect(&rect), config_state); } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) @@ -564,15 +564,12 @@ static BOOL is_rect_smaller(RECT a, RECT b) */ BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struct surface_config *conf) { - RECT rect = map_rect_to_surface(surface, surface->window.rect); - /* 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. * NOTE: Fullscreen combined with maximized is the same as fullscreen. */ - if (conf->state & SURFACE_STATE_FULLSCREEN) - return TRUE; + if (conf->state & SURFACE_STATE_FULLSCREEN) return TRUE; /* We require the same state. */ if ((surface->window.state ^ conf->state) & SURFACE_STATE_MAXIMIZED) return FALSE; @@ -580,7 +577,8 @@ BOOL wayland_surface_config_is_compatible(struct wayland_surface *surface, struc /* 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 & SURFACE_STATE_MAXIMIZED) && is_rect_smaller(rect, conf->rect)) return FALSE; + if ((conf->state & SURFACE_STATE_MAXIMIZED) && is_rect_smaller(surface->window.rect, conf->rect)) + return FALSE; return TRUE; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 551f14e76e9..9a109e4e5cf 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -226,7 +226,7 @@ struct wayland_output struct surface_config { - RECT rect; /* rect of the compositor surface (in surface coordinates) */ + RECT rect; /* rect of the compositor surface (in win32u coordinates) */ enum surface_state state; /* state of the compositor surface */ uint32_t serial; /* serial of the corresponding xdg_surface_configure event */ BOOL processed; /* config has been fully applied to the surface win32 window */ diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index b3cc1e135aa..dbff4d53489 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -506,7 +506,7 @@ static void wayland_configure_window(HWND hwnd) surface->processing = surface->requested; memset(&surface->requested, 0, sizeof(surface->requested)); - rect = map_rect_from_surface(surface, surface->processing.rect); + rect = surface->processing.rect; state = surface->processing.state; /* Ignore size hints if we don't have a state that requires strict -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
From: Etaash Mathamsetty <etaash.mathamsetty@gmail.com> --- dlls/winewayland.drv/wayland.c | 8 +- dlls/winewayland.drv/wayland_surface.c | 261 ++++++++++++++++--------- dlls/winewayland.drv/waylanddrv.h | 16 +- dlls/winewayland.drv/window.c | 54 ++--- 4 files changed, 200 insertions(+), 139 deletions(-) diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index d5fb36417c1..59bd6f97ff3 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -120,11 +120,9 @@ static void registry_handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, "xdg_wm_base") == 0) { - /* Bind version 2 so that compositors (e.g., sway) can properly send tiled - * states, instead of falling back to (ab)using the maximized state. */ - process_wayland.xdg_wm_base = - wl_registry_bind(registry, id, &xdg_wm_base_interface, - version < 2 ? version : 2); + /* version 3 is required for xdg_popup::reposition */ + if (version < 3) return; + process_wayland.xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 3); xdg_wm_base_add_listener(process_wayland.xdg_wm_base, &xdg_wm_base_listener, NULL); } else if (strcmp(interface, "wl_shm") == 0) diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index b5fd69194be..ecebbd5bf81 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -34,6 +34,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); +/* rountrip through WindowPosChanged to refresh the host window state */ +static void update_window_state(HWND hwnd) +{ + static const UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW; + static const RECT rect; + + NtUserSetRawWindowPos(hwnd, rect, swp_flags, FALSE); +} + static void xdg_surface_handle_configure(void *private, struct xdg_surface *xdg_surface, uint32_t serial) { @@ -48,19 +58,21 @@ static void xdg_surface_handle_configure(void *private, struct xdg_surface *xdg_ /* Handle this event only if wayland_surface is still associated with * the target xdg_surface. */ - if ((surface = data->wayland_surface) && wayland_surface_is_toplevel(surface) && - surface->xdg_surface == xdg_surface) + if (!(surface = data->wayland_surface) || surface->xdg_surface != xdg_surface) { - /* If we have a previously requested config, we have already sent a - * WM_WAYLAND_CONFIGURE which hasn't been handled yet. In that case, - * avoid sending another message to reduce message queue traffic. */ - should_post = surface->requested.serial == 0; - initial_configure = surface->current.serial == 0; - surface->pending.serial = serial; - surface->requested = surface->pending; - memset(&surface->pending, 0, sizeof(surface->pending)); + wayland_win_data_release(data); + return; } + /* If we have a previously requested config, we have already sent a + * WM_WAYLAND_CONFIGURE which hasn't been handled yet. In that case, + * avoid sending another message to reduce message queue traffic. */ + should_post = surface->requested.serial == 0; + initial_configure = surface->current.serial == 0; + surface->pending.serial = serial; + surface->requested = surface->pending; + memset(&surface->pending, 0, sizeof(surface->pending)); + wayland_win_data_release(data); if (should_post) NtUserPostMessage(hwnd, WM_WAYLAND_CONFIGURE, 0, 0); @@ -140,6 +152,78 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = xdg_toplevel_handle_close }; +static void xdg_popup_handle_configure(void *private, struct xdg_popup *xdg_popup, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + RECT rect; + HWND hwnd = private; + struct wayland_win_data *data, *owner_data; + struct wayland_surface *surface, *owner; + + SetRect(&rect, x, y, x + width, y + height); + + TRACE("hwnd=%p rect=%s\n", hwnd, wine_dbgstr_rect(&rect)); + + if (!(data = wayland_win_data_get(hwnd))) return; + + if ((surface = data->wayland_surface) && wayland_surface_is_popup(surface)) + { + rect = map_rect_from_surface(surface, rect); + + if (!(owner_data = wayland_win_data_get(surface->owner_hwnd))) + WARN("Lost surface %p owner window\n", surface); + else if (!(owner = owner_data->wayland_surface)) + WARN("Lost surface %p owner surface\n", surface); + else + { + RECT owner_rect = owner->window.rect; + OffsetRect(&rect, owner_rect.left, owner_rect.top); + } + if (owner_data) wayland_win_data_release(owner_data); + + surface->pending.rect = rect; + } + + wayland_win_data_release(data); +} + +static void xdg_popup_handle_done(void *private, struct xdg_popup *xdg_popup) +{ + struct wayland_surface *surface; + struct wayland_win_data *data; + HWND hwnd = private; + + /* Recreate the popup if the compositor dismissed it for some reason. + * The protocol does not explicitly prohibit this from occuring on ungrabbed popups. */ + WARN("Compositor dismissed popup hwnd=%p\n", hwnd); + + /* the protocol requires us to destroy the xdg_popup */ + xdg_popup_destroy(xdg_popup); + + if (!(data = wayland_win_data_get(hwnd))) return; + if ((surface = data->wayland_surface) && surface->xdg_popup == xdg_popup) + { + surface->xdg_popup = NULL; + wayland_surface_clear_role(surface); + } + wayland_win_data_release(data); + + update_window_state(hwnd); +} + +static void xdg_popup_handle_reposition(void *private, struct xdg_popup *xdg_popup, uint32_t token) +{ + /* we also get a configure event in this case */ + TRACE("hwnd=%p\n", private); +} + +static const struct xdg_popup_listener xdg_popup_listener = +{ + xdg_popup_handle_configure, + xdg_popup_handle_done, + xdg_popup_handle_reposition, +}; + void wp_fractional_scale_handle_scale(void* user_data, struct wp_fractional_scale_v1 *fractional_scale_v1, uint32_t scale_fixed) @@ -163,16 +247,10 @@ void wp_fractional_scale_handle_scale(void* user_data, /* reattach client surfaces as their rects have changed */ update_client_surfaces(hwnd); - /* the subsurface rect has changed */ - if (surface->role == WAYLAND_SURFACE_ROLE_SUBSURFACE) - { - surface->processing.serial = 1; - surface->processing.processed = TRUE; - } - wayland_win_data_release(data); NtUserExposeWindowSurface(hwnd, 0, NULL); + update_window_state(hwnd); } static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = @@ -323,6 +401,28 @@ static void wayland_surface_init_fractional_scale(struct wayland_surface *surfac surface->hwnd); } +/* helper to intialize the positioner using a given surface rect */ +static struct xdg_positioner *xdg_positioner_create(RECT rect) +{ + struct xdg_positioner *xdg_positioner; + + if (!(xdg_positioner = xdg_wm_base_create_positioner(process_wayland.xdg_wm_base))) return NULL; + + if (rect.right == rect.left) rect.right = rect.left + 1; + if (rect.bottom == rect.top) rect.bottom = rect.top + 1; + + /* this anchor rect is always valid */ + xdg_positioner_set_anchor_rect(xdg_positioner, 0, 0, 1, 1); + xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); + xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); + + /* then we offset by the requested amount */ + xdg_positioner_set_offset(xdg_positioner, rect.left, rect.top); + xdg_positioner_set_size(xdg_positioner, rect.right - rect.left, rect.bottom - rect.top); + + return xdg_positioner; +} + /********************************************************************** * wayland_surface_make_toplevel * @@ -371,46 +471,66 @@ err: } /********************************************************************** - * wayland_surface_make_subsurface + * wayland_surface_make_popup * - * Gives the subsurface role to a plain wayland surface. + * Gives the popup role to a plain wayland surface. */ -void wayland_surface_make_subsurface(struct wayland_surface *surface, - struct wayland_surface *owner) +void wayland_surface_make_popup(struct wayland_surface *surface, + struct wayland_surface *owner) { - assert(!surface->role || surface->role == WAYLAND_SURFACE_ROLE_SUBSURFACE); - if (surface->wl_subsurface && surface->owner_hwnd == owner->hwnd) return; + struct xdg_positioner *xdg_positioner; + RECT rect = surface->window.rect; - wayland_surface_clear_role(surface); - surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; + assert(!surface->role || surface->role == WAYLAND_SURFACE_ROLE_POPUP); - TRACE("surface=%p owner=%p\n", surface, owner); - - surface->wl_subsurface = - wl_subcompositor_get_subsurface(process_wayland.wl_subcompositor, - surface->wl_surface, - owner->wl_surface); - if (!surface->wl_subsurface) + if (surface->xdg_surface && surface->xdg_popup && surface->owner_hwnd == owner->hwnd) { - ERR("Failed to create client wl_subsurface\n"); - goto err; + struct surface_config *config = surface->processing.serial ? &surface->processing : &surface->current; + if (config->rect.left == rect.left && config->rect.top == rect.top) return; + + TRACE("surface=%p owner=%p rect=%s, repositioning\n", surface, owner, wine_dbgstr_rect(&rect)); + + OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + if (!(xdg_positioner = xdg_positioner_create(map_rect_to_surface(surface, rect)))) goto err; + xdg_popup_reposition(surface->xdg_popup, xdg_positioner, 0); + xdg_positioner_destroy(xdg_positioner); + + wl_surface_commit(surface->wl_surface); + wl_display_flush(process_wayland.wl_display); + return; } + wayland_surface_clear_role(surface); + surface->role = WAYLAND_SURFACE_ROLE_POPUP; + + TRACE("surface=%p owner=%p rect=%s\n", surface, owner, wine_dbgstr_rect(&rect)); + + surface->xdg_surface = xdg_wm_base_get_xdg_surface(process_wayland.xdg_wm_base, + surface->wl_surface); + if (!surface->xdg_surface) goto err; + xdg_surface_add_listener(surface->xdg_surface, &xdg_surface_listener, surface->hwnd); + wayland_surface_init_fractional_scale(surface, owner->window.scale); - surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; - surface->owner_hwnd = owner->hwnd; + OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + if (!(xdg_positioner = xdg_positioner_create(map_rect_to_surface(surface, rect)))) goto err; + surface->xdg_popup = xdg_surface_get_popup(surface->xdg_surface, owner->xdg_surface, + xdg_positioner); + xdg_positioner_destroy(xdg_positioner); + + if (!surface->xdg_popup) goto err; + xdg_popup_add_listener(surface->xdg_popup, &xdg_popup_listener, surface->hwnd); - /* Present contents independently of the owner surface. */ - wl_subsurface_set_desync(surface->wl_subsurface); + surface->owner_hwnd = owner->hwnd; + wl_surface_commit(surface->wl_surface); wl_display_flush(process_wayland.wl_display); return; err: wayland_surface_clear_role(surface); - ERR("Failed to assign subsurface role to wayland surface\n"); + ERR("Failed to assign popup role to wayland surface\n"); } /********************************************************************** @@ -452,25 +572,25 @@ void wayland_surface_clear_role(struct wayland_surface *surface) xdg_toplevel_destroy(surface->xdg_toplevel); surface->xdg_toplevel = NULL; } - - if (surface->xdg_surface) - { - xdg_surface_destroy(surface->xdg_surface); - surface->xdg_surface = NULL; - } break; - case WAYLAND_SURFACE_ROLE_SUBSURFACE: - if (surface->wl_subsurface) + case WAYLAND_SURFACE_ROLE_POPUP: + if (surface->xdg_popup) { - wl_subsurface_destroy(surface->wl_subsurface); - surface->wl_subsurface = NULL; + xdg_popup_destroy(surface->xdg_popup); + surface->xdg_popup = NULL; } surface->owner_hwnd = NULL; break; } + if (surface->xdg_surface) + { + xdg_surface_destroy(surface->xdg_surface); + surface->xdg_surface = NULL; + } + memset(&surface->pending, 0, sizeof(surface->pending)); memset(&surface->requested, 0, sizeof(surface->requested)); memset(&surface->processing, 0, sizeof(surface->processing)); @@ -651,6 +771,8 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface xdg_surface_set_window_geometry(surface->xdg_surface, 0, 0, width, height); + if (!wayland_surface_is_toplevel(surface)) return; + if (surface->window.resizeable) { xdg_toplevel_set_min_size(surface->xdg_toplevel, 0, 0); @@ -753,42 +875,6 @@ static BOOL wayland_surface_reconfigure_xdg(struct wayland_surface *surface) return TRUE; } -/********************************************************************** - * wayland_surface_reconfigure_subsurface - * - * Reconfigures the subsurface as needed to match the latest requested - * state. - */ -static void wayland_surface_reconfigure_subsurface(struct wayland_surface *surface) -{ - struct wayland_win_data *owner_data; - struct wayland_surface *owner_surface; - - if (!surface->processing.serial || !surface->processing.processed) return; - if (!(owner_data = wayland_win_data_get(surface->owner_hwnd))) return; - - if ((owner_surface = owner_data->wayland_surface)) - { - RECT rect = surface->window.rect; - - OffsetRect(&rect, -owner_surface->window.rect.left, -owner_surface->window.rect.top); - rect = map_rect_to_surface(surface, rect); - - TRACE("hwnd=%p rect=%s\n", surface->hwnd, wine_dbgstr_rect(&rect)); - - wl_subsurface_set_position(surface->wl_subsurface, rect.left, rect.top); - if (owner_data->client_surface && owner_data->client_surface->wl_subsurface) - wl_subsurface_place_above(surface->wl_subsurface, owner_data->client_surface->wl_surface); - else - wl_subsurface_place_above(surface->wl_subsurface, owner_surface->wl_surface); - wl_surface_commit(owner_surface->wl_surface); - - memset(&surface->processing, 0, sizeof(surface->processing)); - } - - wayland_win_data_release(owner_data); -} - /********************************************************************** * wayland_surface_reconfigure * @@ -807,13 +893,10 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) case WAYLAND_SURFACE_ROLE_NONE: break; case WAYLAND_SURFACE_ROLE_TOPLEVEL: + case WAYLAND_SURFACE_ROLE_POPUP: if (!surface->xdg_surface) break; /* surface role has been cleared */ if (!wayland_surface_reconfigure_xdg(surface)) return FALSE; break; - case WAYLAND_SURFACE_ROLE_SUBSURFACE: - if (!surface->wl_subsurface) break; /* surface role has been cleared */ - wayland_surface_reconfigure_subsurface(surface); - break; } wayland_surface_reconfigure_size(surface); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 9a109e4e5cf..0b30833a268 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -84,7 +84,7 @@ enum wayland_surface_role { WAYLAND_SURFACE_ROLE_NONE, WAYLAND_SURFACE_ROLE_TOPLEVEL, - WAYLAND_SURFACE_ROLE_SUBSURFACE, + WAYLAND_SURFACE_ROLE_POPUP, }; struct wayland_keyboard @@ -281,17 +281,18 @@ struct wayland_surface struct wayland_shm_buffer *big_icon_buffer; enum wayland_surface_role role; + + struct xdg_surface *xdg_surface; union { struct { - struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel_icon_v1 *xdg_toplevel_icon; }; struct { - struct wl_subsurface *wl_subsurface; + struct xdg_popup *xdg_popup; HWND owner_hwnd; }; }; @@ -329,8 +330,8 @@ void wayland_output_use_xdg_extension(struct wayland_output *output); struct wayland_surface *wayland_surface_create(HWND hwnd); void wayland_surface_destroy(struct wayland_surface *surface); void wayland_surface_make_toplevel(struct wayland_surface *surface); -void wayland_surface_make_subsurface(struct wayland_surface *surface, - struct wayland_surface *parent); +void wayland_surface_make_popup(struct wayland_surface *surface, + struct wayland_surface *parent); void wayland_surface_clear_role(struct wayland_surface *surface); void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, @@ -353,6 +354,11 @@ static inline BOOL wayland_surface_is_toplevel(struct wayland_surface *surface) return surface->role == WAYLAND_SURFACE_ROLE_TOPLEVEL && surface->xdg_toplevel; } +static inline BOOL wayland_surface_is_popup(struct wayland_surface *surface) +{ + return surface->role == WAYLAND_SURFACE_ROLE_POPUP && surface->xdg_popup; +} + /********************************************************************** * Wayland SHM buffer */ diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index dbff4d53489..666529bc03f 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -187,7 +187,7 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat (!(exstyle & WS_EX_LAYERED) || data->layered_attribs_set); if (!visible) role = WAYLAND_SURFACE_ROLE_NONE; - else if (owner_surface) role = WAYLAND_SURFACE_ROLE_SUBSURFACE; + else if (owner_surface) role = WAYLAND_SURFACE_ROLE_POPUP; else role = WAYLAND_SURFACE_ROLE_TOPLEVEL; /* we can temporarily clear the role of a surface but cannot assign a different one after it's set */ @@ -211,6 +211,8 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat wl_surface_set_input_region(surface->wl_surface, input_region); if (input_region) wl_region_destroy(input_region); + wayland_win_data_get_config(data, &surface->window); + /* 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. */ @@ -222,13 +224,11 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat case WAYLAND_SURFACE_ROLE_TOPLEVEL: wayland_surface_make_toplevel(surface); break; - case WAYLAND_SURFACE_ROLE_SUBSURFACE: - wayland_surface_make_subsurface(surface, owner_surface); + case WAYLAND_SURFACE_ROLE_POPUP: + wayland_surface_make_popup(surface, owner_surface); break; } - wayland_win_data_get_config(data, &surface->window); - /* Size/position changes affect the effective pointer constraint, so update * it as needed. */ if (data->hwnd == NtUserGetForegroundWindow()) reapply_cursor_clipping(); @@ -238,8 +238,9 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat return TRUE; } -static void wayland_surface_update_state_toplevel(struct wayland_surface *surface) +static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) { + struct wayland_surface *surface = data->wayland_surface; BOOL processing_config = surface->processing.serial && !surface->processing.processed; @@ -250,7 +251,11 @@ static void wayland_surface_update_state_toplevel(struct wayland_surface *surfac /* If we are not processing a compositor requested config, use the * window state to determine and update the Wayland state. */ - if (!processing_config) + if (processing_config) + { + surface->processing.processed = TRUE; + } + else if (wayland_surface_is_toplevel(surface)) { /* First do all state unsettings, before setting new state. Some * Wayland compositors misbehave if the order is reversed. */ @@ -282,32 +287,6 @@ static void wayland_surface_update_state_toplevel(struct wayland_surface *surfac xdg_toplevel_set_minimized(surface->xdg_toplevel); } } - else - { - surface->processing.processed = TRUE; - } -} - -static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) -{ - struct wayland_surface *surface = data->wayland_surface; - - switch (surface->role) - { - case WAYLAND_SURFACE_ROLE_NONE: - break; - case WAYLAND_SURFACE_ROLE_TOPLEVEL: - if (!surface->xdg_surface) break; /* surface role has been cleared */ - wayland_surface_update_state_toplevel(surface); - break; - case WAYLAND_SURFACE_ROLE_SUBSURFACE: - TRACE("hwnd=%p subsurface owner=%p\n", surface->hwnd, surface->owner_hwnd); - /* Although subsurfaces don't have a dedicated surface config mechanism, - * we use the config fields to mark them as updated. */ - surface->processing.serial = 1; - surface->processing.processed = TRUE; - break; - } wl_display_flush(process_wayland.wl_display); } @@ -447,6 +426,8 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN if (!(data = wayland_win_data_get(hwnd))) return; owner_data = owner && owner != hwnd ? wayland_win_data_get(owner) : NULL; owner_surface = owner_data ? owner_data->wayland_surface : NULL; + /* for it to be a popup, we need a valid xdg surface. */ + if (owner_surface && !owner_surface->xdg_surface) owner_surface = NULL; data->rects = *new_rects; data->is_fullscreen = fullscreen; @@ -489,13 +470,6 @@ static void wayland_configure_window(HWND hwnd) return; } - if (!wayland_surface_is_toplevel(surface)) - { - TRACE("missing xdg_toplevel, returning\n"); - wayland_win_data_release(data); - return; - } - if (!surface->requested.serial) { TRACE("requested configure event already handled, returning\n"); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11321
It turns out we can store the unscaled rect in physical coordinates in the surface config structure with no problems; I'll write a more concrete explanation about why tomorrow. Draft because some stuff related to the implementation needs to be worked on a bit (and I also want to sync the popup position with win32u) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11321#note_144897
participants (3)
-
Etaash Mathamsetty -
Etaash Mathamsetty (@etaash.mathamsetty) -
Rémi Bernon