Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UnleashedRecomp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
205 changes: 202 additions & 3 deletions UnleashedRecomp/os/linux/media_linux.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,206 @@
#include <os/media.h>
#include <os/logger.h>
#include <dbus/dbus.h>

bool os::media::IsExternalMediaPlaying()
#include "app.h"

#define MEDIA_CHECK_RATE 1

static float g_mediaCheckTimer;
static bool g_mediaLastResult;

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<std::string> GetMediaPlayerBusNames(DBusConnection* connection)
{
// This functionality is not supported in Linux.
return false;
assert(connection != nullptr);

auto result = std::vector<std::string>();

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, 40))
{
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, 40))
{
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)
dbus_message_iter_recurse(&rootIterator, &arrayIterator);
else
arrayIterator = rootIterator;

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()
{
//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;

bool result = false;

std::vector<std::string> busNames = GetMediaPlayerBusNames(dbusConnection);
for (const auto& bus : busNames)
{
if (IsMediaPlayerPlaying(dbusConnection, bus))
{
result = true;
break;
}
}

DestroyDBusConnection(dbusConnection);
g_mediaLastResult = result;
return result;
}
8 changes: 7 additions & 1 deletion UnleashedRecomp/patches/audio_patches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ static be<float>* 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
Expand Down
59 changes: 59 additions & 0 deletions thirdparty/dbus/DBusConfig.cmake
Original file line number Diff line number Diff line change
@@ -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 <rakuco@webkit.org>
#
# 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)