diff --git a/src/dbus.cpp b/src/dbus.cpp index ec99d115..00139a3c 100644 --- a/src/dbus.cpp +++ b/src/dbus.cpp @@ -1,59 +1,55 @@ +#include #include #include #include -#include + +#include "dbus_helpers.hpp" #include "dbus_info.h" #include "string_utils.h" -#include "dbus_helpers.hpp" using ms = std::chrono::milliseconds; using namespace DBus_helpers; -#define DBUS_TIMEOUT 2000 // ms +#define DBUS_TIMEOUT 2000 // ms struct mutexed_metadata main_metadata; -namespace dbusmgr { +namespace dbusmgr { dbus_manager dbus_mgr; } -template -static void assign_metadata_value(metadata& meta, const std::string& key, const T& value) { - std::cerr << "Assigning Metadata: " << key << " -> " << value << "\n"; - if(key == "PlaybackStatus") { +template +static void assign_metadata_value(metadata& meta, const std::string& key, + const T& value) { + if (key == "PlaybackStatus") { meta.playing = (value == "Playing"); meta.got_playback_data = true; - } - else if(key == "xesam:title"){ + } else if (key == "xesam:title") { meta.title = value; meta.got_song_data = true; meta.valid = true; - } - else if(key == "xesam:artist") { + } else if (key == "xesam:artist") { meta.artists = value; meta.got_song_data = true; meta.valid = true; - } - else if(key == "xesam:album") { + } else if (key == "xesam:album") { meta.album = value; meta.got_song_data = true; meta.valid = true; - } - else if(key == "mpris:artUrl"){ + } else if (key == "mpris:artUrl") { meta.artUrl = value; meta.got_song_data = true; } } -std::string format_signal(const dbusmgr::DBusSignal& s) -{ +std::string format_signal(const dbusmgr::DBusSignal& s) { std::stringstream ss; ss << "type='signal',interface='" << s.intf << "'"; ss << ",member='" << s.signal << "'"; return ss.str(); } -static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std::string& source, metadata& meta) -{ +static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage* msg, + std::string& source, metadata& meta) { /** * Expected response Format: * string, @@ -61,55 +57,43 @@ static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std:: * "Metadata" -> multimap, * "PlaybackStatus" -> string * } - */ + */ std::string key, val; auto iter = DBusMessageIter_wrap(msg, &dbus); // Should be 'org.mpris.MediaPlayer2.Player' - if (not iter.is_string()){ - std::cerr << "Not a string\n"; //TODO + if (not iter.is_string()) { + std::cerr << "Not a string\n"; // TODO return; } source = iter.get_primitive(); - if (source != "org.mpris.MediaPlayer2.Player") - return; + if (source != "org.mpris.MediaPlayer2.Player") return; iter.next(); - if (not iter.is_array()) - return; - - //std::cerr << "Parsing mpris update...\n"; - string_map_for_each(iter, [&](std::string& key, DBusMessageIter_wrap it){ - if(key == "Metadata"){ - //std::cerr << "\tMetadata:\n"; - string_map_for_each(it, [&](const std::string& key, DBusMessageIter_wrap it){ - if(it.is_primitive()){ - auto val = it.get_stringified(); - //std::cerr << "\t\t" << key << " -> " << val << "\n"; - assign_metadata_value(meta, key, val); - } - else if(it.is_array()){ - std::string val; - array_for_each_stringify(it, [&](const std::string& str){ - if(val.empty()){ + if (not iter.is_array()) return; + + iter.string_map_for_each([&](std::string& key, DBusMessageIter_wrap it) { + if (key == "Metadata") { + it.string_map_for_each([&](const std::string& key, + DBusMessageIter_wrap it) { + std::string val; + if (it.is_primitive()) { + val = it.get_stringified(); + } else if (it.is_array()) { + it.array_for_each_stringify([&](const std::string& str) { + if (val.empty()) { val = str; - } - else { + } else { val += ", " + str; } }); - //std::cerr << "\t\t" << key << " -> " << val << "\n"; - assign_metadata_value(meta, key, val); } - }); - string_multimap_for_each_stringify(it, [&](const std::string& key, const std::string& val){ assign_metadata_value(meta, key, val); }); - } - else if(key == "PlaybackStatus"){ + } else if (key == "PlaybackStatus") { auto val = it.get_stringified(); assign_metadata_value(meta, key, val); } @@ -117,47 +101,43 @@ static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std:: meta.valid = (meta.artists.size() || !meta.title.empty()); } -bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr, std::string& name_owner, const char *name) -{ - auto reply = DBusMessage_wrap::new_method_call( - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetNameOwner", - &dbus_mgr.dbus() - ).argument(name).send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); - if(not reply) return false; +bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr, + std::string& name_owner, const char* name) { + auto reply = + DBusMessage_wrap::new_method_call( + "org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "GetNameOwner", &dbus_mgr.dbus()) + .argument(name) + .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); + if (not reply) return false; auto iter = reply.iter(); - if(not iter.is_string()) return false; + if (not iter.is_string()) return false; name_owner = iter.get_primitive(); return true; } -bool dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, metadata& meta, const char * dest, const char * prop) -{ - auto reply = DBusMessage_wrap::new_method_call( - dest, - "/org/mpris/MediaPlayer2", - "org.freedesktop.DBus.Properties", - "Get", - &dbus_mgr.dbus() - ).argument("org.mpris.MediaPlayer2.Player") - .argument(prop) - .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); +bool dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, metadata& meta, + const char* dest, const char* prop) { + auto reply = + DBusMessage_wrap::new_method_call(dest, "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", + "Get", &dbus_mgr.dbus()) + .argument("org.mpris.MediaPlayer2.Player") + .argument(prop) + .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); - if(not reply) return false; + if (not reply) return false; auto iter = reply.iter(); - if(iter.is_array()){ - string_multimap_for_each_stringify(iter, [&](const std::string& key, const std::string& val){ - assign_metadata_value(meta, key, val); - }); - } - else if(iter.is_primitive()){ + if (iter.is_array()) { + iter.string_multimap_for_each_stringify( + [&](const std::string& key, const std::string& val) { + assign_metadata_value(meta, key, val); + }); + } else if (iter.is_primitive()) { assign_metadata_value(meta, prop, iter.get_stringified()); - } - else { + } else { return false; } return true; @@ -165,8 +145,8 @@ bool dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, metadata& meta, c namespace dbusmgr { bool dbus_manager::get_media_player_metadata(metadata& meta, std::string name) { - if(name == "") name = m_active_player; - if(name == "") return false; + if (name == "") name = m_active_player; + if (name == "") return false; meta.clear(); dbus_get_player_property(*this, meta, name.c_str(), "Metadata"); dbus_get_player_property(*this, meta, name.c_str(), "PlaybackStatus"); @@ -174,11 +154,9 @@ bool dbus_manager::get_media_player_metadata(metadata& meta, std::string name) { return true; } -bool dbus_manager::init(const std::string& requested_player) -{ - if (m_inited) - return true; - +bool dbus_manager::init(const std::string& requested_player) { + if (m_inited) return true; + m_requested_player = "org.mpris.MediaPlayer2." + requested_player; if (!m_dbus_ldr.IsLoaded() && !m_dbus_ldr.Load("libdbus-1.so.3")) { @@ -190,60 +168,65 @@ bool dbus_manager::init(const std::string& requested_player) m_dbus_ldr.threads_init_default(); - if ( nullptr == (m_dbus_conn = m_dbus_ldr.bus_get(DBUS_BUS_SESSION, &m_error)) ) { + if (nullptr == + (m_dbus_conn = m_dbus_ldr.bus_get(DBUS_BUS_SESSION, &m_error))) { std::cerr << "MANGOHUD: " << m_error.message << std::endl; m_dbus_ldr.error_free(&m_error); return false; } - std::cout << "MANGOHUD: Connected to D-Bus as \"" << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"." << std::endl; + std::cout << "MANGOHUD: Connected to D-Bus as \"" + << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"." + << std::endl; dbus_list_name_to_owner(); connect_to_signals(); - select_active_player(); - { - std::lock_guard lck(main_metadata.mtx); - get_media_player_metadata(main_metadata.meta); - } m_inited = true; return true; } -bool dbus_manager::select_active_player(metadata* store_meta) { +bool dbus_manager::select_active_player() { + auto old_active_player = m_active_player; + m_active_player = ""; + metadata meta{}; // If the requested player is available, use it - if(m_name_owners.count(m_requested_player) > 0) { + if (m_name_owners.count(m_requested_player) > 0) { m_active_player = m_requested_player; - std::cerr << "Selecting requested player: " << m_requested_player << "\n"; - if(store_meta) get_media_player_metadata(*store_meta, m_active_player); - return true; + std::cerr << "Selecting requested player: " << m_requested_player + << "\n"; + get_media_player_metadata(meta, m_active_player); } // Else, use any player that is currently playing.. - for(const auto& entry : m_name_owners) { - const auto& name = std::get<0>(entry); - metadata meta; - get_media_player_metadata(meta, name); - if(meta.playing) { - m_active_player = name; - std::cerr << "Selecting fallback player: " << name << "\n"; - if(store_meta) *store_meta = meta; - return true; + if (m_active_player.empty()) { + for (const auto& entry : m_name_owners) { + const auto& name = std::get<0>(entry); + get_media_player_metadata(meta, name); + if (meta.playing) { + m_active_player = name; + std::cerr << "Selecting fallback player: " << name << "\n"; + } } } - // No media players are active - std::cerr << "No active players\n"; - m_active_player = ""; - if(store_meta) store_meta->clear(); - return false; + if (not m_active_player.empty()) { + if (m_active_player != old_active_player) { + onNewPlayer(meta); + } + return true; + } else { + std::cerr << "No active players\n"; + if (not old_active_player.empty()) { + onNoPlayer(); + } + return false; + } } -void dbus_manager::deinit() -{ - if (!m_inited) - return; +void dbus_manager::deinit() { + if (!m_inited) return; // unreference system bus connection instead of closing it if (m_dbus_conn) { @@ -255,18 +238,17 @@ void dbus_manager::deinit() m_inited = false; } -dbus_manager::~dbus_manager() -{ - deinit(); -} +dbus_manager::~dbus_manager() { deinit(); } -DBusHandlerResult dbus_manager::filter_signals(DBusConnection* conn, DBusMessage* msg, void* userData) { +DBusHandlerResult dbus_manager::filter_signals(DBusConnection* conn, + DBusMessage* msg, + void* userData) { auto& manager = *reinterpret_cast(userData); - for(auto& sig : manager.m_signals) { - if(manager.m_dbus_ldr.message_is_signal(msg, sig.intf, sig.signal)){ + for (auto& sig : manager.m_signals) { + if (manager.m_dbus_ldr.message_is_signal(msg, sig.intf, sig.signal)) { auto sender = manager.m_dbus_ldr.message_get_sender(msg); - if((manager.*(sig.handler))(msg, sender)) + if ((manager.*(sig.handler))(msg, sender)) return DBUS_HANDLER_RESULT_HANDLED; else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -275,7 +257,8 @@ DBusHandlerResult dbus_manager::filter_signals(DBusConnection* conn, DBusMessage return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -bool dbus_manager::handle_properties_changed(DBusMessage* msg, const char* sender) { +bool dbus_manager::handle_properties_changed(DBusMessage* msg, + const char* sender) { std::string source; metadata meta; @@ -284,88 +267,49 @@ bool dbus_manager::handle_properties_changed(DBusMessage* msg, const char* sende std::cerr << "PropertiesChanged Signal received:\n"; std::cerr << "\tSource: " << source << "\n"; std::cerr << "active_player: " << m_active_player << "\n"; - std::cerr << "active_player's owner: " << m_name_owners[m_active_player] << "\n"; + std::cerr << "active_player's owner: " << m_name_owners[m_active_player] + << "\n"; std::cerr << "sender: " << sender << "\n"; #endif - if (source != "org.mpris.MediaPlayer2.Player") - return false; + if (source != "org.mpris.MediaPlayer2.Player") return false; - if(m_active_player == "") { - select_active_player(&meta); + if (m_active_player == "") { + select_active_player(); + } else if (m_name_owners[m_active_player] == sender) { + onPlayerUpdate(meta); } - if (m_name_owners[m_active_player] == sender) { - std::lock_guard lck(main_metadata.mtx); - if(meta.got_song_data){ - // If the song has changed, reset the ticker - if( - main_metadata.meta.artists != meta.artists || - main_metadata.meta.album != meta.album || - main_metadata.meta.title != meta.title - ){ - main_metadata.ticker = {}; - } - - main_metadata.meta = meta; - main_metadata.meta.playing = true; - } - if(meta.got_playback_data){ - main_metadata.meta.playing = meta.playing; - } - } - std::cerr << "Main metadata valid: " << std::boolalpha << main_metadata.meta.valid << "\n"; return true; } -bool dbus_manager::handle_name_owner_changed(DBusMessage* msg, const char* sender) { - DBusMessageIter iter; - m_dbus_ldr.message_iter_init (msg, &iter); +bool dbus_manager::handle_name_owner_changed(DBusMessage* _msg, + const char* sender) { std::vector str; - const char *value = nullptr; - while (m_dbus_ldr.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { - m_dbus_ldr.message_iter_get_basic (&iter, &value); - str.push_back(value); - m_dbus_ldr.message_iter_next (&iter); + for (auto iter = DBusMessageIter_wrap(_msg, &m_dbus_ldr); iter; + iter.next()) { + str.push_back(iter.get_primitive()); } // register new name - if (str.size() == 3 - && starts_with(str[0], "org.mpris.MediaPlayer2.") - && !str[2].empty() - ) - { + if (str.size() == 3 && starts_with(str[0], "org.mpris.MediaPlayer2.") && + !str[2].empty()) { m_name_owners[str[0]] = str[2]; - if(str[0] == m_requested_player){ - metadata tmp; - select_active_player(&tmp); - { - std::lock_guard lck(main_metadata.mtx); - main_metadata.meta = tmp; - main_metadata.ticker = {}; - } + if (str[0] == m_requested_player) { + select_active_player(); } } // did a player quit? if (str[2].empty()) { - if (str.size() == 3 - && str[0] == m_active_player - ) { - metadata tmp; + if (str.size() == 3 && str[0] == m_active_player) { m_name_owners.erase(str[0]); select_active_player(); - get_media_player_metadata(tmp); - { - std::lock_guard lck(main_metadata.mtx); - std::swap(tmp, main_metadata.meta); - } } } return true; } -void dbus_manager::connect_to_signals() -{ +void dbus_manager::connect_to_signals() { for (auto kv : m_signals) { auto signal = format_signal(kv); m_dbus_ldr.bus_add_match(m_dbus_conn, signal.c_str(), &m_error); @@ -373,17 +317,18 @@ void dbus_manager::connect_to_signals() ::perror(m_error.name); ::perror(m_error.message); m_dbus_ldr.error_free(&m_error); - //return; + // return; } } - m_dbus_ldr.connection_add_filter(m_dbus_conn, filter_signals, reinterpret_cast(this), nullptr); + m_dbus_ldr.connection_add_filter(m_dbus_conn, filter_signals, + reinterpret_cast(this), nullptr); start_thread(); } -void dbus_manager::disconnect_from_signals() -{ - m_dbus_ldr.connection_remove_filter(m_dbus_conn, filter_signals, reinterpret_cast(this)); +void dbus_manager::disconnect_from_signals() { + m_dbus_ldr.connection_remove_filter(m_dbus_conn, filter_signals, + reinterpret_cast(this)); for (auto kv : m_signals) { auto signal = format_signal(kv); m_dbus_ldr.bus_remove_match(m_dbus_conn, signal.c_str(), &m_error); @@ -397,51 +342,74 @@ void dbus_manager::disconnect_from_signals() stop_thread(); } -bool dbus_manager::dbus_list_name_to_owner() -{ - auto reply = DBusMessage_wrap::new_method_call( - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListNames", - &dbus_mgr.dbus() - ).send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); - if(not reply) return false; +bool dbus_manager::dbus_list_name_to_owner() { + auto reply = + DBusMessage_wrap::new_method_call( + "org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "ListNames", &dbus_mgr.dbus()) + .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); + if (not reply) return false; auto iter = reply.iter(); - if(not iter.is_array()) { + if (not iter.is_array()) { return false; } - array_for_each(iter, [&](std::string name){ - if(!starts_with(name.c_str(), "org.mpris.MediaPlayer2.")) return; + iter.array_for_each_value([&](std::string name) { + if (!starts_with(name.c_str(), "org.mpris.MediaPlayer2.")) return; std::string owner; - if(dbus_get_name_owner(dbus_mgr, owner, name.c_str())){ + if (dbus_get_name_owner(dbus_mgr, owner, name.c_str())) { m_name_owners[name] = owner; } }); return true; } -void dbus_manager::stop_thread() -{ +void dbus_manager::stop_thread() { m_quit = true; - if (m_thread.joinable()) - m_thread.join(); + if (m_thread.joinable()) m_thread.join(); } -void dbus_manager::start_thread() -{ +void dbus_manager::start_thread() { stop_thread(); m_quit = false; m_thread = std::thread(&dbus_manager::dbus_thread, this); } -void dbus_manager::dbus_thread() -{ +void dbus_manager::dbus_thread() { using namespace std::chrono_literals; - while(!m_quit && m_dbus_ldr.connection_read_write_dispatch(m_dbus_conn, 0)) + while (!m_quit && m_dbus_ldr.connection_read_write_dispatch(m_dbus_conn, 0)) std::this_thread::sleep_for(10ms); } +void dbus_manager::onNoPlayer() { + std::lock_guard lck(main_metadata.mtx); + main_metadata.meta = {}; + main_metadata.ticker = {}; } + +void dbus_manager::onNewPlayer(metadata& meta) { + std::lock_guard lck(main_metadata.mtx); + main_metadata.meta = meta; + main_metadata.ticker = {}; +} + +void dbus_manager::onPlayerUpdate(metadata& meta) { + std::lock_guard lck(main_metadata.mtx); + if (meta.got_song_data) { + // If the song has changed, reset the ticker + if (main_metadata.meta.artists != meta.artists || + main_metadata.meta.album != meta.album || + main_metadata.meta.title != meta.title) { + main_metadata.ticker = {}; + } + + main_metadata.meta = meta; + main_metadata.meta.playing = true; + } + if (meta.got_playback_data) { + main_metadata.meta.playing = meta.playing; + } +} + +} // namespace dbusmgr diff --git a/src/dbus_helpers.hpp b/src/dbus_helpers.hpp index b64dcd5e..e6422b8b 100644 --- a/src/dbus_helpers.hpp +++ b/src/dbus_helpers.hpp @@ -2,83 +2,86 @@ #ifndef MANGOHUD_DBUS_HELPERS #define MANGOHUD_DBUS_HELPERS +#include + #include "loaders/loader_dbus.h" -namespace DBus_helpers{ -namespace detail{ -template struct dbus_type_identifier{}; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_BYTE; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_UINT16; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_UINT32; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_UINT64; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_INT16; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_INT32; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_INT64; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_DOUBLE; }; -template<> struct dbus_type_identifier { const int value = DBUS_TYPE_STRING; }; - -template -const int dbus_type_identifier_v = dbus_type_identifier().value; -} //namespace detail +namespace DBus_helpers { +namespace detail { +// clang-format off +template struct dbus_type_traits{}; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_BYTE; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT16; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT32; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT64; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT16; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT32; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT64; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_DOUBLE; const bool is_fixed = true; }; +template<> struct dbus_type_traits { const int value = DBUS_TYPE_STRING; const bool is_fixed = false; }; +// clang-format on + +template +const int dbus_type_identifier = dbus_type_traits().value; + +template +const bool is_fixed = dbus_type_traits().is_fiexd; +} // namespace detail class DBusMessageIter_wrap { + public: + DBusMessageIter_wrap(DBusMessage* msg, libdbus_loader* loader); + DBusMessageIter_wrap(DBusMessageIter iter, libdbus_loader* loader); -public: - DBusMessageIter_wrap(DBusMessage* msg, libdbus_loader* loader) - { - m_DBus = loader; - if(msg){ - m_DBus->message_iter_init(msg, &m_Iter); - m_resolved_iter = resolve_variants(); - m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter); - } - else { - m_type = DBUS_TYPE_INVALID; - } - } - - DBusMessageIter_wrap(DBusMessageIter iter, libdbus_loader* loader) - : m_Iter(iter), m_DBus(loader) - { - m_resolved_iter = resolve_variants(); - m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter); - } - - - - int type() const noexcept { return m_type; } + // Type accessors + int type() const noexcept { return m_type; } bool is_unsigned() const noexcept; bool is_signed() const noexcept; bool is_string() const noexcept; bool is_double() const noexcept; bool is_primitive() const noexcept; bool is_array() const noexcept; - operator bool() const noexcept { - return type() != DBUS_TYPE_INVALID; - } + operator bool() const noexcept { return type() != DBUS_TYPE_INVALID; } - - template + // Value accessors + // Primitives + template auto get_primitive() -> T; auto get_unsigned() -> uint64_t; auto get_signed() -> int64_t; auto get_stringified() -> std::string; + // Composites auto get_array_iter() -> DBusMessageIter_wrap; auto get_dict_entry_iter() -> DBusMessageIter_wrap; + // Looping + template + void array_for_each(Callable); + template + void array_for_each_stringify(Callable); + template + void array_for_each_value(Callable); + + template + void string_map_for_each(Callable); + template + void string_multimap_for_each_stringify(Callable); + auto next() { + if (not *this) return *this; m_DBus->message_iter_next(&m_Iter); // Resolve any variants m_resolved_iter = resolve_variants(); m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter); return *this; } -private: + + private: DBusMessageIter resolve_variants() { auto iter = m_Iter; auto field_type = m_DBus->message_iter_get_arg_type(&m_Iter); - while(field_type == DBUS_TYPE_VARIANT){ + while (field_type == DBUS_TYPE_VARIANT) { m_DBus->message_iter_recurse(&iter, &iter); field_type = m_DBus->message_iter_get_arg_type(&iter); } @@ -91,23 +94,33 @@ private: libdbus_loader* m_DBus; }; +DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessage* msg, + libdbus_loader* loader) { + m_DBus = loader; + if (msg) { + m_DBus->message_iter_init(msg, &m_Iter); + m_resolved_iter = resolve_variants(); + m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter); + } else { + m_type = DBUS_TYPE_INVALID; + } +} +DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessageIter iter, + libdbus_loader* loader) + : m_Iter(iter), m_DBus(loader) { + m_resolved_iter = resolve_variants(); + m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter); +} bool DBusMessageIter_wrap::is_unsigned() const noexcept { - return ( - (type() == DBUS_TYPE_BYTE) || - (type() == DBUS_TYPE_INT16) || - (type() == DBUS_TYPE_INT32) || - (type() == DBUS_TYPE_INT64) - ); + return ((type() == DBUS_TYPE_BYTE) || (type() == DBUS_TYPE_INT16) || + (type() == DBUS_TYPE_INT32) || (type() == DBUS_TYPE_INT64)); } bool DBusMessageIter_wrap::is_signed() const noexcept { - return ( - (type() == DBUS_TYPE_INT16) || - (type() == DBUS_TYPE_INT32) || - (type() == DBUS_TYPE_INT64) - ); + return ((type() == DBUS_TYPE_INT16) || (type() == DBUS_TYPE_INT32) || + (type() == DBUS_TYPE_INT64)); } bool DBusMessageIter_wrap::is_string() const noexcept { @@ -119,24 +132,19 @@ bool DBusMessageIter_wrap::is_double() const noexcept { } bool DBusMessageIter_wrap::is_primitive() const noexcept { - return ( - is_double() || - is_signed() || - is_unsigned() || - is_string() - ); + return (is_double() || is_signed() || is_unsigned() || is_string()); } bool DBusMessageIter_wrap::is_array() const noexcept { return (type() == DBUS_TYPE_ARRAY); } - -template +template auto DBusMessageIter_wrap::get_primitive() -> T { - auto requested_type = detail::dbus_type_identifier_v; - if(requested_type != type()){ - std::cerr << "Type mismatch: '" << (char) requested_type << "' vs '" << (char) type() << "'\n"; + auto requested_type = detail::dbus_type_identifier; + if (requested_type != type()) { + std::cerr << "Type mismatch: '" << ((char)requested_type) << "' vs '" + << (char)type() << "'"; #ifndef NDEBUG exit(-1); #else @@ -149,55 +157,53 @@ auto DBusMessageIter_wrap::get_primitive() -> T { return ret; } -template<> +template <> auto DBusMessageIter_wrap::get_primitive() -> std::string { return std::string(get_primitive()); } uint64_t DBusMessageIter_wrap::get_unsigned() { auto t = type(); - switch (t) - { - case DBUS_TYPE_BYTE: - return get_primitive(); - case DBUS_TYPE_UINT16: - return get_primitive(); - case DBUS_TYPE_UINT32: - return get_primitive(); - case DBUS_TYPE_UINT64: - return get_primitive(); - default: - return 0; + switch (t) { + case DBUS_TYPE_BYTE: + return get_primitive(); + case DBUS_TYPE_UINT16: + return get_primitive(); + case DBUS_TYPE_UINT32: + return get_primitive(); + case DBUS_TYPE_UINT64: + return get_primitive(); + default: + return 0; } } int64_t DBusMessageIter_wrap::get_signed() { auto t = type(); - switch (t) - { - case DBUS_TYPE_INT16: - return get_primitive(); - case DBUS_TYPE_INT32: - return get_primitive(); - case DBUS_TYPE_INT64: - return get_primitive(); - default: - return 0; + switch (t) { + case DBUS_TYPE_INT16: + return get_primitive(); + case DBUS_TYPE_INT32: + return get_primitive(); + case DBUS_TYPE_INT64: + return get_primitive(); + default: + return 0; } } auto DBusMessageIter_wrap::get_stringified() -> std::string { - if(is_string()) return get_primitive(); - if(is_unsigned()) return std::to_string(get_unsigned()); - if(is_signed()) return std::to_string(get_signed()); - if(is_double()) return std::to_string(get_primitive()); + if (is_string()) return get_primitive(); + if (is_unsigned()) return std::to_string(get_unsigned()); + if (is_signed()) return std::to_string(get_signed()); + if (is_double()) return std::to_string(get_primitive()); std::cerr << "stringify failed\n"; return std::string(); } auto DBusMessageIter_wrap::get_array_iter() -> DBusMessageIter_wrap { - if(not is_array()) { - std::cerr << "Not an array\n"; + if (not is_array()) { + std::cerr << "Not an array; " << (char)type() << "\n"; return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus); } @@ -207,8 +213,8 @@ auto DBusMessageIter_wrap::get_array_iter() -> DBusMessageIter_wrap { } auto DBusMessageIter_wrap::get_dict_entry_iter() -> DBusMessageIter_wrap { - if(type() != DBUS_TYPE_DICT_ENTRY){ - std::cerr << "Not a dict entry\n"; + if (type() != DBUS_TYPE_DICT_ENTRY) { + std::cerr << "Not a dict entry" << (char)type() << "\n"; return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus); } @@ -217,146 +223,139 @@ auto DBusMessageIter_wrap::get_dict_entry_iter() -> DBusMessageIter_wrap { return DBusMessageIter_wrap(ret, m_DBus); } - - - - - -// Precondition: iter points to a dict of string -> any -// executes action(key, value_iter) for all entries -template -void string_map_for_each(DBusMessageIter_wrap iter, T action) { - iter = iter.get_array_iter(); - for(; iter; iter.next()) { - auto it = iter.get_dict_entry_iter(); - auto key = it.get_primitive(); - - it.next(); - action(key, it); +template +void DBusMessageIter_wrap::array_for_each_value(Callable action) { + auto iter = get_array_iter(); + for (; iter; iter.next()) { + action(iter.get_primitive()); } } -template -void array_for_each(DBusMessageIter_wrap iter, Callable action) { - iter = iter.get_array_iter(); - for(; iter; iter.next()){ - action(iter.get_primitive()); +template +void DBusMessageIter_wrap::array_for_each(Callable action) { + auto iter = get_array_iter(); + for (; iter; iter.next()) { + action(iter); } } -template -void array_for_each_stringify(DBusMessageIter_wrap iter, Callable action) { - iter = iter.get_array_iter(); - for(; iter; iter.next()){ +template +void DBusMessageIter_wrap::array_for_each_stringify(Callable action) { + auto iter = get_array_iter(); + for (; iter; iter.next()) { action(iter.get_stringified()); } } -template -void string_multimap_for_each_stringify(DBusMessageIter_wrap iter, T action) { - string_map_for_each(iter, [&](const std::string& key, DBusMessageIter_wrap it){ - if(it.is_array()){ - array_for_each_stringify(it, [&](const std::string& val){ - action(key, val); - }); - } - else if(it.is_primitive()){ - action(key, it.get_stringified()); - } - }); +template +void DBusMessageIter_wrap::string_map_for_each(T action) { + auto iter = get_array_iter(); + for (; iter; iter.next()) { + auto it = iter.get_dict_entry_iter(); + auto key = it.get_primitive(); + + it.next(); + action(key, it); + } } +template +void DBusMessageIter_wrap::string_multimap_for_each_stringify(T action) { + string_map_for_each([&action](const std::string& key, DBusMessageIter_wrap it) { + if (it.is_array()) { + it.array_for_each_stringify( + [&](const std::string& val) { action(key, val); }); + } else if (it.is_primitive()) { + action(key, it.get_stringified()); + } + }); +} class DBusMessage_wrap { -public: + public: DBusMessage_wrap(DBusMessage* msg, libdbus_loader* ldr, bool owning = false) - : m_owning(owning), m_msg(msg), m_DBus(ldr) - {} + : m_owning(owning), m_msg(msg), m_DBus(ldr) {} - ~DBusMessage_wrap(){ - free_if_owning(); - } + ~DBusMessage_wrap() { free_if_owning(); } DBusMessage_wrap(const DBusMessage_wrap&) = delete; DBusMessage_wrap(DBusMessage_wrap&&) = default; - operator bool() const { - return m_msg != nullptr; - } + operator bool() const noexcept { return m_msg != nullptr; } - template - DBusMessage_wrap& argument(T arg) { - if(not m_msg) return *this; - if(not m_DBus->message_append_args( - m_msg, - detail::dbus_type_identifier_v, - &arg, - DBUS_TYPE_INVALID - )){ - free_if_owning(); - } - return *this; - } + template + DBusMessage_wrap& argument(T arg); - DBusMessage_wrap send_with_reply_and_block(DBusConnection* conn, int timeout) { - if(not m_msg){ - return DBusMessage_wrap(nullptr, m_DBus); - } - DBusError err; - m_DBus->error_init(&err); - auto reply = m_DBus->connection_send_with_reply_and_block( - conn, - m_msg, - timeout, - &err - ); - if(reply == nullptr) { - std::cerr << "MangoHud[" << __func__ << "]: " << err.message << "\n"; - free_if_owning(); - m_DBus->error_free(&err); - } - return DBusMessage_wrap(reply, m_DBus, true); - } + DBusMessage_wrap send_with_reply_and_block(DBusConnection* conn, + int timeout); - DBusMessageIter_wrap iter() { - return DBusMessageIter_wrap(m_msg, m_DBus); - } + DBusMessageIter_wrap iter() { return DBusMessageIter_wrap(m_msg, m_DBus); } - static DBusMessage_wrap new_method_call( - const std::string& bus_name, - const std::string& path, - const std::string& iface, - const std::string& method, - libdbus_loader* loader - ){ - auto msg = loader->message_new_method_call( - (bus_name.empty()) ? nullptr : bus_name.c_str(), - path.c_str(), - (iface.empty()) ? nullptr : iface.c_str(), - method.c_str() - ); - return DBusMessage_wrap(msg, loader, true); - } -private: - void free_if_owning() { - if(m_msg and m_owning) { - m_DBus->message_unref(m_msg); - } - m_msg = nullptr; - } + static DBusMessage_wrap new_method_call(const std::string& bus_name, + const std::string& path, + const std::string& iface, + const std::string& method, + libdbus_loader* loader); + + private: + void free_if_owning(); bool m_owning; DBusMessage* m_msg; libdbus_loader* m_DBus; std::vector m_args; }; -template<> -DBusMessage_wrap& DBusMessage_wrap::argument(const std::string& str) -{ +template +DBusMessage_wrap& DBusMessage_wrap::argument(T arg) { + if (not m_msg) return *this; + if (not m_DBus->message_append_args(m_msg, detail::dbus_type_identifier, + &arg, DBUS_TYPE_INVALID)) { + free_if_owning(); + } + return *this; +} + +template <> +DBusMessage_wrap& DBusMessage_wrap::argument( + const std::string& str) { return argument(str.c_str()); } -} //namespace DBus_helpers +DBusMessage_wrap DBusMessage_wrap::send_with_reply_and_block( + DBusConnection* conn, int timeout) { + if (not m_msg) { + return DBusMessage_wrap(nullptr, m_DBus); + } + DBusError err; + m_DBus->error_init(&err); + auto reply = m_DBus->connection_send_with_reply_and_block(conn, m_msg, + timeout, &err); + if (reply == nullptr) { + std::cerr << "MangoHud[" << __func__ << "]: " << err.message << "\n"; + free_if_owning(); + m_DBus->error_free(&err); + } + return DBusMessage_wrap(reply, m_DBus, true); +} + +DBusMessage_wrap DBusMessage_wrap::new_method_call(const std::string& bus_name, + const std::string& path, + const std::string& iface, + const std::string& method, + libdbus_loader* loader) { + auto msg = loader->message_new_method_call( + (bus_name.empty()) ? nullptr : bus_name.c_str(), path.c_str(), + (iface.empty()) ? nullptr : iface.c_str(), method.c_str()); + return DBusMessage_wrap(msg, loader, true); +} + +void DBusMessage_wrap::free_if_owning() { + if (m_msg and m_owning) { + m_DBus->message_unref(m_msg); + } + m_msg = nullptr; +} +} // namespace DBus_helpers -#endif //MANGOHUD_DBUS_HELPERS \ No newline at end of file +#endif // MANGOHUD_DBUS_HELPERS \ No newline at end of file diff --git a/src/dbus_info.h b/src/dbus_info.h index eaca8612..f1b25332 100644 --- a/src/dbus_info.h +++ b/src/dbus_info.h @@ -3,31 +3,31 @@ #define MANGOHUD_DBUS_INFO_H #include -#include -#include #include -#include -#include #include -#include #include +#include +#include +#include +#include +#include + #include "loaders/loader_dbus.h" struct metadata { - //std::vector artists; - std::string artists; // pre-concatenate + // std::vector artists; + std::string artists; // pre-concatenate std::string title; std::string album; std::string something; std::string artUrl; bool playing = false; - + bool valid = false; bool got_song_data = false; bool got_playback_data = false; - void clear() - { + void clear() { artists.clear(); title.clear(); album.clear(); @@ -51,101 +51,79 @@ struct mutexed_metadata { } ticker; }; -enum SignalType -{ +enum SignalType { ST_NAMEOWNERCHANGED, ST_PROPERTIESCHANGED, }; - extern struct mutexed_metadata main_metadata; namespace dbusmgr { - class dbus_manager; - using signal_handler_func = bool (dbus_manager::*)(DBusMessage*, const char*); - - struct DBusSignal - { - const char * intf; - const char * signal; - signal_handler_func handler; - }; - - using callback_func = std::function; - - enum CBENUM { - CB_CONNECTED, - CB_DISCONNECTED, - CB_NEW_METADATA, - }; +class dbus_manager; +using signal_handler_func = bool (dbus_manager::*)(DBusMessage*, const char*); - class dbus_manager - { - public: - dbus_manager() - { - } - - ~dbus_manager(); - - bool init(const std::string& requested_player); - void deinit(); - bool get_media_player_metadata(metadata& meta, std::string name = ""); - void add_callback(CBENUM type, callback_func func); - void connect_to_signals(); - void disconnect_from_signals(); - DBusConnection* get_conn() const { - return m_dbus_conn; - } - - libdbus_loader& dbus() { - return m_dbus_ldr; - } - - - protected: - void stop_thread(); - void start_thread(); - void dbus_thread(); - - bool dbus_list_name_to_owner(); - bool select_active_player(metadata* meta = nullptr); - - static DBusHandlerResult filter_signals(DBusConnection*, DBusMessage*, void*); - - bool handle_properties_changed(DBusMessage*, const char*); - bool handle_name_owner_changed(DBusMessage*, const char*); - - DBusError m_error; - DBusConnection * m_dbus_conn = nullptr; - DBusMessage * m_dbus_msg = nullptr; - DBusMessage * m_dbus_reply = nullptr; - bool m_quit = false; - bool m_inited = false; - std::thread m_thread; - std::map m_callbacks; - libdbus_loader m_dbus_ldr; - std::unordered_map m_name_owners; - std::string m_requested_player; - std::string m_active_player; - - - - const std::array m_signals {{ - { "org.freedesktop.DBus", "NameOwnerChanged", &dbus_manager::handle_name_owner_changed }, - { "org.freedesktop.DBus.Properties", "PropertiesChanged", &dbus_manager::handle_properties_changed }, - }}; - - }; +struct DBusSignal { + const char* intf; + const char* signal; + signal_handler_func handler; +}; - extern dbus_manager dbus_mgr; -} +class dbus_manager { + public: + dbus_manager() {} + + ~dbus_manager(); + + bool init(const std::string& requested_player); + void deinit(); + bool get_media_player_metadata(metadata& meta, std::string name = ""); + void connect_to_signals(); + void disconnect_from_signals(); + DBusConnection* get_conn() const { return m_dbus_conn; } + + libdbus_loader& dbus() { return m_dbus_ldr; } + + protected: + void stop_thread(); + void start_thread(); + void dbus_thread(); + + bool dbus_list_name_to_owner(); + bool select_active_player(); + + static DBusHandlerResult filter_signals(DBusConnection*, DBusMessage*, + void*); + + bool handle_properties_changed(DBusMessage*, const char*); + bool handle_name_owner_changed(DBusMessage*, const char*); + + void onNewPlayer( + metadata& meta); // A different player has become the active player + void onNoPlayer(); // There is no longer any player active + void onPlayerUpdate( + metadata& meta); // The active player has sent an update + + DBusError m_error; + DBusConnection* m_dbus_conn = nullptr; + bool m_quit = false; + bool m_inited = false; + std::thread m_thread; + libdbus_loader m_dbus_ldr; + std::unordered_map m_name_owners; + std::string m_requested_player; + std::string m_active_player; + + const std::array m_signals{{ + {"org.freedesktop.DBus", "NameOwnerChanged", + &dbus_manager::handle_name_owner_changed}, + {"org.freedesktop.DBus.Properties", "PropertiesChanged", + &dbus_manager::handle_properties_changed}, + }}; +}; -<<<<<<< HEAD +extern dbus_manager dbus_mgr; +} // namespace dbusmgr bool get_media_player_metadata(dbusmgr::dbus_manager& dbus, const std::string& name, metadata& meta); #endif //MANGOHUD_DBUS_INFO_H -======= -//bool get_media_player_metadata(dbusmgr::dbus_manager& dbus, const std::string& name, metadata& meta); ->>>>>>> 9c064df... Change the media player functionality to allow changing active media