From: Namo Nath <nn.git@tuta.io> Win32 overlays and games often run in isolated processes. To maintain correct Z-order and stacking under Wayland, the compositor must be explicitly informed of these parent-child relationships. Implement zxdg_foreign_unstable_v2 to export and import xdg_toplevel surfaces across process boundaries: - Primary windows export handles converted to 16-bit Win32 Global Atoms, which are attached as window properties. - Owned processes resolve the Atom and import the surface to establish the compositor-level parent relationship. --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/wayland.c | 8 + dlls/winewayland.drv/wayland_surface.c | 116 ++++++++++ dlls/winewayland.drv/waylanddrv.h | 7 + dlls/winewayland.drv/window_surface.c | 11 +- .../xdg-foreign-unstable-v2.xml | 200 ++++++++++++++++++ 6 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 dlls/winewayland.drv/xdg-foreign-unstable-v2.xml diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 1f1b2ccf8af..de4bbdc031c 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -27,6 +27,7 @@ SOURCES = \ window.c \ window_surface.c \ wlr-data-control-unstable-v1.xml \ + xdg-foreign-unstable-v2.xml \ xdg-output-unstable-v1.xml \ xdg-shell.xml \ xdg-toplevel-icon-v1.xml diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index b164210adf0..32ccfd6d0ea 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -200,6 +200,14 @@ static void registry_handle_global(void *data, struct wl_registry *registry, wl_registry_bind(registry, id, &wp_cursor_shape_manager_v1_interface, version < 2 ? version : 2); } + else if (strcmp(interface, "zxdg_exporter_v2") == 0) + { + process_wayland.zxdg_exporter_v2 = wl_registry_bind(registry, id, &zxdg_exporter_v2_interface, 1); + } + else if (strcmp(interface, "zxdg_importer_v2") == 0) + { + process_wayland.zxdg_importer_v2 = wl_registry_bind(registry, id, &zxdg_importer_v2_interface, 1); + } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 2f8275ddb52..3ba630904cf 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -212,6 +212,29 @@ void wayland_surface_destroy(struct wayland_surface *surface) wayland_surface_clear_role(surface); + /* Tear down cross-process bonds only on absolute surface destruction. + * This ensures the zxdg_imported_v2 bond survives transient SW_HIDE toggles. */ + if (surface->zxdg_exported_v2) + { + WCHAR prop_name[] = {'W','a','y','l','a','n','d','H','a','n','d','l','e',0}; + HANDLE prop = NtUserRemoveProp(surface->hwnd, prop_name); + if (prop) + { + RTL_ATOM atom = (RTL_ATOM)(ULONG_PTR)prop; + NtDeleteAtom(atom); + TRACE("Deleted exported atom %x for hwnd %p\n", atom, surface->hwnd); + } + + zxdg_exported_v2_destroy(surface->zxdg_exported_v2); + surface->zxdg_exported_v2 = NULL; + } + + if (surface->zxdg_imported_v2) + { + zxdg_imported_v2_destroy(surface->zxdg_imported_v2); + surface->zxdg_imported_v2 = NULL; + } + if (surface->wp_viewport) { wp_viewport_destroy(surface->wp_viewport); @@ -241,6 +264,60 @@ void wayland_surface_destroy(struct wayland_surface *surface) free(surface); } +/* Helper to store Wayland string handle as a global 16-bit Atom */ +static RTL_ATOM add_global_atom(const char *str) +{ + RTL_ATOM atom = 0; + size_t len = strlen(str); + WCHAR *wstr = malloc((len + 1) * sizeof(WCHAR)); + if (!wstr) return 0; + for (size_t i = 0; i < len; i++) wstr[i] = str[i]; + wstr[len] = 0; + NtAddAtom(wstr, len * sizeof(WCHAR), &atom); + free(wstr); + return atom; +} + +/* Helper to retrieve the Wayland string handle from the Atom Table */ +char *get_global_atom_name(RTL_ATOM atom) +{ + ATOM_BASIC_INFORMATION *info; + ULONG size = 1024; + char *str = NULL; + int i, len; + + if (!(info = malloc(size))) return NULL; + if (!NtQueryInformationAtom(atom, AtomBasicInformation, info, size, NULL)) + { + len = info->NameLength / sizeof(WCHAR); + if ((str = malloc(len + 1))) + { + for (i = 0; i < len; i++) str[i] = (char)info->Name[i]; + str[len] = 0; + } + } + free(info); + return str; +} + +/* Callback triggered by compositor when surface export is successful */ +static void zxdg_exported_v2_handle(void *data, struct zxdg_exported_v2 *exported, const char *handle) +{ + struct wayland_surface *surface = data; + RTL_ATOM atom = add_global_atom(handle); + if (atom) + { + /* Attach the 16-bit Atom to the Game's HWND as a window property */ + WCHAR prop_name[] = {'W','a','y','l','a','n','d','H','a','n','d','l','e',0}; + NtUserSetProp(surface->hwnd, prop_name, (HANDLE)(ULONG_PTR)atom); + TRACE("Exported surface %p (hwnd %p) with handle %s (atom %x)\n", surface, surface->hwnd, handle, atom); + } +} + +static const struct zxdg_exported_v2_listener zxdg_exported_v2_listener = { + zxdg_exported_v2_handle +}; + /********************************************************************** * wayland_surface_make_toplevel * @@ -267,6 +344,34 @@ void wayland_surface_make_toplevel(struct wayland_surface *surface) if (!surface->xdg_toplevel) goto err; xdg_toplevel_add_listener(surface->xdg_toplevel, &xdg_toplevel_listener, surface->hwnd); + /* CROSS-PROCESS HIERARCHY: xdg_foreign Import / Export Routing */ + owner_hwnd = NtUserGetWindowRelative(surface->hwnd, GW_OWNER); + + /* Restore global import/export for all standard owned windows and overlays */ + if (owner_hwnd && process_wayland.zxdg_importer_v2) + { + HANDLE prop = NtUserGetProp(owner_hwnd, prop_name); + if (prop) + { + RTL_ATOM atom = (RTL_ATOM)(ULONG_PTR)prop; + char *handle_str = get_global_atom_name(atom); + if (handle_str) + { + TRACE("Importing surface %p (hwnd %p) to parent %p via handle %s\n", surface, surface->hwnd, owner_hwnd, handle_str); + surface->zxdg_imported_v2 = zxdg_importer_v2_import_toplevel( + process_wayland.zxdg_importer_v2, handle_str); + zxdg_imported_v2_set_parent_of(surface->zxdg_imported_v2, surface->wl_surface); + free(handle_str); + } + } + } + else if (!owner_hwnd && process_wayland.zxdg_exporter_v2) + { + surface->zxdg_exported_v2 = zxdg_exporter_v2_export_toplevel( + process_wayland.zxdg_exporter_v2, surface->wl_surface); + zxdg_exported_v2_add_listener(surface->zxdg_exported_v2, &zxdg_exported_v2_listener, surface); + } + if (process_name) xdg_toplevel_set_app_id(surface->xdg_toplevel, process_name); @@ -344,6 +449,17 @@ void wayland_surface_clear_role(struct wayland_surface *surface) break; case WAYLAND_SURFACE_ROLE_TOPLEVEL: + if (surface->zxdg_exported_v2) + { + zxdg_exported_v2_destroy(surface->zxdg_exported_v2); + surface->zxdg_exported_v2 = NULL; + } + if (surface->zxdg_imported_v2) + { + zxdg_imported_v2_destroy(surface->zxdg_imported_v2); + surface->zxdg_imported_v2 = NULL; + } + if (surface->xdg_toplevel_icon) { xdg_toplevel_icon_manager_v1_set_icon( diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index e064d8cba6a..a64a52274bd 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -38,6 +38,7 @@ #include "xdg-shell-client-protocol.h" #include "wlr-data-control-unstable-v1-client-protocol.h" #include "xdg-toplevel-icon-v1-client-protocol.h" +#include "xdg-foreign-unstable-v2-client-protocol.h" #include "windef.h" #include "winbase.h" @@ -193,6 +194,8 @@ struct wayland struct wl_data_device_manager *wl_data_device_manager; struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1; struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager_v1; + struct zxdg_exporter_v2 *zxdg_exporter_v2; + struct zxdg_importer_v2 *zxdg_importer_v2; struct wayland_seat seat; struct wayland_keyboard keyboard; struct wayland_pointer pointer; @@ -291,6 +294,8 @@ struct wayland_surface struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel_icon_v1 *xdg_toplevel_icon; + struct zxdg_exported_v2 *zxdg_exported_v2; + struct zxdg_imported_v2 *zxdg_imported_v2; }; struct { @@ -355,6 +360,8 @@ static inline BOOL wayland_surface_is_toplevel(struct wayland_surface *surface) return surface->role == WAYLAND_SURFACE_ROLE_TOPLEVEL && surface->xdg_toplevel; } +char *get_global_atom_name(RTL_ATOM atom); + /********************************************************************** * Wayland SHM buffer */ diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index 720d59b60fa..9fe0e59c3c1 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -456,6 +456,7 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, BOOL transparent = (ex_style & WS_EX_TRANSPARENT) != 0; BOOL layered_transparent = (ex_style & WS_EX_LAYERED) && (ex_style & WS_EX_TRANSPARENT); BOOL layered_alpha = FALSE; + BOOL zxdg_imported = FALSE; BOOL dwm_active = FALSE; BOOL has_color_key = FALSE; BOOL has_client = FALSE; @@ -469,6 +470,14 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, margins = data->margins; dwm_active = (dwm_mode != WAYLAND_DWM_EXTEND_NONE); has_client = data->client_surface != NULL; + + /* Detect cross-process parent bond. + * Once established, we MUST use ARGB buffers to ensure transparency. */ + if (data->wayland_surface && data->wayland_surface->zxdg_imported_v2) + { + zxdg_imported = TRUE; + } + TRACE("surface_flush dwm_mode: %d\n", dwm_mode); wayland_win_data_release(data); } @@ -490,7 +499,7 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, /* DWM explicitly requires alpha. Layered requires alpha only if specific * transparency attributes or click-through (Ghost) flags are set. */ - needs_alpha = dwm_active || (layered && (has_color_key || layered_alpha || transparent)) || shape_bits != NULL; + needs_alpha = dwm_active || zxdg_imported || (layered && (has_color_key || layered_alpha || transparent)) || shape_bits != NULL; force_opaque = !needs_alpha; buffer_format = needs_alpha ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888; diff --git a/dlls/winewayland.drv/xdg-foreign-unstable-v2.xml b/dlls/winewayland.drv/xdg-foreign-unstable-v2.xml new file mode 100644 index 00000000000..cc3271dca4d --- /dev/null +++ b/dlls/winewayland.drv/xdg-foreign-unstable-v2.xml @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="xdg_foreign_unstable_v2"> + + <copyright> + Copyright © 2015-2016 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <description summary="Protocol for exporting xdg surface handles"> + This protocol specifies a way for making it possible to reference a surface + of a different client. With such a reference, a client can, by using the + interfaces provided by this protocol, manipulate the relationship between + its own surfaces and the surface of some other client. For example, stack + some of its own surface above the other clients surface. + + In order for a client A to get a reference of a surface of client B, client + B must first export its surface using xdg_exporter.export_toplevel. Upon + doing this, client B will receive a handle (a unique string) that it may + share with client A in some way (for example D-Bus). After client A has + received the handle from client B, it may use xdg_importer.import_toplevel + to create a reference to the surface client B just exported. See the + corresponding requests for details. + + A possible use case for this is out-of-process dialogs. For example when a + sandboxed client without file system access needs the user to select a file + on the file system, given sandbox environment support, it can export its + surface, passing the exported surface handle to an unsandboxed process that + can show a file browser dialog and stack it above the sandboxed client's + surface. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + </description> + + <interface name="zxdg_exporter_v2" version="1"> + <description summary="interface for exporting surfaces"> + A global interface used for exporting surfaces that can later be imported + using xdg_importer. + </description> + + <request name="destroy" type="destructor"> + <description summary="destroy the xdg_exporter object"> + Notify the compositor that the xdg_exporter object will no longer be + used. + </description> + </request> + + <enum name="error"> + <description summary="error values"> + These errors can be emitted in response to invalid xdg_exporter + requests. + </description> + <entry name="invalid_surface" value="0" summary="surface is not an xdg_toplevel"/> + </enum> + + <request name="export_toplevel"> + <description summary="export a toplevel surface"> + The export_toplevel request exports the passed surface so that it can later be + imported via xdg_importer. When called, a new xdg_exported object will + be created and xdg_exported.handle will be sent immediately. See the + corresponding interface and event for details. + + A surface may be exported multiple times, and each exported handle may + be used to create an xdg_imported multiple times. Only xdg_toplevel + equivalent surfaces may be exported, otherwise an invalid_surface + protocol error is sent. + </description> + <arg name="id" type="new_id" interface="zxdg_exported_v2" + summary="the new xdg_exported object"/> + <arg name="surface" type="object" interface="wl_surface" + summary="the surface to export"/> + </request> + </interface> + + <interface name="zxdg_importer_v2" version="1"> + <description summary="interface for importing surfaces"> + A global interface used for importing surfaces exported by xdg_exporter. + With this interface, a client can create a reference to a surface of + another client. + </description> + + <request name="destroy" type="destructor"> + <description summary="destroy the xdg_importer object"> + Notify the compositor that the xdg_importer object will no longer be + used. + </description> + </request> + + <request name="import_toplevel"> + <description summary="import a toplevel surface"> + The import_toplevel request imports a surface from any client given a handle + retrieved by exporting said surface using xdg_exporter.export_toplevel. + When called, a new xdg_imported object will be created. This new object + represents the imported surface, and the importing client can + manipulate its relationship using it. See xdg_imported for details. + </description> + <arg name="id" type="new_id" interface="zxdg_imported_v2" + summary="the new xdg_imported object"/> + <arg name="handle" type="string" + summary="the exported surface handle"/> + </request> + </interface> + + <interface name="zxdg_exported_v2" version="1"> + <description summary="an exported surface handle"> + An xdg_exported object represents an exported reference to a surface. The + exported surface may be referenced as long as the xdg_exported object not + destroyed. Destroying the xdg_exported invalidates any relationship the + importer may have established using xdg_imported. + </description> + + <request name="destroy" type="destructor"> + <description summary="unexport the exported surface"> + Revoke the previously exported surface. This invalidates any + relationship the importer may have set up using the xdg_imported created + given the handle sent via xdg_exported.handle. + </description> + </request> + + <event name="handle"> + <description summary="the exported surface handle"> + The handle event contains the unique handle of this exported surface + reference. It may be shared with any client, which then can use it to + import the surface by calling xdg_importer.import_toplevel. A handle + may be used to import the surface multiple times. + </description> + <arg name="handle" type="string" summary="the exported surface handle"/> + </event> + </interface> + + <interface name="zxdg_imported_v2" version="1"> + <description summary="an imported surface handle"> + An xdg_imported object represents an imported reference to surface exported + by some client. A client can use this interface to manipulate + relationships between its own surfaces and the imported surface. + </description> + + <enum name="error"> + <description summary="error values"> + These errors can be emitted in response to invalid xdg_imported + requests. + </description> + <entry name="invalid_surface" value="0" summary="surface is not an xdg_toplevel"/> + </enum> + + <request name="destroy" type="destructor"> + <description summary="destroy the xdg_imported object"> + Notify the compositor that it will no longer use the xdg_imported + object. Any relationship that may have been set up will at this point + be invalidated. + </description> + </request> + + <request name="set_parent_of"> + <description summary="set as the parent of some surface"> + Set the imported surface as the parent of some surface of the client. + The passed surface must be an xdg_toplevel equivalent, otherwise an + invalid_surface protocol error is sent. Calling this function sets up + a surface to surface relation with the same stacking and positioning + semantics as xdg_toplevel.set_parent. + </description> + <arg name="surface" type="object" interface="wl_surface" + summary="the child surface"/> + </request> + + <event name="destroyed"> + <description summary="the imported surface handle has been destroyed"> + The imported surface handle has been destroyed and any relationship set + up has been invalidated. This may happen for various reasons, for + example if the exported surface or the exported surface handle has been + destroyed, if the handle used for importing was invalid. + </description> + </event> + </interface> + +</protocol> -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10180