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) {
|
||||
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.";
|
||||
return;
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ void PwDefaultTracker::changeConfiguredSinkName(const QString& sink) {
|
|||
|
||||
void PwDefaultTracker::changeConfiguredSource(PwNode* node) {
|
||||
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.";
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qobject.h>
|
||||
#include <qstringliteral.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qtypes.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() {
|
||||
// Bind the device first as pw is in order, meaning the device should be bound before
|
||||
// we want to do anything with it.
|
||||
|
@ -116,21 +131,19 @@ void PwNode::unbindHooks() {
|
|||
void PwNode::initProps(const spa_dict* props) {
|
||||
if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) {
|
||||
if (strcmp(mediaClass, "Audio/Sink") == 0) {
|
||||
this->type = PwNodeType::Audio;
|
||||
this->isSink = true;
|
||||
this->isStream = false;
|
||||
this->type = PwNodeType::AudioSink;
|
||||
} else if (strcmp(mediaClass, "Audio/Source") == 0) {
|
||||
this->type = PwNodeType::Audio;
|
||||
this->isSink = false;
|
||||
this->isStream = false;
|
||||
this->type = PwNodeType::AudioSource;
|
||||
} else if (strcmp(mediaClass, "Audio/Duplex") == 0) {
|
||||
this->type = PwNodeType::AudioDuplex;
|
||||
} else if (strcmp(mediaClass, "Stream/Output/Audio") == 0) {
|
||||
this->type = PwNodeType::Audio;
|
||||
this->isSink = false;
|
||||
this->isStream = true;
|
||||
this->type = PwNodeType::AudioOutStream;
|
||||
} else if (strcmp(mediaClass, "Stream/Input/Audio") == 0) {
|
||||
this->type = PwNodeType::Audio;
|
||||
this->isSink = true;
|
||||
this->isStream = true;
|
||||
this->type = PwNodeType::AudioInStream;
|
||||
} else if (strcmp(mediaClass, "Video/Sink") == 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <pipewire/node.h>
|
||||
#include <pipewire/type.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qflags.h>
|
||||
#include <qmap.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
|
@ -86,12 +87,71 @@ public:
|
|||
/// including aux and custom channel ranges.
|
||||
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 {
|
||||
Untracked,
|
||||
Audio,
|
||||
public:
|
||||
enum Flag : quint8 {
|
||||
// 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;
|
||||
|
||||
struct PwVolumeProps {
|
||||
|
@ -169,9 +229,8 @@ public:
|
|||
QString nick;
|
||||
QMap<QString, QString> properties;
|
||||
|
||||
PwNodeType type = PwNodeType::Untracked;
|
||||
bool isSink = false;
|
||||
bool isStream = false;
|
||||
PwNodeType::Flags type = PwNodeType::Untracked;
|
||||
|
||||
bool ready = false;
|
||||
|
||||
PwNodeBoundData* boundData = nullptr;
|
||||
|
|
|
@ -328,12 +328,14 @@ QString PwNodeIface::description() const { return this->mNode->description; }
|
|||
|
||||
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; }
|
||||
|
||||
PwNodeType::Flags PwNodeIface::type() const { return this->mNode->type; };
|
||||
|
||||
QVariantMap PwNodeIface::properties() const {
|
||||
auto map = QVariantMap();
|
||||
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
|
||||
/// a hardware device.
|
||||
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.
|
||||
/// You can inspect this directly with `pw-cli i <id>`.
|
||||
///
|
||||
|
@ -324,6 +326,7 @@ public:
|
|||
[[nodiscard]] bool isSink() const;
|
||||
[[nodiscard]] bool isStream() const;
|
||||
[[nodiscard]] bool isReady() const;
|
||||
[[nodiscard]] PwNodeType::Flags type() const;
|
||||
[[nodiscard]] QVariantMap properties() const;
|
||||
[[nodiscard]] PwNodeAudioIface* audio() const;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue