Auto reconnect

This commit is contained in:
Joey Yakimowich-Payne 2026-01-27 21:25:58 -07:00
commit f57d39af48
2 changed files with 187 additions and 5 deletions

View file

@ -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<PipeWireController*>(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<QString> 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<LinkInfo> 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;

View file

@ -5,6 +5,8 @@
#include <QMap>
#include <QMutex>
#include <QAtomicInteger>
#include <QHash>
#include <QSet>
#include <atomic>
#include <vector>
@ -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<uint32_t, NodeInfo> m_nodes;
QMap<uint32_t, PortInfo> m_ports;
QMap<uint32_t, LinkInfo> m_links;
QSet<QString> m_linkIntents;
QHash<uint32_t, QString> m_linkIntentKeys;
QSet<uint32_t> m_userRemovedLinks;
QAtomicInteger<bool> m_connected{false};
QAtomicInteger<bool> m_initialized{false};