Skip to content

Commit

Permalink
Implement support for inhibiting the screensaver on Wayland
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cgutman committed Jan 24, 2021
1 parent 7ff3832 commit bd553ea
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/video/wayland/SDL_waylandvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -456,6 +460,10 @@ Wayland_VideoInit(_THIS)

WAYLAND_wl_display_flush(data->display);

#if SDL_USE_LIBDBUS
SDL_DBus_Init();
#endif

return 0;
}

Expand Down Expand Up @@ -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);

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

Expand Down
3 changes: 3 additions & 0 deletions src/video/wayland/SDL_waylandvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <EGL/egl.h>
#include "wayland-util.h"

#include "../../core/linux/SDL_dbus.h"

struct xkb_context;
struct SDL_WaylandInput;

Expand Down Expand Up @@ -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;
Expand Down
46 changes: 46 additions & 0 deletions src/video/wayland/SDL_waylandwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/video/wayland/SDL_waylandwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
83 changes: 83 additions & 0 deletions wayland-protocols/idle-inhibit-unstable-v1.xml
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit bd553ea

Please sign in to comment.