diff --git a/src/pipewire/pipewirecontroller.cpp b/src/pipewire/pipewirecontroller.cpp index fe46060..c85d56c 100644 --- a/src/pipewire/pipewirecontroller.cpp +++ b/src/pipewire/pipewirecontroller.cpp @@ -156,7 +156,7 @@ void registryEventGlobal(void *data, uint32_t id, uint32_t permissions, void registryEventGlobalRemove(void *data, uint32_t id) { auto *self = static_cast(data); - + { QMutexLocker lock(&self->m_nodesMutex); if (self->m_nodes.contains(id)) { @@ -164,18 +164,27 @@ void registryEventGlobalRemove(void *data, uint32_t id) emit self->nodeRemoved(id); return; } - + if (self->m_ports.contains(id)) { self->m_ports.remove(id); return; } - + } + + bool linkRemoved = false; + { + QMutexLocker lock(&self->m_nodesMutex); if (self->m_links.contains(id)) { self->m_links.remove(id); - emit self->linkRemoved(id); - return; + linkRemoved = true; } } + + if (linkRemoved) { + emit self->linkRemoved(id); + self->handleLinkRemoval(id); + return; + } } void coreEventDone(void *data, uint32_t id, int seq) @@ -769,6 +778,11 @@ bool PipeWireController::destroyLink(uint32_t linkId) return false; } linkInfo = m_links.value(linkId); + m_userRemovedLinks.insert(linkId); + if (m_linkIntentKeys.contains(linkId)) { + const QString key = m_linkIntentKeys.take(linkId); + m_linkIntents.remove(key); + } } lock(); @@ -786,6 +800,154 @@ bool PipeWireController::destroyLink(uint32_t linkId) return true; } +void PipeWireController::rememberLinkIntent(const LinkInfo &link) +{ + QString key; + if (!buildLinkIntentKey(link, key)) { + return; + } + + QMutexLocker lock(&m_nodesMutex); + m_linkIntents.insert(key); + m_linkIntentKeys.insert(link.id, key); +} + +void PipeWireController::handleLinkRemoval(uint32_t linkId) +{ + QMutexLocker lock(&m_nodesMutex); + if (m_userRemovedLinks.contains(linkId)) { + m_userRemovedLinks.remove(linkId); + } + if (m_linkIntentKeys.contains(linkId)) { + m_linkIntentKeys.remove(linkId); + } +} + +void PipeWireController::tryRestoreLinks() +{ + QList intents; + { + QMutexLocker lock(&m_nodesMutex); + intents = m_linkIntents.values(); + } + + for (const auto &key : intents) { + uint32_t outNodeId = 0; + uint32_t outPortId = 0; + uint32_t inNodeId = 0; + uint32_t inPortId = 0; + if (!resolveLinkIntentKey(key, outNodeId, outPortId, inNodeId, inPortId)) { + continue; + } + createLink(outNodeId, outPortId, inNodeId, inPortId); + } +} + +void PipeWireController::updateLinkIntentsForNode(uint32_t nodeId) +{ + QVector links; + { + QMutexLocker lock(&m_nodesMutex); + for (auto it = m_links.cbegin(); it != m_links.cend(); ++it) { + const LinkInfo &link = it.value(); + if (link.outputNodeId == nodeId || link.inputNodeId == nodeId) { + links.append(link); + } + } + } + + for (const auto &link : links) { + rememberLinkIntent(link); + } +} + +static bool isMeterNodeName(const QString &name) +{ + return name.startsWith("Potato-Meter") || name.startsWith("Potato-Node-Meter"); +} + +bool PipeWireController::buildLinkIntentKey(const LinkInfo &link, QString &key) const +{ + QMutexLocker lock(&m_nodesMutex); + if (!m_nodes.contains(link.outputNodeId) || !m_nodes.contains(link.inputNodeId)) { + return false; + } + + const NodeInfo &outNode = m_nodes.value(link.outputNodeId); + const NodeInfo &inNode = m_nodes.value(link.inputNodeId); + if (isMeterNodeName(outNode.name) || isMeterNodeName(inNode.name)) { + return false; + } + + QString outPortName; + QString inPortName; + for (const auto &port : outNode.outputPorts) { + if (port.id == link.outputPortId) { + outPortName = port.name; + break; + } + } + for (const auto &port : inNode.inputPorts) { + if (port.id == link.inputPortId) { + inPortName = port.name; + break; + } + } + if (outPortName.isEmpty() || inPortName.isEmpty()) { + return false; + } + if (outNode.stableId.isEmpty() || inNode.stableId.isEmpty()) { + return false; + } + + key = QString("%1||%2>>%3||%4").arg(outNode.stableId, outPortName, inNode.stableId, inPortName); + return true; +} + +bool PipeWireController::resolveLinkIntentKey(const QString &key, uint32_t &outNodeId, uint32_t &outPortId, + uint32_t &inNodeId, uint32_t &inPortId) const +{ + const QStringList halves = key.split(">>"); + if (halves.size() != 2) { + return false; + } + const QStringList outParts = halves.at(0).split("||"); + const QStringList inParts = halves.at(1).split("||"); + if (outParts.size() != 2 || inParts.size() != 2) { + return false; + } + + const QString outStableId = outParts.at(0); + const QString outPortName = outParts.at(1); + const QString inStableId = inParts.at(0); + const QString inPortName = inParts.at(1); + + QMutexLocker lock(&m_nodesMutex); + for (const auto &nodeEntry : m_nodes) { + const NodeInfo &node = nodeEntry; + if (node.stableId == outStableId) { + for (const auto &port : node.outputPorts) { + if (port.name == outPortName) { + outNodeId = node.id; + outPortId = port.id; + break; + } + } + } + if (node.stableId == inStableId) { + for (const auto &port : node.inputPorts) { + if (port.name == inPortName) { + inNodeId = node.id; + inPortId = port.id; + break; + } + } + } + } + + return outNodeId != 0 && outPortId != 0 && inNodeId != 0 && inPortId != 0; +} + QString PipeWireController::dumpGraph() const { QMutexLocker lock(&m_nodesMutex); @@ -870,6 +1032,9 @@ void PipeWireController::handleNodeInfo(uint32_t id, const struct spa_dict *prop qDebug() << "Node changed:" << node.id << node.name; } } + + updateLinkIntentsForNode(id); + tryRestoreLinks(); } void PipeWireController::handlePortInfo(uint32_t id, const struct spa_dict *props) @@ -922,6 +1087,9 @@ void PipeWireController::handlePortInfo(uint32_t id, const struct spa_dict *prop emit nodeChanged(nodeSnapshot); } + updateLinkIntentsForNode(nodeId); + tryRestoreLinks(); + qDebug() << "Port added:" << id << portName << "direction:" << direction; } @@ -949,6 +1117,8 @@ void PipeWireController::handleLinkInfo(uint32_t id, const struct spa_dict *prop } emit linkAdded(link); + + rememberLinkIntent(link); qDebug() << "Link added:" << id << "from" << outputNode << ":" << outputPort << "to" << inputNode << ":" << inputPort; diff --git a/src/pipewire/pipewirecontroller.h b/src/pipewire/pipewirecontroller.h index 5cd88a1..dd07e2f 100644 --- a/src/pipewire/pipewirecontroller.h +++ b/src/pipewire/pipewirecontroller.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -82,6 +84,13 @@ private: bool createVirtualDevice(const QString &name, const QString &description, const char *factoryName, const char *mediaClass, int channels, int rate); + void rememberLinkIntent(const LinkInfo &link); + void handleLinkRemoval(uint32_t linkId); + void tryRestoreLinks(); + void updateLinkIntentsForNode(uint32_t nodeId); + bool buildLinkIntentKey(const LinkInfo &link, QString &key) const; + bool resolveLinkIntentKey(const QString &key, uint32_t &outNodeId, uint32_t &outPortId, + uint32_t &inNodeId, uint32_t &inPortId) const; void lock(); void unlock(); @@ -99,6 +108,9 @@ private: QMap m_nodes; QMap m_ports; QMap m_links; + QSet m_linkIntents; + QHash m_linkIntentKeys; + QSet m_userRemovedLinks; QAtomicInteger m_connected{false}; QAtomicInteger m_initialized{false};