From d53e55e056679d0727b4025f7435e4fb0f3e43c4 Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Tue, 20 Jan 2026 00:41:16 -0500 Subject: [PATCH 1/7] Allow music attenuation to be enabled on Linux --- UnleashedRecomp/patches/audio_patches.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UnleashedRecomp/patches/audio_patches.cpp b/UnleashedRecomp/patches/audio_patches.cpp index 749d1c69..94beecd8 100644 --- a/UnleashedRecomp/patches/audio_patches.cpp +++ b/UnleashedRecomp/patches/audio_patches.cpp @@ -20,13 +20,19 @@ static be* GetVolume(bool isMusic = true) bool AudioPatches::CanAttenuate() { -#if _WIN32 +#if defined(_WIN32) || defined(__linux__) if (m_isAttenuationSupported >= 0) return m_isAttenuationSupported; auto version = os::version::GetOSVersion(); +#if defined(_WIN32) m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763; +#endif + +#if defined(__linux__) + m_isAttenuationSupported = true; +#endif return m_isAttenuationSupported; #else From d28dab8840e21fcf87e8ba18fe1baaf5c845d3b7 Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Tue, 20 Jan 2026 22:19:02 -0500 Subject: [PATCH 2/7] Import D-Bus --- UnleashedRecomp/CMakeLists.txt | 5 ++ UnleashedRecomp/os/linux/media_linux.cpp | 3 +- thirdparty/dbus/DBusConfig.cmake | 59 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 thirdparty/dbus/DBusConfig.cmake diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 01e040ab..8ae7fa66 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -413,6 +413,11 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(X11 REQUIRED) target_include_directories(UnleashedRecomp PRIVATE ${X11_INCLUDE_DIR}) target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES}) + + set(DBus_DIR "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/dbus") + find_package(DBus REQUIRED) + include_directories(UnleashedRecomp PRIVATE ${DBUS_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${DBUS_LIBRARIES}) endif() target_precompile_headers(UnleashedRecomp PUBLIC ${UNLEASHED_RECOMP_PRECOMPILED_HEADERS}) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index 9fc19b0a..3bb41076 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -1,7 +1,8 @@ #include +#include +#include bool os::media::IsExternalMediaPlaying() { - // This functionality is not supported in Linux. return false; } diff --git a/thirdparty/dbus/DBusConfig.cmake b/thirdparty/dbus/DBusConfig.cmake new file mode 100644 index 00000000..d739df2c --- /dev/null +++ b/thirdparty/dbus/DBusConfig.cmake @@ -0,0 +1,59 @@ +# - Try to find DBus +# Once done, this will define +# +# DBUS_FOUND - system has DBus +# DBUS_INCLUDE_DIRS - the DBus include directories +# DBUS_LIBRARIES - link these to use DBus +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1) + +FIND_LIBRARY(DBUS_LIBRARIES + NAMES dbus-1 + HINTS ${PC_DBUS_LIBDIR} + ${PC_DBUS_LIBRARY_DIRS} +) + +FIND_PATH(DBUS_INCLUDE_DIR + NAMES dbus/dbus.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} +) + +GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH) +FIND_PATH(DBUS_ARCH_INCLUDE_DIR + NAMES dbus/dbus-arch-deps.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} + ${_DBUS_LIBRARY_DIR} + ${DBUS_INCLUDE_DIR} + PATH_SUFFIXES include +) + +SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES) From 43da7d33f3bcef546c59db33a808ec8a66f4d4cc Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Tue, 20 Jan 2026 23:09:32 -0500 Subject: [PATCH 3/7] Implement music attenuation for Linux --- UnleashedRecomp/os/linux/media_linux.cpp | 183 ++++++++++++++++++++++- 1 file changed, 182 insertions(+), 1 deletion(-) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index 3bb41076..53bd2d54 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -2,7 +2,188 @@ #include #include +static DBusConnection* CreateDBusConnection() +{ + DBusError dbusError; + dbus_error_init(&dbusError); + + DBusConnection* result = dbus_bus_get(DBUS_BUS_SESSION, &dbusError); + if (dbus_error_is_set(&dbusError)) + { + LOGF_ERROR("Failed to create DBus connection: {0}", dbusError.name); + return nullptr; + } + + return result; +} + +static void DestroyDBusConnection(DBusConnection* connection) +{ + assert(connection != nullptr); + dbus_connection_unref(connection); +} + +static std::vector GetMediaPlayerBusNames(DBusConnection* connection) +{ + assert(connection != nullptr); + + std::vector result; + + DBusMessageIter rootIterator; + DBusMessageIter arrayIterator; + DBusPendingCall* pendingReturn; + + DBusMessage *message = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); + if (!message) + { + LOG_ERROR("Failed to create D-Bus Message!"); + return result; + } + + dbus_message_iter_init_append(message, &rootIterator); + if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, -1)) + { + LOG_ERROR("Failed to create D-Bus Message!"); + return result; + } + + if (!pendingReturn) + { + LOG_ERROR("D-Bus Pending call is null!"); + return result; + } + + dbus_connection_flush(connection); + dbus_message_unref(message); + dbus_pending_call_block(pendingReturn); + message = dbus_pending_call_steal_reply(pendingReturn); + if (!message) + { + LOG_ERROR("Failed to get D-Bus reply!"); + return result; + } + + dbus_pending_call_unref(pendingReturn); + + if (!dbus_message_iter_init(message, &rootIterator)) + { + LOG_ERROR("D-Bus message has no arguments!"); + return result; + } + + if (dbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY) + { + LOG_ERROR("D-Bus message returned invalid type!"); + return result; + } + + dbus_message_iter_recurse(&rootIterator, &arrayIterator); + do + { + const char *rawName; + dbus_message_iter_get_basic(&arrayIterator, &rawName); + const std::string name = rawName; + if (name.starts_with("org.mpris.MediaPlayer2.")) + result.emplace_back(name); + } while (dbus_message_iter_next(&arrayIterator)); + + return result; +} + +static bool IsMediaPlayerPlaying(DBusConnection* connection, const std::string& busName) +{ + assert(connection != nullptr); + + DBusMessageIter rootIterator; + DBusMessageIter arrayIterator; + DBusPendingCall* pendingReturn; + + DBusMessage* message = dbus_message_new_method_call(busName.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + if (!message) + { + LOG_ERROR("Failed to create D-Bus Message!"); + return false; + } + + auto interfaceName = "org.mpris.MediaPlayer2.Player"; + auto propertyName = "PlaybackStatus"; + + dbus_message_iter_init_append(message, &rootIterator); + if (!dbus_message_iter_append_basic(&rootIterator, DBUS_TYPE_STRING, &interfaceName)) + { + LOG_ERROR("Failed to append interface name to D-Bus Message!"); + return false; + } + + if (!dbus_message_iter_append_basic(&rootIterator, DBUS_TYPE_STRING, &propertyName)) + { + LOG_ERROR("Failed to append property name to D-Bus Message!"); + return false; + } + + if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, -1)) + { + LOG_ERROR("Failed to create D-Bus Message!"); + return false; + } + + if (!pendingReturn) + { + LOG_ERROR("D-Bus Pending call is null!"); + return false; + } + + dbus_connection_flush(connection); + dbus_message_unref(message); + dbus_pending_call_block(pendingReturn); + message = dbus_pending_call_steal_reply(pendingReturn); + if (!message) + { + LOG_ERROR("Failed to get D-Bus reply!"); + return false; + } + + dbus_pending_call_unref(pendingReturn); + + if (!dbus_message_iter_init(message, &rootIterator)) + { + LOG_ERROR("D-Bus message has no arguments!"); + return false; + } + + if (dbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT) + { + LOG_ERROR("D-Bus message returned invalid type!"); + return false; + } + + dbus_message_iter_recurse(&rootIterator, &arrayIterator); + if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING) + { + LOG_ERROR("D-Bus message returned invalid type!"); + return false; + } + + const char *rawStatus; + dbus_message_iter_get_basic(&arrayIterator, &rawStatus); + const std::string status = rawStatus; + return status == "Playing"; +} + bool os::media::IsExternalMediaPlaying() { + const auto dbusConnection = CreateDBusConnection(); + if (!dbusConnection) + return false; + + std::vector busNames = GetMediaPlayerBusNames(dbusConnection); + for (const auto& bus : busNames) + { + if (IsMediaPlayerPlaying(dbusConnection, bus)) + return true; + } + + DestroyDBusConnection(dbusConnection); + return false; -} +} \ No newline at end of file From 809c4fcb93b5e2c21322aec93e6b4f412e349e2d Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Tue, 20 Jan 2026 23:43:04 -0500 Subject: [PATCH 4/7] Dereference D-Bus connection even when media is playing --- UnleashedRecomp/os/linux/media_linux.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index 53bd2d54..fc40e687 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -176,14 +176,18 @@ bool os::media::IsExternalMediaPlaying() if (!dbusConnection) return false; + bool result = false; + std::vector busNames = GetMediaPlayerBusNames(dbusConnection); for (const auto& bus : busNames) { if (IsMediaPlayerPlaying(dbusConnection, bus)) - return true; + { + result = true; + break; + } } DestroyDBusConnection(dbusConnection); - - return false; + return result; } \ No newline at end of file From 34896b0205c0030b7da53101bbc45ed50c25f158 Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Wed, 21 Jan 2026 00:17:57 -0500 Subject: [PATCH 5/7] Handle when PlaybackStatus doesn't return variant --- UnleashedRecomp/os/linux/media_linux.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index fc40e687..98f2fdf8 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -151,10 +151,13 @@ static bool IsMediaPlayerPlaying(DBusConnection* connection, const std::string& return false; } - if (dbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT) + if (dbus_message_iter_get_arg_type(&rootIterator) == DBUS_TYPE_VARIANT) { - LOG_ERROR("D-Bus message returned invalid type!"); - return false; + dbus_message_iter_recurse(&rootIterator, &arrayIterator); + } + else + { + arrayIterator = rootIterator; } dbus_message_iter_recurse(&rootIterator, &arrayIterator); From 11181136d84cb5b05ae01e6b12a3160bafeb0f70 Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Wed, 21 Jan 2026 14:10:14 -0500 Subject: [PATCH 6/7] Improve music attenuation error handling --- UnleashedRecomp/os/linux/media_linux.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index 98f2fdf8..91347193 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -27,7 +27,7 @@ static std::vector GetMediaPlayerBusNames(DBusConnection* connectio { assert(connection != nullptr); - std::vector result; + auto result = std::vector(); DBusMessageIter rootIterator; DBusMessageIter arrayIterator; @@ -41,7 +41,7 @@ static std::vector GetMediaPlayerBusNames(DBusConnection* connectio } dbus_message_iter_init_append(message, &rootIterator); - if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, -1)) + if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, 40)) { LOG_ERROR("Failed to create D-Bus Message!"); return result; @@ -121,7 +121,7 @@ static bool IsMediaPlayerPlaying(DBusConnection* connection, const std::string& return false; } - if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, -1)) + if (!dbus_connection_send_with_reply(connection, message, &pendingReturn, 40)) { LOG_ERROR("Failed to create D-Bus Message!"); return false; @@ -152,15 +152,10 @@ static bool IsMediaPlayerPlaying(DBusConnection* connection, const std::string& } if (dbus_message_iter_get_arg_type(&rootIterator) == DBUS_TYPE_VARIANT) - { dbus_message_iter_recurse(&rootIterator, &arrayIterator); - } else - { arrayIterator = rootIterator; - } - dbus_message_iter_recurse(&rootIterator, &arrayIterator); if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING) { LOG_ERROR("D-Bus message returned invalid type!"); From a75f88ed1a01e8f12926230e57778ff61b0a62bb Mon Sep 17 00:00:00 2001 From: wAABBsif Date: Wed, 21 Jan 2026 14:15:31 -0500 Subject: [PATCH 7/7] Run Linux music attenuation code on timer --- UnleashedRecomp/os/linux/media_linux.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/UnleashedRecomp/os/linux/media_linux.cpp b/UnleashedRecomp/os/linux/media_linux.cpp index 91347193..0274508c 100644 --- a/UnleashedRecomp/os/linux/media_linux.cpp +++ b/UnleashedRecomp/os/linux/media_linux.cpp @@ -2,6 +2,13 @@ #include #include +#include "app.h" + +#define MEDIA_CHECK_RATE 1 + +static float g_mediaCheckTimer; +static bool g_mediaLastResult; + static DBusConnection* CreateDBusConnection() { DBusError dbusError; @@ -170,6 +177,13 @@ static bool IsMediaPlayerPlaying(DBusConnection* connection, const std::string& bool os::media::IsExternalMediaPlaying() { + //Calling D-Bus functions too much seems cause D-Bus to stop working, so perhaps it should be run less often. + g_mediaCheckTimer -= App::s_deltaTime; + if (g_mediaCheckTimer > 0) + return g_mediaLastResult; + + g_mediaCheckTimer = MEDIA_CHECK_RATE; + const auto dbusConnection = CreateDBusConnection(); if (!dbusConnection) return false; @@ -187,5 +201,6 @@ bool os::media::IsExternalMediaPlaying() } DestroyDBusConnection(dbusConnection); + g_mediaLastResult = result; return result; } \ No newline at end of file