A broad group of applications have some sort of display running without user feedback, an obvious example are multimedia applications. For instance the user of a video editor is viewing his work in-progress, in such use cases it would be frustrating if the screensaver kicked in, blocking the video. So how do we disable the screensaver temporarily from our application?
Recently there have been some changes around GNOME's screensaver and which component handles the requests to disable it. It used to be
gnome-screensaver directly but now it is managed by
gnome-session. What you need to do is simply send the DBus message "Inhibit" to org.gnome.SessionManager, and later send "Uninhibit" to reactivate it.
Sebastian Dröge implemented this in
snappy, basing his code heavily in that of
totem. We have the following
function that depending on the second argument, disables or enables the screensaver:
static void
screensaver_inhibit_dbus (ScreenSaver * screensaver, gboolean inhibit)
{
if (!screensaver->gs_proxy)
return;
if (inhibit) {
guint xid;
xid = screensaver->window;
g_dbus_proxy_call (screensaver->gs_proxy,
"Inhibit",
g_variant_new ("(susu)",
g_get_application_name (),
xid,
REASON,
GS_NO_IDLE_FLAG),
G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, on_inhibit_cb, screensaver);
} else {
if (screensaver->cookie > 0) {
g_dbus_proxy_call (screensaver->gs_proxy,
"Uninhibit",
g_variant_new ("(u)", screensaver->cookie),
G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, on_uninhibit_cb,
screensaver);
}
}
}
When the "Inhibit" request is satisfied asynchronously, the on_inhibit_cb () callback is called. This checks if the operation happened succesfully, and saves the cookie that we see used above when uninhibiting. gnome-session uses this cookie to track the disabling and enabling of the screensaver by multiple applications. If two applications request a screensaver disable and later only one has enabled it, it will still wait for the second one before doing so.
static void
on_inhibit_cb (GObject * source_object, GAsyncResult * res, gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY (source_object);
ScreenSaver *screensaver = (ScreenSaver *) user_data;
GVariant *value;
GError *error = NULL;
value = g_dbus_proxy_call_finish (proxy, res, &error);
if (!value) {
g_warning ("Problem inhibiting the screensaver: %s", error->message);
g_error_free (error);
return;
}
/* save the cookie */
if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(u)")))
g_variant_get (value, "(u)", &screensaver->cookie);
else
screensaver->cookie = 0;
g_variant_unref (value);
}
on_uninhibit_cb () does the reverse, checks if operation finished succesfully and it clears the cookie.
For those who want the whole picture... getting the GDBusProxy (screensaver->gs_proxy) which needs to be set before running the proxy call in screensaver_inhibit_dbus () above, is set by:
static void
screensaver_init_dbus (ScreenSaver * screensaver)
{
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
GS_SERVICE,
GS_PATH, GS_INTERFACE, NULL, screensaver_dbus_proxy_new_cb, screensaver);
}
The screensaver_dbus_proxy_new_cb callback checks the operation and then runs:
screensaver->gs_proxy = g_dbus_proxy_new_for_bus_finish (result, NULL);
Here is the full code if you want to take a look at it.
pd: Soon part II, the XTst variant of screensaver disabling for x11.