WIP: Fixes #2026 Screensaver suspend on Linux via Dbus

One some systems (tested with Gnome 3 on Arch Linux) the current method
of using `xdg-screensaver` to suspend the screensaver does not work.
Instead, using DBus to issue an `Inhibit` request is recommended.

The request returns a cookie that needs to be re-used to un-inhibit the
screensaver later. Additionally if the DBus connection is closed the
current inhibition is discarded. Thus, the DBus connection needs to stay
connected for the duration of the screenshot inhibition.

The code is heavily inspired from the [SDL 2.x
code](http://hg.libsdl.org/SDL/file/default/src/core/linux/SDL_dbus.c#l172).
I didn't call the SDL 2 code though since this it to fix the issue with
the GL driver, and I assume one would want to have screensaver inhibited
even when SDL 2 is not available (but GL is).

I've set "WIP" because:
* I haven't done C in a long time so my code is probably not great
* There's a dependency on DBus which I don't know is acceptable or
not
* I've put my code where I could to check it works, but `x11_common` may
not be the best place
* The code need and "init" and "deinit" kind of method as it needs to
initialise the DBus connection, and on deinit close it properly. I've
used `x11_connect` and `x11_window_destroy` but they don't sound like
the best choices.
* I'm a bit unclear as to what happens when "suspend screensaver" is
ticked on/off in the menu. This doesn't seem to call
`x11_suspend_screensaver` everytime, so I'm not sure if there's a hook
somewhere (as disabling screensaver suspend in the menu should cause a
DBus unhinibit request to be sent).
* Should I just call the SDL 2.x code (meaning that the GL driver would
depend on SDL 2.x at runtime)?

So, first of all are you ok with the approach, and if yes I'd gladly get
feedback about the code, how to architecture it and the best place to
put it.

Thanks!
This commit is contained in:
Nicolas Guillaumin 2016-09-08 15:18:37 -07:00
parent ea9793a17c
commit 05df632bd8
4 changed files with 127 additions and 0 deletions

View File

@ -612,6 +612,11 @@ ifeq ($(HAVE_XKBCOMMON), 1)
LIBS += $(XKBCOMMON_LIBS)
endif
ifeq ($(HAVE_DBUS), 1)
LIBS += $(DBUS_LIBS)
CFLAGS += $(DBUS_CFLAGS)
endif
ifeq ($(HAVE_UDEV), 1)
DEFINES += $(UDEV_CFLAGS)
LIBS += $(UDEV_LIBS)

View File

@ -34,6 +34,12 @@
#include "../../verbosity.h"
#include "../../runloop.h"
#ifdef HAVE_DBUS
#include <dbus/dbus.h>
static DBusConnection* dbus_connection;
static unsigned int dbus_screensaver_cookie = 0;
#endif
Colormap g_x11_cmap;
Window g_x11_win;
Display *g_x11_dpy;
@ -146,6 +152,75 @@ void x11_set_window_attr(Display *dpy, Window win)
}
void x11_suspend_screensaver(Window wnd, bool enable)
{
x11_suspend_screensaver_xdg_screensaver(wnd, enable);
#ifdef HAVE_DBUS
x11_suspend_screensaver_dbus(enable);
#endif
}
#ifdef HAVE_DBUS
void x11_suspend_screensaver_dbus(bool enable)
{
const char *app = "RetroArch";
const char *reason = "Playing a game";
DBusMessage *msg, *reply;
if (!enable)
return;
if (dbus_screensaver_cookie > 0)
return; // Already suspended
if (dbus_connection == NULL)
return; // DBus connection was not obtained
msg = dbus_message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit");
if (msg != NULL)
{
dbus_message_append_args(msg,
DBUS_TYPE_STRING, &app,
DBUS_TYPE_STRING, &reason,
DBUS_TYPE_INVALID);
}
if (msg != NULL)
{
reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, 300, NULL);
if (reply != NULL) {
if (!dbus_message_get_args(reply, NULL,
DBUS_TYPE_UINT32, &dbus_screensaver_cookie,
DBUS_TYPE_INVALID))
{
dbus_screensaver_cookie = 0;
}
dbus_message_unref(reply);
}
dbus_message_unref(msg);
}
if (dbus_screensaver_cookie == 0)
{
RARCH_ERR("[DBus]: Failed to suspend screensaver via DBus.\n");
}
else
{
RARCH_LOG("[DBus]: Suspended screensaver.\n");
}
return;
}
#endif
void x11_suspend_screensaver_xdg_screensaver(Window wnd, bool enable)
{
int ret;
char cmd[64] = {0};
@ -533,6 +608,22 @@ bool x11_connect(void)
return false;
}
#ifdef HAVE_DBUS
DBusError err;
int ret;
dbus_error_init(&err);
dbus_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
RARCH_ERR("[DBus]: Failed to get DBus connection. Screensaver will not be suspended.\n");
dbus_error_free(&err);
}
if (dbus_connection != NULL) {
dbus_connection_set_exit_on_disconnect(dbus_connection, true);
}
#endif
return true;
}
@ -573,6 +664,32 @@ void x11_window_destroy(bool fullscreen)
if (!fullscreen)
XDestroyWindow(g_x11_dpy, g_x11_win);
g_x11_win = None;
#ifdef HAVE_DBUS
if (dbus_connection != NULL)
{
DBusMessage *msg = dbus_message_new_method_call("org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit");
dbus_message_append_args (msg,
DBUS_TYPE_UINT32, &dbus_screensaver_cookie,
DBUS_TYPE_INVALID);
if (msg != NULL) {
if (dbus_connection_send(dbus_connection, msg, NULL)) {
dbus_connection_flush(dbus_connection);
}
dbus_message_unref(msg);
}
dbus_screensaver_cookie = 0;
dbus_connection_close(dbus_connection);
dbus_connection_unref(dbus_connection);
dbus_shutdown();
}
#endif
}
void x11_colormap_destroy(void)

View File

@ -44,6 +44,10 @@ void x11_save_last_used_monitor(Window win);
void x11_show_mouse(Display *dpy, Window win, bool state);
void x11_windowed_fullscreen(Display *dpy, Window win);
void x11_suspend_screensaver(Window win, bool enable);
void x11_suspend_screensaver_xdg_screensaver(Window win, bool enable);
#ifdef HAVE_DBUS
void x11_suspend_screensaver_dbus(bool enable);
#endif
bool x11_enter_fullscreen(Display *dpy, unsigned width,
unsigned height, XF86VidModeModeInfo *desktop_mode);

View File

@ -382,6 +382,7 @@ check_pkgconf XCB xcb
check_pkgconf WAYLAND wayland-egl
check_pkgconf XKBCOMMON xkbcommon 0.3.2
check_pkgconf DBUS dbus-1
check_pkgconf XEXT xext
check_pkgconf XF86VM xxf86vm
check_pkgconf XINERAMA xinerama