From bd553ea868d1babfff9b3d2fc97ab7949ddb2e4d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 20 Jan 2021 21:17:20 -0600 Subject: [PATCH] Implement support for inhibiting the screensaver on Wayland We support both the org.freedesktop.ScreenSaver D-Bus API (same as the X11 backend) and the Wayland idle_inhibit_unstable_v1 protocol. Some Wayland compositors only support one or the other, so we need both to for broad compatibility. --- src/video/wayland/SDL_waylandvideo.c | 17 ++++ src/video/wayland/SDL_waylandvideo.h | 3 + src/video/wayland/SDL_waylandwindow.c | 46 ++++++++++ src/video/wayland/SDL_waylandwindow.h | 2 + .../idle-inhibit-unstable-v1.xml | 83 +++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 wayland-protocols/idle-inhibit-unstable-v1.xml diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index e5be532cf..addd00634 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -50,6 +50,7 @@ #include "xdg-decoration-unstable-v1-client-protocol.h" #include "org-kde-kwin-server-decoration-manager-client-protocol.h" #include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" +#include "idle-inhibit-unstable-v1-client-protocol.h" #define WAYLANDVID_DRIVER_NAME "wayland" @@ -179,6 +180,7 @@ Wayland_CreateDevice(int devindex) device->SetDisplayMode = Wayland_SetDisplayMode; device->GetDisplayModes = Wayland_GetDisplayModes; device->GetWindowWMInfo = Wayland_GetWindowWMInfo; + device->SuspendScreenSaver = Wayland_SuspendScreenSaver; device->PumpEvents = Wayland_PumpEvents; @@ -397,6 +399,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, Wayland_display_add_pointer_constraints(d, id); } else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) { 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, "wl_data_device_manager") == 0) { d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version)); } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { @@ -456,6 +460,10 @@ Wayland_VideoInit(_THIS) WAYLAND_wl_display_flush(data->display); +#if SDL_USE_LIBDBUS + SDL_DBus_Init(); +#endif + return 0; } @@ -497,6 +505,9 @@ Wayland_VideoQuit(_THIS) Wayland_display_destroy_pointer_constraints(data); Wayland_display_destroy_relative_pointer_manager(data); + if (data->idle_inhibit_manager) + zwp_idle_inhibit_manager_v1_destroy(data->idle_inhibit_manager); + if (data->key_inhibitor_manager) zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(data->key_inhibitor_manager); @@ -535,6 +546,12 @@ Wayland_VideoQuit(_THIS) if (data->registry) wl_registry_destroy(data->registry); +/* !!! FIXME: other subsystems use D-Bus, so we shouldn't quit it here; + have SDL.c do this at a higher level, or add refcounting. */ +#if SDL_USE_LIBDBUS + SDL_DBus_Quit(); +#endif + SDL_free(data->classname); } diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 99e3c019c..ea7e1407b 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -37,6 +37,8 @@ #include #include "wayland-util.h" +#include "../../core/linux/SDL_dbus.h" + struct xkb_context; struct SDL_WaylandInput; @@ -65,6 +67,7 @@ typedef struct { struct zxdg_decoration_manager_v1 *decoration_manager; struct org_kde_kwin_server_decoration_manager *kwin_server_decoration_manager; struct zwp_keyboard_shortcuts_inhibit_manager_v1 *key_inhibitor_manager; + struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; EGLDisplay edpy; EGLContext context; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index cba97fd48..9f9cf6c06 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 "org-kde-kwin-server-decoration-manager-client-protocol.h" +#include "idle-inhibit-unstable-v1-client-protocol.h" static float get_window_scale_factor(SDL_Window *window) { return ((SDL_WindowData*)window->driverdata)->scale_factor; @@ -838,6 +839,9 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) } } + /* We may need to create an idle inhibitor for this new window */ + Wayland_SuspendScreenSaver(_this); + return 0; } @@ -916,6 +920,44 @@ void Wayland_SetWindowTitle(_THIS, SDL_Window * window) WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display ); } +void +Wayland_SuspendScreenSaver(_THIS) +{ + SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; + +#if SDL_USE_LIBDBUS + if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { + return; + } +#endif + + /* The idle_inhibit_unstable_v1 protocol suspends the screensaver + on a per wl_surface basis, but SDL assumes that suspending + the screensaver can be done independently of any window. + + To reconcile these differences, we propagate the idle inhibit + state to each window. If there is no window active, we will + be able to inhibit idle once the first window is created. + */ + if (data->idle_inhibit_manager) { + SDL_Window *window = _this->windows; + while (window) { + SDL_WindowData *win_data = window->driverdata; + + if (_this->suspend_screensaver && !win_data->idle_inhibitor) { + win_data->idle_inhibitor = + zwp_idle_inhibit_manager_v1_create_inhibitor(data->idle_inhibit_manager, + win_data->surface); + } else if (!_this->suspend_screensaver && win_data->idle_inhibitor) { + zwp_idle_inhibitor_v1_destroy(win_data->idle_inhibitor); + win_data->idle_inhibitor = NULL; + } + + window = window->next; + } + } +} + void Wayland_DestroyWindow(_THIS, SDL_Window *window) { SDL_VideoData *data = _this->driverdata; @@ -937,6 +979,10 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window) org_kde_kwin_server_decoration_release(wind->kwin_server_decoration); } + if (wind->idle_inhibitor) { + zwp_idle_inhibitor_v1_destroy(wind->idle_inhibitor); + } + if (data->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel) { xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel); diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 4a7472ccc..88a9768ac 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -66,6 +66,7 @@ typedef struct { struct zxdg_toplevel_decoration_v1 *server_decoration; struct org_kde_kwin_server_decoration *kwin_server_decoration; struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; + struct zwp_idle_inhibitor_v1 *idle_inhibitor; #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH struct qt_extended_surface *extended_surface; @@ -97,6 +98,7 @@ extern int Wayland_CreateWindow(_THIS, SDL_Window *window); extern void Wayland_SetWindowSize(_THIS, SDL_Window * window); extern void Wayland_SetWindowTitle(_THIS, SDL_Window * window); extern void Wayland_DestroyWindow(_THIS, SDL_Window *window); +extern void Wayland_SuspendScreenSaver(_THIS); extern SDL_bool Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info); diff --git a/wayland-protocols/idle-inhibit-unstable-v1.xml b/wayland-protocols/idle-inhibit-unstable-v1.xml new file mode 100644 index 000000000..9c06cdcba --- /dev/null +++ b/wayland-protocols/idle-inhibit-unstable-v1.xml @@ -0,0 +1,83 @@ + + + + + Copyright © 2015 Samsung Electronics Co., Ltd + + 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 interface permits inhibiting the idle behavior such as screen + blanking, locking, and screensaving. The client binds the idle manager + globally, then creates idle-inhibitor objects for each 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. + + + + + Destroy the inhibit manager. + + + + + + Create a new inhibitor object associated with the given surface. + + + + + + + + + + An idle inhibitor prevents the output that the associated surface is + visible on from being set to a state where it is not visually usable due + to lack of user interaction (e.g. blanked, dimmed, locked, set to power + save, etc.) Any screensaver processes are also blocked from displaying. + + If the surface is destroyed, unmapped, becomes occluded, loses + visibility, or otherwise becomes not visually relevant for the user, the + idle inhibitor will not be honored by the compositor; if the surface + subsequently regains visibility the inhibitor takes effect once again. + Likewise, the inhibitor isn't honored if the system was already idled at + the time the inhibitor was established, although if the system later + de-idles and re-idles the inhibitor will take effect. + + + + + Remove the inhibitor effect from the associated wl_surface. + + + + +