mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2025-11-06 19:14:57 +11:00
Fixes discrepancies between pulse and qs volumes, and volumes not persisting across reboot or vt switches.
202 lines
5.4 KiB
C++
202 lines
5.4 KiB
C++
#include "registry.hpp"
|
|
#include <cstring>
|
|
|
|
#include <pipewire/core.h>
|
|
#include <pipewire/device.h>
|
|
#include <pipewire/extensions/metadata.h>
|
|
#include <pipewire/link.h>
|
|
#include <pipewire/node.h>
|
|
#include <pipewire/proxy.h>
|
|
#include <qdebug.h>
|
|
#include <qlogging.h>
|
|
#include <qloggingcategory.h>
|
|
#include <qobject.h>
|
|
#include <qtmetamacros.h>
|
|
#include <qtypes.h>
|
|
|
|
#include "core.hpp"
|
|
#include "device.hpp"
|
|
#include "link.hpp"
|
|
#include "metadata.hpp"
|
|
#include "node.hpp"
|
|
|
|
namespace qs::service::pipewire {
|
|
|
|
Q_LOGGING_CATEGORY(logRegistry, "quickshell.service.pipewire.registry", QtWarningMsg);
|
|
|
|
PwBindableObject::~PwBindableObject() {
|
|
if (this->id != 0) {
|
|
qCFatal(logRegistry) << "Destroyed pipewire object" << this
|
|
<< "without causing safeDestroy. THIS IS UNDEFINED BEHAVIOR.";
|
|
}
|
|
}
|
|
|
|
void PwBindableObject::init(PwRegistry* registry, quint32 id, quint32 perms) {
|
|
this->id = id;
|
|
this->perms = perms;
|
|
this->registry = registry;
|
|
this->setParent(registry);
|
|
qCDebug(logRegistry) << "Creating object" << this;
|
|
}
|
|
|
|
void PwBindableObject::safeDestroy() {
|
|
this->unbind();
|
|
qCDebug(logRegistry) << "Destroying object" << this;
|
|
emit this->destroying(this);
|
|
this->id = 0;
|
|
delete this;
|
|
}
|
|
|
|
void PwBindableObject::debugId(QDebug& debug) const {
|
|
auto saver = QDebugStateSaver(debug);
|
|
debug.nospace() << this->id << "/" << (this->object == nullptr ? "unbound" : "bound");
|
|
}
|
|
|
|
void PwBindableObject::ref() {
|
|
this->refcount++;
|
|
if (this->refcount == 1) this->bind();
|
|
}
|
|
|
|
void PwBindableObject::unref() {
|
|
this->refcount--;
|
|
if (this->refcount == 0) this->unbind();
|
|
}
|
|
|
|
void PwBindableObject::bind() {
|
|
qCDebug(logRegistry) << "Bound object" << this;
|
|
this->bindHooks();
|
|
}
|
|
|
|
void PwBindableObject::unbind() {
|
|
if (this->object == nullptr) return;
|
|
qCDebug(logRegistry) << "Unbinding object" << this;
|
|
this->unbindHooks();
|
|
pw_proxy_destroy(this->object);
|
|
this->object = nullptr;
|
|
}
|
|
|
|
QDebug operator<<(QDebug debug, const PwBindableObject* object) {
|
|
if (object == nullptr) {
|
|
debug << "PwBindableObject(0x0)";
|
|
} else {
|
|
auto saver = QDebugStateSaver(debug);
|
|
// 0 if not present, start of class name if present
|
|
auto idx = QString(object->metaObject()->className()).lastIndexOf(':') + 1;
|
|
debug.nospace() << (object->metaObject()->className() + idx) << '(' // NOLINT
|
|
<< static_cast<const void*>(object) << ", id=";
|
|
object->debugId(debug);
|
|
debug << ')';
|
|
}
|
|
|
|
return debug;
|
|
}
|
|
|
|
PwBindableObjectRef::PwBindableObjectRef(PwBindableObject* object) { this->setObject(object); }
|
|
|
|
PwBindableObjectRef::~PwBindableObjectRef() { this->setObject(nullptr); }
|
|
|
|
void PwBindableObjectRef::setObject(PwBindableObject* object) {
|
|
if (this->mObject != nullptr) {
|
|
this->mObject->unref();
|
|
QObject::disconnect(this->mObject, nullptr, this, nullptr);
|
|
}
|
|
|
|
this->mObject = object;
|
|
|
|
if (object != nullptr) {
|
|
this->mObject->ref();
|
|
QObject::connect(object, &QObject::destroyed, this, &PwBindableObjectRef::onObjectDestroyed);
|
|
}
|
|
}
|
|
|
|
void PwBindableObjectRef::onObjectDestroyed() {
|
|
// allow references to it so consumers can disconnect themselves
|
|
emit this->objectDestroyed();
|
|
this->mObject = nullptr;
|
|
}
|
|
|
|
void PwRegistry::init(PwCore& core) {
|
|
this->core = &core;
|
|
this->object = pw_core_get_registry(core.core, PW_VERSION_REGISTRY, 0);
|
|
pw_registry_add_listener(this->object, &this->listener.hook, &PwRegistry::EVENTS, this);
|
|
}
|
|
|
|
const pw_registry_events PwRegistry::EVENTS = {
|
|
.version = PW_VERSION_REGISTRY_EVENTS,
|
|
.global = &PwRegistry::onGlobal,
|
|
.global_remove = &PwRegistry::onGlobalRemoved,
|
|
};
|
|
|
|
void PwRegistry::onGlobal(
|
|
void* data,
|
|
quint32 id,
|
|
quint32 permissions,
|
|
const char* type,
|
|
quint32 /*version*/,
|
|
const spa_dict* props
|
|
) {
|
|
auto* self = static_cast<PwRegistry*>(data);
|
|
|
|
if (strcmp(type, PW_TYPE_INTERFACE_Metadata) == 0) {
|
|
auto* meta = new PwMetadata();
|
|
meta->init(self, id, permissions);
|
|
meta->initProps(props);
|
|
|
|
self->metadata.emplace(id, meta);
|
|
meta->bind();
|
|
} else if (strcmp(type, PW_TYPE_INTERFACE_Link) == 0) {
|
|
auto* link = new PwLink();
|
|
link->init(self, id, permissions);
|
|
link->initProps(props);
|
|
|
|
self->links.emplace(id, link);
|
|
self->addLinkToGroup(link);
|
|
emit self->linkAdded(link);
|
|
} else if (strcmp(type, PW_TYPE_INTERFACE_Node) == 0) {
|
|
auto* node = new PwNode();
|
|
node->init(self, id, permissions);
|
|
node->initProps(props);
|
|
|
|
self->nodes.emplace(id, node);
|
|
emit self->nodeAdded(node);
|
|
} else if (strcmp(type, PW_TYPE_INTERFACE_Device) == 0) {
|
|
auto* device = new PwDevice();
|
|
device->init(self, id, permissions);
|
|
device->initProps(props);
|
|
|
|
self->devices.emplace(id, device);
|
|
}
|
|
}
|
|
|
|
void PwRegistry::onGlobalRemoved(void* data, quint32 id) {
|
|
auto* self = static_cast<PwRegistry*>(data);
|
|
|
|
if (auto* meta = self->metadata.value(id)) {
|
|
self->metadata.remove(id);
|
|
meta->safeDestroy();
|
|
} else if (auto* link = self->links.value(id)) {
|
|
self->links.remove(id);
|
|
link->safeDestroy();
|
|
} else if (auto* node = self->nodes.value(id)) {
|
|
self->nodes.remove(id);
|
|
node->safeDestroy();
|
|
}
|
|
}
|
|
|
|
void PwRegistry::addLinkToGroup(PwLink* link) {
|
|
for (auto* group: this->linkGroups) {
|
|
if (group->tryAddLink(link)) return;
|
|
}
|
|
|
|
auto* group = new PwLinkGroup(link);
|
|
QObject::connect(group, &QObject::destroyed, this, &PwRegistry::onLinkGroupDestroyed);
|
|
this->linkGroups.push_back(group);
|
|
emit this->linkGroupAdded(group);
|
|
}
|
|
|
|
void PwRegistry::onLinkGroupDestroyed(QObject* object) {
|
|
auto* group = static_cast<PwLinkGroup*>(object); // NOLINT
|
|
this->linkGroups.removeOne(group);
|
|
}
|
|
|
|
} // namespace qs::service::pipewire
|