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.
main
Cameron Gutman 2021-01-20 21:17:20 -06:00
parent 7ff3832e4d
commit bd553ea868
5 changed files with 151 additions and 0 deletions

View File

@ -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);
}

View File

@ -37,6 +37,8 @@
#include <EGL/egl.h>
#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;

View File

@ -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);

View File

@ -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);

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle_inhibit_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwp_idle_inhibit_manager_v1" version="1">
<description summary="control behavior when display idles">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Destroy the inhibit manager.
</description>
</request>
<request name="create_inhibitor">
<description summary="create a new inhibitor object">
Create a new inhibitor object associated with the given surface.
</description>
<arg name="id" type="new_id" interface="zwp_idle_inhibitor_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface that inhibits the idle behavior"/>
</request>
</interface>
<interface name="zwp_idle_inhibitor_v1" version="1">
<description summary="context object for inhibiting idle behavior">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Remove the inhibitor effect from the associated wl_surface.
</description>
</request>
</interface>
</protocol>