mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2025-11-04 19:04:56 +11:00
service/pipewire: add pipewire module
This commit is contained in:
parent
bba8cb8a7d
commit
3e80c4a4fd
21 changed files with 2476 additions and 4 deletions
146
src/services/pipewire/metadata.cpp
Normal file
146
src/services/pipewire/metadata.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "metadata.hpp"
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include <pipewire/extensions/metadata.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qobject.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qtypes.h>
|
||||
#include <spa/utils/json.h>
|
||||
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace qs::service::pipewire {
|
||||
|
||||
Q_LOGGING_CATEGORY(logMeta, "quickshell.service.pipewire.metadata", QtWarningMsg);
|
||||
|
||||
void PwMetadata::bindHooks() {
|
||||
pw_metadata_add_listener(this->proxy(), &this->listener.hook, &PwMetadata::EVENTS, this);
|
||||
}
|
||||
|
||||
void PwMetadata::unbindHooks() { this->listener.remove(); }
|
||||
|
||||
const pw_metadata_events PwMetadata::EVENTS = {
|
||||
.version = PW_VERSION_METADATA_EVENTS,
|
||||
.property = &PwMetadata::onProperty,
|
||||
};
|
||||
|
||||
int PwMetadata::onProperty(
|
||||
void* data,
|
||||
quint32 subject,
|
||||
const char* key,
|
||||
const char* type,
|
||||
const char* value
|
||||
) {
|
||||
auto* self = static_cast<PwMetadata*>(data);
|
||||
qCDebug(logMeta) << "Received metadata for" << self << "- subject:" << subject
|
||||
<< "key:" << QString(key) << "type:" << QString(type)
|
||||
<< "value:" << QString(value);
|
||||
|
||||
emit self->registry->metadataUpdate(self, subject, key, type, value);
|
||||
|
||||
// ideally we'd dealloc metadata that wasn't picked up but there's no information
|
||||
// available about if updates can come in later, so I assume they can.
|
||||
|
||||
return 0; // ??? - no docs and no reason for a callback to return an int
|
||||
}
|
||||
|
||||
PwDefaultsMetadata::PwDefaultsMetadata(PwRegistry* registry) {
|
||||
QObject::connect(
|
||||
registry,
|
||||
&PwRegistry::metadataUpdate,
|
||||
this,
|
||||
&PwDefaultsMetadata::onMetadataUpdate
|
||||
);
|
||||
}
|
||||
|
||||
QString PwDefaultsMetadata::defaultSink() const { return this->mDefaultSink; }
|
||||
|
||||
QString PwDefaultsMetadata::defaultSource() const { return this->mDefaultSource; }
|
||||
|
||||
// we don't really care if the metadata objects are destroyed, but try to ref them so we get property updates
|
||||
void PwDefaultsMetadata::onMetadataUpdate(
|
||||
PwMetadata* metadata,
|
||||
quint32 subject,
|
||||
const char* key,
|
||||
const char* /*type*/,
|
||||
const char* value
|
||||
) {
|
||||
if (subject != 0) return;
|
||||
|
||||
// non "configured" sinks and sources have lower priority as wireplumber seems to only change
|
||||
// the "configured" ones.
|
||||
|
||||
bool sink = false;
|
||||
if (strcmp(key, "default.configured.audio.sink") == 0) {
|
||||
sink = true;
|
||||
this->sinkConfigured = true;
|
||||
} else if ((!this->sinkConfigured && strcmp(key, "default.audio.sink") == 0)) {
|
||||
sink = true;
|
||||
}
|
||||
|
||||
if (sink) {
|
||||
this->defaultSinkHolder.setObject(metadata);
|
||||
|
||||
auto newSink = PwDefaultsMetadata::parseNameSpaJson(value);
|
||||
qCInfo(logMeta) << "Got default sink" << newSink << "configured:" << this->sinkConfigured;
|
||||
if (newSink == this->mDefaultSink) return;
|
||||
|
||||
this->mDefaultSink = newSink;
|
||||
emit this->defaultSinkChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
bool source = false;
|
||||
if (strcmp(key, "default.configured.audio.source") == 0) {
|
||||
source = true;
|
||||
this->sourceConfigured = true;
|
||||
} else if ((!this->sourceConfigured && strcmp(key, "default.audio.source") == 0)) {
|
||||
source = true;
|
||||
}
|
||||
|
||||
if (source) {
|
||||
this->defaultSourceHolder.setObject(metadata);
|
||||
|
||||
auto newSource = PwDefaultsMetadata::parseNameSpaJson(value);
|
||||
qCInfo(logMeta) << "Got default source" << newSource << "configured:" << this->sourceConfigured;
|
||||
if (newSource == this->mDefaultSource) return;
|
||||
|
||||
this->mDefaultSource = newSource;
|
||||
emit this->defaultSourceChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString PwDefaultsMetadata::parseNameSpaJson(const char* spaJson) {
|
||||
auto iter = std::array<spa_json, 2>();
|
||||
spa_json_init(&iter[0], spaJson, strlen(spaJson));
|
||||
|
||||
if (spa_json_enter_object(&iter[0], &iter[1]) < 0) {
|
||||
qCWarning(logMeta) << "Failed to parse source/sink SPA json - failed to enter object of"
|
||||
<< QString(spaJson);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto buf = std::array<char, 512>();
|
||||
while (spa_json_get_string(&iter[1], buf.data(), buf.size()) > 0) {
|
||||
if (strcmp(buf.data(), "name") != 0) continue;
|
||||
|
||||
if (spa_json_get_string(&iter[1], buf.data(), buf.size()) < 0) {
|
||||
qCWarning(logMeta
|
||||
) << "Failed to parse source/sink SPA json - failed to read value of name property"
|
||||
<< QString(spaJson);
|
||||
return "";
|
||||
}
|
||||
|
||||
return QString(buf.data());
|
||||
}
|
||||
|
||||
qCWarning(logMeta) << "Failed to parse source/sink SPA json - failed to find name property of"
|
||||
<< QString(spaJson);
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace qs::service::pipewire
|
||||
Loading…
Add table
Add a link
Reference in a new issue