From 9316276dbce79bbc91c23a5ea1873b1780d2a57b Mon Sep 17 00:00:00 2001 From: EinTim23 Date: Tue, 5 Nov 2024 20:22:11 +0100 Subject: [PATCH] unfinished linux backend --- .gitmodules | 3 + CMakeLists.txt | 6 +- src/backends/linux.cpp | 179 ++++++++++++++++++++++++++++++++++++- vendor/CMakeLists.txt | 5 +- vendor/dbus/CMakeLists.txt | 127 ++++++++++++++++++++++++++ vendor/libdbus | 1 + 6 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 vendor/dbus/CMakeLists.txt create mode 160000 vendor/libdbus diff --git a/.gitmodules b/.gitmodules index c8c6969..634d9d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "vendor/wxWidgets"] path = vendor/wxWidgets url = https://github.com/wxWidgets/wxWidgets.git +[submodule "vendor/libdbus"] + path = vendor/libdbus + url = https://gitlab.freedesktop.org/dbus/dbus.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f8ec4e..734fc52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable (PlayerLink ${SOURCES}) set_property(TARGET PlayerLink PROPERTY CXX_STANDARD 20) add_subdirectory("vendor") set(LIBRARIES discord-rpc libcurl_static mbedcrypto mbedx509 mbedtls wxmono) +set(INCLUDES vendor vendor/libdbus vendor/wxWidgets/include) #use windows subsystem to disable console window and link winrt if(WIN32) @@ -32,6 +33,9 @@ elseif(APPLE) else() message(FATAL_ERROR "MediaRemote framework not found.") endif() +elseif(UNIX AND NOT APPLE) + list(APPEND LIBRARIES dbus) + list(APPEND INCLUDES "${CMAKE_BINARY_DIR}/vendor/dbus") endif() #search directories for the autogenerated wxwidgets setup.h file for all plattforms @@ -42,7 +46,7 @@ file(GLOB wx_setup_dir ) if(wx_setup_dir) message(STATUS "wxWidgets setup.h directory found: ${wx_setup_dir}") - target_include_directories(PlayerLink PRIVATE vendor vendor/wxWidgets/include ${wx_setup_dir}) + target_include_directories(PlayerLink PRIVATE ${INCLUDES} ${wx_setup_dir}) else() message(FATAL_ERROR "wx/setup.h not found. Please check your wxWidgets build configuration.") endif() diff --git a/src/backends/linux.cpp b/src/backends/linux.cpp index 9333c19..6d9814e 100644 --- a/src/backends/linux.cpp +++ b/src/backends/linux.cpp @@ -1,5 +1,182 @@ #if !defined(_WIN32) && !defined(__APPLE__) +#include + +#include + #include "../backend.hpp" -std::shared_ptr backend::getMediaInformation() { return nullptr; } +bool initialized = false; +DBusConnection* conn = nullptr; + +std::string getActivePlayer(DBusConnection* conn) { + DBusMessage* msg; + DBusMessageIter args; + DBusError err; + dbus_error_init(&err); + + msg = dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", + "ListNames"); + DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + dbus_error_free(&err); + return ""; + } + + dbus_message_iter_init(reply, &args); + DBusMessageIter sub; + dbus_message_iter_recurse(&args, &sub); + + std::string active_player; + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + const char* name; + dbus_message_iter_get_basic(&sub, &name); + + if (std::string(name).find("org.mpris.MediaPlayer2.") == 0) { + active_player = name; + break; + } + + dbus_message_iter_next(&sub); + } + + dbus_message_unref(reply); + return active_player; +} + +void getNowPlaying(DBusConnection* conn, const std::string& player) { + DBusMessage* msg; + DBusMessageIter args; + DBusError err; + dbus_error_init(&err); + + msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", + "Get"); + const char* iface = "org.mpris.MediaPlayer2.Player"; + const char* prop = "Metadata"; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID); + + DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + std::cerr << "Failed to get metadata: " << (err.message ? err.message : "Unknown error") << std::endl; + dbus_error_free(&err); + return; + } + + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_VARIANT) { + DBusMessageIter variant; + dbus_message_iter_recurse(&args, &variant); + + if (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY) { + DBusMessageIter array_iter; + dbus_message_iter_recurse(&variant, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter dict_entry; + dbus_message_iter_recurse(&array_iter, &dict_entry); + + const char* key; + dbus_message_iter_get_basic(&dict_entry, &key); + dbus_message_iter_next(&dict_entry); + + if (std::string(key) == "xesam:title") { + DBusMessageIter value_variant; + dbus_message_iter_recurse(&dict_entry, &value_variant); + + if (dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_STRING) { + const char* title; + dbus_message_iter_get_basic(&value_variant, &title); + std::cout << "Title: " << title << std::endl; + } + } else if (std::string(key) == "xesam:artist") { + DBusMessageIter value_variant; + dbus_message_iter_recurse(&dict_entry, &value_variant); + + if (dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_ARRAY) { + DBusMessageIter artist_array; + dbus_message_iter_recurse(&value_variant, &artist_array); + + std::cout << "Artist(s): "; + bool first = true; + while (dbus_message_iter_get_arg_type(&artist_array) == DBUS_TYPE_STRING) { + const char* artist; + dbus_message_iter_get_basic(&artist_array, &artist); + if (!first) + std::cout << ", "; + std::cout << artist; + first = false; + dbus_message_iter_next(&artist_array); + } + std::cout << std::endl; + } + } else if (std::string(key) == "mpris:length") { + DBusMessageIter value_variant; + dbus_message_iter_recurse(&dict_entry, &value_variant); + int arg_type = dbus_message_iter_get_arg_type(&value_variant); + if (arg_type == DBUS_TYPE_INT64 || arg_type == DBUS_TYPE_UINT64) { + int64_t length; + dbus_message_iter_get_basic(&value_variant, &length); + std::cout << "Length (Duration): " << length / 1000000.0 << " seconds" << std::endl; + } + } + + dbus_message_iter_next(&array_iter); + } + } + } else { + std::cerr << "Unexpected reply type for Metadata" << std::endl; + } + + dbus_message_unref(reply); + msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", + "Get"); + prop = "Position"; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID); + + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + std::cerr << "Failed to get position: " << (err.message ? err.message : "Unknown error") << std::endl; + dbus_error_free(&err); + return; + } + + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_VARIANT) { + DBusMessageIter variant; + dbus_message_iter_recurse(&args, &variant); + int arg_type = dbus_message_iter_get_arg_type(&variant); + if (arg_type == DBUS_TYPE_INT64 || arg_type == DBUS_TYPE_UINT64) { + int64_t position; + dbus_message_iter_get_basic(&variant, &position); + std::cout << "Current Position: " << position / 1000000.0 << " seconds" << std::endl; + } + } + dbus_message_unref(reply); +} + +std::shared_ptr backend::getMediaInformation() { + if (!initialized) { + DBusError err; + dbus_error_init(&err); + + conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (!conn) { + dbus_error_free(&err); + return nullptr; + } + initialized = true; + } + std::string player = getActivePlayer(conn); + if (player == "") + return nullptr; + getNowPlaying(conn, player); + return nullptr; +} + bool backend::toggleAutostart(bool enabled) { return false; } #endif \ No newline at end of file diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 5ebbc38..3419eef 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -18,4 +18,7 @@ set(wxBUILD_SHARED OFF) set(wxBUILD_MONOLITHIC ON) set(wxUSE_GUI ON) set(wxUSE_WEBVIEW OFF) -add_subdirectory("wxWidgets") \ No newline at end of file +add_subdirectory("wxWidgets") +if(UNIX AND NOT APPLE) + add_subdirectory("dbus") +endif() \ No newline at end of file diff --git a/vendor/dbus/CMakeLists.txt b/vendor/dbus/CMakeLists.txt new file mode 100644 index 0000000..5671df7 --- /dev/null +++ b/vendor/dbus/CMakeLists.txt @@ -0,0 +1,127 @@ +project(dbus-lib) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/vendor/libdbus/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/vendor/libdbus/cmake/modules") +SET(DBUS_DIR ${CMAKE_SOURCE_DIR}/vendor/libdbus/dbus) + +include(MacrosAutotools) +autoinit(${CMAKE_SOURCE_DIR}/vendor/libdbus/configure.ac) +autoversion(dbus) + +include (TestBigEndian) +test_big_endian(WORDS_BIGENDIAN) + +set(DBUS_PATCH_VERSION "0") + +include(Macros) +string(TIMESTAMP DBUS_BUILD_TIMESTAMP "%Y%m%d%H%M" UTC) +set(BUILD_FILEVERSION ${DBUS_MAJOR_VERSION},${DBUS_MINOR_VERSION},${DBUS_MICRO_VERSION},${DBUS_PATCH_VERSION}) +set(BUILD_TIMESTAMP ${DBUS_BUILD_TIMESTAMP}) +if(UNIX AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(DBUS_LINUX 1) +endif() +set(_GNU_SOURCE 1) +set(DBUS_LIBRARIES dbus-1) +set(DBUS_INTERNAL_LIBRARIES dbus-internal) + +set(DBUS_INTERNAL_ADD_LIBRARY_OPTIONS STATIC) +set(DBUS_INTERNAL_CLIENT_DEFINITIONS "-DDBUS_COMPILATION") +add_definitions(-DHAVE_CONFIG_H) +include(ConfigureChecks) + +set(DBUS_SESSION_SOCKET_DIR "" CACHE STRING "Default directory for session socket") + +set(DBUS_SYSTEM_PID_FILE ${DBUS_RUNSTATEDIR}/dbus/pid) + +set(DBUS_CONSOLE_AUTH_DIR "" CACHE STRING "Directory to check for pam_console/pam_foreground flag files, or empty to ignore") + +set(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=${DBUS_RUNSTATEDIR}/dbus/system_bus_socket" CACHE STRING "system bus default address") + +set(DBUS_SESSION_SOCKET_DIR "" CACHE STRING "Default directory for session socket") +if(UNIX) + if (CMAKE_CROSSCOMPILING) + if (NOT DBUS_SESSION_SOCKET_DIR) + message(FATAL_ERROR "cannot autodetect session socket directory " + "when crosscompiling, pass -DDBUS_SESSION_SOCKET_DIR=...") + endif() + elseif(NOT $ENV{TMPDIR} STREQUAL "") + set(DBUS_SESSION_SOCKET_DIR $ENV{TMPDIR}) + elseif(NOT $ENV{TEMP} STREQUAL "") + set(DBUS_SESSION_SOCKET_DIR $ENV{TEMP}) + elseif(NOT $ENV{TMP} STREQUAL "") + set(DBUS_SESSION_SOCKET_DIR $ENV{TMP}) + else() + set(DBUS_SESSION_SOCKET_DIR /tmp) + endif() +endif() + +set(DBUS_SESSION_BUS_LISTEN_ADDRESS "unix:tmpdir=${DBUS_SESSION_SOCKET_DIR}" CACHE STRING "session bus default listening address") +set(DBUS_SESSION_BUS_CONNECT_ADDRESS "autolaunch:" CACHE STRING "session bus fallback address for clients") +set(DBUS_SYSTEM_CONFIG_FILE ${DBUS_DATADIR}/dbus-1/system.conf) +set(DBUS_SESSION_CONFIG_FILE ${DBUS_DATADIR}/dbus-1/session.conf) +set(DBUS_MACHINE_UUID_FILE ${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/dbus/machine-id) +set(DBUS_USER "messagebus") +set(DBUS_TEST_USER "nobody") +set(DBUS_SESSION_CONF_MAYBE_AUTH_EXTERNAL "EXTERNAL") + + +set(DBUS_DAEMON_NAME "dbus-daemon" CACHE STRING "The name of the dbus daemon executable") +configure_file(${CMAKE_SOURCE_DIR}/vendor/libdbus/cmake/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) +configure_file(${DBUS_DIR}/dbus-arch-deps.h.in ${CMAKE_CURRENT_BINARY_DIR}/dbus/dbus-arch-deps.h ) + +add_definitions(-DDBUS_COMPILATION) + +set (DBUS_LIB_SOURCES + ${DBUS_DIR}/dbus-address.c + ${DBUS_DIR}/dbus-auth.c + ${DBUS_DIR}/dbus-bus.c + ${DBUS_DIR}/dbus-test-tap.c + ${DBUS_DIR}/dbus-connection.c + ${DBUS_DIR}/dbus-credentials.c + ${DBUS_DIR}/dbus-errors.c + ${DBUS_DIR}/dbus-keyring.c + ${DBUS_DIR}/dbus-marshal-header.c + ${DBUS_DIR}/dbus-marshal-byteswap.c + ${DBUS_DIR}/dbus-marshal-recursive.c + ${DBUS_DIR}/dbus-marshal-validate.c + ${DBUS_DIR}/dbus-message.c + ${DBUS_DIR}/dbus-misc.c + ${DBUS_DIR}/dbus-nonce.c + ${DBUS_DIR}/dbus-object-tree.c + ${DBUS_DIR}/dbus-pending-call.c + ${DBUS_DIR}/dbus-resources.c + ${DBUS_DIR}/dbus-server.c + ${DBUS_DIR}/dbus-server-socket.c + ${DBUS_DIR}/dbus-server-debug-pipe.c + ${DBUS_DIR}/dbus-sha.c + ${DBUS_DIR}/dbus-signature.c + ${DBUS_DIR}/dbus-syntax.c + ${DBUS_DIR}/dbus-timeout.c + ${DBUS_DIR}/dbus-threads.c + ${DBUS_DIR}/dbus-transport.c + ${DBUS_DIR}/dbus-transport-socket.c + ${DBUS_DIR}/dbus-watch.c + ${DBUS_DIR}/dbus-transport-unix.c + ${DBUS_DIR}/dbus-server-unix.c + ${DBUS_DIR}/dbus-dataslot.c + ${DBUS_DIR}/dbus-file.c + ${DBUS_DIR}/dbus-hash.c + ${DBUS_DIR}/dbus-internals.c + ${DBUS_DIR}/dbus-list.c + ${DBUS_DIR}/dbus-marshal-basic.c + ${DBUS_DIR}/dbus-memory.c + ${DBUS_DIR}/dbus-mempool.c + ${DBUS_DIR}/dbus-string.c + ${DBUS_DIR}/dbus-sysdeps.c + ${DBUS_DIR}/dbus-pipe.c + ${DBUS_DIR}/dbus-file-unix.c + ${DBUS_DIR}/dbus-pipe-unix.c + ${DBUS_DIR}/dbus-sysdeps-unix.c + ${DBUS_DIR}/dbus-sysdeps-pthread.c + ${DBUS_DIR}/dbus-userdb.c +) +file(GLOB libdbus_config_dir + "${CMAKE_BINARY_DIR}/vendor/dbus" +) + +add_library(dbus STATIC ${DBUS_LIB_SOURCES}) +target_include_directories(dbus PRIVATE ${libdbus_config_dir} ${CMAKE_SOURCE_DIR}/vendor/libdbus) \ No newline at end of file diff --git a/vendor/libdbus b/vendor/libdbus new file mode 160000 index 0000000..fbd7a99 --- /dev/null +++ b/vendor/libdbus @@ -0,0 +1 @@ +Subproject commit fbd7a993fb966330e41c59634dafb212399d7a21