diff --git a/.github/workflows/scripts/linux/appimage-qt.sh b/.github/workflows/scripts/linux/appimage-qt.sh index eba55a37ef..253449b373 100755 --- a/.github/workflows/scripts/linux/appimage-qt.sh +++ b/.github/workflows/scripts/linux/appimage-qt.sh @@ -158,6 +158,7 @@ declare -a SYSLIBS=( "libhx509.so.5" "libsqlite3.so.0" "libcrypt.so.1" + "libdbus-1.so.3" ) declare -a DEPLIBS=( diff --git a/cmake/BuildParameters.cmake b/cmake/BuildParameters.cmake index 2a50035f5b..05ee34e5bd 100644 --- a/cmake/BuildParameters.cmake +++ b/cmake/BuildParameters.cmake @@ -34,6 +34,7 @@ if(UNIX AND NOT APPLE) option(USE_LEGACY_USER_DIRECTORY "Use legacy home/PCSX2 user directory instead of XDG standard" OFF) option(X11_API "Enable X11 support" ON) option(WAYLAND_API "Enable Wayland support" ON) + option(DBUS_API "Enable DBus support for screensaver inhibiting" ON) endif() if(APPLE) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 95927bce5b..4baa21db47 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -218,6 +218,14 @@ if(APPLE) target_link_options(common PRIVATE -fobjc-link-runtime) endif() +if(DBUS_API) + target_compile_definitions(common PRIVATE DBUS_API) + find_package(PkgConfig REQUIRED) + pkg_check_modules(DBUS REQUIRED dbus-1) + target_include_directories(common PRIVATE ${DBUS_INCLUDE_DIRS}) + target_link_libraries(common PRIVATE ${DBUS_LINK_LIBRARIES}) +endif() + if(USE_OPENGL) if(WIN32) target_sources(common PRIVATE diff --git a/common/Linux/LnxMisc.cpp b/common/Linux/LnxMisc.cpp index 732ea683e2..7b210e5865 100644 --- a/common/Linux/LnxMisc.cpp +++ b/common/Linux/LnxMisc.cpp @@ -31,6 +31,10 @@ #include "common/Threading.h" #include "common/WindowInfo.h" +#ifdef DBUS_API +#include +#endif + // Returns 0 on failure (not supported by the operating system). u64 GetPhysicalMemory() { @@ -69,7 +73,74 @@ std::string GetOSVersionString() #endif } -#ifdef X11_API +#ifdef DBUS_API + +static dbus_uint32_t s_screensaver_dbus_cookie; +bool ChangeScreenSaverStateDBus(const bool inhibit_requested, const char* program_name, const char* reason) +{ + // "error_dbus" doesn't need to be cleared in the end with "dbus_message_unref" at least if there is + // no error set, since calling "dbus_error_free" reinitializes it like "dbus_error_init" after freeing. + DBusError error_dbus; + dbus_error_init(&error_dbus); + DBusConnection* connection = nullptr; + DBusMessage* message = nullptr; + DBusMessage* response = nullptr; + // Initialized here because initializations should be before "goto" statements. + const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit"; + // "dbus_bus_get" gets a pointer to the same connection in libdbus, if exists, without creating a new connection. + // this doesn't need to be deleted, except if there's an error then calling "dbus_connection_unref", to free it, + // might be better so a new connection is established on the next try. + if (!(connection = dbus_bus_get(DBUS_BUS_SESSION, &error_dbus)) || (dbus_error_is_set(&error_dbus))) + goto cleanup; + if (!(message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", bus_method))) + goto cleanup; + // Initialize an append iterator for the message, gets freed with the message. + DBusMessageIter message_itr; + dbus_message_iter_init_append(message, &message_itr); + if (inhibit_requested) + { + // Append process/window name. + if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name)) + goto cleanup; + // Append reason for inhibiting the screensaver. + if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason)) + goto cleanup; + } + else + { + // Only Append the cookie. + if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_screensaver_dbus_cookie)) + goto cleanup; + s_screensaver_dbus_cookie = 0; + } + // Send message and get response. + if (!(response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error_dbus)) + || dbus_error_is_set(&error_dbus)) + goto cleanup; + if (inhibit_requested) + { + // Get the cookie from the response message. + if (!dbus_message_get_args(response, &error_dbus, DBUS_TYPE_UINT32, &s_screensaver_dbus_cookie, DBUS_TYPE_INVALID)) + goto cleanup; + } + dbus_message_unref(message); + dbus_message_unref(response); + return true; + cleanup: + if (dbus_error_is_set(&error_dbus)) + dbus_error_free(&error_dbus); + if (connection) + dbus_connection_unref(connection); + if (message) + dbus_message_unref(message); + if (response) + dbus_message_unref(response); + return false; +} + +#endif + +#if !defined(DBUS_API) && defined(X11_API) static bool SetScreensaverInhibitX11(const WindowInfo& wi, bool inhibit) { @@ -88,8 +159,6 @@ static bool SetScreensaverInhibitX11(const WindowInfo& wi, bool inhibit) return (res == 0); } -#endif - static bool SetScreensaverInhibit(const WindowInfo& wi, bool inhibit) { switch (wi.type) @@ -106,8 +175,18 @@ static bool SetScreensaverInhibit(const WindowInfo& wi, bool inhibit) static std::optional s_inhibit_window_info; +#endif + bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit) { + +#ifdef DBUS_API + + return ChangeScreenSaverStateDBus(inhibit, "PCSX2", "PCSX2 VM is running."); + +#else + + //ChangeScreenSaverStateDBus if (s_inhibit_window_info.has_value()) { // Bit of extra logic here, because wx spams it and we don't want to @@ -132,6 +211,9 @@ bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit) s_inhibit_window_info = wi; return true; + +#endif + } bool Common::PlaySoundAsync(const char* path)