From 4bf7a3f2f03e0f68747584cc826cbab49664501d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Kr=C3=A4mer?= <24323470+larskraemer@users.noreply.github.com> Date: Tue, 30 Jun 2020 16:24:49 +0200 Subject: [PATCH] Make DBus signal handling more event-based --- src/dbus.cpp | 204 ++++++++++++++++-------------------- src/dbus_info.h | 26 +++-- src/loaders/loader_dbus.cpp | 40 +++++++ src/loaders/loader_dbus.h | 7 +- 4 files changed, 157 insertions(+), 120 deletions(-) diff --git a/src/dbus.cpp b/src/dbus.cpp index 76ae70ec..8aa63f6a 100644 --- a/src/dbus.cpp +++ b/src/dbus.cpp @@ -13,9 +13,13 @@ typedef std::vector> string_pair_vec; typedef std::unordered_map string_pair_vec_map; typedef std::unordered_map string_map; +namespace dbusmgr { +dbus_manager dbus_mgr; +} + #define DBUS_TIMEOUT 2000 // ms -std::string format_signal(const DBusSignal& s) +std::string format_signal(const dbusmgr::DBusSignal& s) { std::stringstream ss; ss << "type='signal',interface='" << s.intf << "'"; @@ -144,8 +148,10 @@ static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std:: dbus.message_iter_init (msg, &stack.back()); // Should be 'org.mpris.MediaPlayer2.Player' - if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_STRING)) + if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_STRING)){ + std::cerr << "Not a string\n"; return; + } dbus.message_iter_get_basic(&stack.back(), &val_char); source = val_char; @@ -154,7 +160,6 @@ static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std:: return; dbus.message_iter_next (&stack.back()); - //std::cerr << "type: " << (char)dbus.message_iter_get_arg_type(&stack.back()) << std::endl; if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_ARRAY)) return; @@ -455,8 +460,6 @@ bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr, std::string& name_owne return true; } - - bool dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, string_pair_vec& entries, const char * dest, const char * prop) { auto& dbus = dbus_mgr.dbus(); @@ -595,6 +598,86 @@ dbus_manager::~dbus_manager() deinit(); } +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)){ + auto sender = manager.m_dbus_ldr.message_get_sender(msg); + if((manager.*(sig.handler))(msg, sender)) + return DBUS_HANDLER_RESULT_HANDLED; + else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +bool dbus_manager::handle_properties_changed(DBusMessage* msg, const char* sender) { + std::string source; + string_pair_vec_map entries_map; + + //parse_property_changed(msg, source, entries); + parse_mpris_properties(m_dbus_ldr, msg, source, entries_map); +#ifndef NDEBUG + std::cerr << "Source: " << source << "\n"; +#endif + if (source != "org.mpris.MediaPlayer2.Player") + return false; + + if(m_active_player == "") { + select_active_player(); + } +#ifndef NDEBUG + std::cerr << "active_player: " << m_active_player << "\n"; + std::cerr << "active_player's owner: " << m_name_owners[m_active_player] << "\n"; + std::cerr << "sender: " << sender << "\n"; +#endif + if (m_name_owners[m_active_player] == sender) { + assign_metadata(main_metadata, entries_map); + } + return true; +} + +bool dbus_manager::handle_name_owner_changed(DBusMessage* msg, const char* sender) { + DBusMessageIter iter; + m_dbus_ldr.message_iter_init (msg, &iter); + 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); + } + + // register new name + 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){ + select_active_player(); + get_media_player_metadata(main_metadata); + } + } + + // did a player quit? + if (str[2].empty()) { + if (str.size() == 3 + && str[0] == m_active_player + ) { + main_metadata.clear(); + m_name_owners.erase(str[0]); + select_active_player(); + get_media_player_metadata(main_metadata); + } + } + return true; +} + void dbus_manager::connect_to_signals() { for (auto kv : m_signals) { @@ -607,12 +690,14 @@ void dbus_manager::connect_to_signals() //return; } } + 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)); for (auto kv : m_signals) { auto signal = format_signal(kv); m_dbus_ldr.bus_remove_match(m_dbus_conn, signal.c_str(), &m_error); @@ -693,112 +778,9 @@ void dbus_manager::start_thread() void dbus_manager::dbus_thread() { - (void)parse_property_changed; - DBusMessage *msg = nullptr; - - // loop listening for signals being emmitted - while (!m_quit) { - - // non blocking read of the next available message - if (!m_dbus_ldr.connection_read_write(m_dbus_conn, 0)) - return; // connection closed - - msg = m_dbus_ldr.connection_pop_message(m_dbus_conn); - - // loop again if we haven't read a message - if (nullptr == msg) { - std::this_thread::yield(); - //std::this_thread::sleep_for(ms(10)); - continue; - } - - for (auto& sig : m_signals) { - if (m_dbus_ldr.message_is_signal(msg, sig.intf, sig.signal)) - { - - const char *sender = m_dbus_ldr.message_get_sender(msg); -#ifndef NDEBUG - std::cerr << __func__ << ": " << sig.intf << "::" << sig.signal << "\n"; - std::cerr << "Sender: " << sender << "\n"; -#endif - - switch (sig.type) { - case ST_PROPERTIESCHANGED: - { - std::string source; - string_pair_vec_map entries_map; - - //parse_property_changed(msg, source, entries); - parse_mpris_properties(m_dbus_ldr, msg, source, entries_map); -#ifndef NDEBUG - std::cerr << "Source: " << source << "\n"; -#endif - if (source != "org.mpris.MediaPlayer2.Player") - break; - - if(m_active_player == "") { - select_active_player(); - } -#ifndef NDEBUG - std::cerr << "active_player: " << m_active_player << "\n"; - std::cerr << "active_player's owner: " << m_name_owners[m_active_player] << "\n"; - std::cerr << "sender: " << sender << "\n"; -#endif - if (m_name_owners[m_active_player] == sender) { - assign_metadata(main_metadata, entries_map); - } - } - break; - case ST_NAMEOWNERCHANGED: - { - DBusMessageIter iter; - m_dbus_ldr.message_iter_init (msg, &iter); - 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); - } - - // register new name - 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){ - select_active_player(); - get_media_player_metadata(main_metadata); - } - } - - // did a player quit? - if (str[2].empty()) { - if (str.size() == 3 - && str[0] == m_active_player - ) { - main_metadata.clear(); - m_name_owners.erase(str[0]); - select_active_player(); - get_media_player_metadata(main_metadata); - } - } - - } - break; - default: - break; - } - } - } - - // free the message - m_dbus_ldr.message_unref(msg); - } + using namespace std::chrono_literals; + while(!m_quit && m_dbus_ldr.connection_read_write_dispatch(m_dbus_conn, 0)) + std::this_thread::sleep_for(10ms); } - dbus_manager dbus_mgr; } diff --git a/src/dbus_info.h b/src/dbus_info.h index feaf7fe4..9cb9c7fd 100644 --- a/src/dbus_info.h +++ b/src/dbus_info.h @@ -55,16 +55,21 @@ enum SignalType ST_PROPERTIESCHANGED, }; -struct DBusSignal -{ - const char * intf; - const char * signal; - SignalType type; -}; extern struct 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 { @@ -105,6 +110,11 @@ namespace dbusmgr { 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*); + DBusError m_error; DBusConnection * m_dbus_conn = nullptr; DBusMessage * m_dbus_msg = nullptr; @@ -121,8 +131,8 @@ namespace dbusmgr { const std::array m_signals {{ - { "org.freedesktop.DBus", "NameOwnerChanged", ST_NAMEOWNERCHANGED }, - { "org.freedesktop.DBus.Properties", "PropertiesChanged", ST_PROPERTIESCHANGED }, + { "org.freedesktop.DBus", "NameOwnerChanged", &dbus_manager::handle_name_owner_changed }, + { "org.freedesktop.DBus.Properties", "PropertiesChanged", &dbus_manager::handle_properties_changed }, }}; }; diff --git a/src/loaders/loader_dbus.cpp b/src/loaders/loader_dbus.cpp index af0a38ab..0c981a33 100644 --- a/src/loaders/loader_dbus.cpp +++ b/src/loaders/loader_dbus.cpp @@ -63,6 +63,14 @@ bool libdbus_loader::Load(const std::string& library_name) { return false; } + connection_add_filter = + reinterpret_castconnection_add_filter)>( + dlsym(library_, "dbus_connection_add_filter")); + if (!connection_add_filter) { + CleanUp(true); + return false; + } + connection_pop_message = reinterpret_castconnection_pop_message)>( dlsym(library_, "dbus_connection_pop_message")); @@ -79,6 +87,22 @@ bool libdbus_loader::Load(const std::string& library_name) { return false; } + connection_read_write_dispatch = + reinterpret_castconnection_read_write)>( + dlsym(library_, "dbus_connection_read_write_dispatch")); + if (!connection_read_write_dispatch) { + CleanUp(true); + return false; + } + + connection_remove_filter = + reinterpret_castconnection_remove_filter)>( + dlsym(library_, "dbus_connection_remove_filter")); + if (!connection_remove_filter) { + CleanUp(true); + return false; + } + connection_send_with_reply_and_block = reinterpret_castconnection_send_with_reply_and_block)>( dlsym(library_, "dbus_connection_send_with_reply_and_block")); @@ -127,6 +151,22 @@ bool libdbus_loader::Load(const std::string& library_name) { return false; } + message_get_interface = + reinterpret_castmessage_get_interface)>( + dlsym(library_, "dbus_message_get_interface")); + if (!message_get_interface) { + CleanUp(true); + return false; + } + + message_get_member = + reinterpret_castmessage_get_member)>( + dlsym(library_, "dbus_message_get_member")); + if (!message_get_member) { + CleanUp(true); + return false; + } + message_is_signal = reinterpret_castmessage_is_signal)>( dlsym(library_, "dbus_message_is_signal")); diff --git a/src/loaders/loader_dbus.h b/src/loaders/loader_dbus.h index f522fa96..a14418b3 100644 --- a/src/loaders/loader_dbus.h +++ b/src/loaders/loader_dbus.h @@ -24,14 +24,20 @@ class libdbus_loader { decltype(&::dbus_bus_get) bus_get; decltype(&::dbus_bus_get_unique_name) bus_get_unique_name; decltype(&::dbus_bus_remove_match) bus_remove_match; + decltype(&::dbus_connection_add_filter) connection_add_filter; decltype(&::dbus_connection_pop_message) connection_pop_message; decltype(&::dbus_connection_read_write) connection_read_write; + decltype(&::dbus_connection_read_write_dispatch) connection_read_write_dispatch; + decltype(&::dbus_connection_remove_filter) connection_remove_filter; decltype(&::dbus_connection_send_with_reply_and_block) connection_send_with_reply_and_block; decltype(&::dbus_connection_unref) connection_unref; decltype(&::dbus_error_free) error_free; decltype(&::dbus_error_init) error_init; decltype(&::dbus_error_is_set) error_is_set; decltype(&::dbus_message_append_args) message_append_args; + decltype(&::dbus_message_get_sender) message_get_sender; + decltype(&::dbus_message_get_interface) message_get_interface; + decltype(&::dbus_message_get_member) message_get_member; decltype(&::dbus_message_is_signal) message_is_signal; decltype(&::dbus_message_iter_get_arg_type) message_iter_get_arg_type; decltype(&::dbus_message_iter_get_basic) message_iter_get_basic; @@ -42,7 +48,6 @@ class libdbus_loader { decltype(&::dbus_message_unref) message_unref; decltype(&::dbus_move_error) move_error; decltype(&::dbus_threads_init_default) threads_init_default; - decltype(&::dbus_message_get_sender) message_get_sender; private: