|
3 | 3 |
|
4 | 4 | #include "WifiConnection.h" |
5 | 5 |
|
6 | | -#include <QThread> |
7 | | - |
8 | 6 | namespace dish::net { |
9 | 7 |
|
| 8 | +namespace { |
| 9 | + |
| 10 | +QString controllerAckErrorMessage(std::uint8_t result) { |
| 11 | + // Matches the satellite/src/core/types.h codes verbatim. |
| 12 | + switch (result) { |
| 13 | + case 0x01: |
| 14 | + return QStringLiteral( |
| 15 | + "Server has no virtual gamepad backend — controller cannot be created"); |
| 16 | + case 0x02: |
| 17 | + return QStringLiteral("Server has no free controller slots"); |
| 18 | + case 0x03: |
| 19 | + return QStringLiteral("Controller already added on the server"); |
| 20 | + case 0x04: |
| 21 | + return QStringLiteral("Controller not found on the server"); |
| 22 | + case 0x05: |
| 23 | + return QStringLiteral("Server failed to plug in the virtual controller"); |
| 24 | + default: |
| 25 | + return QStringLiteral("Server rejected controller add (code %1)").arg(result); |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +} // namespace |
| 30 | + |
10 | 31 | WifiConnection::WifiConnection(QString id, models::DiscoveredServer server, QObject* parent) |
11 | 32 | : QObject(parent), id_(std::move(id)), server_(std::move(server)) {} |
12 | 33 |
|
@@ -64,6 +85,8 @@ void WifiConnection::markDisconnected() { |
64 | 85 | aliveTimer_->deleteLater(); |
65 | 86 | aliveTimer_ = nullptr; |
66 | 87 | } |
| 88 | + if (ackPollTimer_ != nullptr) { ackPollTimer_->stop(); } |
| 89 | + controllerRegistering_ = false; |
67 | 90 | if (existing) { |
68 | 91 | existing->stopHeartbeat(); |
69 | 92 | existing->stopReceiveLoop(); |
@@ -96,20 +119,59 @@ void WifiConnection::detachSlot() { |
96 | 119 | void WifiConnection::registerController(int type) { |
97 | 120 | auto c = clientRef_.get(); |
98 | 121 | if (!c) { return; } |
| 122 | + pendingControllerType_ = type; |
99 | 123 | c->resetControllerAck(); |
100 | 124 | c->controllerAdd(kDefaultCtrlIndex, kDefaultCaps); |
101 | | - // Spin briefly waiting for the server's controller ACK; same shape as the |
102 | | - // Mac client. This blocks the calling (main) thread for up to ~2s in the |
103 | | - // worst case, but the satellite normally responds within a few ms. |
104 | | - for (int i = 0; i < kAckWaitAttempts && c->lastControllerAck() == -1; ++i) { |
105 | | - QThread::msleep(kAckWaitIntervalMs); |
| 125 | + ackPollCount_ = 0; |
| 126 | + controllerRegistering_ = true; |
| 127 | + if (ackPollTimer_ == nullptr) { |
| 128 | + ackPollTimer_ = new QTimer(this); |
| 129 | + ackPollTimer_->setInterval(kAckWaitIntervalMs); |
| 130 | + QObject::connect(ackPollTimer_, &QTimer::timeout, this, &WifiConnection::pollControllerAck); |
106 | 131 | } |
107 | | - if (c->lastControllerAck() != -1) { |
108 | | - c->sendControllerType(kDefaultCtrlIndex, type); |
| 132 | + ackPollTimer_->start(); |
| 133 | + emit changed(); |
| 134 | +} |
| 135 | + |
| 136 | +void WifiConnection::pollControllerAck() { |
| 137 | + auto c = clientRef_.get(); |
| 138 | + if (!c) { |
| 139 | + const auto slotId = boundSlotId_.value_or(QString()); |
| 140 | + finishRegistration(); |
| 141 | + emit errorOccurred(QStringLiteral("Connection dropped before controller acknowledgement")); |
| 142 | + if (!slotId.isEmpty()) { emit registrationFailed(slotId); } |
| 143 | + return; |
| 144 | + } |
| 145 | + const auto ack = c->lastControllerAck(); |
| 146 | + if (ack == -1) { |
| 147 | + if (++ackPollCount_ >= kAckWaitAttempts) { |
| 148 | + const auto slotId = boundSlotId_.value_or(QString()); |
| 149 | + finishRegistration(); |
| 150 | + emit errorOccurred( |
| 151 | + QStringLiteral("Server did not acknowledge controller add (timeout)")); |
| 152 | + if (!slotId.isEmpty()) { emit registrationFailed(slotId); } |
| 153 | + } |
| 154 | + return; |
| 155 | + } |
| 156 | + const std::uint8_t result = static_cast<std::uint8_t>(ack & 0xFF); |
| 157 | + if (result == 0x00 /* ACK_OK */) { |
| 158 | + c->sendControllerType(kDefaultCtrlIndex, pendingControllerType_); |
109 | 159 | controllerAdded_ = true; |
| 160 | + finishRegistration(); |
| 161 | + } else { |
| 162 | + const auto slotId = boundSlotId_.value_or(QString()); |
| 163 | + finishRegistration(); |
| 164 | + emit errorOccurred(controllerAckErrorMessage(result)); |
| 165 | + if (!slotId.isEmpty()) { emit registrationFailed(slotId); } |
110 | 166 | } |
111 | 167 | } |
112 | 168 |
|
| 169 | +void WifiConnection::finishRegistration() { |
| 170 | + if (ackPollTimer_ != nullptr) { ackPollTimer_->stop(); } |
| 171 | + controllerRegistering_ = false; |
| 172 | + emit changed(); |
| 173 | +} |
| 174 | + |
113 | 175 | void WifiConnection::sendReport(std::uint16_t buttons, std::uint8_t lt, std::uint8_t rt, |
114 | 176 | std::int16_t lx, std::int16_t ly, std::int16_t rx, |
115 | 177 | std::int16_t ry) { |
|
0 commit comments