diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 0bfa620d..570b7707 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -217,6 +217,7 @@ video tutorials. - Brandon Schaefer - Sebastian Schuberth - Scr3amer + - Jan Schuerkamp - Christian Sdunek - Matt Sealey - Steve Sexton diff --git a/README.md b/README.md index fd759897..23ba596d 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ information on what to include when reporting a bug. - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) - [Cocoa] Bugfix: Touching event queue from secondary thread before main thread would abort (#1649) + - [Wayland] Added support for `glfwRequestWindowAttention` (#2287) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled - [X11] Bugfix: Termination would segfault if the IM had been destroyed diff --git a/deps/wayland/xdg-activation-v1.xml b/deps/wayland/xdg-activation-v1.xml new file mode 100644 index 00000000..9adcc274 --- /dev/null +++ b/deps/wayland/xdg-activation-v1.xml @@ -0,0 +1,200 @@ + + + + + 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 forwarded to the client, which is supposed to activate + one of its surfaces, through a separate band of communication. + + One established way of doing this is through the XDG_ACTIVATION_TOKEN + environment variable of a newly launched child process. The child process + should unset the environment variable again right after reading it out in + order to avoid propagating it to other child processes. + + Another established way exists for Applications implementing the D-Bus + interface org.freedesktop.Application, which should get their token under + activation-token on their platform_data. + + In general activation tokens may be transferred across clients through + means not described in this protocol. + + 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 activation 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. + + The serial can come from an input or focus event. For instance, if a + click triggers the launch of a third-party client, the launcher client + should send a set_serial request with the serial and seat from the + wl_pointer.button event. + + Some compositors might refuse to activate toplevels when the token + doesn't have a valid and recent enough event serial. + + 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. + + + + + + + This request sets the surface requesting the activation. Note, this is + different from the surface that will be activated. + + Some compositors might refuse to activate toplevels when the token + doesn't have a requesting surface. + + Must be sent 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. + + + + + + + Notify the compositor that the xdg_activation_token_v1 object will no + longer be used. The received token stays valid. + + + + diff --git a/docs/compat.dox b/docs/compat.dox index 49b52bf4..bcd110c9 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -140,6 +140,12 @@ GLFW itself, using the alongside subsurfaces. If the running compositor does not support these protocols either, no decorations will be drawn around windows. +GLFW uses the [xdg-activation +protocol](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/staging/xdg-activation/xdg-activation-v1.xml) +to enable attention requests. This protocol is part of +wayland-protocols staging, and mandatory at build time. If the running compositor +does not support this protocol, the attention requests do nothing. + @section compat_glx GLX extensions diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c49b31c9..45941647 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,6 +101,7 @@ if (GLFW_BUILD_WAYLAND) generate_wayland_protocol("idle-inhibit-unstable-v1.xml") generate_wayland_protocol("pointer-constraints-unstable-v1.xml") generate_wayland_protocol("relative-pointer-unstable-v1.xml") + generate_wayland_protocol("xdg-activation-v1.xml") generate_wayland_protocol("xdg-decoration-unstable-v1.xml") endif() diff --git a/src/wl_init.c b/src/wl_init.c index 4b90642c..b0e4e45e 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -46,6 +46,7 @@ #include "viewporter-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" +#include "xdg-activation-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h" // NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of @@ -77,6 +78,10 @@ #include "pointer-constraints-unstable-v1-client-protocol-code.h" #undef types +#define types _glfw_xdg_activation_types +#include "xdg-activation-v1-client-protocol-code.h" +#undef types + #define types _glfw_idle_inhibit_types #include "idle-inhibit-unstable-v1-client-protocol-code.h" #undef types @@ -177,6 +182,13 @@ static void registryHandleGlobal(void* userData, &zwp_idle_inhibit_manager_v1_interface, 1); } + else if (strcmp(interface, "xdg_activation_v1") == 0) + { + _glfw.wl.activationManager = + wl_registry_bind(registry, name, + &xdg_activation_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void* userData, @@ -929,6 +941,8 @@ void _glfwTerminateWayland(void) zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); if (_glfw.wl.idleInhibitManager) zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); + if (_glfw.wl.activationManager) + xdg_activation_v1_destroy(_glfw.wl.activationManager); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) diff --git a/src/wl_platform.h b/src/wl_platform.h index d00e28fe..7e8202e9 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -129,6 +129,8 @@ struct wl_output; #define xdg_surface_interface _glfw_xdg_surface_interface #define xdg_toplevel_interface _glfw_xdg_toplevel_interface #define xdg_wm_base_interface _glfw_xdg_wm_base_interface +#define xdg_activation_v1_interface _glfw_xdg_activation_v1_interface +#define xdg_activation_token_v1_interface _glfw_xdg_activation_token_v1_interface #define GLFW_WAYLAND_WINDOW_STATE _GLFWwindowWayland wl; #define GLFW_WAYLAND_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl; @@ -406,6 +408,7 @@ typedef struct _GLFWwindowWayland struct zwp_confined_pointer_v1* confinedPointer; struct zwp_idle_inhibitor_v1* idleInhibitor; + struct xdg_activation_token_v1* activationToken; struct { struct wl_buffer* buffer; @@ -434,6 +437,7 @@ typedef struct _GLFWlibraryWayland struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + struct xdg_activation_v1* activationManager; _GLFWofferWayland* offers; unsigned int offerCount; diff --git a/src/wl_window.c b/src/wl_window.c index 73ebac4f..53c8c70a 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -48,6 +48,7 @@ #include "viewporter-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" +#include "xdg-activation-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h" #define GLFW_BORDER_SIZE 4 @@ -2054,6 +2055,9 @@ void _glfwDestroyWindowWayland(_GLFWwindow* window) if (window == _glfw.wl.keyboardFocus) _glfw.wl.keyboardFocus = NULL; + if (window->wl.activationToken) + xdg_activation_token_v1_destroy(window->wl.activationToken); + if (window->wl.idleInhibitor) zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); @@ -2335,11 +2339,38 @@ void _glfwHideWindowWayland(_GLFWwindow* window) } } +static void xdgActivationHandleDone(void* userData, + struct xdg_activation_token_v1* activationToken, + const char* token) +{ + _GLFWwindow* window = userData; + + if(activationToken != window->wl.activationToken) + return; + + xdg_activation_v1_activate(_glfw.wl.activationManager, token, window->wl.surface); + xdg_activation_token_v1_destroy(window->wl.activationToken); + window->wl.activationToken = NULL; +} + +static const struct xdg_activation_token_v1_listener xdgActivationListener = +{ + xdgActivationHandleDone +}; + void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) { - // TODO - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, - "Wayland: Window attention request not implemented yet"); + if(!_glfw.wl.activationManager) + return; + + //We're about to overwrite this with a new request + if(window->wl.activationToken) + xdg_activation_token_v1_destroy(window->wl.activationToken); + + window->wl.activationToken = xdg_activation_v1_get_activation_token(_glfw.wl.activationManager); + xdg_activation_token_v1_add_listener(window->wl.activationToken, &xdgActivationListener, window); + + xdg_activation_token_v1_commit(window->wl.activationToken); } void _glfwFocusWindowWayland(_GLFWwindow* window)