Skip to content

Commit

Permalink
wayland: enable screensaver inhibition in GNOME
Browse files Browse the repository at this point in the history
  • Loading branch information
jf2048 committed Jul 26, 2022
1 parent d2467f6 commit 0104715
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 33 deletions.
33 changes: 4 additions & 29 deletions DOCS/man/mpv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1347,35 +1347,10 @@ Disabling Screensaver
By default, mpv tries to disable the OS screensaver during playback (only if
a VO using the OS GUI API is active). ``--stop-screensaver=no`` disables this.

A common problem is that Linux desktop environments ignore the standard
screensaver APIs on which mpv relies. In particular, mpv uses the Screen Saver
extension (XSS) on X11, and the idle-inhibit on Wayland.

GNOME is one of the worst offenders, and ignores even the now widely supported
idle-inhibit protocol. (This is either due to a combination of malice and
incompetence, but since implementing this protocol would only take a few lines
of code, it is most likely the former. You will also notice how GNOME advocates
react offended whenever their sabotage is pointed out, which indicates either
hypocrisy, or even worse ignorance.)

Such incompatible desktop environments (i.e. which ignore standards) typically
require using a DBus API. This is ridiculous in several ways. The immediate
practical problem is that it would require adding a quite unwieldy dependency
for a DBus library, somehow integrating its mainloop into mpv, and other
generally unacceptable things.

However, since mpv does not officially support GNOME, this is not much of a
problem. If you are one of those miserable users who want to use mpv on GNOME,
report a bug on the GNOME issue tracker:
https://gitlab.gnome.org/groups/GNOME/-/issues

Alternatively, you may be able to write a Lua script that calls the
``xdg-screensaver`` command line program. (By the way, this a command line
program is an utterly horrible kludge that tries to identify your DE, and then
tries to send the correct DBus command via a DBus CLI tool.) If you find the
idea of having to write a script just so your screensaver doesn't kick in
ridiculous, do not use GNOME, or use GNOME video software instead of mpv (good
luck).
To inhibit the screensaver, mpv uses the Screen Saver extension (XSS) on X11,
and the idle-inhibit protocol on Wayland. If idle-inhibit is not found on
Wayland, the org.freedesktop.ScreenSaver or org.freedesktop.portal.Inhibit
D-Bus interfaces will be used to inhibit the screensaver.

Before mpv 0.33.0, the X11 backend ran ``xdg-screensaver reset`` in 10 second
intervals when not paused. This hack was removed in 0.33.0.
Expand Down
11 changes: 11 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,16 @@ if d3d11.allowed()
'video/out/d3d11/ra_d3d11.c')
endif

gnome = {
'name': 'gnome',
'deps': dependency('gio-2.0', version: '>= 2.26', required: get_option('gnome')),
}
gnome += {'use': gnome['deps'].found()}
if gnome['use']
dependencies += gnome['deps']
features += gnome['name']
endif

wayland = {
'name': 'wayland',
'deps': [dependency('wayland-client', version: '>= 1.15.0', required: get_option('wayland')),
Expand Down Expand Up @@ -1733,6 +1743,7 @@ conf_data.set10('HAVE_EGL_ANGLE_WIN32', egl_angle_win32.allowed())
conf_data.set10('HAVE_EGL_DRM', egl_drm.allowed())
conf_data.set10('HAVE_EGL_HELPERS', egl_helpers)
conf_data.set10('HAVE_EGL_X11', egl_x11.allowed())
conf_data.set10('HAVE_GIO', gnome['use'])
conf_data.set10('HAVE_GLIBC_THREAD_NAME', glibc_thread_name and posix)
conf_data.set10('HAVE_GL', gl['use'])
conf_data.set10('HAVE_GL_COCOA', gl_cocoa.allowed())
Expand Down
1 change: 1 addition & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ option('cdda', type: 'feature', value: 'disabled', description: 'cdda support (l
option('cplugins', type: 'feature', value: 'auto', description: 'C plugins')
option('dvbin', type: 'feature', value: 'disabled', description: 'DVB input module')
option('dvdnav', type: 'feature', value: 'auto', description: 'dvdnav support')
option('gnome', type: 'feature', value: 'auto', description: 'gnome inhibit support')
option('iconv', type: 'feature', value: 'auto', description: 'iconv')
option('javascript', type: 'feature', value: 'auto', description: 'Javascript (MuJS backend)')
option('lcms2', type: 'feature', value: 'auto', description: 'LCMS2 support')
Expand Down
145 changes: 141 additions & 4 deletions video/out/wayland_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
#include "generated/wayland/xdg-shell.h"
#include "generated/wayland/viewporter.h"

#if HAVE_GIO
#include <gio/gio.h>
#endif

#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 20
#define HAVE_WAYLAND_1_20
#endif
Expand Down Expand Up @@ -144,6 +148,15 @@ struct vo_wayland_output {
struct wl_list link;
};

#if HAVE_GIO
struct vo_wayland_screensaver {
GDBusProxy *proxy;
bool use_portal;
guint32 cookie;
char *portal_inhibit_path;
};
#endif

static int check_for_resize(struct vo_wayland_state *wl, wl_fixed_t x_w, wl_fixed_t y_w,
int edge_pixels, enum xdg_toplevel_resize_edge *edge);
static int get_mods(struct vo_wayland_state *wl);
Expand Down Expand Up @@ -1441,8 +1454,87 @@ static void set_geometry(struct vo_wayland_state *wl)
wl->vdparams.y1 = vo->dheight / wl->scaling;
}

#if HAVE_GIO
const GDBusProxyFlags PROXY_FLAGS = G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
#endif

static int set_screensaver_inhibitor(struct vo_wayland_state *wl, int state)
{
#if HAVE_GIO
struct vo_wayland_screensaver *ss = wl->screensaver;
if (ss) {
if (state == !!(ss->cookie || ss->portal_inhibit_path))
return VO_TRUE;

GError *error = NULL;
if (state) {
const char REASON[] = "video playing";
if (ss->use_portal) {
MP_VERBOSE(wl, "Enabling screen saver portal inhibitor\n");
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&builder, "{sv}",
"reason", g_variant_new_string(REASON));
GVariant *options = g_variant_builder_end(&builder);
GVariant *ret = g_dbus_proxy_call_sync(
ss->proxy, "Inhibit",
g_variant_new("(su@a{sv})", "", 8, options),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (ret) {
g_variant_get(ret, "(o)",
&ss->portal_inhibit_path);
g_variant_unref(ret);
} else if (error) {
MP_VERBOSE(wl, "Failed to inhibit screensaver: %s\n",
error->message);
g_error_free(error);
}
} else {
MP_VERBOSE(wl, "Enabling screen saver inhibitor\n");
GVariant *ret = g_dbus_proxy_call_sync(
ss->proxy, "Inhibit",
g_variant_new("(ss)", "mpv", REASON),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (ret) {
g_variant_get(ret, "(u)", &ss->cookie);
g_variant_unref(ret);
} else if (error) {
MP_VERBOSE(wl, "Failed to inhibit screensaver: %s\n",
error->message);
g_error_free(error);
}
}
} else {
if (ss->portal_inhibit_path) {
MP_VERBOSE(wl, "Disabling screen saver portal inhibitor at '%s'\n",
ss->portal_inhibit_path);
GDBusProxy *proxy =
g_dbus_proxy_new_sync(
g_dbus_proxy_get_connection(ss->proxy),
PROXY_FLAGS, NULL,
"org.freedesktop.portal.Desktop",
ss->portal_inhibit_path,
"org.freedesktop.portal.Request",
NULL, NULL);
if (proxy) {
g_dbus_proxy_call(proxy, "Close", NULL,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
g_object_unref(proxy);
}
} else if (ss->cookie) {
MP_VERBOSE(wl, "Disabling screen saver inhibitor %u\n",
ss->cookie);
g_dbus_proxy_call(ss->proxy, "UnInhibit",
g_variant_new("(u)", ss->cookie),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
g_clear_pointer(&ss->portal_inhibit_path, g_free);
ss->cookie = 0;
}
return VO_TRUE;
}
#endif
if (!wl->idle_inhibit_manager)
return VO_NOTIMPL;
if (state == (!!wl->idle_inhibitor))
Expand Down Expand Up @@ -1822,10 +1914,6 @@ int vo_wayland_init(struct vo *vo)
wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface);
}

const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
if (xdg_current_desktop != NULL && strstr(xdg_current_desktop, "GNOME"))
MP_WARN(wl, "GNOME's wayland compositor lacks support for the idle inhibit protocol. This means the screen can blank during playback.\n");

if (wl->dnd_devman && wl->seat) {
wl->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, wl->seat);
wl_data_device_add_listener(wl->dnd_ddev, &data_device_listener, wl);
Expand Down Expand Up @@ -1853,9 +1941,50 @@ int vo_wayland_init(struct vo *vo)
zxdg_decoration_manager_v1_interface.name);
}

#if HAVE_GIO
if (!wl->idle_inhibit_manager) {
bool use_portal = false;

GDBusProxy *proxy =
g_dbus_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION, PROXY_FLAGS, NULL,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
NULL, NULL);
char *name_owner = g_dbus_proxy_get_name_owner(proxy);
if (!name_owner) {
proxy =
g_dbus_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION, PROXY_FLAGS, NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Inhibit",
NULL, NULL);
if (proxy) {
name_owner = g_dbus_proxy_get_name_owner(proxy);
use_portal = true;
}
}
if (proxy && !name_owner) {
g_clear_object(&proxy);
}
if (proxy) {
wl->screensaver = talloc_zero(wl, struct vo_wayland_screensaver);
wl->screensaver->proxy = proxy;
wl->screensaver->use_portal = use_portal;
}
g_clear_pointer(&name_owner, g_free);
}

if (!wl->idle_inhibit_manager && !wl->screensaver)
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol or the org.freedesktop.ScreenSaver interface!\n",
zwp_idle_inhibit_manager_v1_interface.name);
#else
if (!wl->idle_inhibit_manager)
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
zwp_idle_inhibit_manager_v1_interface.name);
#endif

wl->opts = mp_get_config_group(wl, wl->vo->global, &wayland_conf);
wl->display_fd = wl_display_get_fd(wl->display);
Expand Down Expand Up @@ -1978,6 +2107,14 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);

#if HAVE_GIO
if (wl->screensaver) {
g_clear_pointer(&wl->screensaver->portal_inhibit_path, g_free);
g_clear_object(&wl->screensaver->proxy);
talloc_free(wl->screensaver);
}
#endif

if (wl->keyboard)
wl_keyboard_destroy(wl->keyboard);

Expand Down
6 changes: 6 additions & 0 deletions video/out/wayland_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define MPLAYER_WAYLAND_COMMON_H

#include <wayland-client.h>
#include "config.h"
#include "input/event.h"
#include "vo.h"

Expand Down Expand Up @@ -131,6 +132,11 @@ struct vo_wayland_state {
bool cursor_visible;
int allocated_cursor_scale;
uint32_t pointer_id;

#if HAVE_GIO
/* Screen Saver */
struct vo_wayland_screensaver *screensaver;
#endif
};

bool vo_wayland_check_visible(struct vo *vo);
Expand Down

0 comments on commit 0104715

Please sign in to comment.