From: Alexandros Frantzis alexandros.frantzis@collabora.com
Windows supports changing between the WS_CHILD and WS_POPUP window styles which requires a Wayland surface role change between wl_subsurface and xdg_toplevel.
Since Wayland doesn't support changing a surface's role, to support this scenario we recreate the Wayland surface and depend on win32u updating children surfaces so that they can use the new parent surface. --- dlls/winewayland.drv/wayland_surface.c | 31 ++++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 2 ++ dlls/winewayland.drv/window.c | 29 +++++++++++++++++++----- 3 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 2e5059d0980..6b582ada765 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -1005,6 +1005,37 @@ err: return NULL; }
+/********************************************************************** + * wayland_surface_attach_client + */ +void wayland_surface_attach_client(struct wayland_surface *surface, + struct wayland_client_surface *client) +{ + assert(!surface->client && client); + + if (client->wl_subsurface) wl_subsurface_destroy(client->wl_subsurface); + + /* Create a new subsurface that it is attached to the proper parent. */ + client->wl_subsurface = + wl_subcompositor_get_subsurface(process_wayland.wl_subcompositor, + client->wl_surface, + surface->wl_surface); + if (!client->wl_subsurface) + { + ERR("Failed to create client wl_subsurface\n"); + return; + } + /* Present contents independently of the parent surface. */ + wl_subsurface_set_desync(client->wl_subsurface); + + InterlockedIncrement(&client->ref); + surface->client = client; + + wayland_surface_reconfigure_client(surface); + /* Commit to apply subsurface positioning. */ + wl_surface_commit(surface->wl_surface); +} + static void dummy_buffer_release(void *data, struct wl_buffer *buffer) { struct wayland_shm_buffer *shm_buffer = data; diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index d553f06a420..36e8c550b23 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -273,6 +273,8 @@ void wayland_surface_coords_to_window(struct wayland_surface *surface, int *window_x, int *window_y); struct wayland_client_surface *wayland_surface_get_client(struct wayland_surface *surface); BOOL wayland_client_surface_release(struct wayland_client_surface *client); +void wayland_surface_attach_client(struct wayland_surface *surface, + struct wayland_client_surface *client); void wayland_surface_ensure_contents(struct wayland_surface *surface); void wayland_surface_set_title(struct wayland_surface *surface, LPCWSTR title);
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 8453d73488c..ddac117ee7d 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -210,11 +210,14 @@ static void reapply_cursor_clipping(void) NtUserSetThreadDpiAwarenessContext(context); }
+static void wayland_win_data_update_wayland_state(struct wayland_win_data *data); + static void wayland_win_data_update_wayland_surface(struct wayland_win_data *data) { struct wayland_surface *surface = data->wayland_surface; struct wayland_win_data *parent_data; enum wayland_surface_role role = WAYLAND_SURFACE_ROLE_NONE; + struct wayland_client_surface *client = NULL; WCHAR text[1024];
TRACE("hwnd=%p\n", data->hwnd); @@ -229,19 +232,31 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat }
/* We can temporarily remove a role from a wayland surface and add it back, - * but we can't change a surface's role. - * TODO: Recreate the surface to allow role change. */ - if (surface && role && surface->role && role != surface->role) goto out; + * but we can't change a surface's role. */ + if (surface && role && surface->role && role != surface->role) + { + if (data->window_surface) + wayland_window_surface_update_wayland_surface(data->window_surface, NULL, NULL); + pthread_mutex_lock(&surface->mutex); + if (surface->client) client = wayland_surface_get_client(surface); + pthread_mutex_unlock(&surface->mutex); + wayland_surface_destroy(surface); + surface = NULL; + }
/* Ensure that we have a wayland surface. */ - if (!surface && !(surface = wayland_surface_create(data->hwnd))) goto out; + if (!surface) + { + surface = wayland_surface_create(data->hwnd); + if (!surface) goto out; + }
pthread_mutex_lock(&surface->mutex);
if ((role == WAYLAND_SURFACE_ROLE_TOPLEVEL) != !!(surface->xdg_toplevel) || (role == WAYLAND_SURFACE_ROLE_SUBSURFACE) != !!(surface->wl_subsurface) || - (role == WAYLAND_SURFACE_ROLE_SUBSURFACE && - surface->parent_weak_ref && surface->parent_weak_ref->hwnd != parent_data->hwnd)) + (role == WAYLAND_SURFACE_ROLE_SUBSURFACE && surface->parent_weak_ref && + surface->parent_weak_ref != parent_data->wayland_surface)) { /* If we have a pre-existing surface ensure it has no role. */ if (data->wayland_surface) wayland_surface_clear_role(surface); @@ -266,6 +281,7 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat }
wayland_win_data_get_config(data, &surface->window); + if (client) wayland_surface_attach_client(surface, client);
pthread_mutex_unlock(&surface->mutex);
@@ -280,6 +296,7 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat out: TRACE("hwnd=%p surface=%p=>%p\n", data->hwnd, data->wayland_surface, surface); data->wayland_surface = surface; + if (client) wayland_client_surface_release(client); }
static void wayland_win_data_update_wayland_state(struct wayland_win_data *data)