Skip to content

Commit f889f08

Browse files
committed
service/pipewire: refactor defaults and metadata handling
1 parent 7f9762b commit f889f08

File tree

11 files changed

+454
-143
lines changed

11 files changed

+454
-143
lines changed

src/core/util.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#pragma once
22
#include <type_traits>
33

4+
#include <qobject.h>
45
#include <qtclasshelpermacros.h>
6+
#include <qtmetamacros.h>
57

68
// NOLINTBEGIN
79
#define DROP_EMIT(object, func) \
@@ -211,3 +213,34 @@ class GuardedEmitter {
211213

212214
GuardedEmitBlocker block() { return GuardedEmitBlocker(&this->blocked); }
213215
};
216+
217+
template <auto member, auto destroyedSlot, auto changedSignal>
218+
class SimpleObjectHandleOps {
219+
using Traits = MemberPointerTraits<decltype(member)>;
220+
221+
public:
222+
static bool setObject(Traits::Class* parent, Traits::Type value) {
223+
if (value == parent->*member) return false;
224+
225+
if (parent->*member != nullptr) {
226+
QObject::disconnect(parent->*member, &QObject::destroyed, parent, destroyedSlot);
227+
}
228+
229+
parent->*member = value;
230+
231+
if (value != nullptr) {
232+
QObject::connect(parent->*member, &QObject::destroyed, parent, destroyedSlot);
233+
}
234+
235+
if constexpr (changedSignal != nullptr) {
236+
emit(parent->*changedSignal)();
237+
}
238+
239+
return true;
240+
}
241+
};
242+
243+
template <auto member, auto destroyedSlot, auto changedSignal = nullptr>
244+
bool setSimpleObjectHandle(auto* parent, auto* value) {
245+
return SimpleObjectHandleOps<member, destroyedSlot, changedSignal>::setObject(parent, value);
246+
}

src/services/pipewire/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ qt_add_library(quickshell-service-pipewire STATIC
1010
metadata.cpp
1111
link.cpp
1212
device.cpp
13+
defaults.cpp
1314
)
1415

1516
qt_add_qml_module(quickshell-service-pipewire

src/services/pipewire/connection.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include "core.hpp"
4-
#include "metadata.hpp"
4+
#include "defaults.hpp"
55
#include "registry.hpp"
66

77
namespace qs::service::pipewire {
@@ -13,7 +13,7 @@ class PwConnection: public QObject {
1313
explicit PwConnection(QObject* parent = nullptr);
1414

1515
PwRegistry registry;
16-
PwDefaultsMetadata defaults {&this->registry};
16+
PwDefaultTracker defaults {&this->registry};
1717

1818
static PwConnection* instance();
1919

src/services/pipewire/defaults.cpp

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#include "defaults.hpp"
2+
#include <array>
3+
#include <cstring>
4+
5+
#include <qlogging.h>
6+
#include <qloggingcategory.h>
7+
#include <qobject.h>
8+
#include <qtmetamacros.h>
9+
#include <spa/utils/json.h>
10+
11+
#include "../../core/util.hpp"
12+
#include "metadata.hpp"
13+
#include "node.hpp"
14+
#include "registry.hpp"
15+
16+
namespace qs::service::pipewire {
17+
18+
Q_LOGGING_CATEGORY(logDefaults, "quickshell.service.pipewire.defaults", QtWarningMsg);
19+
20+
PwDefaultTracker::PwDefaultTracker(PwRegistry* registry): registry(registry) {
21+
QObject::connect(registry, &PwRegistry::metadataAdded, this, &PwDefaultTracker::onMetadataAdded);
22+
QObject::connect(registry, &PwRegistry::nodeAdded, this, &PwDefaultTracker::onNodeAdded);
23+
}
24+
25+
void PwDefaultTracker::onMetadataAdded(PwMetadata* metadata) {
26+
if (metadata->name() == "default") {
27+
qCDebug(logDefaults) << "Got new defaults metadata object" << metadata;
28+
29+
if (this->defaultsMetadata.object()) {
30+
QObject::disconnect(this->defaultsMetadata.object(), nullptr, this, nullptr);
31+
}
32+
33+
QObject::connect(
34+
metadata,
35+
&PwMetadata::propertyChanged,
36+
this,
37+
&PwDefaultTracker::onMetadataProperty
38+
);
39+
40+
this->defaultsMetadata.setObject(metadata);
41+
}
42+
}
43+
44+
void PwDefaultTracker::onMetadataProperty(const char* key, const char* type, const char* value) {
45+
void (PwDefaultTracker::*nodeSetter)(PwNode*) = nullptr;
46+
void (PwDefaultTracker::*nameSetter)(const QString&) = nullptr;
47+
48+
qCDebug(logDefaults).nospace() << "Got default metadata update for " << key << ": "
49+
<< QString(value);
50+
51+
if (strcmp(key, "default.audio.sink") == 0) {
52+
nodeSetter = &PwDefaultTracker::setDefaultSink;
53+
nameSetter = &PwDefaultTracker::setDefaultSinkName;
54+
} else if (strcmp(key, "default.audio.source") == 0) {
55+
nodeSetter = &PwDefaultTracker::setDefaultSource;
56+
nameSetter = &PwDefaultTracker::setDefaultSourceName;
57+
} else if (strcmp(key, "default.configured.audio.sink") == 0) {
58+
nodeSetter = &PwDefaultTracker::setDefaultConfiguredSink;
59+
nameSetter = &PwDefaultTracker::setDefaultConfiguredSinkName;
60+
} else if (strcmp(key, "default.configured.audio.source") == 0) {
61+
nodeSetter = &PwDefaultTracker::setDefaultConfiguredSource;
62+
nameSetter = &PwDefaultTracker::setDefaultConfiguredSourceName;
63+
} else return;
64+
65+
QString name;
66+
if (strcmp(type, "Spa:String:JSON") == 0) {
67+
auto failed = true;
68+
auto iter = std::array<spa_json, 2>();
69+
spa_json_init(&iter[0], value, strlen(value));
70+
71+
if (spa_json_enter_object(&iter[0], &iter[1]) > 0) {
72+
auto buf = std::array<char, 1024>();
73+
74+
if (spa_json_get_string(&iter[1], buf.data(), buf.size()) > 0) {
75+
if (strcmp(buf.data(), "name") == 0) {
76+
if (spa_json_get_string(&iter[1], buf.data(), buf.size()) > 0) {
77+
name = buf.data();
78+
failed = false;
79+
}
80+
}
81+
}
82+
}
83+
84+
if (failed) {
85+
qCWarning(logDefaults) << "Failed to parse SPA default json:"
86+
<< QString::fromLocal8Bit(value);
87+
}
88+
}
89+
90+
(this->*nameSetter)(name);
91+
(this->*nodeSetter)(this->registry->findNodeByName(name));
92+
}
93+
94+
void PwDefaultTracker::onNodeAdded(PwNode* node) {
95+
if (node->name.isEmpty()) return;
96+
97+
if (this->mDefaultSink == nullptr && node->name == this->mDefaultSinkName) {
98+
this->setDefaultSink(node);
99+
}
100+
101+
if (this->mDefaultSource == nullptr && node->name == this->mDefaultSourceName) {
102+
this->setDefaultSource(node);
103+
}
104+
105+
if (this->mDefaultConfiguredSink == nullptr && node->name == this->mDefaultConfiguredSinkName) {
106+
this->setDefaultConfiguredSink(node);
107+
}
108+
109+
if (this->mDefaultConfiguredSource == nullptr && node->name == this->mDefaultConfiguredSourceName)
110+
{
111+
this->setDefaultConfiguredSource(node);
112+
}
113+
}
114+
115+
void PwDefaultTracker::onNodeDestroyed(QObject* node) {
116+
if (node == this->mDefaultSink) {
117+
qCInfo(logDefaults) << "Default sink destroyed.";
118+
this->mDefaultSink = nullptr;
119+
emit this->defaultSinkChanged();
120+
}
121+
122+
if (node == this->mDefaultSource) {
123+
qCInfo(logDefaults) << "Default source destroyed.";
124+
this->mDefaultSource = nullptr;
125+
emit this->defaultSourceChanged();
126+
}
127+
128+
if (node == this->mDefaultConfiguredSink) {
129+
qCInfo(logDefaults) << "Default configured sink destroyed.";
130+
this->mDefaultConfiguredSink = nullptr;
131+
emit this->defaultConfiguredSinkChanged();
132+
}
133+
134+
if (node == this->mDefaultConfiguredSource) {
135+
qCInfo(logDefaults) << "Default configured source destroyed.";
136+
this->mDefaultConfiguredSource = nullptr;
137+
emit this->defaultConfiguredSourceChanged();
138+
}
139+
}
140+
141+
void PwDefaultTracker::setDefaultSink(PwNode* node) {
142+
if (node == this->mDefaultSink) return;
143+
qCInfo(logDefaults) << "Default sink changed to" << node;
144+
145+
setSimpleObjectHandle<
146+
&PwDefaultTracker::mDefaultSink,
147+
&PwDefaultTracker::onNodeDestroyed,
148+
&PwDefaultTracker::defaultSinkChanged>(this, node);
149+
}
150+
151+
void PwDefaultTracker::setDefaultSinkName(const QString& name) {
152+
if (name == this->mDefaultSinkName) return;
153+
qCInfo(logDefaults) << "Default sink name changed to" << name;
154+
this->mDefaultSinkName = name;
155+
emit this->defaultSinkNameChanged();
156+
}
157+
158+
void PwDefaultTracker::setDefaultSource(PwNode* node) {
159+
if (node == this->mDefaultSource) return;
160+
qCInfo(logDefaults) << "Default source changed to" << node;
161+
162+
setSimpleObjectHandle<
163+
&PwDefaultTracker::mDefaultSource,
164+
&PwDefaultTracker::onNodeDestroyed,
165+
&PwDefaultTracker::defaultSourceChanged>(this, node);
166+
}
167+
168+
void PwDefaultTracker::setDefaultSourceName(const QString& name) {
169+
if (name == this->mDefaultSourceName) return;
170+
qCInfo(logDefaults) << "Default source name changed to" << name;
171+
this->mDefaultSourceName = name;
172+
emit this->defaultSourceNameChanged();
173+
}
174+
175+
void PwDefaultTracker::setDefaultConfiguredSink(PwNode* node) {
176+
if (node == this->mDefaultConfiguredSink) return;
177+
qCInfo(logDefaults) << "Default configured sink changed to" << node;
178+
179+
setSimpleObjectHandle<
180+
&PwDefaultTracker::mDefaultConfiguredSink,
181+
&PwDefaultTracker::onNodeDestroyed,
182+
&PwDefaultTracker::defaultConfiguredSinkChanged>(this, node);
183+
}
184+
185+
void PwDefaultTracker::setDefaultConfiguredSinkName(const QString& name) {
186+
if (name == this->mDefaultConfiguredSinkName) return;
187+
qCInfo(logDefaults) << "Default configured sink name changed to" << name;
188+
this->mDefaultConfiguredSinkName = name;
189+
emit this->defaultConfiguredSinkNameChanged();
190+
}
191+
192+
void PwDefaultTracker::setDefaultConfiguredSource(PwNode* node) {
193+
if (node == this->mDefaultConfiguredSource) return;
194+
qCInfo(logDefaults) << "Default configured source changed to" << node;
195+
196+
setSimpleObjectHandle<
197+
&PwDefaultTracker::mDefaultConfiguredSource,
198+
&PwDefaultTracker::onNodeDestroyed,
199+
&PwDefaultTracker::defaultConfiguredSourceChanged>(this, node);
200+
}
201+
202+
void PwDefaultTracker::setDefaultConfiguredSourceName(const QString& name) {
203+
if (name == this->mDefaultConfiguredSourceName) return;
204+
qCInfo(logDefaults) << "Default configured source name changed to" << name;
205+
this->mDefaultConfiguredSourceName = name;
206+
emit this->defaultConfiguredSourceNameChanged();
207+
}
208+
209+
PwNode* PwDefaultTracker::defaultSink() const { return this->mDefaultSink; }
210+
PwNode* PwDefaultTracker::defaultSource() const { return this->mDefaultSource; }
211+
212+
PwNode* PwDefaultTracker::defaultConfiguredSink() const { return this->mDefaultConfiguredSink; }
213+
214+
const QString& PwDefaultTracker::defaultConfiguredSinkName() const {
215+
return this->mDefaultConfiguredSinkName;
216+
}
217+
218+
PwNode* PwDefaultTracker::defaultConfiguredSource() const { return this->mDefaultConfiguredSource; }
219+
220+
const QString& PwDefaultTracker::defaultConfiguredSourceName() const {
221+
return this->mDefaultConfiguredSourceName;
222+
}
223+
224+
} // namespace qs::service::pipewire

src/services/pipewire/defaults.hpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#pragma once
2+
3+
#include <qobject.h>
4+
#include <qtmetamacros.h>
5+
6+
#include "registry.hpp"
7+
8+
namespace qs::service::pipewire {
9+
10+
class PwDefaultTracker: public QObject {
11+
Q_OBJECT;
12+
13+
public:
14+
explicit PwDefaultTracker(PwRegistry* registry);
15+
16+
[[nodiscard]] PwNode* defaultSink() const;
17+
[[nodiscard]] PwNode* defaultSource() const;
18+
19+
[[nodiscard]] PwNode* defaultConfiguredSink() const;
20+
[[nodiscard]] const QString& defaultConfiguredSinkName() const;
21+
22+
[[nodiscard]] PwNode* defaultConfiguredSource() const;
23+
[[nodiscard]] const QString& defaultConfiguredSourceName() const;
24+
25+
signals:
26+
void defaultSinkChanged();
27+
void defaultSinkNameChanged();
28+
29+
void defaultSourceChanged();
30+
void defaultSourceNameChanged();
31+
32+
void defaultConfiguredSinkChanged();
33+
void defaultConfiguredSinkNameChanged();
34+
35+
void defaultConfiguredSourceChanged();
36+
void defaultConfiguredSourceNameChanged();
37+
38+
private slots:
39+
void onMetadataAdded(PwMetadata* metadata);
40+
void onMetadataProperty(const char* key, const char* type, const char* value);
41+
void onNodeAdded(PwNode* node);
42+
void onNodeDestroyed(QObject* node);
43+
44+
private:
45+
void setDefaultSink(PwNode* node);
46+
void setDefaultSinkName(const QString& name);
47+
48+
void setDefaultSource(PwNode* node);
49+
void setDefaultSourceName(const QString& name);
50+
51+
void setDefaultConfiguredSink(PwNode* node);
52+
void setDefaultConfiguredSinkName(const QString& name);
53+
54+
void setDefaultConfiguredSource(PwNode* node);
55+
void setDefaultConfiguredSourceName(const QString& name);
56+
57+
PwRegistry* registry;
58+
PwBindableRef<PwMetadata> defaultsMetadata;
59+
60+
PwNode* mDefaultSink = nullptr;
61+
QString mDefaultSinkName;
62+
63+
PwNode* mDefaultSource = nullptr;
64+
QString mDefaultSourceName;
65+
66+
PwNode* mDefaultConfiguredSink = nullptr;
67+
QString mDefaultConfiguredSinkName;
68+
69+
PwNode* mDefaultConfiguredSource = nullptr;
70+
QString mDefaultConfiguredSourceName;
71+
};
72+
73+
} // namespace qs::service::pipewire

0 commit comments

Comments
 (0)