mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-10-30 07:11:05 +00:00
Compare commits
4 commits
1fa436c619
...
ccd8f900e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccd8f900e8 | ||
|
|
3c1badf183 | ||
|
|
3a22976fec | ||
|
|
8baa6d2a20 |
6 changed files with 344 additions and 27 deletions
|
|
@ -23,10 +23,6 @@ if (APPLE)
|
|||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(SDL_VULKAN_ENABLED ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
if (CMAKE_OSX_ARCHITECTURES)
|
||||
set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR)
|
||||
|
|
|
|||
|
|
@ -352,23 +352,13 @@ if (UNLEASHED_RECOMP_FLATPAK)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (UNLEASHED_RECOMP_D3D12)
|
||||
find_package(directx-headers CONFIG REQUIRED)
|
||||
find_package(directx12-agility CONFIG REQUIRED)
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE
|
||||
UNLEASHED_RECOMP_D3D12
|
||||
D3D12MA_USING_DIRECTX_HEADERS
|
||||
D3D12MA_OPTIONS16_SUPPORTED
|
||||
)
|
||||
endif()
|
||||
|
||||
if (SDL_VULKAN_ENABLED)
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE SDL_VULKAN_ENABLED)
|
||||
endif()
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
if (UNLEASHED_RECOMP_D3D12)
|
||||
find_package(directx-headers CONFIG REQUIRED)
|
||||
find_package(directx12-agility CONFIG REQUIRED)
|
||||
target_compile_definitions(UnleashedRecomp PRIVATE UNLEASHED_RECOMP_D3D12)
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
|
||||
add_custom_command(TARGET UnleashedRecomp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_PROPERTY:Microsoft::DirectX12-Core,IMPORTED_LOCATION_RELEASE> $<TARGET_FILE_DIR:UnleashedRecomp>/D3D12
|
||||
|
|
@ -379,9 +369,6 @@ if (UNLEASHED_RECOMP_D3D12)
|
|||
)
|
||||
|
||||
target_link_libraries(UnleashedRecomp PRIVATE
|
||||
Microsoft::DirectX-Headers
|
||||
Microsoft::DirectX-Guids
|
||||
Microsoft::DirectX12-Agility
|
||||
Microsoft::DirectXShaderCompiler
|
||||
Microsoft::DXIL
|
||||
dxgi
|
||||
|
|
@ -423,9 +410,12 @@ target_include_directories(UnleashedRecomp PRIVATE
|
|||
)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
target_include_directories(UnleashedRecomp PRIVATE ${X11_INCLUDE_DIR})
|
||||
target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES})
|
||||
pkg_search_module(GLIB REQUIRED glib-2.0)
|
||||
pkg_search_module(GIO REQUIRED gio-2.0)
|
||||
target_include_directories(UnleashedRecomp PRIVATE ${X11_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS})
|
||||
target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES} ${GLIB_LIBRARIES} ${GIO_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_precompile_headers(UnleashedRecomp PUBLIC ${UNLEASHED_RECOMP_PRECOMPILED_HEADERS})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,328 @@
|
|||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ranges>
|
||||
#include <gio/gio.h>
|
||||
#include <os/media.h>
|
||||
#include <os/logger.h>
|
||||
|
||||
enum class PlaybackStatus
|
||||
{
|
||||
Stopped,
|
||||
Playing,
|
||||
Paused
|
||||
};
|
||||
|
||||
static const char* DBusInterface = "org.freedesktop.DBus";
|
||||
static const char* DBusPropertiesInterface = "org.freedesktop.DBus.Properties";
|
||||
static const char* DBusPath = "/org/freedesktop/DBus";
|
||||
static const char* MPRIS2Interface = "org.mpris.MediaPlayer2";
|
||||
static const char* MPRIS2PlayerInterface = "org.mpris.MediaPlayer2.Player";
|
||||
static const char* MPRIS2Path = "/org/mpris/MediaPlayer2";
|
||||
|
||||
static std::optional<std::thread> g_dbusThread;
|
||||
static std::unordered_map<std::string, PlaybackStatus> g_playerStatus;
|
||||
static std::atomic<bool> g_isPlaying = false;
|
||||
|
||||
static PlaybackStatus PlaybackStatusFromString(const char* str)
|
||||
{
|
||||
if (g_str_equal(str, "Playing"))
|
||||
return PlaybackStatus::Playing;
|
||||
else if (g_str_equal(str, "Paused"))
|
||||
return PlaybackStatus::Paused;
|
||||
else
|
||||
return PlaybackStatus::Stopped;
|
||||
}
|
||||
|
||||
static void UpdateActiveStatus()
|
||||
{
|
||||
g_isPlaying = std::ranges::any_of(
|
||||
g_playerStatus | std::views::values,
|
||||
[](PlaybackStatus status) { return status == PlaybackStatus::Playing; }
|
||||
);
|
||||
}
|
||||
|
||||
static void UpdateActivePlayers(const char* name, PlaybackStatus status)
|
||||
{
|
||||
g_playerStatus.insert_or_assign(name, status);
|
||||
UpdateActiveStatus();
|
||||
}
|
||||
|
||||
static PlaybackStatus MPRISGetPlaybackStatus(GDBusConnection* connection, const gchar* name)
|
||||
{
|
||||
GError* error;
|
||||
GVariant* response;
|
||||
GVariant* tupleChild;
|
||||
GVariant* value;
|
||||
PlaybackStatus status;
|
||||
|
||||
error = NULL;
|
||||
|
||||
response = g_dbus_connection_call_sync(
|
||||
connection,
|
||||
name,
|
||||
MPRIS2Path,
|
||||
DBusPropertiesInterface,
|
||||
"Get",
|
||||
g_variant_new("(ss)", MPRIS2PlayerInterface, "PlaybackStatus"),
|
||||
G_VARIANT_TYPE("(v)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error
|
||||
);
|
||||
|
||||
if (!response)
|
||||
{
|
||||
LOGF_ERROR("Failed to process D-Bus Get: {}", error->message);
|
||||
g_clear_error(&error);
|
||||
return PlaybackStatus::Stopped;
|
||||
}
|
||||
|
||||
tupleChild = g_variant_get_child_value(response, 0);
|
||||
value = g_variant_get_variant(tupleChild);
|
||||
|
||||
if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
LOG_ERROR("Failed to process D-Bus Get");
|
||||
g_variant_unref(tupleChild);
|
||||
return PlaybackStatus::Stopped;
|
||||
}
|
||||
|
||||
status = PlaybackStatusFromString(g_variant_get_string(value, NULL));
|
||||
|
||||
g_variant_unref(value);
|
||||
g_variant_unref(tupleChild);
|
||||
g_variant_unref(response);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Something is very wrong with the system if this happens
|
||||
static void DBusConnectionClosed(GDBusConnection* connection,
|
||||
gboolean remotePeerVanished,
|
||||
GError* error,
|
||||
gpointer userData)
|
||||
{
|
||||
LOG_ERROR("D-Bus connection closed");
|
||||
g_isPlaying = false;
|
||||
g_main_loop_quit((GMainLoop*)userData);
|
||||
}
|
||||
|
||||
static void DBusNameOwnerChanged(GDBusConnection* connection,
|
||||
const gchar* senderName,
|
||||
const gchar* objectPath,
|
||||
const gchar* interfaceName,
|
||||
const gchar* signalName,
|
||||
GVariant* parameters,
|
||||
gpointer userData)
|
||||
{
|
||||
const char* name;
|
||||
const char* oldOwner;
|
||||
const char* newOwner;
|
||||
|
||||
g_variant_get(parameters, "(&s&s&s)", &name, &oldOwner, &newOwner);
|
||||
|
||||
if (g_str_has_prefix(name, MPRIS2Interface))
|
||||
{
|
||||
if (oldOwner[0])
|
||||
{
|
||||
g_playerStatus.erase(oldOwner);
|
||||
}
|
||||
|
||||
UpdateActiveStatus();
|
||||
}
|
||||
}
|
||||
|
||||
static void MPRISPropertiesChanged(GDBusConnection* connection,
|
||||
const gchar* senderName,
|
||||
const gchar* objectPath,
|
||||
const gchar* interfaceName,
|
||||
const gchar* signalName,
|
||||
GVariant* parameters,
|
||||
gpointer userData)
|
||||
{
|
||||
const char* interface;
|
||||
GVariant* changed;
|
||||
GVariantIter iter;
|
||||
const char* key;
|
||||
GVariant* value;
|
||||
PlaybackStatus playbackStatus;
|
||||
|
||||
g_variant_get_child(parameters, 0, "&s", &interface);
|
||||
g_variant_get_child(parameters, 1, "@a{sv}", &changed);
|
||||
|
||||
g_variant_iter_init(&iter, changed);
|
||||
while (g_variant_iter_next(&iter, "{&sv}", &key, &value))
|
||||
{
|
||||
if (g_str_equal(key, "PlaybackStatus"))
|
||||
{
|
||||
playbackStatus = PlaybackStatusFromString(g_variant_get_string(value, NULL));
|
||||
UpdateActivePlayers(senderName, playbackStatus);
|
||||
g_variant_unref(value);
|
||||
break;
|
||||
}
|
||||
g_variant_unref(value);
|
||||
}
|
||||
|
||||
g_variant_unref(changed);
|
||||
}
|
||||
|
||||
/* Called upon CONNECT to discover already active MPRIS2 players by looking for
|
||||
well-known bus names that begin with the MPRIS2 path.
|
||||
g_playerStatus stores unique connection names,
|
||||
not their well-known ones, as the PropertiesChanged signal only provides the
|
||||
former. */
|
||||
static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer userData)
|
||||
{
|
||||
GDBusConnection* connection;
|
||||
GError* error;
|
||||
GVariant* response;
|
||||
GVariant* tupleChild;
|
||||
GVariantIter iter;
|
||||
const gchar* name;
|
||||
|
||||
connection = G_DBUS_CONNECTION(object);
|
||||
error = NULL;
|
||||
response = g_dbus_connection_call_finish(connection, res, &error);
|
||||
|
||||
if (!response)
|
||||
{
|
||||
LOGF_ERROR("Failed to process D-Bus ListNames: {}", error->message);
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
|
||||
tupleChild = g_variant_get_child_value(response, 0);
|
||||
|
||||
g_variant_iter_init(&iter, tupleChild);
|
||||
while (g_variant_iter_next(&iter, "&s", &name))
|
||||
{
|
||||
GVariant* ownerResponse;
|
||||
const gchar* ownerName;
|
||||
PlaybackStatus status;
|
||||
|
||||
if (!g_str_has_prefix(name, MPRIS2Interface))
|
||||
continue;
|
||||
|
||||
ownerResponse = g_dbus_connection_call_sync(
|
||||
connection,
|
||||
DBusInterface,
|
||||
DBusPath,
|
||||
DBusInterface,
|
||||
"GetNameOwner",
|
||||
g_variant_new("(s)", name),
|
||||
G_VARIANT_TYPE("(s)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error
|
||||
);
|
||||
|
||||
if (!ownerResponse)
|
||||
{
|
||||
LOGF_ERROR("Failed to process D-Bus GetNameOwner: {}", error->message);
|
||||
g_clear_error(&error);
|
||||
g_variant_unref(tupleChild);
|
||||
g_variant_unref(response);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_get(ownerResponse, "(&s)", &ownerName);
|
||||
status = MPRISGetPlaybackStatus(connection, ownerName);
|
||||
|
||||
g_playerStatus.insert_or_assign(ownerName, status);
|
||||
g_variant_unref(ownerResponse);
|
||||
}
|
||||
|
||||
UpdateActiveStatus();
|
||||
|
||||
g_variant_unref(tupleChild);
|
||||
g_variant_unref(response);
|
||||
}
|
||||
|
||||
static void DBusThreadProc()
|
||||
{
|
||||
GMainContext* mainContext;
|
||||
GMainLoop* mainLoop;
|
||||
GError* error;
|
||||
GDBusConnection* connection;
|
||||
|
||||
mainContext = g_main_context_new();
|
||||
g_main_context_push_thread_default(mainContext);
|
||||
mainLoop = g_main_loop_new(mainContext, FALSE);
|
||||
error = NULL;
|
||||
|
||||
connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
|
||||
if (!connection)
|
||||
{
|
||||
LOGF_ERROR("Failed to connect to D-Bus: {}", error->message);
|
||||
g_clear_error(&error);
|
||||
g_main_context_unref(mainContext);
|
||||
g_main_loop_unref(mainLoop);
|
||||
return;
|
||||
}
|
||||
|
||||
g_dbus_connection_set_exit_on_close(connection, FALSE);
|
||||
g_signal_connect(connection, "closed", G_CALLBACK(DBusConnectionClosed), mainLoop);
|
||||
|
||||
// Listen for player connection changes
|
||||
g_dbus_connection_signal_subscribe(
|
||||
connection,
|
||||
DBusInterface,
|
||||
DBusInterface,
|
||||
"NameOwnerChanged",
|
||||
DBusPath,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
DBusNameOwnerChanged,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
// Listen for player status changes
|
||||
g_dbus_connection_signal_subscribe(
|
||||
connection,
|
||||
NULL,
|
||||
DBusPropertiesInterface,
|
||||
"PropertiesChanged",
|
||||
MPRIS2Path,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
MPRISPropertiesChanged,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
// Request list of current players
|
||||
g_dbus_connection_call(
|
||||
connection,
|
||||
DBusInterface,
|
||||
DBusPath,
|
||||
DBusInterface,
|
||||
"ListNames",
|
||||
NULL,
|
||||
G_VARIANT_TYPE("(as)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
DBusListNamesReceived,
|
||||
NULL
|
||||
);
|
||||
|
||||
g_main_loop_run(mainLoop);
|
||||
}
|
||||
|
||||
bool os::media::IsExternalMediaPlaying()
|
||||
{
|
||||
// This functionality is not supported in Linux.
|
||||
return false;
|
||||
if (!g_dbusThread)
|
||||
{
|
||||
g_dbusThread.emplace(DBusThreadProc);
|
||||
g_dbusThread->detach();
|
||||
}
|
||||
|
||||
return g_isPlaying;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ bool AudioPatches::CanAttenuate()
|
|||
m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763;
|
||||
|
||||
return m_isAttenuationSupported;
|
||||
#elif __linux__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
|
|
|||
8
thirdparty/CMakeLists.txt
vendored
8
thirdparty/CMakeLists.txt
vendored
|
|
@ -15,6 +15,14 @@ set(SDL2MIXER_OPUS OFF)
|
|||
set(SDL2MIXER_VORBIS "VORBISFILE")
|
||||
set(SDL2MIXER_WAVPACK OFF)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(SDL_VULKAN_ENABLED ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(D3D12_AGILITY_SDK_ENABLED ON CACHE BOOL "")
|
||||
endif()
|
||||
|
||||
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/msdf-atlas-gen")
|
||||
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended")
|
||||
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/o1heap")
|
||||
|
|
|
|||
2
thirdparty/plume
vendored
2
thirdparty/plume
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit fffeb35f836d8c945697ec82b735e77db401e2de
|
||||
Subproject commit 11926860e878e68626ea99ec88562ce2b8badc4f
|
||||
Loading…
Add table
Reference in a new issue