mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-20 11:06:46 +08:00
[fix] isolate background connections and password retries
This commit is contained in:
@@ -1867,13 +1867,7 @@ void GuiApplication::Tick() {
|
|||||||
|
|
||||||
if (need_to_rejoin_ && now - last_rejoin_check_time_ >= 1s) {
|
if (need_to_rejoin_ && now - last_rejoin_check_time_ >= 1s) {
|
||||||
last_rejoin_check_time_ = now;
|
last_rejoin_check_time_ = now;
|
||||||
need_to_rejoin_ = false;
|
HandlePendingRejoins();
|
||||||
for (const auto& [_, props] : remote_sessions_) {
|
|
||||||
if (props && props->rejoin_) {
|
|
||||||
ConnectTo(props->remote_id_, props->remote_password_,
|
|
||||||
props->remember_password_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool was_reloading_recent_connections = reload_recent_connections_;
|
const bool was_reloading_recent_connections = reload_recent_connections_;
|
||||||
@@ -3703,6 +3697,7 @@ void GuiApplication::CloseRemoteClosedTabs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::CloseStreamTab(const std::string& remote_id) {
|
void GuiApplication::CloseStreamTab(const std::string& remote_id) {
|
||||||
|
CancelPendingConnection(remote_id);
|
||||||
std::shared_ptr<RemoteSession> props;
|
std::shared_ptr<RemoteSession> props;
|
||||||
{
|
{
|
||||||
std::shared_lock lock(remote_sessions_mutex_);
|
std::shared_lock lock(remote_sessions_mutex_);
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ void GuiRuntime::HandleConnectionStatusChange() {
|
|||||||
}
|
}
|
||||||
last_presence_request_attempt_at_ = now;
|
last_presence_request_attempt_at_ = now;
|
||||||
const auto message =
|
const auto message =
|
||||||
BuildDevicePresenceRequest(client_id_, recent_connection_ids_, true).dump();
|
BuildDevicePresenceRequest(client_id_, recent_connection_ids_, true, {},
|
||||||
|
gallery_presence_ids_).dump();
|
||||||
if (SendSignalMessage(peer_, message.data(), message.size()) == 0) {
|
if (SendSignalMessage(peer_, message.data(), message.size()) == 0) {
|
||||||
next_presence_refresh_at_ = now + kPresenceRefreshInterval;
|
next_presence_refresh_at_ = now + kPresenceRefreshInterval;
|
||||||
} else {
|
} else {
|
||||||
@@ -59,6 +60,39 @@ void GuiRuntime::HandleConnectionStatusChange() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GuiRuntime::HandlePendingRejoins() {
|
||||||
|
need_to_rejoin_ = false;
|
||||||
|
std::vector<std::shared_ptr<RemoteSession>> pending;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(remote_sessions_mutex_);
|
||||||
|
for (const auto& [_, props] : remote_sessions_) {
|
||||||
|
if (props && props->rejoin_ && !props->closing_)
|
||||||
|
pending.push_back(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& props : pending) {
|
||||||
|
// Password submission already entered the validating state. Let Join
|
||||||
|
// report its result even when this device is absent from the presence
|
||||||
|
// cache (for example, a newly added DeskHub device). A failed presence
|
||||||
|
// probe has no auth callback and would leave the prompt validating forever.
|
||||||
|
ConnectTo(props->remote_id_, props->remote_password_,
|
||||||
|
props->remember_password_, true, props->wall_managed_.load());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiRuntime::CancelPendingConnection(const std::string& remote_id) {
|
||||||
|
pending_reconnects_.erase(remote_id);
|
||||||
|
if (auto props = FindRemoteSession(remote_id)) props->rejoin_ = false;
|
||||||
|
std::lock_guard lock(pending_presence_probe_mutex_);
|
||||||
|
if (pending_presence_remote_id_ != remote_id) return;
|
||||||
|
pending_presence_probe_ = false;
|
||||||
|
pending_presence_result_ready_ = false;
|
||||||
|
pending_presence_online_ = false;
|
||||||
|
pending_presence_remote_id_.clear();
|
||||||
|
pending_presence_password_.clear();
|
||||||
|
pending_presence_remember_password_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
void GuiRuntime::HandlePendingPresenceProbe() {
|
void GuiRuntime::HandlePendingPresenceProbe() {
|
||||||
bool has_action = false;
|
bool has_action = false;
|
||||||
bool should_connect = false;
|
bool should_connect = false;
|
||||||
@@ -211,7 +245,8 @@ int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto s = BuildDevicePresenceRequest(
|
const auto s = BuildDevicePresenceRequest(
|
||||||
client_id_, recent_connection_ids_, false, remote_id)
|
client_id_, recent_connection_ids_, false, remote_id,
|
||||||
|
gallery_presence_ids_)
|
||||||
.dump();
|
.dump();
|
||||||
|
|
||||||
int ret = SendSignalMessage(peer_, s.data(), s.size());
|
int ret = SendSignalMessage(peer_, s.data(), s.size());
|
||||||
@@ -418,7 +453,8 @@ void GuiRuntime::HandleSessionCleanup() {
|
|||||||
recent_connection_image_save_time_ = 0;
|
recent_connection_image_save_time_ = 0;
|
||||||
}
|
}
|
||||||
for (const auto& [id, request] : reconnects) {
|
for (const auto& [id, request] : reconnects) {
|
||||||
ConnectTo(id, request.password.c_str(), request.remember_password);
|
ConnectTo(id, request.password.c_str(), request.remember_password, false,
|
||||||
|
request.background);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,23 +523,28 @@ std::shared_ptr<GuiRuntime::RemoteSession> GuiRuntime::FindRemoteSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
||||||
bool remember_password, bool bypass_presence_check) {
|
bool remember_password, bool bypass_presence_check,
|
||||||
|
bool background) {
|
||||||
// Do not recreate a failed peer or send another probe during a server cooldown.
|
// Do not recreate a failed peer or send another probe during a server cooldown.
|
||||||
if (const auto current = FindRemoteSession(remote_id);
|
if (const auto current = FindRemoteSession(remote_id);
|
||||||
current && current->connection_auth_.Get().retry_after > 0) {
|
current && current->connection_auth_.Get().retry_after > 0) {
|
||||||
focused_remote_id_ = remote_id;
|
if (!background) focused_remote_id_ = remote_id;
|
||||||
show_connection_status_window_ = true;
|
if (!background) show_connection_status_window_ = true;
|
||||||
current->rejoin_ = false;
|
current->rejoin_ = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (session_cleanup_tasks_.count(remote_id)) {
|
if (session_cleanup_tasks_.count(remote_id)) {
|
||||||
pending_reconnects_[remote_id] = {password ? password : "", remember_password};
|
pending_reconnects_[remote_id] = {password ? password : "",
|
||||||
|
remember_password, background};
|
||||||
LOG_INFO("[{}] Reconnect queued until previous peer is destroyed", remote_id);
|
LOG_INFO("[{}] Reconnect queued until previous peer is destroyed", remote_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!bypass_presence_check && !device_presence_cache_.IsOnline(remote_id)) {
|
// Wall requests, including deferred reconnects, must not replace the single
|
||||||
int ret =
|
// interactive presence probe. Join reports availability for each session.
|
||||||
RequestSingleDevicePresence(remote_id, password, remember_password);
|
if (!background && !bypass_presence_check &&
|
||||||
|
!device_presence_cache_.IsOnline(remote_id)) {
|
||||||
|
int ret = RequestSingleDevicePresence(remote_id, password,
|
||||||
|
remember_password);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
offline_warning_text_ =
|
offline_warning_text_ =
|
||||||
localization::device_offline[localization_language_index_];
|
localization::device_offline[localization_language_index_];
|
||||||
@@ -516,7 +557,7 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Connect to [{}]", remote_id);
|
LOG_INFO("Connect to [{}]", remote_id);
|
||||||
focused_remote_id_ = remote_id;
|
if (!background) focused_remote_id_ = remote_id;
|
||||||
|
|
||||||
auto stale = FindRemoteSession(remote_id);
|
auto stale = FindRemoteSession(remote_id);
|
||||||
bool exists = stale != nullptr;
|
bool exists = stale != nullptr;
|
||||||
@@ -546,7 +587,7 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
pending_reconnects_[remote_id] = {
|
pending_reconnects_[remote_id] = {
|
||||||
keep_stored_password ? std::string(stale->remote_password_)
|
keep_stored_password ? std::string(stale->remote_password_)
|
||||||
: std::string(password),
|
: std::string(password),
|
||||||
remember_password};
|
remember_password, background};
|
||||||
CloseRemoteSession(stale);
|
CloseRemoteSession(stale);
|
||||||
LOG_INFO("[{}] Reconnecting after terminal peer status {}", remote_id,
|
LOG_INFO("[{}] Reconnecting after terminal peer status {}", remote_id,
|
||||||
static_cast<int>(status));
|
static_cast<int>(status));
|
||||||
@@ -566,6 +607,11 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
auto props = remote_sessions_[remote_id];
|
auto props = remote_sessions_[remote_id];
|
||||||
props->local_id_ = "C-" + std::string(client_id_);
|
props->local_id_ = "C-" + std::string(client_id_);
|
||||||
props->remote_id_ = remote_id;
|
props->remote_id_ = remote_id;
|
||||||
|
props->wall_managed_ = background;
|
||||||
|
props->tab_opened_ = !background;
|
||||||
|
props->control_mouse_ = !background;
|
||||||
|
props->enable_mouse_control_ = !background;
|
||||||
|
props->audio_capture_button_pressed_ = !background;
|
||||||
memcpy(&props->params_, ¶ms_, sizeof(Params));
|
memcpy(&props->params_, ¶ms_, sizeof(Params));
|
||||||
props->params_.user_id = props->local_id_.c_str();
|
props->params_.user_id = props->local_id_.c_str();
|
||||||
props->peer_events_ = std::make_shared<PeerEventHandler>(*this);
|
props->peer_events_ = std::make_shared<PeerEventHandler>(*this);
|
||||||
@@ -573,7 +619,7 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
props->peer_ = CreatePeer(&props->params_);
|
props->peer_ = CreatePeer(&props->params_);
|
||||||
|
|
||||||
props->connection_status_.store(ConnectionStatus::Connecting);
|
props->connection_status_.store(ConnectionStatus::Connecting);
|
||||||
show_connection_status_window_ = true;
|
if (!background) show_connection_status_window_ = true;
|
||||||
|
|
||||||
if (!props->peer_) {
|
if (!props->peer_) {
|
||||||
LOG_INFO("Create peer [{}] instance failed", props->local_id_);
|
LOG_INFO("Create peer [{}] instance failed", props->local_id_);
|
||||||
@@ -612,7 +658,7 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
auto props = FindRemoteSession(remote_id);
|
auto props = FindRemoteSession(remote_id);
|
||||||
if (!props || props->closing_) return -1;
|
if (!props || props->closing_) return -1;
|
||||||
if (!props->connection_established_) {
|
if (!props->connection_established_) {
|
||||||
show_connection_status_window_ = true;
|
if (!background) show_connection_status_window_ = true;
|
||||||
|
|
||||||
props->remember_password_ = remember_password;
|
props->remember_password_ = remember_password;
|
||||||
if (password && *password &&
|
if (password && *password &&
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -33,12 +34,19 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsOnline(const std::string &device_id,
|
std::optional<bool> GetOnline(const std::string &device_id,
|
||||||
Clock::time_point now = Clock::now()) const {
|
Clock::time_point now = Clock::now()) const {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
const auto it = cache_.find(device_id);
|
const auto it = cache_.find(device_id);
|
||||||
return signal_connected_ && it != cache_.end() && it->second.online &&
|
if (!signal_connected_ || it == cache_.end() ||
|
||||||
now - it->second.updated_at < kMaxAge;
|
now - it->second.updated_at >= kMaxAge)
|
||||||
|
return std::nullopt;
|
||||||
|
return it->second.online;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsOnline(const std::string &device_id,
|
||||||
|
Clock::time_point now = Clock::now()) const {
|
||||||
|
return GetOnline(device_id, now).value_or(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
@@ -58,4 +66,4 @@ private:
|
|||||||
|
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,22 +16,25 @@ namespace crossdesk {
|
|||||||
|
|
||||||
inline nlohmann::json BuildDevicePresenceRequest(
|
inline nlohmann::json BuildDevicePresenceRequest(
|
||||||
const std::string& client_id, const std::vector<std::string>& recent_ids,
|
const std::string& client_id, const std::vector<std::string>& recent_ids,
|
||||||
bool subscribe, const std::string& probe_id = {}) {
|
bool subscribe, const std::string& probe_id = {},
|
||||||
|
const std::vector<std::string>& additional_ids = {}) {
|
||||||
std::vector<std::string> devices;
|
std::vector<std::string> devices;
|
||||||
for (const auto& id : recent_ids) {
|
const auto append = [&](const std::string& id) {
|
||||||
// Recent connection keys contain Y/N plus display settings after the ID.
|
// Recent connection keys contain Y/N plus display settings after the ID.
|
||||||
const std::string pure_id = id.substr(0, id.find_first_of("YN"));
|
const std::string pure_id = id.substr(0, id.find_first_of("YN"));
|
||||||
if (!pure_id.empty() &&
|
if (!pure_id.empty() &&
|
||||||
std::find(devices.begin(), devices.end(), pure_id) == devices.end()) {
|
std::find(devices.begin(), devices.end(), pure_id) == devices.end()) {
|
||||||
devices.push_back(pure_id);
|
devices.push_back(pure_id);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
for (const auto& id : recent_ids) append(id);
|
||||||
|
for (const auto& id : additional_ids) append(id);
|
||||||
if (!probe_id.empty() &&
|
if (!probe_id.empty() &&
|
||||||
std::find(devices.begin(), devices.end(), probe_id) == devices.end()) {
|
std::find(devices.begin(), devices.end(), probe_id) == devices.end()) {
|
||||||
devices.push_back(probe_id);
|
devices.push_back(probe_id);
|
||||||
}
|
}
|
||||||
// Include recent devices even in a one-shot probe: older servers ignore
|
// Keep recent and wall devices even in a one-shot probe: older servers
|
||||||
// subscribe=false and replace their subscriptions with this device list.
|
// ignore subscribe=false and replace subscriptions with this device list.
|
||||||
return {{"type", "recent_connections_presence"},
|
return {{"type", "recent_connections_presence"},
|
||||||
{"user_id", client_id},
|
{"user_id", client_id},
|
||||||
{"devices", devices},
|
{"devices", devices},
|
||||||
@@ -40,4 +43,4 @@ inline nlohmann::json BuildDevicePresenceRequest(
|
|||||||
|
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -51,13 +51,16 @@ class GuiRuntime : protected gui_detail::GuiState {
|
|||||||
|
|
||||||
int CreateConnectionPeer();
|
int CreateConnectionPeer();
|
||||||
int ConnectTo(const std::string& remote_id, const char* password,
|
int ConnectTo(const std::string& remote_id, const char* password,
|
||||||
bool remember_password, bool bypass_presence_check = false);
|
bool remember_password, bool bypass_presence_check = false,
|
||||||
|
bool background = false);
|
||||||
int RequestSingleDevicePresence(const std::string& remote_id,
|
int RequestSingleDevicePresence(const std::string& remote_id,
|
||||||
const char* password, bool remember_password);
|
const char* password, bool remember_password);
|
||||||
int ForceSignalReconnect();
|
int ForceSignalReconnect();
|
||||||
|
|
||||||
void HandleRecentConnections();
|
void HandleRecentConnections();
|
||||||
void HandleConnectionStatusChange();
|
void HandleConnectionStatusChange();
|
||||||
|
void HandlePendingRejoins();
|
||||||
|
void CancelPendingConnection(const std::string& remote_id);
|
||||||
void HandlePendingPresenceProbe();
|
void HandlePendingPresenceProbe();
|
||||||
void HandlePresenceProbeTimeout();
|
void HandlePresenceProbeTimeout();
|
||||||
bool HasActiveSession();
|
bool HasActiveSession();
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
|||||||
|
|
||||||
if (props) {
|
if (props) {
|
||||||
runtime->is_client_mode_ = true;
|
runtime->is_client_mode_ = true;
|
||||||
runtime->show_connection_status_window_ = true;
|
if (!props->wall_managed_) runtime->show_connection_status_window_ = true;
|
||||||
props->connection_status_.store(status);
|
props->connection_status_.store(status);
|
||||||
if (status != ConnectionStatus::Connecting && status != ConnectionStatus::Gathering)
|
if (status != ConnectionStatus::Connecting && status != ConnectionStatus::Gathering)
|
||||||
props->connection_auth_.Complete();
|
props->connection_auth_.Complete();
|
||||||
@@ -304,13 +304,13 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
|||||||
FreeRemoteAction(remote_action);
|
FreeRemoteAction(remote_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!runtime->need_to_create_stream_window_) {
|
if (!props->wall_managed_ && !runtime->need_to_create_stream_window_) {
|
||||||
runtime->need_to_create_stream_window_ = true;
|
runtime->need_to_create_stream_window_ = true;
|
||||||
}
|
}
|
||||||
props->connection_established_ = true;
|
props->connection_established_ = true;
|
||||||
props->was_connected_ = true;
|
props->was_connected_ = true;
|
||||||
props->recent_connection_dirty_ = true;
|
props->recent_connection_dirty_ = true;
|
||||||
runtime->start_keyboard_capturer_ = true;
|
if (!props->wall_managed_) runtime->start_keyboard_capturer_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectionStatus::Disconnected:
|
case ConnectionStatus::Disconnected:
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ struct FileTransferState {
|
|||||||
// Runtime state for one connected or connecting remote endpoint. It groups the
|
// Runtime state for one connected or connecting remote endpoint. It groups the
|
||||||
// connection, media, input, window and transfer data that share one lifetime.
|
// connection, media, input, window and transfer data that share one lifetime.
|
||||||
struct RemoteSession {
|
struct RemoteSession {
|
||||||
|
// Wall membership, stream-window visibility and input ownership are separate.
|
||||||
|
std::atomic<bool> wall_managed_{false};
|
||||||
std::atomic<bool> preview_subscription_supported_{false};
|
std::atomic<bool> preview_subscription_supported_{false};
|
||||||
std::atomic<uint32_t> preview_ack_{0};
|
std::atomic<uint32_t> preview_ack_{0};
|
||||||
std::atomic<bool> preview_accepted_{false};
|
std::atomic<bool> preview_accepted_{false};
|
||||||
@@ -136,7 +138,7 @@ struct RemoteSession {
|
|||||||
std::shared_ptr<VideoLatencyStats> video_latency_ =
|
std::shared_ptr<VideoLatencyStats> video_latency_ =
|
||||||
std::make_shared<VideoLatencyStats>();
|
std::make_shared<VideoLatencyStats>();
|
||||||
bool tab_selected_ = false;
|
bool tab_selected_ = false;
|
||||||
bool tab_opened_ = true;
|
std::atomic<bool> tab_opened_{true};
|
||||||
std::string remote_host_name_;
|
std::string remote_host_name_;
|
||||||
// Host metadata and dismissal state belong to this connection and are
|
// Host metadata and dismissal state belong to this connection and are
|
||||||
// protected by remote_version_mutex_.
|
// protected by remote_version_mutex_.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ struct RecentConnectionsState {
|
|||||||
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
|
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
|
||||||
recent_connections_;
|
recent_connections_;
|
||||||
std::vector<std::string> recent_connection_ids_;
|
std::vector<std::string> recent_connection_ids_;
|
||||||
|
std::vector<std::string> gallery_presence_ids_;
|
||||||
int recent_connection_image_width_ = 160;
|
int recent_connection_image_width_ = 160;
|
||||||
int recent_connection_image_height_ = 90;
|
int recent_connection_image_height_ = 90;
|
||||||
uint32_t recent_connection_image_save_time_ = 0;
|
uint32_t recent_connection_image_save_time_ = 0;
|
||||||
@@ -192,6 +193,7 @@ struct ConnectionState {
|
|||||||
struct PendingReconnect {
|
struct PendingReconnect {
|
||||||
std::string password;
|
std::string password;
|
||||||
bool remember_password = false;
|
bool remember_password = false;
|
||||||
|
bool background = false;
|
||||||
};
|
};
|
||||||
std::unordered_map<std::string, PendingReconnect> pending_reconnects_;
|
std::unordered_map<std::string, PendingReconnect> pending_reconnects_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user