From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winewayland.drv/wayland_surface.c | 90 ++++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 8 +++ dlls/winewayland.drv/window.c | 37 +++++++++-- 3 files changed, 131 insertions(+), 4 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 136155ce859..9fdb75a3f06 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -266,6 +266,47 @@ err: ERR("Failed to assign toplevel role to wayland surface\n"); }
+/********************************************************************** + * wayland_surface_make_subsurface + * + * Gives the subsurface role to a plain wayland surface. + */ +void wayland_surface_make_subsurface(struct wayland_surface *surface, + struct wayland_surface *parent) +{ + assert(!surface->role || surface->role == WAYLAND_SURFACE_ROLE_SUBSURFACE); + if (surface->wl_subsurface && surface->toplevel_hwnd == parent->hwnd) return; + + wayland_surface_clear_role(surface); + surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; + + TRACE("surface=%p parent=%p\n", surface, parent); + + surface->wl_subsurface = + wl_subcompositor_get_subsurface(process_wayland.wl_subcompositor, + surface->wl_surface, + parent->wl_surface); + if (!surface->wl_subsurface) + { + ERR("Failed to create client wl_subsurface\n"); + goto err; + } + + surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; + surface->toplevel_hwnd = parent->hwnd; + + /* Present contents independently of the parent surface. */ + wl_subsurface_set_desync(surface->wl_subsurface); + + wl_display_flush(process_wayland.wl_display); + + return; + +err: + wayland_surface_clear_role(surface); + ERR("Failed to assign subsurface role to wayland surface\n"); +} + /********************************************************************** * wayland_surface_clear_role * @@ -295,12 +336,23 @@ void wayland_surface_clear_role(struct wayland_surface *surface) surface->xdg_surface = NULL; } break; + + case WAYLAND_SURFACE_ROLE_SUBSURFACE: + if (surface->wl_subsurface) + { + wl_subsurface_destroy(surface->wl_subsurface); + surface->wl_subsurface = NULL; + } + + surface->toplevel_hwnd = 0; + break; }
memset(&surface->pending, 0, sizeof(surface->pending)); memset(&surface->requested, 0, sizeof(surface->requested)); memset(&surface->processing, 0, sizeof(surface->processing)); memset(&surface->current, 0, sizeof(surface->current)); + surface->toplevel_hwnd = 0;
/* Ensure no buffer is attached, otherwise future role assignments may fail. */ wl_surface_attach(surface->wl_surface, NULL, 0, 0); @@ -585,6 +637,40 @@ 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 *toplevel_data; + struct wayland_surface *toplevel_surface; + int local_x, local_y, x, y; + + if (surface->processing.serial && surface->processing.processed && + (toplevel_data = wayland_win_data_get_nolock(surface->toplevel_hwnd)) && + (toplevel_surface = toplevel_data->wayland_surface)) + { + local_x = surface->window.rect.left - toplevel_surface->window.rect.left; + local_y = surface->window.rect.top - toplevel_surface->window.rect.top; + + wayland_surface_coords_from_window(surface, local_x, local_y, &x, &y); + + TRACE("hwnd=%p pos=%d,%d\n", surface->hwnd, x, y); + + wl_subsurface_set_position(surface->wl_subsurface, x, y); + if (toplevel_data->client_surface) + wl_subsurface_place_above(surface->wl_subsurface, toplevel_data->client_surface->wl_surface); + else + wl_subsurface_place_above(surface->wl_subsurface, toplevel_surface->wl_surface); + wl_surface_commit(toplevel_surface->wl_surface); + + memset(&surface->processing, 0, sizeof(surface->processing)); + } +} + /********************************************************************** * wayland_surface_reconfigure * @@ -616,6 +702,10 @@ BOOL wayland_surface_reconfigure(struct wayland_surface *surface) if (!surface->xdg_surface) break; /* surface role has been cleared */ if (!wayland_surface_reconfigure_xdg(surface, width, height)) 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, width, height); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 5234ad0e49b..ef744b715fc 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -77,6 +77,7 @@ enum wayland_surface_role { WAYLAND_SURFACE_ROLE_NONE, WAYLAND_SURFACE_ROLE_TOPLEVEL, + WAYLAND_SURFACE_ROLE_SUBSURFACE, };
struct wayland_keyboard @@ -212,6 +213,11 @@ struct wayland_surface struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; }; + struct + { + struct wl_subsurface *wl_subsurface; + HWND toplevel_hwnd; + }; };
struct wayland_surface_config pending, requested, processing, current; @@ -254,6 +260,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_clear_role(struct wayland_surface *surface); void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index b95ab7771e9..15ba5cfe9fd 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -55,6 +55,15 @@ static BOOL set_window_pos(HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, return ret; }
+/* per-monitor DPI aware NtUserWindowFromPoint call */ +static HWND window_from_point(INT x, INT y) +{ + UINT context = NtUserSetThreadDpiAwarenessContext(NTUSER_DPI_PER_MONITOR_AWARE_V2); + HWND ret = NtUserWindowFromPoint(x, y); + NtUserSetThreadDpiAwarenessContext(context); + return ret; +} +
static int wayland_win_data_cmp_rb(const void *key, const struct rb_entry *entry) @@ -206,7 +215,7 @@ static void reapply_cursor_clipping(void) NtUserSetThreadDpiAwarenessContext(context); }
-static BOOL wayland_win_data_update_wayland_surface(struct wayland_win_data *data) +static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *data, struct wayland_surface *toplevel_surface) { struct wayland_client_surface *client = data->client_surface; struct wayland_surface *surface; @@ -217,6 +226,7 @@ static BOOL wayland_win_data_update_wayland_surface(struct wayland_win_data *dat
visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE; if (!visible) role = WAYLAND_SURFACE_ROLE_NONE; + else if (toplevel_surface) role = WAYLAND_SURFACE_ROLE_SUBSURFACE; 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 */ @@ -240,6 +250,9 @@ static BOOL wayland_win_data_update_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, toplevel_surface); + break; }
if (visible && client) wayland_client_surface_attach(client, data->hwnd); @@ -310,6 +323,13 @@ static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) 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 parent=%p\n", surface->hwnd, surface->toplevel_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); @@ -446,8 +466,9 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, BOOL const struct window_rects *new_rects, struct window_surface *surface) { HWND toplevel = NtUserGetAncestor(hwnd, GA_ROOT); + struct wayland_surface *toplevel_surface; struct wayland_client_surface *client; - struct wayland_win_data *data; + struct wayland_win_data *data, *toplevel_data; BOOL managed;
TRACE("hwnd %p new_rects %s after %p flags %08x\n", hwnd, debugstr_window_rects(new_rects), insert_after, swp_flags); @@ -455,9 +476,17 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, BOOL /* Get the managed state with win_data unlocked, as is_window_managed * may need to query win_data information about other HWNDs and thus * acquire the lock itself internally. */ - managed = is_window_managed(hwnd, swp_flags, &new_rects->window); + if (!(managed = is_window_managed(hwnd, swp_flags, &new_rects->window)) && surface) + { + toplevel = NtUserGetWindowRelative(hwnd, GW_OWNER); + /* fallback to any window that is right below our top left corner */ + if (!toplevel) toplevel = window_from_point(new_rects->window.left - 1, new_rects->window.top - 1); + if (toplevel) toplevel = NtUserGetAncestor(toplevel, GA_ROOT); + }
if (!(data = wayland_win_data_get(hwnd))) return; + toplevel_data = toplevel && toplevel != hwnd ? wayland_win_data_get_nolock(toplevel) : NULL; + toplevel_surface = toplevel_data ? toplevel_data->wayland_surface : NULL;
data->rects = *new_rects; data->is_fullscreen = fullscreen; @@ -479,7 +508,7 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, BOOL data->wayland_surface = NULL; } } - else if (wayland_win_data_update_wayland_surface(data)) + else if (wayland_win_data_create_wayland_surface(data, toplevel_surface)) { wayland_win_data_update_wayland_state(data); }