|
| 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 |
0 commit comments