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)
|
enable_language(OBJC OBJCXX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|
||||||
set(SDL_VULKAN_ENABLED ON CACHE BOOL "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_OSX_ARCHITECTURES)
|
if (CMAKE_OSX_ARCHITECTURES)
|
||||||
set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR)
|
elseif(CMAKE_SYSTEM_PROCESSOR)
|
||||||
|
|
|
||||||
|
|
@ -352,23 +352,13 @@ if (UNLEASHED_RECOMP_FLATPAK)
|
||||||
)
|
)
|
||||||
endif()
|
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)
|
find_package(CURL REQUIRED)
|
||||||
|
|
||||||
if (UNLEASHED_RECOMP_D3D12)
|
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)
|
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12)
|
||||||
add_custom_command(TARGET UnleashedRecomp POST_BUILD
|
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
|
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
|
target_link_libraries(UnleashedRecomp PRIVATE
|
||||||
Microsoft::DirectX-Headers
|
|
||||||
Microsoft::DirectX-Guids
|
|
||||||
Microsoft::DirectX12-Agility
|
|
||||||
Microsoft::DirectXShaderCompiler
|
Microsoft::DirectXShaderCompiler
|
||||||
Microsoft::DXIL
|
Microsoft::DXIL
|
||||||
dxgi
|
dxgi
|
||||||
|
|
@ -395,7 +382,7 @@ if (WIN32)
|
||||||
ntdll
|
ntdll
|
||||||
Shcore
|
Shcore
|
||||||
Synchronization
|
Synchronization
|
||||||
winmm
|
winmm
|
||||||
windowsapp
|
windowsapp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -423,9 +410,12 @@ target_include_directories(UnleashedRecomp PRIVATE
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
target_include_directories(UnleashedRecomp PRIVATE ${X11_INCLUDE_DIR})
|
pkg_search_module(GLIB REQUIRED glib-2.0)
|
||||||
target_link_libraries(UnleashedRecomp PRIVATE ${X11_LIBRARIES})
|
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()
|
endif()
|
||||||
|
|
||||||
target_precompile_headers(UnleashedRecomp PUBLIC ${UNLEASHED_RECOMP_PRECOMPILED_HEADERS})
|
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/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()
|
bool os::media::IsExternalMediaPlaying()
|
||||||
{
|
{
|
||||||
// This functionality is not supported in Linux.
|
if (!g_dbusThread)
|
||||||
return false;
|
{
|
||||||
|
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;
|
m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763;
|
||||||
|
|
||||||
return m_isAttenuationSupported;
|
return m_isAttenuationSupported;
|
||||||
|
#elif __linux__
|
||||||
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#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_VORBIS "VORBISFILE")
|
||||||
set(SDL2MIXER_WAVPACK OFF)
|
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}/msdf-atlas-gen")
|
||||||
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended")
|
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended")
|
||||||
add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/o1heap")
|
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