diff --git a/meson.build b/meson.build index ea1e33d9..9dcf9ef6 100644 --- a/meson.build +++ b/meson.build @@ -115,7 +115,7 @@ inc_common = [ dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan')) dep_pthread = dependency('threads') -dep_dbus = dependency('dbus-1') +dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true) # Check for generic C arguments c_args = [] diff --git a/meson_options.txt b/meson_options.txt index 9c634547..4de3b4f3 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,3 +6,4 @@ option('include_doc', type : 'boolean', value : true, description: 'Include the option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support') option('with_x11', type : 'feature', value : 'enabled') option('with_wayland', type : 'feature', value : 'disabled') +option('with_dbus', type : 'feature', value : 'enabled') diff --git a/src/dbus.cpp b/src/dbus.cpp index 4ffb6e75..b2d092e9 100644 --- a/src/dbus.cpp +++ b/src/dbus.cpp @@ -17,104 +17,107 @@ std::string format_signal(const DBusSignal& s) return ss.str(); } - -static bool check_msg_arg(DBusMessageIter *iter, int type) +static bool check_msg_arg(libdbus_loader& dbus, DBusMessageIter *iter, int type) { int curr_type = DBUS_TYPE_INVALID; - if ((curr_type = dbus_message_iter_get_arg_type (iter)) != type) { + if ((curr_type = dbus.message_iter_get_arg_type (iter)) != type) { +#ifndef NDEBUG std::cerr << "Argument is not of type '" << (char)type << "' != '" << (char) curr_type << "'" << std::endl; +#endif return false; } return true; } -bool get_string_array(DBusMessageIter *iter_, std::vector& entries) +bool get_string_array(libdbus_loader& dbus, DBusMessageIter *iter_, std::vector& entries) { DBusMessageIter iter = *iter_; DBusMessageIter subiter; int current_type = DBUS_TYPE_INVALID; - current_type = dbus_message_iter_get_arg_type (&iter); + current_type = dbus.message_iter_get_arg_type (&iter); if (current_type == DBUS_TYPE_VARIANT) { - dbus_message_iter_recurse (&iter, &iter); - current_type = dbus_message_iter_get_arg_type (&iter); + dbus.message_iter_recurse (&iter, &iter); + current_type = dbus.message_iter_get_arg_type (&iter); } if (current_type != DBUS_TYPE_ARRAY) { +#ifndef NDEBUG std::cerr << "Not an array: '" << (char)current_type << "'" << std::endl; +#endif return false; } char *val = nullptr; - dbus_message_iter_recurse (&iter, &subiter); + dbus.message_iter_recurse (&iter, &subiter); entries.clear(); - while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) { + while ((current_type = dbus.message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) { if (current_type == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic (&subiter, &val); + dbus.message_iter_get_basic (&subiter, &val); entries.push_back(val); } - dbus_message_iter_next (&subiter); + dbus.message_iter_next (&subiter); } return true; } -static bool get_variant_string(DBusMessageIter *iter_, std::string &val, bool key_or_value = false) +static bool get_variant_string(libdbus_loader& dbus, DBusMessageIter *iter_, std::string &val, bool key_or_value = false) { DBusMessageIter iter = *iter_; char *str = nullptr; - int type = dbus_message_iter_get_arg_type (&iter); + int type = dbus.message_iter_get_arg_type (&iter); if (type != DBUS_TYPE_VARIANT && type != DBUS_TYPE_DICT_ENTRY) return false; - dbus_message_iter_recurse (&iter, &iter); + dbus.message_iter_recurse (&iter, &iter); if (key_or_value) { - dbus_message_iter_next (&iter); - if (!check_msg_arg (&iter, DBUS_TYPE_VARIANT)) + dbus.message_iter_next (&iter); + if (!check_msg_arg (dbus, &iter, DBUS_TYPE_VARIANT)) return false; - dbus_message_iter_recurse (&iter, &iter); + dbus.message_iter_recurse (&iter, &iter); } - if (!check_msg_arg (&iter, DBUS_TYPE_STRING)) + if (!check_msg_arg (dbus, &iter, DBUS_TYPE_STRING)) return false; - dbus_message_iter_get_basic(&iter, &str); + dbus.message_iter_get_basic(&iter, &str); val = str; return true; } -static bool get_variant_string(DBusMessage *msg, std::string &val, bool key_or_value = false) +static bool get_variant_string(libdbus_loader& dbus, DBusMessage *msg, std::string &val, bool key_or_value = false) { DBusMessageIter iter; - dbus_message_iter_init (msg, &iter); - return get_variant_string(&iter, val, key_or_value); + dbus.message_iter_init (msg, &iter); + return get_variant_string(dbus, &iter, val, key_or_value); } -static void parse_mpris_metadata(DBusMessageIter *iter_, string_pair_vec& entries) +static void parse_mpris_metadata(libdbus_loader& dbus, DBusMessageIter *iter_, string_pair_vec& entries) { DBusMessageIter subiter, iter = *iter_; std::string key, val; std::vector list; - while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) + while (dbus.message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) { - dbus_message_iter_next (&iter); - //std::cerr << "\ttype: " << (char)dbus_message_iter_get_arg_type(&iter) << std::endl; - if (!get_variant_string(&iter, key)) + dbus.message_iter_next (&iter); + //std::cerr << "\ttype: " << (char)dbus.message_iter_get_arg_type(&iter) << std::endl; + if (!get_variant_string(dbus, &iter, key)) return; - dbus_message_iter_recurse (&iter, &subiter); - dbus_message_iter_next (&subiter); + dbus.message_iter_recurse (&iter, &subiter); + dbus.message_iter_next (&subiter); //std::cerr << "\tkey: " << key << std::endl; - if (get_variant_string(&subiter, val)) { + if (get_variant_string(dbus, &subiter, val)) { //std::cerr << "\t\t" << val << std::endl; entries.push_back({key, val}); } - else if (get_string_array(&subiter, list)) { + else if (get_string_array(dbus, &subiter, list)) { for (auto& s : list) { //std::cerr << "\t\t" << s << std::endl; entries.push_back({key, s}); @@ -123,7 +126,7 @@ static void parse_mpris_metadata(DBusMessageIter *iter_, string_pair_vec& entrie } } -static void parse_mpris_properties(DBusMessage *msg, std::string& source, string_pair_vec& entries) +static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std::string& source, string_pair_vec& entries) { const char *val_char = nullptr; DBusMessageIter iter; @@ -132,30 +135,30 @@ static void parse_mpris_properties(DBusMessage *msg, std::string& source, string std::vector stack; stack.push_back({}); - dbus_message_iter_init (msg, &stack.back()); + dbus.message_iter_init (msg, &stack.back()); // Should be 'org.mpris.MediaPlayer2.Player' - if (!check_msg_arg(&stack.back(), DBUS_TYPE_STRING)) + if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_STRING)) return; - dbus_message_iter_get_basic(&stack.back(), &val_char); + dbus.message_iter_get_basic(&stack.back(), &val_char); source = val_char; if (source != "org.mpris.MediaPlayer2.Player") 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(&stack.back(), DBUS_TYPE_ARRAY)) + 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; - dbus_message_iter_recurse (&stack.back(), &iter); + dbus.message_iter_recurse (&stack.back(), &iter); stack.push_back(iter); - while (dbus_message_iter_get_arg_type(&stack.back()) != DBUS_TYPE_INVALID) + while (dbus.message_iter_get_arg_type(&stack.back()) != DBUS_TYPE_INVALID) { - if (!get_variant_string(&stack.back(), key)) { - dbus_message_iter_next (&stack.back()); + if (!get_variant_string(dbus, &stack.back(), key)) { + dbus.message_iter_next (&stack.back()); continue; } @@ -165,33 +168,33 @@ static void parse_mpris_properties(DBusMessage *msg, std::string& source, string #endif // dive into Metadata - dbus_message_iter_recurse (&stack.back(), &iter); + dbus.message_iter_recurse (&stack.back(), &iter); // get the array of entries - dbus_message_iter_next (&iter); - if (!check_msg_arg(&iter, DBUS_TYPE_VARIANT)) + dbus.message_iter_next (&iter); + if (!check_msg_arg(dbus, &iter, DBUS_TYPE_VARIANT)) continue; - dbus_message_iter_recurse (&iter, &iter); + dbus.message_iter_recurse (&iter, &iter); - if (!check_msg_arg(&iter, DBUS_TYPE_ARRAY)) + if (!check_msg_arg(dbus, &iter, DBUS_TYPE_ARRAY)) continue; - dbus_message_iter_recurse (&iter, &iter); + dbus.message_iter_recurse (&iter, &iter); - parse_mpris_metadata(&iter, entries); + parse_mpris_metadata(dbus, &iter, entries); } else if (key == "PlaybackStatus") { - dbus_message_iter_recurse (&stack.back(), &iter); - dbus_message_iter_next (&iter); + dbus.message_iter_recurse (&stack.back(), &iter); + dbus.message_iter_next (&iter); - if (get_variant_string(&iter, val)) + if (get_variant_string(dbus, &iter, val)) entries.push_back({key, val}); } - dbus_message_iter_next (&stack.back()); + dbus.message_iter_next (&stack.back()); } } -static void parse_property_changed(DBusMessage *msg, std::string& source, string_pair_vec& entries) +static void parse_property_changed(libdbus_loader& dbus, DBusMessage *msg, std::string& source, string_pair_vec& entries) { char *name = nullptr; int i; @@ -202,20 +205,22 @@ static void parse_property_changed(DBusMessage *msg, std::string& source, string stack.push_back({}); #ifndef NDEBUG - std::vector tabs; - tabs.push_back('\0'); + std::vector padding; + padding.push_back('\0'); #endif - dbus_message_iter_init (msg, &stack.back()); + dbus.message_iter_init (msg, &stack.back()); int type, prev_type = 0; - type = dbus_message_iter_get_arg_type (&stack.back()); + type = dbus.message_iter_get_arg_type (&stack.back()); if (type != DBUS_TYPE_STRING) { +#ifndef NDEBUG std::cerr << __func__ << "First element is not a string" << std::endl; +#endif return; } - dbus_message_iter_get_basic(&stack.back(), &name); + dbus.message_iter_get_basic(&stack.back(), &name); source = name; #ifndef NDEBUG std::cout << name << std::endl; @@ -223,17 +228,18 @@ static void parse_property_changed(DBusMessage *msg, std::string& source, string std::pair kv; - dbus_message_iter_next (&stack.back()); - while ((type = dbus_message_iter_get_arg_type (&stack.back())) != DBUS_TYPE_INVALID) { + dbus.message_iter_next (&stack.back()); + // the loop should be able parse the whole message if used for generic use-cases + while ((type = dbus.message_iter_get_arg_type (&stack.back())) != DBUS_TYPE_INVALID) { #ifndef NDEBUG - tabs.back() = ' '; - tabs.resize(stack.size() + 1, ' '); - tabs.back() = '\0'; - std::cout << tabs.data() << "Type: " << (char)type; + padding.back() = ' '; + padding.resize(stack.size() + 1, ' '); + padding.back() = '\0'; + std::cout << padding.data() << "Type: " << (char)type; #endif if (type == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic(&stack.back(), &name); + dbus.message_iter_get_basic(&stack.back(), &name); #ifndef NDEBUG std::cout << "=" << name << std::endl; #endif @@ -245,19 +251,19 @@ static void parse_property_changed(DBusMessage *msg, std::string& source, string } } else if (type == DBUS_TYPE_INT32) { - dbus_message_iter_get_basic(&stack.back(), &i); + dbus.message_iter_get_basic(&stack.back(), &i); #ifndef NDEBUG std::cout << "=" << i << std::endl; #endif } else if (type == DBUS_TYPE_UINT64) { - dbus_message_iter_get_basic(&stack.back(), &u64); + dbus.message_iter_get_basic(&stack.back(), &u64); #ifndef NDEBUG std::cout << "=" << u64 << std::endl; #endif } else if (type == DBUS_TYPE_DOUBLE) { - dbus_message_iter_get_basic(&stack.back(), &d); + dbus.message_iter_get_basic(&stack.back(), &d); #ifndef NDEBUG std::cout << "=" << d << std::endl; #endif @@ -268,8 +274,8 @@ static void parse_property_changed(DBusMessage *msg, std::string& source, string #endif prev_type = type; DBusMessageIter iter; - dbus_message_iter_recurse (&stack.back(), &iter); - if (dbus_message_iter_get_arg_type (&stack.back()) != DBUS_TYPE_INVALID) + dbus.message_iter_recurse (&stack.back(), &iter); + if (dbus.message_iter_get_arg_type (&stack.back()) != DBUS_TYPE_INVALID) stack.push_back(iter); continue; } else { @@ -278,67 +284,69 @@ static void parse_property_changed(DBusMessage *msg, std::string& source, string #endif } - while(FALSE == dbus_message_iter_next (&stack.back()) && stack.size() > 1) { + while(FALSE == dbus.message_iter_next (&stack.back()) && stack.size() > 1) { stack.pop_back(); prev_type = 0; } } } -bool get_dict_string_array(DBusMessage *msg, string_pair_vec& entries) +bool get_dict_string_array(libdbus_loader& dbus, DBusMessage *msg, string_pair_vec& entries) { DBusMessageIter iter, outer_iter; - dbus_message_iter_init (msg, &outer_iter); + dbus.message_iter_init (msg, &outer_iter); int current_type = DBUS_TYPE_INVALID; - current_type = dbus_message_iter_get_arg_type (&outer_iter); + current_type = dbus.message_iter_get_arg_type (&outer_iter); if (current_type == DBUS_TYPE_VARIANT) { - dbus_message_iter_recurse (&outer_iter, &outer_iter); - current_type = dbus_message_iter_get_arg_type (&outer_iter); + dbus.message_iter_recurse (&outer_iter, &outer_iter); + current_type = dbus.message_iter_get_arg_type (&outer_iter); } if (current_type != DBUS_TYPE_ARRAY) { +#ifndef NDEBUG std::cerr << "Not an array " << (char)current_type << std::endl; +#endif return false; } char *val_key = nullptr, *val_value = nullptr; - dbus_message_iter_recurse (&outer_iter, &outer_iter); - while ((current_type = dbus_message_iter_get_arg_type (&outer_iter)) != DBUS_TYPE_INVALID) { + dbus.message_iter_recurse (&outer_iter, &outer_iter); + while ((current_type = dbus.message_iter_get_arg_type (&outer_iter)) != DBUS_TYPE_INVALID) { // printf("type: %d\n", current_type); if (current_type == DBUS_TYPE_DICT_ENTRY) { - dbus_message_iter_recurse (&outer_iter, &iter); + dbus.message_iter_recurse (&outer_iter, &iter); // dict entry key - //printf("\tentry: {%c, ", dbus_message_iter_get_arg_type (&iter)); - dbus_message_iter_get_basic (&iter, &val_key); + //printf("\tentry: {%c, ", dbus.message_iter_get_arg_type (&iter)); + dbus.message_iter_get_basic (&iter, &val_key); std::string key = val_key; // dict entry value - dbus_message_iter_next (&iter); + dbus.message_iter_next (&iter); - if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT) - dbus_message_iter_recurse (&iter, &iter); + if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT) + dbus.message_iter_recurse (&iter, &iter); - if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) { - dbus_message_iter_recurse (&iter, &iter); - if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { - //printf("%c}\n", dbus_message_iter_get_arg_type (&iter)); - dbus_message_iter_get_basic (&iter, &val_value); + if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) { + dbus.message_iter_recurse (&iter, &iter); + if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { + //printf("%c}\n", dbus.message_iter_get_arg_type (&iter)); + dbus.message_iter_get_basic (&iter, &val_value); entries.push_back({val_key, val_value}); } } - else if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { - //printf("%c}\n", dbus_message_iter_get_arg_type (&iter)); - dbus_message_iter_get_basic (&iter, &val_value); + else if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { + //printf("%c}\n", dbus.message_iter_get_arg_type (&iter)); + dbus.message_iter_get_basic (&iter, &val_value); entries.push_back({val_key, val_value}); } } - dbus_message_iter_next (&outer_iter); + dbus.message_iter_next (&outer_iter); } return true; } @@ -346,18 +354,14 @@ bool get_dict_string_array(DBusMessage *msg, string_pair_vec& entries) static void assign_metadata(metadata& meta, string_pair_vec& entries) { std::lock_guard lk(meta.mutex); + std::vector artists; meta.valid = false; - bool artists_cleared = false; for (auto& kv : entries) { #ifndef NDEBUG std::cerr << kv.first << " = " << kv.second << std::endl; #endif if (kv.first == "xesam:artist") { - if (!artists_cleared) { - artists_cleared = true; - meta.artists.clear(); - } - meta.artists.push_back(kv.second); + artists.push_back(kv.second); } else if (kv.first == "xesam:title") meta.title = kv.second; @@ -369,20 +373,34 @@ static void assign_metadata(metadata& meta, string_pair_vec& entries) meta.playing = (kv.second == "Playing"); } + // XXX Spotify only sends one artist anyway + meta.artists.clear(); + for (auto p = artists.begin(); p != artists.end(); p++) { + meta.artists += *p; + if (p != artists.end() - 1) + meta.artists += ", "; + } + if (meta.artists.size() || !meta.title.empty()) meta.valid = meta.playing; + + meta.ticker.needs_recalc = true; + meta.ticker.pos = 0; + meta.ticker.longest = 0; + meta.ticker.dir = -1; } -void dbus_get_spotify_property(dbusmgr::dbus_manager& dbus, string_pair_vec& entries, const char * prop) +void dbus_get_spotify_property(dbusmgr::dbus_manager& dbus_mgr, string_pair_vec& entries, const char * prop) { + auto& dbus = dbus_mgr.dbus(); DBusError error; - ::dbus_error_init(&error); + dbus.error_init(&error); DBusMessage * dbus_reply = nullptr; DBusMessage * dbus_msg = nullptr; // dbus-send --print-reply --session --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'Metadata' - if (nullptr == (dbus_msg = ::dbus_message_new_method_call("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"))) { + if (nullptr == (dbus_msg = dbus.message_new_method_call("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"))) { throw std::runtime_error("unable to allocate memory for dbus message"); } @@ -391,26 +409,26 @@ void dbus_get_spotify_property(dbusmgr::dbus_manager& dbus, string_pair_vec& ent }; std::cerr << __func__ << ": " << prop << std::endl; - if (!dbus_message_append_args (dbus_msg, DBUS_TYPE_STRING, &v_STRINGS[0], DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID)) { - ::dbus_message_unref(dbus_msg); + if (!dbus.message_append_args (dbus_msg, DBUS_TYPE_STRING, &v_STRINGS[0], DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID)) { + dbus.message_unref(dbus_msg); throw std::runtime_error(error.message); } - if (nullptr == (dbus_reply = ::dbus_connection_send_with_reply_and_block(dbus.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) { - ::dbus_message_unref(dbus_msg); - throw dbusmgr::dbus_error(&error); + if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) { + dbus.message_unref(dbus_msg); + throw dbusmgr::dbus_error(dbus, &error); } std::string entry; - if (get_dict_string_array(dbus_reply, entries)) { + if (get_dict_string_array(dbus, dbus_reply, entries)) { // nothing - } else if (get_variant_string(dbus_reply, entry)) { + } else if (get_variant_string(dbus, dbus_reply, entry)) { entries.push_back({prop, entry}); } - ::dbus_message_unref(dbus_msg); - ::dbus_message_unref(dbus_reply); - ::dbus_error_free(&error); + dbus.message_unref(dbus_msg); + dbus.message_unref(dbus_reply); + dbus.error_free(&error); } void get_spotify_metadata(dbusmgr::dbus_manager& dbus, metadata& meta) @@ -425,12 +443,17 @@ void get_spotify_metadata(dbusmgr::dbus_manager& dbus, metadata& meta) namespace dbusmgr { void dbus_manager::init() { - ::dbus_threads_init_default(); + if (!m_dbus_ldr.Load("libdbus-1.so.3")) + throw std::runtime_error("Could not load libdbus-1.so.3"); + + m_dbus_ldr.error_init(&m_error); - if ( nullptr == (m_dbus_conn = ::dbus_bus_get(DBUS_BUS_SESSION, &m_error)) ) { - throw dbus_error(&m_error); + m_dbus_ldr.threads_init_default(); + + if ( nullptr == (m_dbus_conn = m_dbus_ldr.bus_get(DBUS_BUS_SESSION, &m_error)) ) { + throw dbus_error(m_dbus_ldr, &m_error); } - std::cout << "Connected to D-Bus as \"" << ::dbus_bus_get_unique_name(m_dbus_conn) << "\"." << std::endl; + std::cout << "Connected to D-Bus as \"" << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"." << std::endl; connect_to_signals(); m_inited = true; @@ -441,21 +464,21 @@ dbus_manager::~dbus_manager() // unreference system bus connection instead of closing it if (m_dbus_conn) { disconnect_from_signals(); - ::dbus_connection_unref(m_dbus_conn); + m_dbus_ldr.connection_unref(m_dbus_conn); m_dbus_conn = nullptr; } - ::dbus_error_free(&m_error); + m_dbus_ldr.error_free(&m_error); } void dbus_manager::connect_to_signals() { for (auto kv : m_signals) { auto signal = format_signal(kv); - ::dbus_bus_add_match(m_dbus_conn, signal.c_str(), &m_error); - if (::dbus_error_is_set(&m_error)) { + m_dbus_ldr.bus_add_match(m_dbus_conn, signal.c_str(), &m_error); + if (m_dbus_ldr.error_is_set(&m_error)) { ::perror(m_error.name); ::perror(m_error.message); - ::dbus_error_free(&m_error); + m_dbus_ldr.error_free(&m_error); //return; } } @@ -467,11 +490,11 @@ void dbus_manager::disconnect_from_signals() { for (auto kv : m_signals) { auto signal = format_signal(kv); - ::dbus_bus_remove_match(m_dbus_conn, signal.c_str(), &m_error); - if (dbus_error_is_set(&m_error)) { + m_dbus_ldr.bus_remove_match(m_dbus_conn, signal.c_str(), &m_error); + if (m_dbus_ldr.error_is_set(&m_error)) { ::perror(m_error.name); ::perror(m_error.message); - ::dbus_error_free(&m_error); + m_dbus_ldr.error_free(&m_error); } } @@ -499,19 +522,18 @@ void dbus_manager::start_thread() void dbus_manager::dbus_thread(dbus_manager *pmgr) { - DBusError error; + (void)parse_property_changed; DBusMessage *msg = nullptr; - - ::dbus_error_init(&error); + auto& dbus = pmgr->dbus(); // loop listening for signals being emmitted while (!pmgr->m_quit) { // non blocking read of the next available message - if (!::dbus_connection_read_write(pmgr->m_dbus_conn, 0)) + if (!dbus.connection_read_write(pmgr->m_dbus_conn, 0)) return; // connection closed - msg = ::dbus_connection_pop_message(pmgr->m_dbus_conn); + msg = dbus.connection_pop_message(pmgr->m_dbus_conn); // loop again if we haven't read a message if (nullptr == msg) { @@ -520,7 +542,7 @@ void dbus_manager::dbus_thread(dbus_manager *pmgr) } for (auto& sig : pmgr->m_signals) { - if (::dbus_message_is_signal(msg, sig.intf, sig.signal)) + if (dbus.message_is_signal(msg, sig.intf, sig.signal)) { #ifndef NDEBUG @@ -534,7 +556,7 @@ void dbus_manager::dbus_thread(dbus_manager *pmgr) std::vector> entries; //parse_property_changed(msg, source, entries); - parse_mpris_properties(msg, source, entries); + parse_mpris_properties(dbus, msg, source, entries); #ifndef NDEBUG std::cerr << "Source: " << source << std::endl; #endif @@ -546,14 +568,14 @@ void dbus_manager::dbus_thread(dbus_manager *pmgr) case ST_NAMEOWNERCHANGED: { DBusMessageIter iter; - dbus_message_iter_init (msg, &iter); + dbus.message_iter_init (msg, &iter); std::vector str; const char *value = nullptr; - while (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { - dbus_message_iter_get_basic (&iter, &value); + while (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) { + dbus.message_iter_get_basic (&iter, &value); str.push_back(value); - dbus_message_iter_next (&iter); + dbus.message_iter_next (&iter); } // did spotify quit? @@ -569,15 +591,11 @@ void dbus_manager::dbus_thread(dbus_manager *pmgr) default: break; } - if (dbus_error_is_set(&error)) { - std::cerr << error.message << std::endl; - dbus_error_free(&error); - } } } // free the message - dbus_message_unref(msg); + dbus.message_unref(msg); } } diff --git a/src/dbus_info.h b/src/dbus_info.h index 4ff8210b..595e90b7 100644 --- a/src/dbus_info.h +++ b/src/dbus_info.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include @@ -7,14 +6,26 @@ #include #include #include +#include "loaders/loader_dbus.h" struct metadata { - std::vector artists; + //std::vector artists; + std::string artists; // pre-concatenate std::string title; std::string album; std::string something; std::string artUrl; bool playing = false; + struct { + float pos; + float longest; + int dir = -1; + bool needs_recalc; + + float tw0; + float tw1; + float tw2; + } ticker; bool valid = false; std::mutex mutex; @@ -47,14 +58,16 @@ namespace dbusmgr { class dbus_error : public std::runtime_error { public: - dbus_error(DBusError *src) : std::runtime_error(src->message) + dbus_error(libdbus_loader& dbus_, DBusError *src) : std::runtime_error(src->message) { - dbus_error_init(&error); - dbus_move_error (src, &error); + dbus = &dbus_; + dbus->error_init(&error); + dbus->move_error (src, &error); } - virtual ~dbus_error() { dbus_error_free (&error); } + virtual ~dbus_error() { dbus->error_free (&error); } private: DBusError error; + libdbus_loader *dbus; }; class dbus_manager @@ -62,7 +75,6 @@ namespace dbusmgr { public: dbus_manager() { - ::dbus_error_init(&m_error); } ~dbus_manager(); @@ -75,6 +87,11 @@ namespace dbusmgr { return m_dbus_conn; } + libdbus_loader& dbus() { + return m_dbus_ldr; + } + + protected: void stop_thread(); void start_thread(); @@ -88,6 +105,7 @@ namespace dbusmgr { bool m_inited = false; std::thread m_thread; std::map m_callbacks; + libdbus_loader m_dbus_ldr; const std::array m_signals {{ { "org.freedesktop.DBus", "NameOwnerChanged", ST_NAMEOWNERCHANGED }, diff --git a/src/gl/imgui_hud_shared.cpp b/src/gl/imgui_hud_shared.cpp index 50198134..47f8bb34 100644 --- a/src/gl/imgui_hud_shared.cpp +++ b/src/gl/imgui_hud_shared.cpp @@ -3,7 +3,10 @@ #include #include #include "imgui_hud_shared.h" + +#ifdef HAVE_DBUS #include "dbus_info.h" +#endif namespace MangoHud { namespace GL { @@ -28,6 +31,8 @@ void imgui_init() init_system_info(); cfg_inited = true; init_cpu_stats(params); + +#ifdef HAVE_DBUS if (params.enabled[OVERLAY_PARAM_ENABLED_media_player]) { try { dbusmgr::dbus_mgr.init(); @@ -36,6 +41,7 @@ void imgui_init() std::cerr << "Failed to get initial Spotify metadata: " << e.what() << std::endl; } } +#endif } }} // namespaces diff --git a/src/loaders/loader_dbus.cpp b/src/loaders/loader_dbus.cpp new file mode 100644 index 00000000..baf3d03b --- /dev/null +++ b/src/loaders/loader_dbus.cpp @@ -0,0 +1,270 @@ + +#include "loaders/loader_dbus.h" + +// Put these sanity checks here so that they fire at most once +// (to avoid cluttering the build output). +#if !defined(LIBRARY_LOADER_DBUS_H_DLOPEN) && !defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED) +#error neither LIBRARY_LOADER_DBUS_H_DLOPEN nor LIBRARY_LOADER_DBUS_H_DT_NEEDED defined +#endif +#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN) && defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED) +#error both LIBRARY_LOADER_DBUS_H_DLOPEN and LIBRARY_LOADER_DBUS_H_DT_NEEDED defined +#endif + +libdbus_loader::libdbus_loader() : loaded_(false) { +} + +libdbus_loader::~libdbus_loader() { + CleanUp(loaded_); +} + +bool libdbus_loader::Load(const std::string& library_name) { + if (loaded_) { + return false; + } + +#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN) + library_ = dlopen(library_name.c_str(), RTLD_LAZY); + if (!library_) + return false; + + + bus_add_match = + reinterpret_castbus_add_match)>( + dlsym(library_, "dbus_bus_add_match")); + if (!bus_add_match) { + CleanUp(true); + return false; + } + + bus_get = + reinterpret_castbus_get)>( + dlsym(library_, "dbus_bus_get")); + if (!bus_get) { + CleanUp(true); + return false; + } + + bus_get_unique_name = + reinterpret_castbus_get_unique_name)>( + dlsym(library_, "dbus_bus_get_unique_name")); + if (!bus_get_unique_name) { + CleanUp(true); + return false; + } + + bus_remove_match = + reinterpret_castbus_remove_match)>( + dlsym(library_, "dbus_bus_remove_match")); + if (!bus_remove_match) { + CleanUp(true); + return false; + } + + connection_pop_message = + reinterpret_castconnection_pop_message)>( + dlsym(library_, "dbus_connection_pop_message")); + if (!connection_pop_message) { + CleanUp(true); + return false; + } + + connection_read_write = + reinterpret_castconnection_read_write)>( + dlsym(library_, "dbus_connection_read_write")); + if (!connection_read_write) { + 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")); + if (!connection_send_with_reply_and_block) { + CleanUp(true); + return false; + } + + connection_unref = + reinterpret_castconnection_unref)>( + dlsym(library_, "dbus_connection_unref")); + if (!connection_unref) { + CleanUp(true); + return false; + } + + error_free = + reinterpret_casterror_free)>( + dlsym(library_, "dbus_error_free")); + if (!error_free) { + CleanUp(true); + return false; + } + + error_init = + reinterpret_casterror_init)>( + dlsym(library_, "dbus_error_init")); + if (!error_init) { + CleanUp(true); + return false; + } + + error_is_set = + reinterpret_casterror_is_set)>( + dlsym(library_, "dbus_error_is_set")); + if (!error_is_set) { + CleanUp(true); + return false; + } + + message_append_args = + reinterpret_castmessage_append_args)>( + dlsym(library_, "dbus_message_append_args")); + if (!message_append_args) { + CleanUp(true); + return false; + } + + message_is_signal = + reinterpret_castmessage_is_signal)>( + dlsym(library_, "dbus_message_is_signal")); + if (!message_is_signal) { + CleanUp(true); + return false; + } + + message_iter_get_arg_type = + reinterpret_castmessage_iter_get_arg_type)>( + dlsym(library_, "dbus_message_iter_get_arg_type")); + if (!message_iter_get_arg_type) { + CleanUp(true); + return false; + } + + message_iter_get_basic = + reinterpret_castmessage_iter_get_basic)>( + dlsym(library_, "dbus_message_iter_get_basic")); + if (!message_iter_get_basic) { + CleanUp(true); + return false; + } + + message_iter_init = + reinterpret_castmessage_iter_init)>( + dlsym(library_, "dbus_message_iter_init")); + if (!message_iter_init) { + CleanUp(true); + return false; + } + + message_iter_next = + reinterpret_castmessage_iter_next)>( + dlsym(library_, "dbus_message_iter_next")); + if (!message_iter_next) { + CleanUp(true); + return false; + } + + message_iter_recurse = + reinterpret_castmessage_iter_recurse)>( + dlsym(library_, "dbus_message_iter_recurse")); + if (!message_iter_recurse) { + CleanUp(true); + return false; + } + + message_new_method_call = + reinterpret_castmessage_new_method_call)>( + dlsym(library_, "dbus_message_new_method_call")); + if (!message_new_method_call) { + CleanUp(true); + return false; + } + + message_unref = + reinterpret_castmessage_unref)>( + dlsym(library_, "dbus_message_unref")); + if (!message_unref) { + CleanUp(true); + return false; + } + + move_error = + reinterpret_castmove_error)>( + dlsym(library_, "dbus_move_error")); + if (!move_error) { + CleanUp(true); + return false; + } + + threads_init_default = + reinterpret_castthreads_init_default)>( + dlsym(library_, "dbus_threads_init_default")); + if (!threads_init_default) { + CleanUp(true); + return false; + } + +#endif + +#if defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED) + bus_add_match = &::dbus_bus_add_match; + bus_get = &::dbus_bus_get; + bus_get_unique_name = &::dbus_bus_get_unique_name; + bus_remove_match = &::dbus_bus_remove_match; + connection_pop_message = &::dbus_connection_pop_message; + connection_read_write = &::dbus_connection_read_write; + connection_send_with_reply_and_block = &::dbus_connection_send_with_reply_and_block; + connection_unref = &::dbus_connection_unref; + error_free = &::dbus_error_free; + error_init = &::dbus_error_init; + error_is_set = &::dbus_error_is_set; + message_append_args = &::dbus_message_append_args; + message_is_signal = &::dbus_message_is_signal; + message_iter_get_arg_type = &::dbus_message_iter_get_arg_type; + message_iter_get_basic = &::dbus_message_iter_get_basic; + message_iter_init = &::dbus_message_iter_init; + message_iter_next = &::dbus_message_iter_next; + message_iter_recurse = &::dbus_message_iter_recurse; + message_new_method_call = &::dbus_message_new_method_call; + message_unref = &::dbus_message_unref; + move_error = &::dbus_move_error; + threads_init_default = &::dbus_threads_init_default; + +#endif + + loaded_ = true; + return true; +} + +void libdbus_loader::CleanUp(bool unload) { +#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN) + if (unload) { + dlclose(library_); + library_ = NULL; + } +#endif + loaded_ = false; + bus_add_match = NULL; + bus_get = NULL; + bus_get_unique_name = NULL; + bus_remove_match = NULL; + connection_pop_message = NULL; + connection_read_write = NULL; + connection_send_with_reply_and_block = NULL; + connection_unref = NULL; + error_free = NULL; + error_init = NULL; + error_is_set = NULL; + message_append_args = NULL; + message_is_signal = NULL; + message_iter_get_arg_type = NULL; + message_iter_get_basic = NULL; + message_iter_init = NULL; + message_iter_next = NULL; + message_iter_recurse = NULL; + message_new_method_call = NULL; + message_unref = NULL; + move_error = NULL; + threads_init_default = NULL; + +} diff --git a/src/loaders/loader_dbus.h b/src/loaders/loader_dbus.h new file mode 100644 index 00000000..d24001d3 --- /dev/null +++ b/src/loaders/loader_dbus.h @@ -0,0 +1,61 @@ + +#ifndef LIBRARY_LOADER_DBUS_H +#define LIBRARY_LOADER_DBUS_H + +#include +#define LIBRARY_LOADER_DBUS_H_DLOPEN + + +#include +#include + +class libdbus_loader { + public: + libdbus_loader(); + libdbus_loader(const std::string& library_name) : libdbus_loader() { + Load(library_name); + } + ~libdbus_loader(); + + bool Load(const std::string& library_name); + bool IsLoaded() { return loaded_; } + + decltype(&::dbus_bus_add_match) bus_add_match; + 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_pop_message) connection_pop_message; + decltype(&::dbus_connection_read_write) connection_read_write; + 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_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; + decltype(&::dbus_message_iter_init) message_iter_init; + decltype(&::dbus_message_iter_next) message_iter_next; + decltype(&::dbus_message_iter_recurse) message_iter_recurse; + decltype(&::dbus_message_new_method_call) message_new_method_call; + decltype(&::dbus_message_unref) message_unref; + decltype(&::dbus_move_error) move_error; + decltype(&::dbus_threads_init_default) threads_init_default; + + + private: + void CleanUp(bool unload); + +#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN) + void* library_; +#endif + + bool loaded_; + + // Disallow copy constructor and assignment operator. + libdbus_loader(const libdbus_loader&); + void operator=(const libdbus_loader&); +}; + +#endif // LIBRARY_LOADER_DBUS_H diff --git a/src/meson.build b/src/meson.build index 910515d6..b326d74e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -53,7 +53,6 @@ vklayer_files = files( 'notify.cpp', 'elfhacks.cpp', 'real_dlsym.cpp', - 'dbus.cpp', ) opengl_files = files( @@ -152,6 +151,14 @@ if false endif +if dbus_dep.found() and get_option('with_dbus').enabled() + pre_args += '-DHAVE_DBUS' + vklayer_files += files( + 'dbus.cpp', + 'loaders/loader_dbus.cpp', + ) +endif + vklayer_mesa_overlay = shared_library( 'MangoHud', util_files, @@ -176,9 +183,9 @@ vklayer_mesa_overlay = shared_library( libimgui_core_dep, glimgui_glx_dep, glimgui_egl_dep, + dbus_dep, dep_dl, dep_pthread, - dep_dbus, dep_vulkan], include_directories : [inc_common, inc_opengl], link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro']), diff --git a/src/overlay.cpp b/src/overlay.cpp index 8d43b8cc..ca494370 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -57,12 +57,15 @@ #include "loaders/loader_nvml.h" #include "memory.h" #include "notify.h" + +#ifdef HAVE_DBUS #include "dbus_info.h" +float g_overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */; +#endif bool open = false; string gpuString; float offset_x, offset_y, hudSpacing; -float hudTicker = 50.f, overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */; int hudFirstRow, hudSecondRow; struct amdGpu amdgpu; struct fps_limit fps_limit_stats; @@ -1000,6 +1003,26 @@ static void right_aligned_text(float off_x, const char *fmt, ...) ImGui::Text("%s", buffer); } +float get_ticker_limited_pos(float pos, float tw, float& left_limit, float& right_limit) +{ + float cw = ImGui::GetContentRegionAvailWidth(); + float new_pos_x = ImGui::GetCursorPosX(); + left_limit = cw - tw + new_pos_x; + right_limit = new_pos_x; + + if (cw < tw) { + new_pos_x += pos; + // acts as a delay before it starts scrolling again + if (new_pos_x < left_limit) + return left_limit; + else if (new_pos_x > right_limit) + return right_limit; + else + return new_pos_x; + } + return new_pos_x; +} + void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan) { uint32_t f_idx = (data.n_frames - 1) % ARRAY_SIZE(data.frames_stats); @@ -1220,6 +1243,7 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& ImGui::PopFont(); } +#ifdef HAVE_DBUS { scoped_lock lk(spotify.mutex); if (spotify.valid) { @@ -1227,60 +1251,48 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& ImGui::Dummy(ImVec2(0.0f, 20.0f)); ImGui::PushFont(data.font1); - float tw = ImGui::CalcTextSize(spotify.title.c_str()).x; - float cw = ImGui::GetContentRegionAvailWidth(); - //if (hudTicker < -tw) - // hudTicker = cw; - - static int dir = 1; - float limited_pos, new_pos_x = ImGui::GetCursorPosX(); - float left_limit = cw - tw + new_pos_x; - float right_limit = ImGui::GetCursorPosX(); - - if (cw < tw) { - if (hudTicker < left_limit - overflow * .25f - new_pos_x) { - dir = -1; - hudTicker = (left_limit - overflow * .25f) + 1.f - new_pos_x; - } else if (hudTicker > right_limit + overflow - new_pos_x) { - dir = 1; - hudTicker = (right_limit + overflow) - 1.f - new_pos_x; - } - - hudTicker -= .5f * (frame_timing / 16666.7f) * dir; - new_pos_x += hudTicker; - - // acts as a delay before it starts scrolling again - if (new_pos_x < left_limit) - limited_pos = left_limit; - else if (new_pos_x > right_limit) - limited_pos = right_limit; - else - limited_pos = new_pos_x; + if (spotify.ticker.needs_recalc) { + spotify.ticker.tw0 = ImGui::CalcTextSize(spotify.title.c_str()).x; + spotify.ticker.tw1 = ImGui::CalcTextSize(spotify.artists.c_str()).x; + spotify.ticker.tw2 = ImGui::CalcTextSize(spotify.album.c_str()).x; + spotify.ticker.longest = std::max(std::max( + spotify.ticker.tw0, + spotify.ticker.tw1), + spotify.ticker.tw2); + spotify.ticker.needs_recalc = false; + } + + float new_pos, left_limit = 0, right_limit = 0; + get_ticker_limited_pos(spotify.ticker.pos, spotify.ticker.longest, left_limit, right_limit); - } else { - limited_pos = new_pos_x; - hudTicker = overflow; + if (spotify.ticker.pos < left_limit - g_overflow * .5f) { + spotify.ticker.dir = -1; + spotify.ticker.pos = (left_limit - g_overflow * .5f) + 1.f /* random */; + } else if (spotify.ticker.pos > right_limit + g_overflow) { + spotify.ticker.dir = 1; + spotify.ticker.pos = (right_limit + g_overflow) - 1.f /* random */; } - ImGui::SetCursorPosX(limited_pos); + spotify.ticker.pos -= .5f * (frame_timing / 16666.7f) * spotify.ticker.dir; + + new_pos = get_ticker_limited_pos(spotify.ticker.pos, spotify.ticker.tw0, left_limit, right_limit); + ImGui::SetCursorPosX(new_pos); ImGui::Text("%s", spotify.title.c_str()); - //std::cerr << "ticker: " << hudTicker << ", " << left_limit << "<>" << right_limit << ", " << dir << std::endl; - - for (size_t i = 0; i < spotify.artists.size(); i++) { - ImGui::Text("%s", spotify.artists[i].c_str()); - if (i < spotify.artists.size() - 1) { - ImGui::SameLine(0, 1.0f); - ImGui::Text(","); - ImGui::SameLine(0, 1.0f); - } - } + + new_pos = get_ticker_limited_pos(spotify.ticker.pos, spotify.ticker.tw1, left_limit, right_limit); + ImGui::SetCursorPosX(new_pos); + ImGui::Text("%s", spotify.artists.c_str()); //ImGui::NewLine(); - if (!spotify.album.empty()) + if (!spotify.album.empty()) { + new_pos = get_ticker_limited_pos(spotify.ticker.pos, spotify.ticker.tw2, left_limit, right_limit); + ImGui::SetCursorPosX(new_pos); ImGui::Text("%s", spotify.album.c_str()); + } ImGui::PopFont(); ImGui::PopStyleVar(); } } +#endif window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 10.0f); ImGui::End(); @@ -2519,6 +2531,7 @@ static VkResult overlay_CreateInstance( init_cpu_stats(instance_data->params); +#ifdef HAVE_DBUS if (instance_data->params.enabled[OVERLAY_PARAM_ENABLED_media_player]) { try { dbusmgr::dbus_mgr.init(); @@ -2527,6 +2540,7 @@ static VkResult overlay_CreateInstance( std::cerr << "Failed to get initial Spotify metadata: " << e.what() << std::endl; } } +#endif // Adjust height for DXVK/VKD3D version number if (engineName == "DXVK" || engineName == "VKD3D"){