From 19764ffcc811a498a508fe524a986455d25cf7e1 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 15 Mar 2024 11:36:05 -0400 Subject: [PATCH] wayland: Add the xdg-foreign-unstable-v2 protocol Add the xdg-foreign-unstable-v2 protocol and use it to create export handles for toplevel windows, which will be used when an external component, such as the file chooser portal, requires it. --- include/SDL3/SDL_video.h | 57 ++--- src/video/wayland/SDL_waylandvideo.c | 8 + src/video/wayland/SDL_waylandvideo.h | 1 + src/video/wayland/SDL_waylandwindow.c | 39 +++- src/video/wayland/SDL_waylandwindow.h | 2 + wayland-protocols/xdg-foreign-unstable-v2.xml | 200 ++++++++++++++++++ 6 files changed, 279 insertions(+), 28 deletions(-) create mode 100644 wayland-protocols/xdg-foreign-unstable-v2.xml diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index fde2f56b5..eff00923d 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1111,6 +1111,8 @@ extern DECLSPEC SDL_Window *SDLCALL SDL_GetWindowParent(SDL_Window *window); * with the window * - `SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER`: the xdg_toplevel role * associated with the window + * - 'SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_POINTER': the export + * handle associated with the window * - `SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER`: the xdg_popup role * associated with the window * - `SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER`: the xdg_positioner @@ -1136,33 +1138,34 @@ extern DECLSPEC SDL_Window *SDLCALL SDL_GetWindowParent(SDL_Window *window); */ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window *window); -#define SDL_PROP_WINDOW_SHAPE_POINTER "SDL.window.shape" -#define SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER "SDL.window.android.window" -#define SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER "SDL.window.android.surface" -#define SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER "SDL.window.uikit.window" -#define SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER "SDL.window.uikit.metal_view_tag" -#define SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER "SDL.window.kmsdrm.dev_index" -#define SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER "SDL.window.kmsdrm.drm_fd" -#define SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER "SDL.window.kmsdrm.gbm_dev" -#define SDL_PROP_WINDOW_COCOA_WINDOW_POINTER "SDL.window.cocoa.window" -#define SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER "SDL.window.cocoa.metal_view_tag" -#define SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER "SDL.window.vivante.display" -#define SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER "SDL.window.vivante.window" -#define SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER "SDL.window.vivante.surface" -#define SDL_PROP_WINDOW_WINRT_WINDOW_POINTER "SDL.window.winrt.window" -#define SDL_PROP_WINDOW_WIN32_HWND_POINTER "SDL.window.win32.hwnd" -#define SDL_PROP_WINDOW_WIN32_HDC_POINTER "SDL.window.win32.hdc" -#define SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER "SDL.window.win32.instance" -#define SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER "SDL.window.wayland.display" -#define SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER "SDL.window.wayland.surface" -#define SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER "SDL.window.wayland.egl_window" -#define SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER "SDL.window.wayland.xdg_surface" -#define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER "SDL.window.wayland.xdg_toplevel" -#define SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER "SDL.window.wayland.xdg_popup" -#define SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER "SDL.window.wayland.xdg_positioner" -#define SDL_PROP_WINDOW_X11_DISPLAY_POINTER "SDL.window.x11.display" -#define SDL_PROP_WINDOW_X11_SCREEN_NUMBER "SDL.window.x11.screen" -#define SDL_PROP_WINDOW_X11_WINDOW_NUMBER "SDL.window.x11.window" +#define SDL_PROP_WINDOW_SHAPE_POINTER "SDL.window.shape" +#define SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER "SDL.window.android.window" +#define SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER "SDL.window.android.surface" +#define SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER "SDL.window.uikit.window" +#define SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER "SDL.window.uikit.metal_view_tag" +#define SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER "SDL.window.kmsdrm.dev_index" +#define SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER "SDL.window.kmsdrm.drm_fd" +#define SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER "SDL.window.kmsdrm.gbm_dev" +#define SDL_PROP_WINDOW_COCOA_WINDOW_POINTER "SDL.window.cocoa.window" +#define SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER "SDL.window.cocoa.metal_view_tag" +#define SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER "SDL.window.vivante.display" +#define SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER "SDL.window.vivante.window" +#define SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER "SDL.window.vivante.surface" +#define SDL_PROP_WINDOW_WINRT_WINDOW_POINTER "SDL.window.winrt.window" +#define SDL_PROP_WINDOW_WIN32_HWND_POINTER "SDL.window.win32.hwnd" +#define SDL_PROP_WINDOW_WIN32_HDC_POINTER "SDL.window.win32.hdc" +#define SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER "SDL.window.win32.instance" +#define SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER "SDL.window.wayland.display" +#define SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER "SDL.window.wayland.surface" +#define SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER "SDL.window.wayland.egl_window" +#define SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER "SDL.window.wayland.xdg_surface" +#define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER "SDL.window.wayland.xdg_toplevel" +#define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_POINTER "SDL.window.wayland.xdg_toplevel_export_handle" +#define SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER "SDL.window.wayland.xdg_popup" +#define SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER "SDL.window.wayland.xdg_positioner" +#define SDL_PROP_WINDOW_X11_DISPLAY_POINTER "SDL.window.x11.display" +#define SDL_PROP_WINDOW_X11_SCREEN_NUMBER "SDL.window.x11.screen" +#define SDL_PROP_WINDOW_X11_WINDOW_NUMBER "SDL.window.x11.window" /** * Get the window flags. diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 2054471eb..4b372efec 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -57,6 +57,7 @@ #include "viewporter-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" +#include "xdg-foreign-unstable-v2-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -1078,6 +1079,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint if (d->input) { Wayland_CreateCursorShapeDevice(d->input); } + } else if (SDL_strcmp(interface, "zxdg_exporter_v2") == 0) { + d->zxdg_exporter_v2 = wl_registry_bind(d->registry, id, &zxdg_exporter_v2_interface, 1); } else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) { d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1); kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d); @@ -1331,6 +1334,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) data->cursor_shape_manager = NULL; } + if (data->zxdg_exporter_v2) { + zxdg_exporter_v2_destroy(data->zxdg_exporter_v2); + data->zxdg_exporter_v2 = NULL; + } + if (data->kde_output_order) { Wayland_FlushOutputOrder(data); kde_output_order_v1_destroy(data->kde_output_order); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 874a33ae8..c6ee45788 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -79,6 +79,7 @@ struct SDL_VideoData struct wp_viewporter *viewporter; struct wp_fractional_scale_manager_v1 *fractional_scale_manager; struct zwp_input_timestamps_manager_v1 *input_timestamps_manager; + struct zxdg_exporter_v2 *zxdg_exporter_v2; struct kde_output_order_v1 *kde_output_order; struct xkb_context *xkb_context; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index e211a7b1f..dfc203ffe 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -38,6 +38,7 @@ #include "xdg-activation-v1-client-protocol.h" #include "viewporter-client-protocol.h" #include "fractional-scale-v1-client-protocol.h" +#include "xdg-foreign-unstable-v2-client-protocol.h" #ifdef HAVE_LIBDECOR_H #include @@ -1484,6 +1485,21 @@ static struct wl_callback_listener show_hide_sync_listener = { show_hide_sync_handler }; +static void exported_handle_handler(void *data, struct zxdg_exported_v2 *zxdg_exported_v2, const char *handle) +{ + SDL_WindowData *wind = (SDL_WindowData*)data; + SDL_PropertiesID props = SDL_GetWindowProperties(wind->sdlwindow); + + SDL_free(wind->export_handle); + wind->export_handle = SDL_strdup(handle); + + SDL_SetProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_POINTER, wind->export_handle); +} + +static struct zxdg_exported_v2_listener exported_v2_listener = { + exported_handle_handler +}; + void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *c = _this->driverdata; @@ -1549,6 +1565,11 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, data->app_id); libdecor_frame_map(data->shell_surface.libdecor.frame); + if (c->zxdg_exporter_v2) { + data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface); + zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data); + } + SDL_SetProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, libdecor_frame_get_xdg_surface(data->shell_surface.libdecor.frame)); SDL_SetProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, libdecor_frame_get_xdg_toplevel(data->shell_surface.libdecor.frame)); } @@ -1623,6 +1644,12 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) data->shell_surface.xdg.roleobj.toplevel = xdg_surface_get_toplevel(data->shell_surface.xdg.surface); xdg_toplevel_set_app_id(data->shell_surface.xdg.roleobj.toplevel, data->app_id); xdg_toplevel_add_listener(data->shell_surface.xdg.roleobj.toplevel, &toplevel_listener_xdg, data); + + if (c->zxdg_exporter_v2) { + data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface); + zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data); + } + SDL_SetProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, data->shell_surface.xdg.roleobj.toplevel); } } @@ -1699,7 +1726,6 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) Wayland_SetWindowResizable(_this, window, !!(window->flags & SDL_WINDOW_RESIZABLE)); Wayland_SetWindowBordered(_this, window, !(window->flags & SDL_WINDOW_BORDERLESS)); - /* We're finally done putting the window together, raise if possible */ if (c->activation_manager) { /* Note that we don't check for empty strings, as that is still @@ -1798,6 +1824,17 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) wl_surface_commit(wind->surface); } + /* Clean up the export handle. */ + if (wind->exported) { + zxdg_exported_v2_destroy(wind->exported); + wind->exported = NULL; + + SDL_free(wind->export_handle); + wind->export_handle = NULL; + + SDL_SetProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_POINTER, NULL); + } + #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) { if (wind->shell_surface.libdecor.frame) { diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index afd348c20..5900122b8 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -95,6 +95,7 @@ struct SDL_WindowData struct xdg_activation_token_v1 *activation_token; struct wp_viewport *viewport; struct wp_fractional_scale_v1 *fractional_scale; + struct zxdg_exported_v2 *exported; SDL_AtomicInt swap_interval_ready; @@ -104,6 +105,7 @@ struct SDL_WindowData SDL_Window *keyboard_focus; char *app_id; + char *export_handle; float windowed_scale_factor; struct diff --git a/wayland-protocols/xdg-foreign-unstable-v2.xml b/wayland-protocols/xdg-foreign-unstable-v2.xml new file mode 100644 index 000000000..cc3271dca --- /dev/null +++ b/wayland-protocols/xdg-foreign-unstable-v2.xml @@ -0,0 +1,200 @@ + + + + + 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. + + + + 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. + + + + + A global interface used for exporting surfaces that can later be imported + using xdg_importer. + + + + + Notify the compositor that the xdg_exporter object will no longer be + used. + + + + + + These errors can be emitted in response to invalid xdg_exporter + requests. + + + + + + + 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. + + + + + + + + + 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. + + + + + Notify the compositor that the xdg_importer object will no longer be + used. + + + + + + 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. + + + + + + + + + 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. + + + + + 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. + + + + + + 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. + + + + + + + + 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. + + + + + These errors can be emitted in response to invalid xdg_imported + requests. + + + + + + + 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. + + + + + + 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. + + + + + + + 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. + + + + +