From 151f953815f3fba7b03ac63c76faf9e197f597a5 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Wed, 2 Jun 2021 11:41:44 -0400 Subject: [PATCH] wayland: Implement RaiseWindow with xdg-activation --- src/video/wayland/SDL_waylandvideo.c | 7 + src/video/wayland/SDL_waylandvideo.h | 1 + src/video/wayland/SDL_waylandwindow.c | 56 +++++++ src/video/wayland/SDL_waylandwindow.h | 2 + wayland-protocols/xdg-activation-v1.xml | 186 ++++++++++++++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 wayland-protocols/xdg-activation-v1.xml diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 759e0b16a..bae83ac19 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -51,6 +51,7 @@ #include "xdg-decoration-unstable-v1-client-protocol.h" #include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h" +#include "xdg-activation-v1-client-protocol.h" #define WAYLANDVID_DRIVER_NAME "wayland" @@ -199,6 +200,7 @@ Wayland_CreateDevice(int devindex) device->CreateSDLWindow = Wayland_CreateWindow; device->ShowWindow = Wayland_ShowWindow; device->HideWindow = Wayland_HideWindow; + device->RaiseWindow = Wayland_RaiseWindow; device->SetWindowFullscreen = Wayland_SetWindowFullscreen; device->MaximizeWindow = Wayland_MaximizeWindow; device->MinimizeWindow = Wayland_MinimizeWindow; @@ -463,6 +465,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, d->key_inhibitor_manager = wl_registry_bind(d->registry, id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1); } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) { d->idle_inhibit_manager = wl_registry_bind(d->registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); + } else if (strcmp(interface, "xdg_activation_v1") == 0) { + d->activation_manager = wl_registry_bind(d->registry, id, &xdg_activation_v1_interface, 1); } else if (strcmp(interface, "wl_data_device_manager") == 0) { Wayland_add_data_device_manager(d, id, version); } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { @@ -583,6 +587,9 @@ Wayland_VideoQuit(_THIS) Wayland_display_destroy_pointer_constraints(data); Wayland_display_destroy_relative_pointer_manager(data); + if (data->activation_manager) + xdg_activation_v1_destroy(data->activation_manager); + if (data->idle_inhibit_manager) zwp_idle_inhibit_manager_v1_destroy(data->idle_inhibit_manager); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 930ccf54b..0c6957f00 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -69,6 +69,7 @@ typedef struct { struct zxdg_decoration_manager_v1 *decoration_manager; struct zwp_keyboard_shortcuts_inhibit_manager_v1 *key_inhibitor_manager; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; + struct xdg_activation_v1 *activation_manager; EGLDisplay edpy; EGLContext context; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index cbd732578..cf4c81273 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -37,6 +37,7 @@ #include "xdg-shell-unstable-v6-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h" +#include "xdg-activation-v1-client-protocol.h" static float get_window_scale_factor(SDL_Window *window) { return ((SDL_WindowData*)window->driverdata)->scale_factor; @@ -812,6 +813,57 @@ void Wayland_HideWindow(_THIS, SDL_Window *window) } } +static void +handle_xdg_activation_done(void *data, + struct xdg_activation_token_v1 *xdg_activation_token_v1, + const char *token) +{ + SDL_WindowData *window = data; + if (xdg_activation_token_v1 == window->activation_token) { + xdg_activation_v1_activate(window->waylandData->activation_manager, + token, + window->surface); + xdg_activation_token_v1_destroy(window->activation_token); + window->activation_token = NULL; + } +} + +static const struct xdg_activation_token_v1_listener activation_listener_xdg = { + handle_xdg_activation_done +}; + +void Wayland_RaiseWindow(_THIS, SDL_Window *window) +{ + SDL_VideoData *data = _this->driverdata; + SDL_WindowData *wind = window->driverdata; + + if (data->activation_manager) { + if (wind->activation_token != NULL) { + /* We're about to overwrite this with a new request */ + xdg_activation_token_v1_destroy(wind->activation_token); + } + + wind->activation_token = xdg_activation_v1_get_activation_token(data->activation_manager); + xdg_activation_token_v1_add_listener(wind->activation_token, + &activation_listener_xdg, + wind); + + /* Note that we are not setting the app_id or serial here. + * + * Hypothetically we could set the app_id from data->classname, but + * that part of the API is for _external_ programs, not ourselves. + * + * As for a serial, this Raise event is arbitrary and doesn't come from + * an event, so it's actually very likely that this token will be + * ignored! TODO: Maybe add support for Raise via serial? + * + * -flibit + */ + xdg_activation_token_v1_set_surface(wind->activation_token, wind->surface); + xdg_activation_token_v1_commit(wind->activation_token); + } +} + #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH static void SDLCALL QtExtendedSurface_OnHintChanged(void *userdata, const char *name, @@ -1277,6 +1329,10 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window) zwp_idle_inhibitor_v1_destroy(wind->idle_inhibitor); } + if (wind->activation_token) { + xdg_activation_token_v1_destroy(wind->activation_token); + } + SDL_free(wind->outputs); if (wind->frame_callback) { diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 4aa1040d6..49b3ee447 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -67,6 +67,7 @@ typedef struct { struct zxdg_toplevel_decoration_v1 *server_decoration; struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; struct zwp_idle_inhibitor_v1 *idle_inhibitor; + struct xdg_activation_token_v1 *activation_token; SDL_atomic_t swap_interval_ready; @@ -89,6 +90,7 @@ typedef struct { extern void Wayland_ShowWindow(_THIS, SDL_Window *window); extern void Wayland_HideWindow(_THIS, SDL_Window *window); +extern void Wayland_RaiseWindow(_THIS, SDL_Window *window); extern void Wayland_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen); diff --git a/wayland-protocols/xdg-activation-v1.xml b/wayland-protocols/xdg-activation-v1.xml new file mode 100644 index 000000000..d87e63374 --- /dev/null +++ b/wayland-protocols/xdg-activation-v1.xml @@ -0,0 +1,186 @@ + + + + + Copyright © 2020 Aleix Pol Gonzalez <aleixpol@kde.org> + Copyright © 2020 Carlos Garnacho <carlosg@gnome.org> + + 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. + + + + The way for a client to pass focus to another toplevel is as follows. + + The client that intends to activate another toplevel uses the + xdg_activation_v1.get_activation_token request to get an activation token. + This token is then passed to the client to be activated through a separate + band of communication. The client to be activated will then pass the token + it received to the xdg_activation_v1.activate request. The compositor can + then use this token to decide how to react to the activation request. + + The token the activating client gets may be ineffective either already at + the time it receives it, for example if it was not focused, for focus + stealing prevention. The activating client will have no way to discover + the validity of the token, and may still forward it to the to be activated + client. + + The created activation token may optionally get information attached to it + that can be used by the compositor to identify the application that we + intend to activate. This can for example be used to display a visual hint + about what application is being started. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + A global interface used for informing the compositor about applications + being activated or started, or for applications to request to be + activated. + + + + + Notify the compositor that the xdg_activation object will no longer be + used. + + The child objects created via this interface are unaffected and should + be destroyed separately. + + + + + + Creates an xdg_activation_token_v1 object that will provide + the initiating client with a unique token for this activation. This + token should be offered to the clients to be activated. + + + + + + + + Requests surface activation. It's up to the compositor to display + this information as desired, for example by placing the surface above + the rest. + + The compositor may know who requested this by checking the activation + token and might decide not to follow through with the activation if it's + considered unwanted. + + Compositors can ignore unknown presentation tokens when an invalid + token is passed. + + + + + + + + + An object for setting up a token and receiving a token handle that can + be passed as an activation token to another client. + + The object is created using the xdg_activation_v1.get_activation_token + request. This object should then be populated with the app_id, surface + and serial information and committed. The compositor shall then issue a + done event with the token. In case the request's parameters are invalid, + the compositor will provide an invalid token. + + + + + + + + + Provides information about the seat and serial event that requested the + token. + + Must be sent before commit. This information is optional. + + + + + + + + The requesting client can specify an app_id to associate the token + being created with it. + + Must be sent before commit. This information is optional. + + + + + + + The requesting client can specify a surface to associate the token + being created with it. + + Must be triggered before commit. This information is optional. + + + + + + + Requests an activation token based on the different parameters that + have been offered through set_serial, set_surface and set_app_id. + + + + + + The 'done' event contains the unique token of this activation request + and notifies that the provider is done. + + Applications will typically receive the token through the + XDG_ACTIVATION_TOKEN environment variable as set by its launcher, and + should unset the environment variable right after this request, in + order to avoid propagating it to child processes. + + Applications implementing the D-Bus interface org.freedesktop.Application + should get their token under XDG_ACTIVATION_TOKEN on their platform_data. + + Presentation tokens may be transferred across clients through means not + described in this protocol. + + + + + + + Notify the compositor that the xdg_activation_token_v1 object will no + longer be used. + + + +