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}
46
47ServerPrivate::ServerPrivate(Server *q)
48 : q(q)
49 , m_defaultSink(nullptr)
50 , m_defaultSource(nullptr)
51{
52}
53
54ServerPrivate::~ServerPrivate()
55{
56}
57
58Sink *Server::defaultSink() const
59{
60 return d->m_defaultSink;
61}
62
63void Server::setDefaultSink(Sink *sink)
64{
65 Q_ASSERT(sink);
66 Context::instance()->setDefaultSink(sink->name());
67}
68
69Source *Server::defaultSource() const
70{
71 return d->m_defaultSource;
72}
73
74void Server::setDefaultSource(Source *source)
75{
76 Q_ASSERT(source);
77 Context::instance()->setDefaultSource(source->name());
78}
79
80void Server::reset()
81{
82 if (d->m_defaultSink) {
83 d->m_defaultSink = nullptr;
84 Q_EMIT defaultSinkChanged(d->m_defaultSink);
85 }
86
87 if (d->m_defaultSource) {
88 d->m_defaultSource = nullptr;
89 Q_EMIT defaultSourceChanged(d->m_defaultSource);
90 }
91}
92
93void Server::disconnectSignals()
94{
95 disconnect(this, &Server::defaultSinkChanged, nullptr, nullptr);
96 disconnect(this, &Server::defaultSourceChanged, nullptr, nullptr);
97 disconnect(this, &Server::isPipeWireChanged, nullptr, nullptr);
98 disconnect(this, &Server::updated, nullptr, nullptr);
99 disconnect(this, &Server::hasWirePlumberChanged, nullptr, nullptr);
100}
101
102void ServerPrivate::update(const pa_server_info *info)
103{
104 m_defaultSinkName = QString::fromUtf8(info->default_sink_name);
105 m_defaultSourceName = QString::fromUtf8(info->default_source_name);
106
107 const bool isPw = QString::fromUtf8(info->server_name).contains(QLatin1String("PipeWire"));
108
109 if (isPw != m_isPipeWire) {
110 m_isPipeWire = isPw;
111 Q_EMIT q->isPipeWireChanged();
112 }
113
114 q->updateDefaultDevices();
115
116 Q_EMIT q->updated();
117}
118
119/** @private */
120template<typename Type, typename Vector>
121static Type *findByName(const Vector &vector, const QString &name)
122{
123 Type *out = nullptr;
124 if (name.isEmpty()) {
125 return out;
126 }
127 for (Type *t : vector) {
128 out = t;
129 if (out->name() == name) {
130 return out;
131 }
132 }
133 qCWarning(PULSEAUDIOQT) << "No object for name" << name;
134 return out;
135}
136
137void Server::updateDefaultDevices()
138{
139 Sink *sink = findByName<Sink>(Context::instance()->d->m_sinks.data(), d->m_defaultSinkName);
140 Source *source = findByName<Source>(Context::instance()->d->m_sources.data(), d->m_defaultSourceName);
141
142 if (d->m_defaultSink != sink) {
143 qCDebug(PULSEAUDIOQT) << "Default sink changed" << sink;
144 d->m_defaultSink = sink;
145 Q_EMIT defaultSinkChanged(d->m_defaultSink);
146 }
147
148 if (d->m_defaultSource != source) {
149 qCDebug(PULSEAUDIOQT) << "Default source changed" << source;
150 d->m_defaultSource = source;
151 Q_EMIT defaultSourceChanged(d->m_defaultSource);
152 }
153}
154
155bool Server::isPipeWire() const
156{
157 return d->m_isPipeWire;
158}
159
160void ServerPrivate::findWirePlumber()
161{
162 if (!m_isPipeWire) {
163 return;
164 }
165
166 const auto clients = Context::instance()->clients();
167 for (const auto &client : clients) {
168 if (client->properties().value(QStringLiteral("wireplumber.daemon")) == QLatin1String("true")) {
169 m_hasWirePlumber = true;
170 Q_EMIT q->hasWirePlumberChanged();
171 return;
172 }
173 }
174
175 // Found no plumber, mark false
176 m_hasWirePlumber = false;
177 Q_EMIT q->hasWirePlumberChanged();
178}
179
180bool Server::hasWirePlumber() const
181{
182 return d->m_hasWirePlumber;
183}
184} // PulseAudioQt
QString name(GameStandardAction 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 Nov 29 2024 11:50:05 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.