forked from quickshell/quickshell
		
	services/pipewire: expose node type
This commit is contained in:
		
							parent
							
								
									6b3d64e32a
								
							
						
					
					
						commit
						ee570ec623
					
				
					 5 changed files with 100 additions and 23 deletions
				
			
		| 
						 | 
					@ -146,7 +146,7 @@ void PwDefaultTracker::onNodeDestroyed(QObject* node) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PwDefaultTracker::changeConfiguredSink(PwNode* node) {
 | 
					void PwDefaultTracker::changeConfiguredSink(PwNode* node) {
 | 
				
			||||||
	if (node != nullptr) {
 | 
						if (node != nullptr) {
 | 
				
			||||||
		if (!node->isSink) {
 | 
							if (!node->type.testFlags(PwNodeType::AudioSink)) {
 | 
				
			||||||
			qCCritical(logDefaults) << "Cannot change default sink to a node that is not a sink.";
 | 
								qCCritical(logDefaults) << "Cannot change default sink to a node that is not a sink.";
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -168,7 +168,7 @@ void PwDefaultTracker::changeConfiguredSinkName(const QString& sink) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PwDefaultTracker::changeConfiguredSource(PwNode* node) {
 | 
					void PwDefaultTracker::changeConfiguredSource(PwNode* node) {
 | 
				
			||||||
	if (node != nullptr) {
 | 
						if (node != nullptr) {
 | 
				
			||||||
		if (node->isSink) {
 | 
							if (!node->type.testFlags(PwNodeType::AudioSource)) {
 | 
				
			||||||
			qCCritical(logDefaults) << "Cannot change default source to a node that is not a source.";
 | 
								qCCritical(logDefaults) << "Cannot change default source to a node that is not a source.";
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
#include <qlogging.h>
 | 
					#include <qlogging.h>
 | 
				
			||||||
#include <qloggingcategory.h>
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qstringliteral.h>
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
#include <qtypes.h>
 | 
					#include <qtypes.h>
 | 
				
			||||||
#include <spa/node/keys.h>
 | 
					#include <spa/node/keys.h>
 | 
				
			||||||
| 
						 | 
					@ -85,6 +86,20 @@ QString PwAudioChannel::toString(Enum value) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString PwNodeType::toString(PwNodeType::Flags type) {
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case PwNodeType::VideoSource: return QStringLiteral("VideoSource");
 | 
				
			||||||
 | 
						case PwNodeType::VideoSink: return QStringLiteral("VideoSink");
 | 
				
			||||||
 | 
						case PwNodeType::AudioSource: return QStringLiteral("AudioSource");
 | 
				
			||||||
 | 
						case PwNodeType::AudioSink: return QStringLiteral("AudioSink");
 | 
				
			||||||
 | 
						case PwNodeType::AudioDuplex: return QStringLiteral("AudioDuplex");
 | 
				
			||||||
 | 
						case PwNodeType::AudioOutStream: return QStringLiteral("AudioOutStream");
 | 
				
			||||||
 | 
						case PwNodeType::AudioInStream: return QStringLiteral("AudioInStream");
 | 
				
			||||||
 | 
						case PwNodeType::Untracked: return QStringLiteral("Untracked");
 | 
				
			||||||
 | 
						default: return QStringLiteral("Invalid");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PwNode::bindHooks() {
 | 
					void PwNode::bindHooks() {
 | 
				
			||||||
	// Bind the device first as pw is in order, meaning the device should be bound before
 | 
						// Bind the device first as pw is in order, meaning the device should be bound before
 | 
				
			||||||
	// we want to do anything with it.
 | 
						// we want to do anything with it.
 | 
				
			||||||
| 
						 | 
					@ -116,21 +131,19 @@ void PwNode::unbindHooks() {
 | 
				
			||||||
void PwNode::initProps(const spa_dict* props) {
 | 
					void PwNode::initProps(const spa_dict* props) {
 | 
				
			||||||
	if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) {
 | 
						if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) {
 | 
				
			||||||
		if (strcmp(mediaClass, "Audio/Sink") == 0) {
 | 
							if (strcmp(mediaClass, "Audio/Sink") == 0) {
 | 
				
			||||||
			this->type = PwNodeType::Audio;
 | 
								this->type = PwNodeType::AudioSink;
 | 
				
			||||||
			this->isSink = true;
 | 
					 | 
				
			||||||
			this->isStream = false;
 | 
					 | 
				
			||||||
		} else if (strcmp(mediaClass, "Audio/Source") == 0) {
 | 
							} else if (strcmp(mediaClass, "Audio/Source") == 0) {
 | 
				
			||||||
			this->type = PwNodeType::Audio;
 | 
								this->type = PwNodeType::AudioSource;
 | 
				
			||||||
			this->isSink = false;
 | 
							} else if (strcmp(mediaClass, "Audio/Duplex") == 0) {
 | 
				
			||||||
			this->isStream = false;
 | 
								this->type = PwNodeType::AudioDuplex;
 | 
				
			||||||
		} else if (strcmp(mediaClass, "Stream/Output/Audio") == 0) {
 | 
							} else if (strcmp(mediaClass, "Stream/Output/Audio") == 0) {
 | 
				
			||||||
			this->type = PwNodeType::Audio;
 | 
								this->type = PwNodeType::AudioOutStream;
 | 
				
			||||||
			this->isSink = false;
 | 
					 | 
				
			||||||
			this->isStream = true;
 | 
					 | 
				
			||||||
		} else if (strcmp(mediaClass, "Stream/Input/Audio") == 0) {
 | 
							} else if (strcmp(mediaClass, "Stream/Input/Audio") == 0) {
 | 
				
			||||||
			this->type = PwNodeType::Audio;
 | 
								this->type = PwNodeType::AudioInStream;
 | 
				
			||||||
			this->isSink = true;
 | 
							} else if (strcmp(mediaClass, "Video/Sink") == 0) {
 | 
				
			||||||
			this->isStream = true;
 | 
								this->type = PwNodeType::VideoSink;
 | 
				
			||||||
 | 
							} else if (strcmp(mediaClass, "Video/Source") == 0) {
 | 
				
			||||||
 | 
								this->type = PwNodeType::VideoSource;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,7 +177,7 @@ void PwNode::initProps(const spa_dict* props) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (this->type == PwNodeType::Audio) {
 | 
						if (this->type.testFlags(PwNodeType::Audio)) {
 | 
				
			||||||
		this->boundData = new PwNodeBoundAudio(this);
 | 
							this->boundData = new PwNodeBoundAudio(this);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
#include <pipewire/node.h>
 | 
					#include <pipewire/node.h>
 | 
				
			||||||
#include <pipewire/type.h>
 | 
					#include <pipewire/type.h>
 | 
				
			||||||
#include <qcontainerfwd.h>
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qflags.h>
 | 
				
			||||||
#include <qmap.h>
 | 
					#include <qmap.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qqmlintegration.h>
 | 
					#include <qqmlintegration.h>
 | 
				
			||||||
| 
						 | 
					@ -86,11 +87,70 @@ public:
 | 
				
			||||||
	/// including aux and custom channel ranges.
 | 
						/// including aux and custom channel ranges.
 | 
				
			||||||
	Q_INVOKABLE static QString toString(qs::service::pipewire::PwAudioChannel::Enum value);
 | 
						Q_INVOKABLE static QString toString(qs::service::pipewire::PwAudioChannel::Enum value);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					///! The type of a pipewire node.
 | 
				
			||||||
 | 
					/// Use bitwise comparisons to filter for audio, video, sink, source or stream nodes
 | 
				
			||||||
 | 
					class PwNodeType: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
						QML_ELEMENT;
 | 
				
			||||||
 | 
						QML_SINGLETON;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class PwNodeType : quint8 {
 | 
					public:
 | 
				
			||||||
	Untracked,
 | 
						enum Flag : quint8 {
 | 
				
			||||||
	Audio,
 | 
							// A Pipewire node which is not being managed.
 | 
				
			||||||
 | 
							Untracked = 0b0,
 | 
				
			||||||
 | 
							// This flag is set when this node is an Audio node.
 | 
				
			||||||
 | 
							Audio = 0b1,
 | 
				
			||||||
 | 
							// This flag is set when this node is an Video node.
 | 
				
			||||||
 | 
							Video = 0b10,
 | 
				
			||||||
 | 
							// This flag is set when this node is a stream node.
 | 
				
			||||||
 | 
							Stream = 0b100,
 | 
				
			||||||
 | 
							// This flag is set when this node is producing some form of data,
 | 
				
			||||||
 | 
							// such as a microphone, screenshare or webcam.
 | 
				
			||||||
 | 
							Source = 0b1000,
 | 
				
			||||||
 | 
							// This flag is set when this node is receiving data.
 | 
				
			||||||
 | 
							Sink = 0b10000,
 | 
				
			||||||
 | 
							// A sink for audio samples, like an audio card.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Video/Source` and is
 | 
				
			||||||
 | 
							// composed of the @@PwNodeType.Audio and @@PwNodeType.Sink flags.
 | 
				
			||||||
 | 
							AudioSink = Audio | Sink,
 | 
				
			||||||
 | 
							// A source of audio samples like a microphone.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is quivalent to the media class `Video/Sink` and is composed
 | 
				
			||||||
 | 
							// of the @@PwNodeType.Audio and @@PwNodeType.Source flags.
 | 
				
			||||||
 | 
							AudioSource = Audio | Source,
 | 
				
			||||||
 | 
							// A node that is both a sink and a source.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Audio/Duplex` and is composed of the
 | 
				
			||||||
 | 
							// @@PwNodeType.Audio, @@PwNodeType.Source and @@PwNodeType.Sink flags.
 | 
				
			||||||
 | 
							AudioDuplex = Audio | Sink | Source,
 | 
				
			||||||
 | 
							// A playback stream.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Stream/Output/Audio` and is composed
 | 
				
			||||||
 | 
							// of the @@PwNodeType.Audio, @@PwNodeType.Sink and @@PwNodeType.Stream flags.
 | 
				
			||||||
 | 
							AudioOutStream = Audio | Sink | Stream,
 | 
				
			||||||
 | 
							// A capture stream.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Stream/Input/Audio` and is composed
 | 
				
			||||||
 | 
							// of the @@PwNodeType.Audio, @@PwNodeType.Source and @@PwNodeType.Stream flags.
 | 
				
			||||||
 | 
							AudioInStream = Audio | Source | Stream,
 | 
				
			||||||
 | 
							// A producer of video, like a webcam or a screenshare.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Video/Source` and is composed
 | 
				
			||||||
 | 
							// of the @@PwNodeType.Video and @@PwNodeType.Source flags.
 | 
				
			||||||
 | 
							VideoSource = Video | Source,
 | 
				
			||||||
 | 
							// A consumer of video, such as a program that is recieving a video stream.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// This is equivalent to the media class `Video/Sink` and is composed of the
 | 
				
			||||||
 | 
							// @@PwNodeType.Video and @@PwNodeType.Sink flags.
 | 
				
			||||||
 | 
							VideoSink = Video | Sink,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						Q_ENUM(Flag)
 | 
				
			||||||
 | 
						Q_DECLARE_FLAGS(Flags, Flag)
 | 
				
			||||||
 | 
						Q_INVOKABLE static QString toString(qs::service::pipewire::PwNodeType::Flags type);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_OPERATORS_FOR_FLAGS(PwNodeType::Flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PwNode;
 | 
					class PwNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,9 +229,8 @@ public:
 | 
				
			||||||
	QString nick;
 | 
						QString nick;
 | 
				
			||||||
	QMap<QString, QString> properties;
 | 
						QMap<QString, QString> properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PwNodeType type = PwNodeType::Untracked;
 | 
						PwNodeType::Flags type = PwNodeType::Untracked;
 | 
				
			||||||
	bool isSink = false;
 | 
					
 | 
				
			||||||
	bool isStream = false;
 | 
					 | 
				
			||||||
	bool ready = false;
 | 
						bool ready = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PwNodeBoundData* boundData = nullptr;
 | 
						PwNodeBoundData* boundData = nullptr;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -328,12 +328,14 @@ QString PwNodeIface::description() const { return this->mNode->description; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QString PwNodeIface::nickname() const { return this->mNode->nick; }
 | 
					QString PwNodeIface::nickname() const { return this->mNode->nick; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool PwNodeIface::isSink() const { return this->mNode->isSink; }
 | 
					bool PwNodeIface::isSink() const { return this->mNode->type.testFlags(PwNodeType::Sink); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool PwNodeIface::isStream() const { return this->mNode->isStream; }
 | 
					bool PwNodeIface::isStream() const { return this->mNode->type.testFlags(PwNodeType::Stream); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool PwNodeIface::isReady() const { return this->mNode->ready; }
 | 
					bool PwNodeIface::isReady() const { return this->mNode->ready; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PwNodeType::Flags PwNodeIface::type() const { return this->mNode->type; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QVariantMap PwNodeIface::properties() const {
 | 
					QVariantMap PwNodeIface::properties() const {
 | 
				
			||||||
	auto map = QVariantMap();
 | 
						auto map = QVariantMap();
 | 
				
			||||||
	for (auto [k, v]: this->mNode->properties.asKeyValueRange()) {
 | 
						for (auto [k, v]: this->mNode->properties.asKeyValueRange()) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -287,6 +287,8 @@ class PwNodeIface: public PwObjectIface {
 | 
				
			||||||
	/// If `true` then the node is likely to be a program, if `false` it is likely to be
 | 
						/// If `true` then the node is likely to be a program, if `false` it is likely to be
 | 
				
			||||||
	/// a hardware device.
 | 
						/// a hardware device.
 | 
				
			||||||
	Q_PROPERTY(bool isStream READ isStream CONSTANT);
 | 
						Q_PROPERTY(bool isStream READ isStream CONSTANT);
 | 
				
			||||||
 | 
						/// The type of this node. Reflects Pipewire's [media.class](https://docs.pipewire.org/page_man_pipewire-props_7.html).
 | 
				
			||||||
 | 
						Q_PROPERTY(qs::service::pipewire::PwNodeType::Flags type READ type CONSTANT);
 | 
				
			||||||
	/// The property set present on the node, as an object containing key-value pairs.
 | 
						/// The property set present on the node, as an object containing key-value pairs.
 | 
				
			||||||
	/// You can inspect this directly with `pw-cli i <id>`.
 | 
						/// You can inspect this directly with `pw-cli i <id>`.
 | 
				
			||||||
	///
 | 
						///
 | 
				
			||||||
| 
						 | 
					@ -324,6 +326,7 @@ public:
 | 
				
			||||||
	[[nodiscard]] bool isSink() const;
 | 
						[[nodiscard]] bool isSink() const;
 | 
				
			||||||
	[[nodiscard]] bool isStream() const;
 | 
						[[nodiscard]] bool isStream() const;
 | 
				
			||||||
	[[nodiscard]] bool isReady() const;
 | 
						[[nodiscard]] bool isReady() const;
 | 
				
			||||||
 | 
						[[nodiscard]] PwNodeType::Flags type() const;
 | 
				
			||||||
	[[nodiscard]] QVariantMap properties() const;
 | 
						[[nodiscard]] QVariantMap properties() const;
 | 
				
			||||||
	[[nodiscard]] PwNodeAudioIface* audio() const;
 | 
						[[nodiscard]] PwNodeAudioIface* audio() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue