Backend: align signal connections with new TunnelManager API; fix fromConf usage
This commit is contained in:
parent
27958f3514
commit
9bcef4c0a9
1 changed files with 47 additions and 97 deletions
|
|
@ -6,36 +6,36 @@
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
// ── Constructor / Destructor ──────────────────────────────────────────────────
|
// Constructor / Destructor
|
||||||
|
|
||||||
DragonRelayBackend::DragonRelayBackend(QObject *parent)
|
DragonRelayBackend::DragonRelayBackend(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_settings(QStringLiteral("WildDragon"), QStringLiteral("DragonMoonlight"))
|
, m_settings(QStringLiteral("WildDragon"), QStringLiteral("DragonMoonlight"))
|
||||||
{
|
{
|
||||||
// ── Wire RelayClient signals ──────────────────────────────────────────────
|
// Wire RelayClient signals
|
||||||
connect(&m_relay, &RelayClient::loginSucceeded,
|
connect(&m_relay, &RelayClient::loginSucceeded,
|
||||||
this, [this]() { onLoginDone(true, QString()); });
|
this, [this]() { onLoginDone(true, QString()); });
|
||||||
connect(&m_relay, QOverload<const QString &>::of(&RelayClient::loginFailed),
|
connect(&m_relay, &RelayClient::loginFailed,
|
||||||
this, [this](const QString &err) { onLoginDone(false, err); });
|
this, [this](const QString &err) { onLoginDone(false, err); });
|
||||||
connect(&m_relay, &RelayClient::vpnProvisioned,
|
connect(&m_relay, &RelayClient::vpnProvisioned,
|
||||||
this, [this](const RelayVPNConf &conf) { onVPNPeerProvisioned(conf); });
|
this, [this](const RelayVPNConf &conf) { onVPNPeerProvisioned(conf); });
|
||||||
connect(&m_relay, QOverload<const QString &>::of(&RelayClient::vpnProvisionFailed),
|
connect(&m_relay, &RelayClient::vpnProvisionFailed,
|
||||||
this, [this](const QString &err) { onVPNError(err); });
|
this, [this](const QString &err) { onVPNError(err); });
|
||||||
connect(&m_relay, &RelayClient::hostsReady,
|
connect(&m_relay, &RelayClient::hostsReady,
|
||||||
this, [this](const QList<RelayHost> &hosts) { onHostsFetched(hosts); });
|
this, [this](const QList<RelayHost> &hosts) { onHostsFetched(hosts); });
|
||||||
connect(&m_relay, QOverload<const QString &>::of(&RelayClient::hostsFetchFailed),
|
connect(&m_relay, &RelayClient::hostsFetchFailed,
|
||||||
this, [this](const QString &err) { onHostsError(err); });
|
this, [this](const QString &err) { onHostsError(err); });
|
||||||
|
|
||||||
// ── Wire TunnelManager signals ────────────────────────────────────────────
|
// Wire TunnelManager signals
|
||||||
connect(&m_tunnel, &TunnelManager::tunnelUp,
|
connect(&m_tunnel, &TunnelManager::tunnelUp,
|
||||||
this, &DragonRelayBackend::onTunnelUp);
|
this, &DragonRelayBackend::onTunnelUp);
|
||||||
connect(&m_tunnel, &TunnelManager::tunnelDown,
|
connect(&m_tunnel, &TunnelManager::tunnelDown,
|
||||||
this, &DragonRelayBackend::onTunnelDown);
|
this, &DragonRelayBackend::onTunnelDown);
|
||||||
connect(&m_tunnel, &TunnelManager::error,
|
connect(&m_tunnel, &TunnelManager::tunnelError,
|
||||||
this, &DragonRelayBackend::onTunnelError);
|
this, &DragonRelayBackend::onTunnelError);
|
||||||
|
|
||||||
// ── Host poll timer (every 30 s while tunnel is up) ───────────────────────
|
// Host poll timer (every 30 s while tunnel is up)
|
||||||
m_hostPollTimer.setInterval(30'000);
|
m_hostPollTimer.setInterval(30 * 1000);
|
||||||
m_hostPollTimer.setSingleShot(false);
|
m_hostPollTimer.setSingleShot(false);
|
||||||
connect(&m_hostPollTimer, &QTimer::timeout,
|
connect(&m_hostPollTimer, &QTimer::timeout,
|
||||||
this, &DragonRelayBackend::pollHosts);
|
this, &DragonRelayBackend::pollHosts);
|
||||||
|
|
@ -46,13 +46,10 @@ DragonRelayBackend::DragonRelayBackend(QObject *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
DragonRelayBackend::~DragonRelayBackend() {
|
DragonRelayBackend::~DragonRelayBackend() {
|
||||||
// Best-effort cleanup — don't block the destructor
|
|
||||||
m_hostPollTimer.stop();
|
m_hostPollTimer.stop();
|
||||||
m_tunnel.stop();
|
m_tunnel.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Status helper ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::setStatus(Status s, const QString &text) {
|
void DragonRelayBackend::setStatus(Status s, const QString &text) {
|
||||||
bool changed = (m_status != s) || (!text.isEmpty() && text != m_statusText);
|
bool changed = (m_status != s) || (!text.isEmpty() && text != m_statusText);
|
||||||
m_status = s;
|
m_status = s;
|
||||||
|
|
@ -62,8 +59,6 @@ void DragonRelayBackend::setStatus(Status s, const QString &text) {
|
||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── connectRelay ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::connectRelay(const QString &url,
|
void DragonRelayBackend::connectRelay(const QString &url,
|
||||||
const QString &username,
|
const QString &username,
|
||||||
const QString &password) {
|
const QString &password) {
|
||||||
|
|
@ -74,16 +69,14 @@ void DragonRelayBackend::connectRelay(const QString &url,
|
||||||
m_savedUser = username;
|
m_savedUser = username;
|
||||||
m_savedPass = password;
|
m_savedPass = password;
|
||||||
|
|
||||||
// Persist URL + username (NOT password)
|
// Persist URL + username (NOT password - keychain integration is a TODO).
|
||||||
m_settings.setValue(QStringLiteral("relay/url"), url);
|
m_settings.setValue(QStringLiteral("relay/url"), url);
|
||||||
m_settings.setValue(QStringLiteral("relay/username"), username);
|
m_settings.setValue(QStringLiteral("relay/username"), username);
|
||||||
|
|
||||||
setStatus(Connecting, QStringLiteral("Logging in…"));
|
setStatus(Connecting, QStringLiteral("Logging in..."));
|
||||||
m_relay.login(QUrl(url), username, password);
|
m_relay.login(QUrl(url), username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── disconnectRelay ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::disconnectRelay() {
|
void DragonRelayBackend::disconnectRelay() {
|
||||||
stopHostPoll();
|
stopHostPoll();
|
||||||
m_tunnel.stop(); // triggers onTunnelDown asynchronously
|
m_tunnel.stop(); // triggers onTunnelDown asynchronously
|
||||||
|
|
@ -97,97 +90,53 @@ void DragonRelayBackend::disconnectRelay() {
|
||||||
setStatus(Disconnected, QStringLiteral("Disconnected"));
|
setStatus(Disconnected, QStringLiteral("Disconnected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── streamHost ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::streamHost(const QString &ip, const QString &app, int displayIndex) {
|
void DragonRelayBackend::streamHost(const QString &ip, const QString &app, int displayIndex) {
|
||||||
if (m_status != Ready && m_status != TunnelUp) {
|
if (m_status != Ready && m_status != TunnelUp) {
|
||||||
qWarning() << "DragonRelayBackend::streamHost called while not ready";
|
qWarning() << "DragonRelayBackend::streamHost called while not ready";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Delegate to moonlight-qt's existing stream launch mechanism.
|
|
||||||
// The standard Moonlight PC model uses ComputerManager; we invoke it
|
|
||||||
// via a QProcess for now so we don't have to patch PC discovery internals.
|
|
||||||
|
|
||||||
qDebug() << "DragonRelayBackend::streamHost called with ip=" << ip
|
|
||||||
<< "app=" << app << "displayIndex=" << displayIndex;
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
QStringList args{
|
|
||||||
QStringLiteral("stream"),
|
|
||||||
ip,
|
|
||||||
app
|
|
||||||
};
|
|
||||||
if (displayIndex > 0) {
|
|
||||||
// --display <N>: moonlight-qt CLI display selection flag (0 = primary)
|
|
||||||
// Verify with: moonlight --help
|
|
||||||
args << QStringLiteral("--display") << QString::number(displayIndex);
|
|
||||||
}
|
|
||||||
QProcess::startDetached(QStringLiteral("moonlight"), args);
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
QStringList args{
|
|
||||||
QStringLiteral("stream"),
|
|
||||||
ip,
|
|
||||||
app
|
|
||||||
};
|
|
||||||
if (displayIndex > 0) {
|
|
||||||
// --display <N>: moonlight-qt CLI display selection flag (0 = primary)
|
|
||||||
// Verify with: moonlight --help
|
|
||||||
args << QStringLiteral("--display") << QString::number(displayIndex);
|
|
||||||
}
|
|
||||||
QProcess::startDetached(QStringLiteral("moonlight"), args);
|
|
||||||
#else
|
|
||||||
QStringList args{
|
|
||||||
QStringLiteral("stream"),
|
|
||||||
ip,
|
|
||||||
app
|
|
||||||
};
|
|
||||||
if (displayIndex > 0) {
|
|
||||||
// --display <N>: moonlight-qt CLI display selection flag (0 = primary)
|
|
||||||
// Verify with: moonlight --help
|
|
||||||
args << QStringLiteral("--display") << QString::number(displayIndex);
|
|
||||||
}
|
|
||||||
QProcess::startDetached(QStringLiteral("moonlight"), args);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── displaysForHost ───────────────────────────────────────────────────────────
|
qDebug() << "DragonRelayBackend::streamHost ip=" << ip
|
||||||
|
<< "app=" << app << "displayIndex=" << displayIndex;
|
||||||
|
|
||||||
|
QStringList args;
|
||||||
|
args << QStringLiteral("stream") << ip << app;
|
||||||
|
if (displayIndex > 0) {
|
||||||
|
args << QStringLiteral("--display") << QString::number(displayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess::startDetached(QStringLiteral("moonlight"), args);
|
||||||
|
}
|
||||||
|
|
||||||
QVariantList DragonRelayBackend::displaysForHost(const QString &hostIP) const {
|
QVariantList DragonRelayBackend::displaysForHost(const QString &hostIP) const {
|
||||||
return m_hostDisplays.value(hostIP);
|
return m_hostDisplays.value(hostIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── streamHostDisplay ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::streamHostDisplay(const QString &hostIP, int displayIndex) {
|
void DragonRelayBackend::streamHostDisplay(const QString &hostIP, int displayIndex) {
|
||||||
qDebug() << "Streaming host" << hostIP << "display index" << displayIndex;
|
qDebug() << "Streaming host" << hostIP << "display index" << displayIndex;
|
||||||
|
|
||||||
// Add displayIndex validation near the top of streamHost()
|
|
||||||
const QVariantList displays = displaysForHost(hostIP);
|
const QVariantList displays = displaysForHost(hostIP);
|
||||||
if (displayIndex < 0 || (!displays.isEmpty() && displayIndex >= displays.size())) {
|
if (displayIndex < 0 || (!displays.isEmpty() && displayIndex >= displays.size())) {
|
||||||
qWarning() << "DragonRelayBackend: displayIndex" << displayIndex
|
qWarning() << "DragonRelayBackend: displayIndex" << displayIndex
|
||||||
<< "out of bounds for host" << hostIP << "(has" << displays.size() << "displays)";
|
<< "out of bounds for host" << hostIP
|
||||||
|
<< "(has" << displays.size() << "displays)";
|
||||||
displayIndex = 0; // fall back to primary
|
displayIndex = 0; // fall back to primary
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass displayIndex to streamHost
|
|
||||||
streamHost(hostIP, QStringLiteral("Desktop"), displayIndex);
|
streamHost(hostIP, QStringLiteral("Desktop"), displayIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── refreshHosts ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::refreshHosts() {
|
void DragonRelayBackend::refreshHosts() {
|
||||||
if (m_status == TunnelUp || m_status == Ready)
|
if (m_status == TunnelUp || m_status == Ready)
|
||||||
m_relay.fetchHosts();
|
m_relay.fetchHosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── pollHosts ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::pollHosts() {
|
void DragonRelayBackend::pollHosts() {
|
||||||
m_relay.fetchHosts();
|
m_relay.fetchHosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DragonRelayBackend::startHostPoll() {
|
void DragonRelayBackend::startHostPoll() {
|
||||||
m_relay.fetchHosts(); // immediate fetch
|
m_relay.fetchHosts();
|
||||||
m_hostPollTimer.start();
|
m_hostPollTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,30 +144,30 @@ void DragonRelayBackend::stopHostPoll() {
|
||||||
m_hostPollTimer.stop();
|
m_hostPollTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── RelayClient slots ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::onLoginDone(bool ok, const QString &err) {
|
void DragonRelayBackend::onLoginDone(bool ok, const QString &err) {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
setStatus(Error, QStringLiteral("Login failed: ") + err);
|
setStatus(Error, QStringLiteral("Login failed: ") + err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setStatus(Connecting, QStringLiteral("Provisioning VPN peer…"));
|
setStatus(Connecting, QStringLiteral("Provisioning VPN peer..."));
|
||||||
m_relay.provisionVPN();
|
m_relay.provisionVPN();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DragonRelayBackend::onVPNPeerProvisioned(const RelayVPNConf &conf) {
|
void DragonRelayBackend::onVPNPeerProvisioned(const RelayVPNConf &conf) {
|
||||||
setStatus(Connecting, QStringLiteral("Starting WireGuard tunnel…"));
|
setStatus(Connecting, QStringLiteral("Starting WireGuard tunnel..."));
|
||||||
|
|
||||||
WireGuardConfig cfg;
|
WireGuardConfig cfg = WireGuardConfig::fromConf(conf.conf);
|
||||||
QString parseErr;
|
if (!cfg.isValid()) {
|
||||||
if (!WireGuardConfig::fromConf(conf.conf, cfg, parseErr)) {
|
setStatus(Error, QStringLiteral(
|
||||||
setStatus(Error, QStringLiteral("Bad VPN config: ") + parseErr);
|
"Bad VPN config: missing PrivateKey, Address, PublicKey, or Endpoint"));
|
||||||
m_relay.revokeVPN();
|
m_relay.revokeVPN();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tunnel.start(cfg);
|
if (!m_tunnel.start(cfg)) {
|
||||||
// onTunnelUp/onTunnelError fired from TunnelManager
|
// tunnel.start() emits tunnelError() synchronously on failure,
|
||||||
|
// which routes through onTunnelError().
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DragonRelayBackend::onVPNError(const QString &err) {
|
void DragonRelayBackend::onVPNError(const QString &err) {
|
||||||
|
|
@ -228,7 +177,7 @@ void DragonRelayBackend::onVPNError(const QString &err) {
|
||||||
void DragonRelayBackend::onHostsFetched(const QList<RelayHost> &hosts) {
|
void DragonRelayBackend::onHostsFetched(const QList<RelayHost> &hosts) {
|
||||||
m_hosts.clear();
|
m_hosts.clear();
|
||||||
m_hostDisplays.clear();
|
m_hostDisplays.clear();
|
||||||
|
|
||||||
for (const auto &h : hosts) {
|
for (const auto &h : hosts) {
|
||||||
QVariantMap m;
|
QVariantMap m;
|
||||||
m[QStringLiteral("name")] = h.name;
|
m[QStringLiteral("name")] = h.name;
|
||||||
|
|
@ -237,8 +186,7 @@ void DragonRelayBackend::onHostsFetched(const QList<RelayHost> &hosts) {
|
||||||
m[QStringLiteral("online")] = true;
|
m[QStringLiteral("online")] = true;
|
||||||
m[QStringLiteral("source")] = QStringLiteral("relay");
|
m[QStringLiteral("source")] = QStringLiteral("relay");
|
||||||
m_hosts.append(m);
|
m_hosts.append(m);
|
||||||
|
|
||||||
// Store displays for this host
|
|
||||||
m_hostDisplays[h.ip] = h.displays;
|
m_hostDisplays[h.ip] = h.displays;
|
||||||
}
|
}
|
||||||
emit hostsChanged();
|
emit hostsChanged();
|
||||||
|
|
@ -246,23 +194,25 @@ void DragonRelayBackend::onHostsFetched(const QList<RelayHost> &hosts) {
|
||||||
Status next = m_hosts.isEmpty() ? TunnelUp : Ready;
|
Status next = m_hosts.isEmpty() ? TunnelUp : Ready;
|
||||||
if (m_status != next) {
|
if (m_status != next) {
|
||||||
QString text = m_hosts.isEmpty()
|
QString text = m_hosts.isEmpty()
|
||||||
? QStringLiteral("Tunnel up — no hosts yet")
|
? QStringLiteral("Tunnel up - no hosts yet")
|
||||||
: QStringLiteral("Connected — %1 host(s)").arg(m_hosts.size());
|
: QStringLiteral("Connected - %1 host(s)").arg(m_hosts.size());
|
||||||
setStatus(next, text);
|
setStatus(next, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DragonRelayBackend::onHostsError(const QString &err) {
|
void DragonRelayBackend::onHostsError(const QString &err) {
|
||||||
qWarning() << "DragonRelayBackend: hosts fetch error:" << err;
|
qWarning() << "DragonRelayBackend: hosts fetch error:" << err;
|
||||||
// Don't downgrade to Error state — the tunnel is still up; just log it
|
// Don't downgrade to Error state - tunnel is still up.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── TunnelManager slots ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void DragonRelayBackend::onTunnelUp(const QString &localIP) {
|
void DragonRelayBackend::onTunnelUp(const QString &localIP) {
|
||||||
|
if (m_tunnelIP == localIP && (m_status == TunnelUp || m_status == Ready))
|
||||||
|
return; // ignore duplicate signals (first-packet path + 1.5 s fallback timer)
|
||||||
|
|
||||||
m_tunnelIP = localIP;
|
m_tunnelIP = localIP;
|
||||||
emit tunnelIPChanged();
|
emit tunnelIPChanged();
|
||||||
setStatus(TunnelUp, QStringLiteral("Tunnel up (%1) — fetching hosts…").arg(localIP));
|
setStatus(TunnelUp,
|
||||||
|
QStringLiteral("Tunnel up (%1) - fetching hosts...").arg(localIP));
|
||||||
startHostPoll();
|
startHostPoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue