| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- #include "DBusMediaSource.h"
- #include "WallpaperEngine/Data/Utils/ScopeGuard.h"
- #include "WallpaperEngine/Logging/Log.h"
- using namespace WallpaperEngine::Media;
- DBusHandlerResult dbus_message_filter (DBusConnection* connection, DBusMessage* message, void* user_data) {
- const auto mediaSource = static_cast<DBusMediaSource*> (user_data);
- if (!dbus_message_is_signal (message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- const char* interface = nullptr;
- DBusMessageIter iter;
- dbus_message_iter_init (message, &iter);
- dbus_message_iter_get_basic (&iter, &interface);
- std::string iface = interface ?: "";
- if (iface != "org.mpris.MediaPlayer2.Player") {
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- DBusMessageIter changed;
- dbus_message_iter_next (&iter);
- dbus_message_iter_recurse (&iter, &changed);
- while (dbus_message_iter_get_arg_type (&changed) == DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter entry;
- dbus_message_iter_recurse (&changed, &entry);
- const char* key = nullptr;
- dbus_message_iter_get_basic (&entry, &key);
- std::string keyStr = key ?: "";
- DBusMessageIter value;
- dbus_message_iter_next (&entry);
- dbus_message_iter_recurse (&entry, &value);
- if (keyStr == "Metadata") {
- mediaSource->parseMetadata (value);
- } else if (keyStr == "PlaybackStatus") {
- mediaSource->parsePlaybackStatus (value, dbus_message_get_sender (message));
- }
- dbus_message_iter_next (&changed);
- }
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- DBusMediaSource::DBusMediaSource (std::chrono::milliseconds updateInterval) : MediaSource (updateInterval) {
- DBusError err;
- dbus_error_init (&err);
- this->m_connection = dbus_bus_get (DBUS_BUS_SESSION, &err);
- if (!this->m_connection) {
- sLog.exception ("Could not connect to DBus: ", err.message);
- }
- dbus_connection_add_filter (this->m_connection, dbus_message_filter, this, nullptr);
- dbus_bus_add_match (
- this->m_connection, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'",
- nullptr
- );
- dbus_connection_flush (this->m_connection);
- this->detectPlayer ();
- this->initialStatusFetch ();
- }
- DBusMediaSource::~DBusMediaSource () {
- dbus_connection_remove_filter (this->m_connection, dbus_message_filter, this);
- dbus_connection_unref (this->m_connection);
- }
- void DBusMediaSource::parseMetadata (DBusMessageIter& variant) {
- DBusMessageIter dict;
- dbus_message_iter_recurse (&variant, &dict);
- bool metadataUpdate = false;
- bool albumUpdate = false;
- while (dbus_message_iter_get_arg_type (&dict) == DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter entry;
- dbus_message_iter_recurse (&dict, &entry);
- const char* key = nullptr;
- dbus_message_iter_get_basic (&entry, &key);
- std::string keyStr = key ?: "";
- DBusMessageIter value;
- dbus_message_iter_next (&entry);
- dbus_message_iter_recurse (&entry, &value);
- if (keyStr == "xesam:title") {
- const char* title = nullptr;
- dbus_message_iter_get_basic (&value, &title);
- std::string titleStr = title ?: "";
- if (this->m_mediaInfo.title != titleStr) {
- this->m_mediaInfo.title = titleStr;
- metadataUpdate = true;
- }
- } else if (keyStr == "xesam:artist") {
- DBusMessageIter arr;
- dbus_message_iter_recurse (&value, &arr);
- if (dbus_message_iter_get_arg_type (&arr) == DBUS_TYPE_STRING) {
- const char* artist = nullptr;
- dbus_message_iter_get_basic (&arr, &artist);
- std::string artistStr = artist ?: "";
- if (this->m_mediaInfo.artist != artistStr) {
- this->m_mediaInfo.artist = artistStr;
- metadataUpdate = true;
- }
- }
- } else if (keyStr == "xesam:album") {
- const char* album = nullptr;
- dbus_message_iter_get_basic (&value, &album);
- std::string albumStr = album ?: "";
- if (this->m_mediaInfo.album != albumStr) {
- this->m_mediaInfo.album = albumStr;
- albumUpdate = true;
- }
- } else if (keyStr == "mpris:artUrl") {
- const char* artUrl = nullptr;
- dbus_message_iter_get_basic (&value, &artUrl);
- std::string artUrlStr = artUrl ?: "";
- if (artUrlStr.empty () && this->m_mediaInfo.url.has_value ()) {
- this->m_mediaInfo.url.reset ();
- albumUpdate = true;
- } else if (this->m_mediaInfo.url.has_value () && artUrlStr != *this->m_mediaInfo.url) {
- this->m_mediaInfo.url = artUrlStr;
- albumUpdate = true;
- } else if (!this->m_mediaInfo.url.has_value ()) {
- this->m_mediaInfo.url = artUrlStr;
- albumUpdate = true;
- }
- } else if (keyStr == "mpris:length") {
- int64_t length = 0;
- dbus_message_iter_get_basic (&value, &length);
- if (this->m_mediaInfo.duration != length) {
- this->m_mediaInfo.duration = length;
- metadataUpdate = true;
- }
- }
- dbus_message_iter_next (&dict);
- }
- sLog.debug (
- "Player metadata received: title=", this->m_mediaInfo.title, ",artist=", this->m_mediaInfo.artist,
- ",album=", this->m_mediaInfo.album, ",url=", this->m_mediaInfo.url.has_value () ? *this->m_mediaInfo.url : ""
- );
- if (metadataUpdate) {
- this->fireMetadataListeners ();
- }
- if (albumUpdate) {
- this->fireAlbumArtListeners ();
- }
- }
- void DBusMediaSource::parsePlaybackStatus (DBusMessageIter& variant, const char* sender) {
- const char* status = nullptr;
- dbus_message_iter_get_basic (&variant, &status);
- std::string statusStr = status ?: "";
- PlaybackState newState = this->m_mediaInfo.playbackState;
- if (statusStr == "Playing") {
- if (sender != nullptr) {
- this->m_currentPlayer = sender;
- }
- newState = PlaybackState::Playing;
- } else if (statusStr == "Paused") {
- newState = PlaybackState::Paused;
- } else {
- newState = PlaybackState::Stopped;
- }
- if (newState != this->m_mediaInfo.playbackState) {
- this->m_mediaInfo.playbackState = newState;
- this->fireMetadataListeners ();
- }
- }
- void DBusMediaSource::parsePosition (DBusMessageIter& variant) {
- int64_t position = 0;
- dbus_message_iter_get_basic (&variant, &position);
- if (this->m_mediaInfo.position != position) {
- this->m_mediaInfo.position = position;
- this->fireMetadataListeners ();
- }
- }
- void DBusMediaSource::update () {
- this->MediaSource::update ();
- // drain any dbus events
- dbus_connection_read_write (this->m_connection, 0);
- while (dbus_connection_dispatch (this->m_connection) == DBUS_DISPATCH_DATA_REMAINS)
- ;
- }
- DBusMessage* DBusMediaSource::dbusMessage (
- const char* bus_name, const char* path, const char* interface, const char* method, const char* iface,
- const char* prop
- ) {
- DBusError err;
- dbus_error_init (&err);
- DBusMessage* msg = dbus_message_new_method_call (bus_name, path, interface, method);
- Data::Utils::ScopeGuard guard ([msg] { dbus_message_unref (msg); });
- if (iface != nullptr && prop != nullptr) {
- dbus_message_append_args (msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID);
- }
- DBusMessage* reply = dbus_connection_send_with_reply_and_block (m_connection, msg, -1, &err);
- if (reply == nullptr) {
- sLog.error ("DBus error: ", err.message, " (", err.name, ")");
- return nullptr;
- }
- return reply;
- }
- void DBusMediaSource::detectPlayer () {
- DBusMessage* reply
- = this->dbusMessage ("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
- if (reply == nullptr) {
- return;
- }
- Data::Utils::ScopeGuard guard ([reply] { dbus_message_unref (reply); });
- std::vector<std::string> players;
- DBusMessageIter iter;
- dbus_message_iter_init (reply, &iter);
- if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) {
- return;
- }
- DBusMessageIter array;
- dbus_message_iter_recurse (&iter, &array);
- while (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_INVALID) {
- char* name;
- dbus_message_iter_get_basic (&array, &name);
- std::string service = name;
- if (service.starts_with ("org.mpris.MediaPlayer2.")) {
- players.push_back (service);
- }
- dbus_message_iter_next (&array);
- }
- if (players.empty ()) {
- return;
- }
- for (const auto& player : players) {
- // also get playback status
- reply = this->dbusMessage (
- player.c_str (), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get",
- "org.mpris.MediaPlayer2.Player", "PlaybackStatus"
- );
- if (reply == nullptr) {
- return;
- }
- Data::Utils::ScopeGuard guard2 ([reply] { dbus_message_unref (reply); });
- DBusMessageIter outer;
- dbus_message_iter_init (reply, &outer);
- DBusMessageIter variant;
- dbus_message_iter_recurse (&outer, &variant);
- this->parsePlaybackStatus (variant, player.c_str ());
- if (this->m_currentPlayer.has_value ()) {
- break;
- }
- }
- }
- void DBusMediaSource::initialStatusFetch () {
- if (this->m_currentPlayer.has_value () == false) {
- return;
- }
- DBusMessage* reply = this->dbusMessage (
- this->m_currentPlayer.value ().c_str (), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get",
- "org.mpris.MediaPlayer2.Player", "Metadata"
- );
- if (reply == nullptr) {
- return;
- }
- Data::Utils::ScopeGuard guard ([reply] { dbus_message_unref (reply); });
- DBusMessageIter outer;
- dbus_message_iter_init (reply, &outer);
- DBusMessageIter variant;
- dbus_message_iter_recurse (&outer, &variant);
- this->parseMetadata (variant);
- }
- void DBusMediaSource::performUpdate () {
- // nothing to do if no player is detected
- if (!this->m_currentPlayer.has_value ()) {
- return;
- }
- DBusMessage* reply = this->dbusMessage (
- this->m_currentPlayer.value ().c_str (), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get",
- "org.mpris.MediaPlayer2.Player", "Position"
- );
- if (reply == nullptr) {
- return;
- }
- Data::Utils::ScopeGuard guard ([reply] { dbus_message_unref (reply); });
- DBusMessageIter outer;
- dbus_message_iter_init (reply, &outer);
- DBusMessageIter variant;
- dbus_message_iter_recurse (&outer, &variant);
- dbus_int64_t position = 0;
- dbus_message_iter_get_basic (&variant, &position);
- if (this->m_mediaInfo.position != position) {
- this->m_mediaInfo.position = position;
- this->fireMetadataListeners ();
- }
- }
|