diff --git a/CMakeLists.txt b/CMakeLists.txt index b4b4b7cd8..ad201f9c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ if((LINUX OR FREEBSD) AND NOT ANDROID) option(USE_DRMKMS "Support DRM/KMS OpenGL contexts" OFF) option(USE_FBDEV "Support FBDev OpenGL contexts" OFF) option(USE_EVDEV "Support EVDev controller interface" OFF) + option(USE_DBUS "Enable DBus support for screensaver inhibiting" ON) endif() # Force EGL when using Wayland diff --git a/scripts/make-appimage.sh b/scripts/make-appimage.sh index 25518b484..2d04d0df6 100755 --- a/scripts/make-appimage.sh +++ b/scripts/make-appimage.sh @@ -148,6 +148,7 @@ declare -a SYSLIBS=( "libhx509.so.5" "libsqlite3.so.0" "libcrypt.so.1" + "libdbus-1.so.3" ) declare -a DEPLIBS=( diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index 6787bf87c..1496aad3f 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -126,6 +126,14 @@ if(USE_X11) target_include_directories(frontend-common PRIVATE "${X11_INCLUDE_DIR}") endif() +if(USE_DBUS) + target_compile_definitions(frontend-common PRIVATE USE_DBUS) + find_package(PkgConfig REQUIRED) + pkg_check_modules(DBUS REQUIRED dbus-1) + target_include_directories(frontend-common PRIVATE ${DBUS_INCLUDE_DIRS}) + target_link_libraries(frontend-common PRIVATE ${DBUS_LINK_LIBRARIES}) +endif() + if(ENABLE_DISCORD_PRESENCE) target_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1) target_link_libraries(frontend-common PRIVATE discord-rpc) diff --git a/src/frontend-common/platform_misc_unix.cpp b/src/frontend-common/platform_misc_unix.cpp index 3b48a5814..0033b1ebc 100644 --- a/src/frontend-common/platform_misc_unix.cpp +++ b/src/frontend-common/platform_misc_unix.cpp @@ -3,19 +3,18 @@ #include "common/log.h" #include "common/string.h" -#include "platform_misc.h" #include "input_manager.h" +#include "platform_misc.h" #include Log_SetChannel(FrontendCommon); #include #include -#ifdef USE_X11 +#if !defined(USE_DBUS) && defined(USE_X11) #include #include - static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi) { TinyString command; @@ -40,10 +39,80 @@ static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi) return true; } -#endif // USE_X11 +#elif defined(USE_DBUS) +#include +bool ChangeScreenSaverStateDBus(const bool inhibit_requested, const char* program_name, const char* reason) +{ + static dbus_uint32_t s_cookie; + // "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_cookie)) + goto cleanup; + } + // 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_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 static bool SetScreensaverInhibit(bool inhibit) { +#ifdef USE_DBUS + return ChangeScreenSaverStateDBus(inhibit, "DuckStation", "DuckStation VM is running."); +#else + std::optional wi(Host::GetTopLevelWindowInfo()); if (!wi.has_value()) { @@ -62,6 +131,7 @@ static bool SetScreensaverInhibit(bool inhibit) Log_ErrorPrintf("Unknown type: %u", static_cast(wi->type)); return false; } +#endif } static bool s_screensaver_suspended;