PulseAudio Qt Bindings

server.cpp
1/*
2 SPDX-FileCopyrightText: 2016 David Rosca <nowrep@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "server.h"
8#include "server_p.h"
9
10#include "context.h"
11#include "context_p.h"
12#include "debug.h"
13#include "sink.h"
14#include "source.h"
15
16using namespace std::chrono_literals;
17
18namespace PulseAudioQt
19{
20Server::Server(Context *context)
21 : QObject(context)
22 , d(new ServerPrivate(this))
23{
24 Q_ASSERT(context);
25
26 connect(&context->d->m_sinks, &MapBaseQObject::added, this, &Server::updateDefaultDevices);
27 connect(&context->d->m_sinks, &MapBaseQObject::removed, this, &Server::updateDefaultDevices);
28 connect(&context->d->m_sources, &MapBaseQObject::added, this, &Server::updateDefaultDevices);
29 connect(&context->d->m_sources, &MapBaseQObject::removed, this, &Server::updateDefaultDevices);
30
31 // WirePlumber detection works based on connected clients.
32 // Since we act on individual client changes let's compress them otherwise we may be switching state multiple times
33 // for no reason.
34 d->m_wirePlumberFindTimer.setInterval(250ms); // arbitrary compression time
35 d->m_wirePlumberFindTimer.setSingleShot(true);
36 connect(&d->m_wirePlumberFindTimer, &QTimer::timeout, this, [this] {
37 d->findWirePlumber();
38 });
39 connect(&context->d->m_clients, &MapBaseQObject::added, &d->m_wirePlumberFindTimer, qOverload<>(&QTimer::start));
40 connect(&context->d->m_clients, &MapBaseQObject::removed, &d->m_wirePlumberFindTimer, qOverload<>(&QTimer::start));
41}
42
43Server::~Server()
44{
45 delete d;
46}
47
48ServerPrivate::ServerPrivate(Server *q)
49 : q(q)
50 , m_defaultSink(nullptr)
51 , m_defaultSource(nullptr)
52{
53}
54
55ServerPrivate::~ServerPrivate()
56{
57}
58
59Sink *Server::defaultSink() const
60{
61 return d->m_defaultSink;
62}
63
64void Server::setDefaultSink(Sink *sink)
65{
66 Q_ASSERT(sink);
67 Context::instance()->setDefaultSink(sink->name());
68}
69
70Source *Server::defaultSource() const
71{
72 return d->m_defaultSource;
73}
74
75void Server::setDefaultSource(Source *source)
76{
77 Q_ASSERT(source);
78 Context::instance()->setDefaultSource(source->name());
79}
80
81void Server::reset()
82{
83 if (d->m_defaultSink) {
84 d->m_defaultSink = nullptr;
85 Q_EMIT defaultSinkChanged(d->m_defaultSink);
86 }
87
88 if (d->m_defaultSource) {
89 d->m_defaultSource = nullptr;
90 Q_EMIT defaultSourceChanged(d->m_defaultSource);
91 }
92}
93
94void Server::disconnectSignals()
95{
96 disconnect(this, &Server::defaultSinkChanged, nullptr, nullptr);
97 disconnect(this, &Server::defaultSourceChanged, nullptr, nullptr);
98 disconnect(this, &Server::isPipeWireChanged, nullptr, nullptr);
99 disconnect(this, &Server::updated, nullptr, nullptr);
100 disconnect(this, &Server::hasWirePlumberChanged, nullptr, nullptr);
101}
102
103void ServerPrivate::update(const pa_server_info *info)
104{
105 m_defaultSinkName = QString::fromUtf8(info->default_sink_name);
106 m_defaultSourceName = QString::fromUtf8(info->default_source_name);
107
108 const bool isPw = QString::fromUtf8(info->server_name).contains("PipeWire");
109
110 if (isPw != m_isPipeWire) {
111 m_isPipeWire = isPw;
112 Q_EMIT q->isPipeWireChanged();
113 }
114
115 q->updateDefaultDevices();
116
117 Q_EMIT q->updated();
118}
119
120/** @private */
121template<typename Type, typename Vector>
122static Type *findByName(const Vector &vector, const QString &name)
123{
124 Type *out = nullptr;
125 if (name.isEmpty()) {
126 return out;
127 }
128 for (Type *t : vector) {
129 out = t;
130 if (out->name() == name) {
131 return out;
132 }
133 }
134 qCWarning(PULSEAUDIOQT) << "No object for name" << name;
135 return out;
136}
137
138void Server::updateDefaultDevices()
139{
140 Sink *sink = findByName<Sink>(Context::instance()->d->m_sinks.data(), d->m_defaultSinkName);
141 Source *source = findByName<Source>(Context::instance()->d->m_sources.data(), d->m_defaultSourceName);
142
143 if (d->m_defaultSink != sink) {
144 qCDebug(PULSEAUDIOQT) << "Default sink changed" << sink;
145 d->m_defaultSink = sink;
146 Q_EMIT defaultSinkChanged(d->m_defaultSink);
147 }
148
149 if (d->m_defaultSource != source) {
150 qCDebug(PULSEAUDIOQT) << "Default source changed" << source;
151 d->m_defaultSource = source;
152 Q_EMIT defaultSourceChanged(d->m_defaultSource);
153 }
154}
155
156bool Server::isPipeWire() const
157{
158 return d->m_isPipeWire;
159}
160
161void ServerPrivate::findWirePlumber()
162{
163 if (!m_isPipeWire) {
164 return;
165 }
166
167 const auto clients = Context::instance()->clients();
168 for (const auto &client : clients) {
169 if (client->properties().value(QStringLiteral("wireplumber.daemon")) == QLatin1String("true")) {
170 m_hasWirePlumber = true;
171 Q_EMIT q->hasWirePlumberChanged();
172 return;
173 }
174 }
175
176 // Found no plumber, mark false
177 m_hasWirePlumber = false;
178 Q_EMIT q->hasWirePlumberChanged();
179}
180
181bool Server::hasWirePlumber() const
182{
183 return d->m_hasWirePlumber;
184}
185} // PulseAudioQt
QString name(StandardAction id)
StandardShortcut findByName(const QString &name)
The primary namespace of PulseAudioQt.
Definition card.cpp:17
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start()
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:12:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.