Skip to content

Commit

Permalink
linux: Use D-Bus to inhibit screensaver
Browse files Browse the repository at this point in the history
  • Loading branch information
robxnano committed Sep 27, 2024
1 parent 4bc1546 commit 2748771
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 41 deletions.
3 changes: 2 additions & 1 deletion Source/Core/DolphinNoGUI/PlatformX11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ static constexpr auto X_None = None;
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include "UICommon/UICommon.h"
#include "UICommon/X11Utils.h"
#include "VideoCommon/Present.h"

Expand Down Expand Up @@ -110,7 +111,7 @@ bool PlatformX11::Init()
ProcessEvents();

if (Config::Get(Config::MAIN_DISABLE_SCREENSAVER))
X11Utils::InhibitScreensaver(m_window, true);
UICommon::InhibitScreenSaver(true);

#ifdef HAVE_XRANDR
m_xrr_config = new X11Utils::XRRConfiguration(m_display, m_window);
Expand Down
5 changes: 0 additions & 5 deletions Source/Core/DolphinQt/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1686,12 +1686,7 @@ void MainWindow::UpdateScreenSaverInhibition()

m_is_screensaver_inhibited = inhibit;

#ifdef HAVE_X11
if (GetWindowSystemType() == WindowSystemType::X11)
UICommon::InhibitScreenSaver(winId(), inhibit);
#else
UICommon::InhibitScreenSaver(inhibit);
#endif
}

bool MainWindow::eventFilter(QObject* object, QEvent* event)
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/UICommon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ if ((DEFINED CMAKE_ANDROID_ARCH_ABI AND CMAKE_ANDROID_ARCH_ABI MATCHES "x86|x86_
target_link_libraries(uicommon PRIVATE bdisasm)
endif()

if(UNIX AND NOT APPLE AND NOT ANDROID AND ENABLE_QT)
find_package(Qt6 REQUIRED COMPONENTS DBus)
target_sources(uicommon PRIVATE DBusUtils.cpp)
target_compile_definitions(uicommon PRIVATE -DHAVE_QTDBUS=1)
target_link_libraries(uicommon PUBLIC Qt6::DBus)
endif()

if(X11_FOUND)
target_sources(uicommon PRIVATE X11Utils.cpp)
target_link_libraries(uicommon PUBLIC PkgConfig::XRANDR PkgConfig::X11)
Expand Down
204 changes: 204 additions & 0 deletions Source/Core/UICommon/DBusUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "UICommon/DBusUtils.h"

#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QHash>
#include <QObject>
#include <QString>

#include "Common/Logging/Log.h"

namespace DBusUtils
{
static bool InhibitFDO();
static bool InhibitXfce();
static bool InhibitMate();
static bool InhibitPortal();
static void Uninhibit();

static constexpr char s_app_id[] = "org.DolphinEmu.dolphin-emu";

// Cookie for the org.freedesktop.ScreenSaver interface
static uint32_t s_fdo_cookie = 0;
// Cookie for the org.xfce.ScreenSaver interface
static uint32_t s_xfce_cookie = 0;
// Cookie for the org.mate.ScreenSaver interface
static uint32_t s_mate_cookie = 0;
// Return handle for the org.freedesktop.portal.Desktop interface
static QString s_portal_handle;

// Uses D-Bus to inhibit the screensaver
// Tries various D-Bus interfaces until it finds one that works
void InhibitScreenSaver(bool inhibit)
{
if (inhibit)
{
if (s_fdo_cookie || s_xfce_cookie || s_mate_cookie || !s_portal_handle.isEmpty())
return;
if (!InhibitFDO() && !InhibitXfce() && !InhibitMate() && !InhibitPortal())
INFO_LOG_FMT(VIDEO, "Could not inhibit screensaver: No services available");
}
else
Uninhibit();
}

// Inhibits screensaver on Xfce desktop
static bool InhibitXfce()
{
QDBusInterface interface("org.xfce.ScreenSaver", "/", "org.xfce.ScreenSaver");
if (!interface.isValid())
return false;

QDBusReply<uint32_t> reply = interface.call("Inhibit", s_app_id, QObject::tr("Playing a game"));
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.xfce.ScreenSaver::Inhibit failed: {}",
interface.lastError().message().toStdString());
return false;
}
INFO_LOG_FMT(VIDEO, "org.xfce.ScreenSaver::Inhibit succeeded");
s_xfce_cookie = reply;
return true;
}

// Inhibits screensaver on the MATE desktop
// MATE advertises support for xdg-desktop-portal Inhibit,
// but it doesn't work as of Fedora 40
static bool InhibitMate()
{
QDBusInterface interface("org.mate.ScreenSaver", "/org/mate/ScreenSaver", "org.mate.ScreenSaver");
if (!interface.isValid())
return false;

QDBusReply<uint32_t> reply = interface.call("Inhibit", s_app_id, QObject::tr("Playing a game"));
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.mate.ScreenSaver::Inhibit failed: {}",
interface.lastError().message().toStdString());
return false;
}
INFO_LOG_FMT(VIDEO, "org.mate.ScreenSaver::Inhibit succeeded");
s_mate_cookie = reply;
return true;
}

// Inhibits screensaver on GNOME, KDE and Cinnamon
static bool InhibitFDO()
{
QDBusInterface interface("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver");
if (!interface.isValid())
return false;

QDBusReply<uint32_t> reply = interface.call("Inhibit", s_app_id, QObject::tr("Playing a game"));
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.freedesktop.ScreenSaver::Inhibit failed: {}",
interface.lastError().message().toStdString());
return false;
}
INFO_LOG_FMT(VIDEO, "org.freedesktop.ScreenSaver::Inhibit succeeded");
s_fdo_cookie = reply;
return true;
}

// Inhibits screensaver when sandboxed through Flatpak
// Does not work on KDE Plasma versions before 6.1.5
static bool InhibitPortal()
{
QDBusInterface interface("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Inhibit");
if (!interface.isValid())
return false;

QHash<QString, QVariant> options;
options["handle_token"] = "dolphin_" + QString::number(std::rand(), 0x10);
options["reason"] = QObject::tr("Playing a game");
uint32_t flags = 9; // logout | idle
QDBusReply<QDBusObjectPath> reply = interface.call("Inhibit", "", flags, options);
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.freedesktop.portal.Desktop::Inhibit failed: {}",
interface.lastError().message().toStdString());
return false;
}
INFO_LOG_FMT(VIDEO, "org.freedesktop.portal.Desktop::Inhibit succeeded");
s_portal_handle = reply.value().path();
return true;
}

static void Uninhibit()
{
if (s_fdo_cookie)
{
QDBusInterface interface("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver");
interface.call("UnInhibit", s_fdo_cookie);
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.freedesktop.ScreenSaver::UnInhibit failed: {}",
interface.lastError().message().toStdString());
}
else
{
INFO_LOG_FMT(VIDEO, "org.freedesktop.ScreenSaver::UnInhibit succeeded");
}
s_fdo_cookie = 0;
}

if (s_xfce_cookie)
{
QDBusInterface interface("org.xfce.ScreenSaver", "/org/xfce/ScreenSaver",
"org.xfce.ScreenSaver");
interface.call("UnInhibit", s_xfce_cookie);
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.xfce.ScreenSaver::UnInhibit failed: {}",
interface.lastError().message().toStdString());
}
else
{
INFO_LOG_FMT(VIDEO, "org.xfce.ScreenSaver::UnInhibit succeeded");
}
s_xfce_cookie = 0;
}

if (s_mate_cookie)
{
QDBusInterface interface("org.mate.ScreenSaver", "/", "org.mate.ScreenSaver");
interface.call("UnInhibit", s_mate_cookie);
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.mate.ScreenSaver::UnInhibit failed: {}",
interface.lastError().message().toStdString());
}
else
{
INFO_LOG_FMT(VIDEO, "org.mate.ScreenSaver::UnInhibit succeeded");
}
s_mate_cookie = 0;
}

if (!s_portal_handle.isEmpty())
{
QDBusInterface interface("org.freedesktop.portal.Desktop", s_portal_handle,
"org.freedesktop.portal.Request");
interface.call("Close");
if (interface.lastError().isValid())
{
WARN_LOG_FMT(VIDEO, "org.freedesktop.portal.Request::Close failed: {}",
interface.lastError().message().toStdString());
}
else
{
INFO_LOG_FMT(VIDEO, "org.freedesktop.portal.Request::Close succeeded");
}
s_portal_handle = QString();
}
}

} // namespace DBusUtils
11 changes: 11 additions & 0 deletions Source/Core/UICommon/DBusUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace DBusUtils
{

void InhibitScreenSaver(bool inhibit);

} // namespace DBusUtils
12 changes: 4 additions & 8 deletions Source/Core/UICommon/UICommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
#include "UICommon/DiscordPresence.h"
#include "UICommon/USBUtils.h"

#ifdef HAVE_X11
#include "UICommon/X11Utils.h"
#ifdef HAVE_QTDBUS
#include "UICommon/DBusUtils.h"
#endif

#ifdef __APPLE__
Expand Down Expand Up @@ -480,17 +480,13 @@ bool TriggerSTMPowerEvent()
return true;
}

#ifdef HAVE_X11
void InhibitScreenSaver(Window win, bool inhibit)
#else
void InhibitScreenSaver(bool inhibit)
#endif
{
// Inhibit the screensaver. Depending on the operating system this may also
// disable low-power states and/or screen dimming.

#ifdef HAVE_X11
X11Utils::InhibitScreensaver(win, inhibit);
#ifdef HAVE_QTDBUS
DBusUtils::InhibitScreenSaver(inhibit);
#endif

#ifdef _WIN32
Expand Down
6 changes: 1 addition & 5 deletions Source/Core/UICommon/UICommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ void Shutdown();
void InitControllers(const WindowSystemInfo& wsi);
void ShutdownControllers();

#ifdef HAVE_X11
void InhibitScreenSaver(unsigned long win, bool enable);
#else
void InhibitScreenSaver(bool enable);
#endif
void InhibitScreenSaver(bool inhibit);

// Calls std::locale::global, selecting a fallback locale if the
// requested locale isn't available
Expand Down
18 changes: 0 additions & 18 deletions Source/Core/UICommon/X11Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,6 @@ bool ToggleFullscreen(Display* dpy, Window win)
return true;
}

void InhibitScreensaver(Window win, bool suspend)
{
char id[11];
snprintf(id, sizeof(id), "0x%lx", win);

// Call xdg-screensaver
char* argv[4] = {(char*)"xdg-screensaver", (char*)(suspend ? "suspend" : "resume"), id, nullptr};
pid_t pid;
if (!posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ))
{
int status;
while (waitpid(pid, &status, 0) == -1)
;

INFO_LOG_FMT(VIDEO, "Started xdg-screensaver (PID = {})", pid);
}
}

#ifdef HAVE_XRANDR
XRRConfiguration::XRRConfiguration(Display* _dpy, Window _win)
: dpy(_dpy), win(_win), screenResources(nullptr), outputInfo(nullptr), crtcInfo(nullptr),
Expand Down
4 changes: 0 additions & 4 deletions Source/Core/UICommon/X11Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
namespace X11Utils
{
bool ToggleFullscreen(Display* dpy, Window win);
Window XWindowFromHandle(void* Handle);
Display* XDisplayFromHandle(void* Handle);

void InhibitScreensaver(Window win, bool suspend);

#ifdef HAVE_XRANDR
class XRRConfiguration
Expand Down

0 comments on commit 2748771

Please sign in to comment.