forked from quickshell/quickshell
		
	service/mpris: add uniqueId property to detect track changes
Also emits all property changes after trackChanged
This commit is contained in:
		
							parent
							
								
									d9f66e63a3
								
							
						
					
					
						commit
						abc0201f6e
					
				
					 4 changed files with 104 additions and 34 deletions
				
			
		| 
						 | 
					@ -21,3 +21,22 @@ Qt::Edges Edges::toQt(Edges::Flags edges) { return Qt::Edges(edges.toInt()); }
 | 
				
			||||||
bool Edges::isOpposing(Edges::Flags edges) {
 | 
					bool Edges::isOpposing(Edges::Flags edges) {
 | 
				
			||||||
	return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right);
 | 
						return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DropEmitter::DropEmitter(DropEmitter&& other) noexcept: object(other.object), signal(other.signal) {
 | 
				
			||||||
 | 
						other.object = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DropEmitter& DropEmitter::operator=(DropEmitter&& other) noexcept {
 | 
				
			||||||
 | 
						this->object = other.object;
 | 
				
			||||||
 | 
						this->signal = other.signal;
 | 
				
			||||||
 | 
						other.object = nullptr;
 | 
				
			||||||
 | 
						return *this;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DropEmitter::~DropEmitter() { this->call(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DropEmitter::call() {
 | 
				
			||||||
 | 
						if (!this->object) return;
 | 
				
			||||||
 | 
						this->signal(this->object);
 | 
				
			||||||
 | 
						this->object = nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
#include <qdebug.h>
 | 
					#include <qdebug.h>
 | 
				
			||||||
#include <qnamespace.h>
 | 
					#include <qnamespace.h>
 | 
				
			||||||
#include <qpoint.h>
 | 
					#include <qpoint.h>
 | 
				
			||||||
 | 
					#include <qrect.h>
 | 
				
			||||||
#include <qqmlintegration.h>
 | 
					#include <qqmlintegration.h>
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,3 +69,28 @@ bool isOpposing(Flags edges);
 | 
				
			||||||
}; // namespace Edges
 | 
					}; // namespace Edges
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Edges::Flags);
 | 
					Q_DECLARE_OPERATORS_FOR_FLAGS(Edges::Flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOLINTBEGIN
 | 
				
			||||||
 | 
					#define DROP_EMIT(object, func)                                                                    \
 | 
				
			||||||
 | 
						DropEmitter(object, static_cast<void (*)(typeof(object))>([](typeof(object) o) { o->func(); }))
 | 
				
			||||||
 | 
					// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DropEmitter {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						template <class O>
 | 
				
			||||||
 | 
						DropEmitter(O* object, void (*signal)(O*))
 | 
				
			||||||
 | 
						    : object(object)
 | 
				
			||||||
 | 
						    , signal(*reinterpret_cast<void (*)(void*)>(signal)) {} // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter() = default;
 | 
				
			||||||
 | 
						DropEmitter(DropEmitter&& other) noexcept;
 | 
				
			||||||
 | 
						DropEmitter& operator=(DropEmitter&& other) noexcept;
 | 
				
			||||||
 | 
						~DropEmitter();
 | 
				
			||||||
 | 
						Q_DISABLE_COPY(DropEmitter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void call();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void* object = nullptr;
 | 
				
			||||||
 | 
						void (*signal)(void*) = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
#include <qtypes.h>
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../core/types.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "dbus_player.h"
 | 
					#include "dbus_player.h"
 | 
				
			||||||
#include "dbus_player_app.h"
 | 
					#include "dbus_player_app.h"
 | 
				
			||||||
| 
						 | 
					@ -255,6 +256,7 @@ void MprisPlayer::setVolume(qreal volume) {
 | 
				
			||||||
	this->pVolume.write();
 | 
						this->pVolume.write();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					quint32 MprisPlayer::uniqueId() const { return this->mUniqueId; }
 | 
				
			||||||
QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); }
 | 
					QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); }
 | 
				
			||||||
QString MprisPlayer::trackTitle() const { return this->mTrackTitle; }
 | 
					QString MprisPlayer::trackTitle() const { return this->mTrackTitle; }
 | 
				
			||||||
QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; }
 | 
					QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; }
 | 
				
			||||||
| 
						 | 
					@ -271,10 +273,7 @@ void MprisPlayer::onMetadataChanged() {
 | 
				
			||||||
		length = lengthVariant.value<qlonglong>();
 | 
							length = lengthVariant.value<qlonglong>();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (length != this->mLength) {
 | 
						auto emitLengthChanged = this->setLength(length);
 | 
				
			||||||
		this->mLength = length;
 | 
					 | 
				
			||||||
		emit this->lengthChanged();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto trackChanged = false;
 | 
						auto trackChanged = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -299,81 +298,94 @@ void MprisPlayer::onMetadataChanged() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter emitTrackTitle;
 | 
				
			||||||
	auto trackTitle = this->pMetadata.get().value("xesam:title");
 | 
						auto trackTitle = this->pMetadata.get().value("xesam:title");
 | 
				
			||||||
	if (trackTitle.isValid() && trackTitle.canConvert<QString>()) {
 | 
						if (trackTitle.isValid() && trackTitle.canConvert<QString>()) {
 | 
				
			||||||
		this->setTrackTitle(trackTitle.toString());
 | 
							emitTrackTitle = this->setTrackTitle(trackTitle.toString());
 | 
				
			||||||
	} else if (trackChanged) {
 | 
						} else if (trackChanged) {
 | 
				
			||||||
		this->setTrackTitle("Unknown Track");
 | 
							emitTrackTitle = this->setTrackTitle("Unknown Track");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter emitTrackAlbum;
 | 
				
			||||||
	auto trackAlbum = this->pMetadata.get().value("xesam:album");
 | 
						auto trackAlbum = this->pMetadata.get().value("xesam:album");
 | 
				
			||||||
	if (trackAlbum.isValid() && trackAlbum.canConvert<QString>()) {
 | 
						if (trackAlbum.isValid() && trackAlbum.canConvert<QString>()) {
 | 
				
			||||||
		this->setTrackAlbum(trackAlbum.toString());
 | 
							emitTrackAlbum = this->setTrackAlbum(trackAlbum.toString());
 | 
				
			||||||
	} else if (trackChanged) {
 | 
						} else if (trackChanged) {
 | 
				
			||||||
		this->setTrackAlbum("Unknown Album");
 | 
							emitTrackAlbum = this->setTrackAlbum("Unknown Album");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter emitTrackAlbumArtist;
 | 
				
			||||||
	auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist");
 | 
						auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist");
 | 
				
			||||||
	if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert<QString>()) {
 | 
						if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert<QString>()) {
 | 
				
			||||||
		this->setTrackAlbumArtist(trackAlbumArtist.toString());
 | 
							emitTrackAlbumArtist = this->setTrackAlbumArtist(trackAlbumArtist.toString());
 | 
				
			||||||
	} else if (trackChanged) {
 | 
						} else if (trackChanged) {
 | 
				
			||||||
		this->setTrackAlbumArtist("Unknown Artist");
 | 
							emitTrackAlbumArtist = this->setTrackAlbumArtist("Unknown Artist");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter emitTrackArtists;
 | 
				
			||||||
	auto trackArtists = this->pMetadata.get().value("xesam:artist");
 | 
						auto trackArtists = this->pMetadata.get().value("xesam:artist");
 | 
				
			||||||
	if (trackArtists.isValid() && trackArtists.canConvert<QVector<QString>>()) {
 | 
						if (trackArtists.isValid() && trackArtists.canConvert<QVector<QString>>()) {
 | 
				
			||||||
		this->setTrackArtists(trackArtists.value<QVector<QString>>());
 | 
							emitTrackArtists = this->setTrackArtists(trackArtists.value<QVector<QString>>());
 | 
				
			||||||
	} else if (trackChanged) {
 | 
						} else if (trackChanged) {
 | 
				
			||||||
		this->setTrackArtists({});
 | 
							emitTrackArtists = this->setTrackArtists({});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DropEmitter emitTrackArtUrl;
 | 
				
			||||||
	auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl");
 | 
						auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl");
 | 
				
			||||||
	if (trackArtUrl.isValid() && trackArtUrl.canConvert<QString>()) {
 | 
						if (trackArtUrl.isValid() && trackArtUrl.canConvert<QString>()) {
 | 
				
			||||||
		this->setTrackArtUrl(trackArtUrl.toString());
 | 
							emitTrackArtUrl = this->setTrackArtUrl(trackArtUrl.toString());
 | 
				
			||||||
	} else if (trackChanged) {
 | 
						} else if (trackChanged) {
 | 
				
			||||||
		this->setTrackArtUrl("");
 | 
							emitTrackArtUrl = this->setTrackArtUrl("");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (trackChanged) {
 | 
						if (trackChanged) {
 | 
				
			||||||
 | 
							this->mUniqueId++;
 | 
				
			||||||
		// Some players don't seem to send position updates or seeks on track change.
 | 
							// Some players don't seem to send position updates or seeks on track change.
 | 
				
			||||||
		this->pPosition.update();
 | 
							this->pPosition.update();
 | 
				
			||||||
		emit this->trackChanged();
 | 
							emit this->trackChanged();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MprisPlayer::setTrackTitle(QString title) {
 | 
					DropEmitter MprisPlayer::setLength(qlonglong length) {
 | 
				
			||||||
	if (title == this->mTrackTitle) return;
 | 
						if (length == this->mLength) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->mLength = length;
 | 
				
			||||||
 | 
						return DROP_EMIT(this, lengthChanged);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DropEmitter MprisPlayer::setTrackTitle(QString title) {
 | 
				
			||||||
 | 
						if (title == this->mTrackTitle) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mTrackTitle = std::move(title);
 | 
						this->mTrackTitle = std::move(title);
 | 
				
			||||||
	emit this->trackTitleChanged();
 | 
						return DROP_EMIT(this, trackTitleChanged);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MprisPlayer::setTrackAlbum(QString album) {
 | 
					DropEmitter MprisPlayer::setTrackAlbum(QString album) {
 | 
				
			||||||
	if (album == this->mTrackAlbum) return;
 | 
						if (album == this->mTrackAlbum) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mTrackAlbum = std::move(album);
 | 
						this->mTrackAlbum = std::move(album);
 | 
				
			||||||
	emit this->trackAlbumChanged();
 | 
						return DROP_EMIT(this, trackAlbumChanged);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MprisPlayer::setTrackAlbumArtist(QString albumArtist) {
 | 
					DropEmitter MprisPlayer::setTrackAlbumArtist(QString albumArtist) {
 | 
				
			||||||
	if (albumArtist == this->mTrackAlbumArtist) return;
 | 
						if (albumArtist == this->mTrackAlbumArtist) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mTrackAlbumArtist = std::move(albumArtist);
 | 
						this->mTrackAlbumArtist = std::move(albumArtist);
 | 
				
			||||||
	emit this->trackAlbumArtistChanged();
 | 
						return DROP_EMIT(this, trackAlbumArtistChanged);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MprisPlayer::setTrackArtists(QVector<QString> artists) {
 | 
					DropEmitter MprisPlayer::setTrackArtists(QVector<QString> artists) {
 | 
				
			||||||
	if (artists == this->mTrackArtists) return;
 | 
						if (artists == this->mTrackArtists) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mTrackArtists = std::move(artists);
 | 
						this->mTrackArtists = std::move(artists);
 | 
				
			||||||
	emit this->trackArtistsChanged();
 | 
						return DROP_EMIT(this, trackArtistsChanged);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MprisPlayer::setTrackArtUrl(QString artUrl) {
 | 
					DropEmitter MprisPlayer::setTrackArtUrl(QString artUrl) {
 | 
				
			||||||
	if (artUrl == this->mTrackArtUrl) return;
 | 
						if (artUrl == this->mTrackArtUrl) return DropEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->mTrackArtUrl = std::move(artUrl);
 | 
						this->mTrackArtUrl = std::move(artUrl);
 | 
				
			||||||
	emit this->trackArtUrlChanged();
 | 
						return DROP_EMIT(this, trackArtUrlChanged);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; }
 | 
					MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
#include <qtypes.h>
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../../core/doc.hpp"
 | 
					#include "../../core/doc.hpp"
 | 
				
			||||||
 | 
					#include "../../core/types.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "dbus_player.h"
 | 
					#include "dbus_player.h"
 | 
				
			||||||
#include "dbus_player_app.h"
 | 
					#include "dbus_player_app.h"
 | 
				
			||||||
| 
						 | 
					@ -128,6 +129,11 @@ class MprisPlayer: public QObject {
 | 
				
			||||||
	/// properties have extra logic to guard against bad players sending weird metadata, and should
 | 
						/// properties have extra logic to guard against bad players sending weird metadata, and should
 | 
				
			||||||
	/// be used over grabbing the properties directly from the metadata.
 | 
						/// be used over grabbing the properties directly from the metadata.
 | 
				
			||||||
	Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged);
 | 
						Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged);
 | 
				
			||||||
 | 
						/// An opaque identifier for the current track unique within the current player.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// > [!WARNING] This is NOT `mpris:trackid` as that is sometimes missing or nonunique
 | 
				
			||||||
 | 
						/// > in some players.
 | 
				
			||||||
 | 
						Q_PROPERTY(quint32 uniqueId READ uniqueId NOTIFY trackChanged);
 | 
				
			||||||
	/// The title of the current track, or "Unknown Track" if none was provided.
 | 
						/// The title of the current track, or "Unknown Track" if none was provided.
 | 
				
			||||||
	Q_PROPERTY(QString trackTitle READ trackTitle NOTIFY trackTitleChanged);
 | 
						Q_PROPERTY(QString trackTitle READ trackTitle NOTIFY trackTitleChanged);
 | 
				
			||||||
	/// The current track's album, or "Unknown Album" if none was provided.
 | 
						/// The current track's album, or "Unknown Album" if none was provided.
 | 
				
			||||||
| 
						 | 
					@ -248,6 +254,7 @@ public:
 | 
				
			||||||
	[[nodiscard]] bool volumeSupported() const;
 | 
						[[nodiscard]] bool volumeSupported() const;
 | 
				
			||||||
	void setVolume(qreal volume);
 | 
						void setVolume(qreal volume);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] quint32 uniqueId() const;
 | 
				
			||||||
	[[nodiscard]] QVariantMap metadata() const;
 | 
						[[nodiscard]] QVariantMap metadata() const;
 | 
				
			||||||
	[[nodiscard]] QString trackTitle() const;
 | 
						[[nodiscard]] QString trackTitle() const;
 | 
				
			||||||
	[[nodiscard]] QString trackAlbum() const;
 | 
						[[nodiscard]] QString trackAlbum() const;
 | 
				
			||||||
| 
						 | 
					@ -278,6 +285,10 @@ public:
 | 
				
			||||||
	[[nodiscard]] QList<QString> supportedMimeTypes() const;
 | 
						[[nodiscard]] QList<QString> supportedMimeTypes() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
 | 
						/// The track has changed.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// All track info change signalss will fire immediately after if applicable,
 | 
				
			||||||
 | 
						/// but their values will be updated before the signal fires.
 | 
				
			||||||
	void trackChanged();
 | 
						void trackChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QSDOC_HIDE void ready();
 | 
						QSDOC_HIDE void ready();
 | 
				
			||||||
| 
						 | 
					@ -327,11 +338,12 @@ private slots:
 | 
				
			||||||
	void onLoopStatusChanged();
 | 
						void onLoopStatusChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void setTrackTitle(QString title);
 | 
						DropEmitter setLength(qlonglong length);
 | 
				
			||||||
	void setTrackAlbum(QString album);
 | 
						DropEmitter setTrackTitle(QString title);
 | 
				
			||||||
	void setTrackAlbumArtist(QString albumArtist);
 | 
						DropEmitter setTrackAlbum(QString album);
 | 
				
			||||||
	void setTrackArtists(QVector<QString> artists);
 | 
						DropEmitter setTrackAlbumArtist(QString albumArtist);
 | 
				
			||||||
	void setTrackArtUrl(QString artUrl);
 | 
						DropEmitter setTrackArtists(QVector<QString> artists);
 | 
				
			||||||
 | 
						DropEmitter setTrackArtUrl(QString artUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// clang-format off
 | 
						// clang-format off
 | 
				
			||||||
	dbus::DBusPropertyGroup appProperties;
 | 
						dbus::DBusPropertyGroup appProperties;
 | 
				
			||||||
| 
						 | 
					@ -370,6 +382,7 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DBusMprisPlayerApp* app = nullptr;
 | 
						DBusMprisPlayerApp* app = nullptr;
 | 
				
			||||||
	DBusMprisPlayer* player = nullptr;
 | 
						DBusMprisPlayer* player = nullptr;
 | 
				
			||||||
 | 
						quint32 mUniqueId = 0;
 | 
				
			||||||
	QString mTrackId;
 | 
						QString mTrackId;
 | 
				
			||||||
	QString mTrackUrl;
 | 
						QString mTrackUrl;
 | 
				
			||||||
	QString mTrackTitle;
 | 
						QString mTrackTitle;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue