diff --git a/flake.nix b/flake.nix index 40ddb97..274d988 100644 --- a/flake.nix +++ b/flake.nix @@ -46,9 +46,27 @@ ]; }; - stdenv = pkgs.llvmPackages_18.stdenv; + stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.llvmPackages_18.stdenv; - darwinPkgs = nixpkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin; [ + deps = with ( + if !stdenv.isDarwin + then pkgs.pkgsStatic + else pkgs + ); # TODO: Remove when fixed on darwin + + [ + coost + fmt + glib + libcpr + tomlplusplus + ] + ++ (with pkgs; (lib.optionals hostPlatform.isLinux [ + sdbus-cpp + valgrind + ])); + + darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.darwin; [ apple_sdk.frameworks.CoreFoundation apple_sdk.frameworks.MediaPlayer ]); @@ -66,15 +84,10 @@ pkg-config ]; - propagatedBuildInputs = - [ - libcpr - tomlplusplus - ] - ++ (lib.optionals pkgs.hostPlatform.isLinux [ - glib - playerctl - ]); + propagatedBuildInputs = [ + libcpr + tomlplusplus + ]; buildInputs = [ coost @@ -107,24 +120,16 @@ bear clang-tools_18 meson + lldb ninja pkg-config + unzip - coost - fmt - glib - libcpr - tomlplusplus - ] - ++ (lib.optionals pkgs.hostPlatform.isLinux [playerctl]) - ++ darwinPkgs; - - buildInputs = - [ - coost - libcpr - tomlplusplus + (writeScriptBin "build" "meson compile -C build") + (writeScriptBin "clean" "meson setup build --wipe") + (writeScriptBin "run" "meson compile -C build && build/draconis++") ] + ++ deps ++ darwinPkgs; name = "C++"; diff --git a/meson.build b/meson.build index d8ec305..af4f1e4 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project( - 'draconis++', ['cpp', 'objcpp'], + 'draconis++', 'cpp', version: '0.1.0', default_options: [ 'objc_std=c++20', @@ -14,21 +14,26 @@ project( clangtidy = find_program('clang-tidy', required: false) cpp = meson.get_compiler('cpp') -objcpp = meson.get_compiler('objcpp') -add_project_arguments( - objcpp.get_supported_arguments([ - '-Wno-c++20-compat', - '-Wno-c++98-compat', - '-Wno-c++98-compat-pedantic', - '-Wno-missing-prototypes', - '-Wno-padded', - '-Wno-pre-c++20-compat-pedantic', - '-Wno-switch-default', - '-Wunused-function', - ]), - language: 'objcpp' -) +if host_machine.system() == 'darwin' + add_language('objcpp') + + objcpp = meson.get_compiler('objcpp') + + add_project_arguments( + objcpp.get_supported_arguments([ + '-Wno-c++20-compat', + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic', + '-Wno-missing-prototypes', + '-Wno-padded', + '-Wno-pre-c++20-compat-pedantic', + '-Wno-switch-default', + '-Wunused-function', + ]), + language: 'objcpp' + ) +endif add_project_arguments( cpp.get_supported_arguments([ @@ -77,12 +82,14 @@ deps += cpp.find_library('curl') deps += cpp.find_library('tomlplusplus') deps += dependency('coost') deps += dependency('fmt') -deps += dependency('Foundation') -deps += dependency('MediaPlayer') +if host_machine.system() == 'darwin' + deps += dependency('Foundation') + deps += dependency('MediaPlayer') +endif if host_machine.system() == 'linux' - deps += dependency('playerctl') + deps += dependency('sdbus-c++') endif incdir = include_directories( @@ -90,11 +97,19 @@ incdir = include_directories( is_system: true # Ignores warnings from include dir ) +objc_args = [] +link_args = [] + +if host_machine.system() == 'darwin' + objc_args += ['-fobjc-arc'] + link_args += ['-framework', 'Foundation', '-framework', 'MediaPlayer'] +endif + executable( 'draconis++', sources, + objc_args, + link_args, dependencies: deps, include_directories: incdir, - objc_args: ['-fobjc-arc'], - link_args: ['-framework', 'Foundation', '-framework', 'MediaPlayer'] ) diff --git a/src/main.cpp b/src/main.cpp index 2f5bd2f..337f4d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,8 +45,6 @@ DateNum ParseDate(string const& input) { int main(int argc, char** argv) { flag::parse(argc, argv); - LOG << "hello " << 23; - const Config& config = Config::getInstance(); if (config.getNowPlaying().getEnabled()) diff --git a/src/os/linux.cpp b/src/os/linux.cpp index 1c3506a..b251080 100644 --- a/src/os/linux.cpp +++ b/src/os/linux.cpp @@ -2,12 +2,22 @@ #include #include -#include +#include +#include +#include +#include #include "os.h" using std::string; +static const char *DBUS_INTERFACE = "org.freedesktop.DBus", + *DBUS_OBJECT_PATH = "/org/freedesktop/DBus", + *DBUS_METHOD_LIST_NAMES = "ListNames", + *MPRIS_INTERFACE_NAME = "org.mpris.MediaPlayer2", + *PLAYER_OBJECT_PATH = "/org/mpris/MediaPlayer2", + *PLAYER_INTERFACE_NAME = "org.mpris.MediaPlayer2.Player"; + uint64_t ParseLineAsNumber(const string& input) { // Find the first number string::size_type start = 0; @@ -44,46 +54,59 @@ uint64_t GetMemInfo() { return MeminfoParse(std::ifstream("/proc/meminfo")) * 1024; } -PlayerctlPlayer* InitPlayerctl() { - // Create a player manager - PlayerctlPlayerManager* playerManager = playerctl_player_manager_new(nullptr); +std::vector GetMprisPlayers(sdbus::IConnection& connection) { + auto dbusProxy = + sdbus::createProxy(connection, DBUS_INTERFACE, DBUS_OBJECT_PATH); + std::vector names; + dbusProxy->callMethod(DBUS_METHOD_LIST_NAMES) + .onInterface(DBUS_INTERFACE) + .storeResultsTo(names); - // Create an empty player list - GList* availablePlayers = nullptr; - - // Get the list of available players and put it in the player list - g_object_get(playerManager, "player-names", &availablePlayers, nullptr); - - // If no players are available, return nullptr - if (!availablePlayers) - return nullptr; - - // Get the first player - PlayerctlPlayerName* playerName = - static_cast(availablePlayers->data); - - // Create the player - PlayerctlPlayer* const currentPlayer = - playerctl_player_new_from_name(playerName, nullptr); - - // If no player is available, return nullptr - if (!currentPlayer) - return nullptr; - - // Manage the player - playerctl_player_manager_manage_player(playerManager, currentPlayer); - - // Unref the player - g_object_unref(currentPlayer); - - return currentPlayer; + std::vector mprisPlayers; + for (const auto& name : names) { + if (name.find(MPRIS_INTERFACE_NAME) != std::string::npos) { + mprisPlayers.push_back(name); + } + } + return mprisPlayers; } -string GetNowPlaying() { - if (PlayerctlPlayer* currentPlayer = InitPlayerctl()) - return playerctl_player_get_title(currentPlayer, nullptr); +std::string GetActivePlayer(const std::vector& mprisPlayers) { + if (!mprisPlayers.empty()) { + return mprisPlayers.front(); + } + return ""; +} - return "Could not get now playing info"; +std::string GetNowPlaying() { + try { + auto connection = sdbus::createSessionBusConnection(); + auto mprisPlayers = GetMprisPlayers(*connection); + + if (mprisPlayers.empty()) + return ""; + + std::string activePlayer = GetActivePlayer(mprisPlayers); + + if (activePlayer.empty()) + return ""; + + auto playerProxy = + sdbus::createProxy(*connection, activePlayer, PLAYER_OBJECT_PATH); + + std::map metadata = + playerProxy->getProperty("Metadata").onInterface(PLAYER_INTERFACE_NAME); + + auto iter = metadata.find("xesam:title"); + + if (iter != metadata.end() && + iter->second.containsValueOfType()) + return iter->second.get(); + } catch (const sdbus::Error& e) { + std::cerr << "Error: " << e.what() << std::endl; + } + + return ""; } #endif diff --git a/src/os/macos.cpp b/src/os/macos.cpp index b788cb6..1fd1c5b 100644 --- a/src/os/macos.cpp +++ b/src/os/macos.cpp @@ -16,7 +16,7 @@ uint64_t GetMemInfo() { } std::string GetNowPlaying() { - return getCurrentPlayingTitle(); + return GetCurrentPlayingTitle(); } #endif diff --git a/src/os/macos/NowPlayingBridge.h b/src/os/macos/NowPlayingBridge.h index 0cbfe85..b75e86b 100644 --- a/src/os/macos/NowPlayingBridge.h +++ b/src/os/macos/NowPlayingBridge.h @@ -8,7 +8,7 @@ @end #else extern "C" { -const char* getCurrentPlayingTitle(); -const char* getCurrentPlayingArtist(); +const char* GetCurrentPlayingTitle(); +const char* GetCurrentPlayingArtist(); } #endif diff --git a/src/os/macos/NowPlayingBridge.mm b/src/os/macos/NowPlayingBridge.mm index dae0296..040a98c 100644 --- a/src/os/macos/NowPlayingBridge.mm +++ b/src/os/macos/NowPlayingBridge.mm @@ -62,7 +62,7 @@ typedef void (*MRMediaRemoteGetNowPlayingInfoFunction)( extern "C" { -const char *getCurrentPlayingTitle() { +const char *GetCurrentPlayingTitle() { NSDictionary *metadata = [NowPlayingBridge currentPlayingMetadata]; if (metadata == nil) { return nullptr; @@ -75,7 +75,7 @@ const char *getCurrentPlayingTitle() { return nullptr; } -const char *getCurrentPlayingArtist() { +const char *GetCurrentPlayingArtist() { NSDictionary *metadata = [NowPlayingBridge currentPlayingMetadata]; if (metadata == nil) { return nullptr;