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,12 +87,71 @@ 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;
|
||||||
|
|
||||||
struct PwVolumeProps {
|
struct PwVolumeProps {
|
||||||
|
@ -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