[PATCH v2 0/2] MR11248: winewayland: Use xdg-popup instead of subsurfaces for owned or equivalent windows.
Same as https://gitlab.winehq.org/wine/wine/-/merge_requests/11178 with changes to allow resizing and sharing the logic between toplevel and popups. -- v2: winewayland: Implement xdg-popup for unmanaged windows. winewayland: Don't request any surface geometry x,y. https://gitlab.winehq.org/wine/wine/-/merge_requests/11248
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 0dae0922179..015777ee9ea 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -640,19 +640,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/11248
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 | 14 +- dlls/winewayland.drv/window.c | 53 ++--- 4 files changed, 199 insertions(+), 137 deletions(-) diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 5b2b0c5b25b..7f56e1cfd20 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 015777ee9ea..e094642c878 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); @@ -87,6 +99,7 @@ static void xdg_toplevel_handle_configure(void *private, HWND hwnd = private; uint32_t *state; enum wayland_surface_config_state config_state = 0; + RECT rect = { 0, 0, width, height }; struct wayland_win_data *data; wl_array_for_each(state, states) @@ -113,13 +126,13 @@ static void xdg_toplevel_handle_configure(void *private, } } - 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; } @@ -137,6 +150,64 @@ 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) +{ + HWND hwnd = private; + struct wayland_win_data *data; + struct wayland_surface *surface; + RECT 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)) + { + surface->pending.rect = rect; + surface->pending.state = 0; + } + + 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) @@ -162,16 +233,10 @@ void wp_fractional_scale_handle_scale(void* user_data, if ((client = data->client_surface)) wayland_client_surface_attach(client, client->toplevel); - /* 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, 0); + update_window_state(hwnd); } static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = @@ -322,6 +387,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 * @@ -370,46 +457,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); + OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + rect = map_rect_to_surface(surface, rect); - 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; + if (surface->current.rect.left == rect.left && surface->current.rect.top == rect.top) return; + + TRACE("surface=%p owner=%p rect=%s, repositioning\n", surface, owner, wine_dbgstr_rect(&rect)); + + if (!(xdg_positioner = xdg_positioner_create(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); + + if (!(xdg_positioner = xdg_positioner_create(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); + wayland_surface_init_fractional_scale(surface, owner->window.scale); - surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; surface->owner_hwnd = owner->hwnd; - /* Present contents independently of the owner surface. */ - wl_subsurface_set_desync(surface->wl_subsurface); - + 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"); } /********************************************************************** @@ -451,25 +558,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)); @@ -646,8 +753,12 @@ static void wayland_surface_reconfigure_geometry(struct wayland_surface *surface if (!IsRectEmpty(&rect)) { int width = rect.right - rect.left, height = rect.bottom - rect.top; - xdg_surface_set_window_geometry(surface->xdg_surface, 0, 0, width, height); + } + + if (wayland_surface_is_toplevel(surface)) + { + int width = rect.right - rect.left, height = rect.bottom - rect.top; if (surface->window.resizeable) { @@ -751,39 +862,6 @@ static BOOL wayland_surface_reconfigure_xdg(struct wayland_surface *surface, REC 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 && - (owner_data = wayland_win_data_get_nolock(surface->owner_hwnd)) && - (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_surface_reconfigure * @@ -793,10 +871,12 @@ 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); + struct wayland_win_data *owner_data; + struct wayland_surface *owner; + RECT rect = 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(&window->rect), window->state, wine_dbgstr_rect(&surface->processing.rect), surface->processing.state, wine_dbgstr_rect(&surface->current.rect), surface->current.state); @@ -804,14 +884,15 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) { case WAYLAND_SURFACE_ROLE_NONE: break; + case WAYLAND_SURFACE_ROLE_POPUP: + if (!(owner_data = wayland_win_data_get_nolock(surface->owner_hwnd))) return FALSE; + if ((owner = owner_data->wayland_surface)) OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + /* fallthrough */ case WAYLAND_SURFACE_ROLE_TOPLEVEL: + rect = map_rect_to_surface(surface, rect); if (!surface->xdg_surface) break; /* surface role has been cleared */ if (!wayland_surface_reconfigure_xdg(surface, rect)) 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, rect.right - rect.left, rect.bottom - rect.top); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index e0df8e56bb5..fe8494fa03b 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 @@ -280,17 +280,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; }; }; @@ -326,6 +327,8 @@ 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 *owner); void wayland_surface_clear_role(struct wayland_surface *surface); void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, @@ -349,6 +352,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 afef743755b..d3a722390ea 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -203,7 +203,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 */ @@ -224,6 +224,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. */ @@ -235,13 +237,12 @@ 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; } if (visible && client) wayland_client_surface_attach(client, data->hwnd); - wayland_win_data_get_config(data, &surface->window); /* Size/position changes affect the effective pointer constraint, so update * it as needed. */ @@ -252,8 +253,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; @@ -264,7 +266,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. */ @@ -296,32 +302,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); } @@ -462,6 +442,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_nolock(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; @@ -512,13 +494,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/11248
v2: Move the surface repositioning to `wayland_surface_make_popup` like it was before, remove it from `wayland_surface_reconfigure_geometry`. I'm not sure whether we need to worry about the positioner size vs last committed surface size. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144185
On Thu Jun 25 06:03:13 2026 +0000, Rémi Bernon wrote:
We don't and shouldn't create any surface for child windows. Their `struct window_surface *surface` WindowPosChanged parameter will be NULL and this should trigger the wayland surface destruction if the window was a toplevel and became a children. Then child windows should be roleless to avoid unnessessary configure events.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144188
Is there anything that needs to match between `xdg_surface_set_window_geometry` and `xdg_positioner_set_size` / `xdg_popup_reposition`? What is the positioner size used for exactly?
From my understanding of the protocol, the positioner size is only used to position the popup. The client still retains full control over the xdg_surface size. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144189
On Thu Jun 25 06:41:40 2026 +0000, Etaash Mathamsetty wrote:
Then child windows should be roleless to avoid unnessessary configure events. There is no wayland surface for child windows, there is no role to set.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144190
Etaash Mathamsetty (@etaash.mathamsetty) commented about dlls/winewayland.drv/wayland_surface.c:
- TRACE("surface=%p owner=%p\n", surface, owner); + OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + rect = map_rect_to_surface(surface, rect);
- 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; + if (surface->current.rect.left == rect.left && surface->current.rect.top == rect.top) return; +
to avoid any feedback loop from NtUserSetRawWindowPos I think we should check for if a config is being processed. `if (surface->processing.serial && !surface->processing.processed) return;` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144191
On Thu Jun 25 07:02:21 2026 +0000, Rémi Bernon wrote:
There is no wayland surface for child windows, there is no role to set. I understand it now, thanks
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144192
Etaash Mathamsetty (@etaash.mathamsetty) commented about dlls/winewayland.drv/wayland_surface.c:
{ case WAYLAND_SURFACE_ROLE_NONE: break; + case WAYLAND_SURFACE_ROLE_POPUP: + if (!(owner_data = wayland_win_data_get_nolock(surface->owner_hwnd))) return FALSE; + if ((owner = owner_data->wayland_surface)) OffsetRect(&rect, -owner->window.rect.left, -owner->window.rect.top); + /* fallthrough */ case WAYLAND_SURFACE_ROLE_TOPLEVEL: + rect = map_rect_to_surface(surface, rect);
I dont think we need these changes anymore -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11248#note_144193
participants (3)
-
Etaash Mathamsetty -
Etaash Mathamsetty (@etaash.mathamsetty) -
Rémi Bernon (@rbernon)