From 52ca25398088514ff3b2d29b3b8aa5cdf584c252 Mon Sep 17 00:00:00 2001 From: lamagu <3057978296@qq.com> Date: Fri, 9 Oct 2026 22:11:34 +0800 Subject: [PATCH 1/3] [fix] delay signaling offline indication during reconnects --- .../src/gui/application/gui_application.cpp | 20 ++++++++++++++++++- .../src/gui/application/gui_application.h | 5 +++++ deps/submodules/minirtc | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index b8d989db..1a457ed3 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -74,6 +74,10 @@ namespace { using namespace std::chrono_literals; +// Median reconnect to the signal server lands around 7 s on a lossy path, so a +// drop shorter than this is almost always already being recovered from. +constexpr auto kSignalOfflineGrace = 10s; + std::optional GenerateRandomPassword(std::string_view current) { constexpr std::string_view alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -2471,7 +2475,21 @@ void GuiApplication::SyncMainWindow() { std::lock_guard lock(password_change_mutex_); ui_->main->set_password_change_pending(password_change_pending_); } - ui_->main->set_signal_connected(signal_connected_); + const bool signal_online = signal_connected_.load(); + if (signal_online) { + signal_lost_at_valid_ = false; + } else if (!signal_lost_at_valid_) { + signal_lost_at_ = std::chrono::steady_clock::now(); + signal_lost_at_valid_ = true; + } + // Reconnects routinely take several seconds on a lossy path; only report + // offline once the connection has stayed down long enough to be a real + // outage rather than a blip the reconnect is already recovering from. + const bool signal_offline = + !signal_online && signal_lost_at_valid_ && + std::chrono::steady_clock::now() - signal_lost_at_ > + kSignalOfflineGrace; + ui_->main->set_signal_connected(!signal_offline); ui_->main->set_signal_tls_error(signal_status_ == SignalStatus::SignalTlsCertError); ui_->main->set_update_available(update_available_); diff --git a/apps/desktop/src/gui/application/gui_application.h b/apps/desktop/src/gui/application/gui_application.h index 4a840c1d..29c6d18d 100644 --- a/apps/desktop/src/gui/application/gui_application.h +++ b/apps/desktop/src/gui/application/gui_application.h @@ -106,6 +106,11 @@ private: cursor_delivery_states_; uint32_t cursor_state_sequence_ = 0; std::chrono::steady_clock::time_point next_video_frame_time_{}; + // The signal server is reached over a lossy path that routinely drops for the + // few seconds a reconnect takes, so the main window holds its "connected" + // indication through a brief blip instead of flickering offline. + std::chrono::steady_clock::time_point signal_lost_at_{}; + bool signal_lost_at_valid_ = false; #if defined(__linux__) && !defined(__APPLE__) bool use_xwayland_gui_ = false; bool use_x11_custom_titlebar_ = false; diff --git a/deps/submodules/minirtc b/deps/submodules/minirtc index c44807de..23b5b02c 160000 --- a/deps/submodules/minirtc +++ b/deps/submodules/minirtc @@ -1 +1 @@ -Subproject commit c44807de578d3ec7f21b264ef9c8937ff34f4329 +Subproject commit 23b5b02cc8ae0f6345841295bbeaecf51d12e792 From 0e7d175631b23a458046553b168568548c28e2c4 Mon Sep 17 00:00:00 2001 From: lamagu <3057978296@qq.com> Date: Sat, 10 Oct 2026 13:00:51 +0800 Subject: [PATCH 2/3] [feat] add manual signaling reconnect action --- .../src/gui/application/gui_application.cpp | 6 ++++++ .../assets/localization/localization_data.h | 1 + .../src/gui/runtime/connection_runtime.cpp | 8 +++++++ apps/desktop/src/gui/runtime/gui_runtime.h | 1 + apps/desktop/src/gui/ui/main_window.slint | 21 +++++++++++++++++++ apps/desktop/src/gui/ui/ui_localization.h | 2 ++ 6 files changed, 39 insertions(+) diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 1a457ed3..f2f5c506 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1367,6 +1367,12 @@ void GuiApplication::BindMainCallbacks() { LOG_WARN("Copy local id failed: {}", SDL_GetError()); } }); + main->on_signal_reconnect_requested([this] { + LOG_INFO("Signal reconnect requested from the status bar"); + if (ForceSignalReconnect() != 0) { + LOG_WARN("Signal reconnect could not be started"); + } + }); main->on_toggle_password_visibility([this] { show_password_ = !show_password_; ui_->main->set_password_visible(show_password_); diff --git a/apps/desktop/src/gui/assets/localization/localization_data.h b/apps/desktop/src/gui/assets/localization/localization_data.h index e923cc5b..c70262cb 100644 --- a/apps/desktop/src/gui/assets/localization/localization_data.h +++ b/apps/desktop/src/gui/assets/localization/localization_data.h @@ -258,6 +258,7 @@ struct TranslationRow { X(signal_connected, u8"已连接服务器", "Connected", u8"Подключено к серверу") \ X(signal_disconnected, u8"未连接服务器", "Disconnected", \ u8"Нет подключения к серверу") \ + X(signal_reconnect, u8"重新连接", "Reconnect", u8"Переподключиться") \ X(signal_tls_cert_error, u8"证书验证失败,请重新安装自托管根证书", \ "Certificate verification failed. Reinstall the self-hosted root " \ "certificate.", \ diff --git a/apps/desktop/src/gui/runtime/connection_runtime.cpp b/apps/desktop/src/gui/runtime/connection_runtime.cpp index 3859f2f3..eb12ea4c 100644 --- a/apps/desktop/src/gui/runtime/connection_runtime.cpp +++ b/apps/desktop/src/gui/runtime/connection_runtime.cpp @@ -227,6 +227,14 @@ int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id, return ret; } +int GuiRuntime::ForceSignalReconnect() { + if (!peer_) { + LOG_WARN("Signal reconnect requested with no connection peer"); + return -1; + } + return ReconnectSignal(peer_); +} + void GuiRuntime::CloseRemoteSession(std::shared_ptr props) { if (!props || props->closing_.exchange(true)) return; if (props->peer_events_) props->peer_events_->Deactivate(); diff --git a/apps/desktop/src/gui/runtime/gui_runtime.h b/apps/desktop/src/gui/runtime/gui_runtime.h index 1e4e12c2..9452b488 100644 --- a/apps/desktop/src/gui/runtime/gui_runtime.h +++ b/apps/desktop/src/gui/runtime/gui_runtime.h @@ -54,6 +54,7 @@ class GuiRuntime : protected gui_detail::GuiState { bool remember_password, bool bypass_presence_check = false); int RequestSingleDevicePresence(const std::string& remote_id, const char* password, bool remember_password); + int ForceSignalReconnect(); void HandleRecentConnections(); void HandleConnectionStatusChange(); diff --git a/apps/desktop/src/gui/ui/main_window.slint b/apps/desktop/src/gui/ui/main_window.slint index 64b9769c..4fe4db7f 100644 --- a/apps/desktop/src/gui/ui/main_window.slint +++ b/apps/desktop/src/gui/ui/main_window.slint @@ -65,6 +65,7 @@ export global UiStrings { in-out property signal-connected: "Signal server connected"; in-out property signal-disconnected: "Signal server disconnected"; in-out property tls-error: "Signal server TLS certificate error"; + in-out property signal-reconnect: "Reconnect"; in-out property update-available: "A new version is available"; in-out property new-version: "New version"; in-out property check-for-updates: "Check for updates"; @@ -402,6 +403,7 @@ export component MainWindow inherits Window { callback browse-save-path; callback open-download; callback check-for-updates; + callback signal-reconnect-requested; callback connection-cancel; callback connection-acknowledge; callback connection-submit-password(string, bool); @@ -1222,6 +1224,25 @@ export component MainWindow inherits Window { font-size: ImGuiFontStyle.base; vertical-alignment: center; } + if !root.signal-connected && !root.signal-tls-error: Rectangle { + x: parent.width - self.width - 12px; + y: 3px; + height: 18px; + width: reconnect-label.preferred-width; + reconnect-label := Text { + text: UiStrings.signal-reconnect; + color: #2563eb; + font-size: ImGuiFontStyle.base; + horizontal-alignment: center; + vertical-alignment: center; + } + TouchArea { + mouse-cursor: pointer; + clicked => { + root.signal-reconnect-requested(); + } + } + } } } diff --git a/apps/desktop/src/gui/ui/ui_localization.h b/apps/desktop/src/gui/ui/ui_localization.h index 7001449a..e4754c65 100644 --- a/apps/desktop/src/gui/ui/ui_localization.h +++ b/apps/desktop/src/gui/ui/ui_localization.h @@ -110,6 +110,8 @@ inline int ApplyMainWindowStrings( strings.set_signal_disconnected( Text(localization::signal_disconnected[language])); strings.set_tls_error(Text(localization::signal_tls_cert_error[language])); + strings.set_signal_reconnect( + Text(localization::signal_reconnect[language])); strings.set_update_available( Text(localization::new_version_available[language])); strings.set_new_version(Text(localization::new_version[language])); From 05f6d3d90aa1860ab4f194e7f22a4492ef08e9aa Mon Sep 17 00:00:00 2001 From: dijunkun Date: Sun, 11 Oct 2026 15:29:26 +0800 Subject: [PATCH 3/3] [fix] show truthful signaling recovery state and pin safe retries --- .../src/gui/application/gui_application.cpp | 22 ++--------- .../src/gui/application/gui_application.h | 7 +--- .../application/signal_connection_indicator.h | 38 +++++++++++++++++++ .../assets/localization/localization_data.h | 2 + apps/desktop/src/gui/ui/main_window.slint | 5 ++- apps/desktop/src/gui/ui/ui_localization.h | 2 + deps/submodules/minirtc | 2 +- 7 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 apps/desktop/src/gui/application/signal_connection_indicator.h diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index f2f5c506..393bc658 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -74,10 +74,6 @@ namespace { using namespace std::chrono_literals; -// Median reconnect to the signal server lands around 7 s on a lossy path, so a -// drop shorter than this is almost always already being recovered from. -constexpr auto kSignalOfflineGrace = 10s; - std::optional GenerateRandomPassword(std::string_view current) { constexpr std::string_view alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -1890,6 +1886,7 @@ void GuiApplication::Tick() { HandlePasswordChangeResult(); HandleCredentialRecovery(); if (!peer_) { + signal_connection_indicator_.Reset(); CreateConnectionPeer(); } #ifdef __linux__ @@ -2482,20 +2479,9 @@ void GuiApplication::SyncMainWindow() { ui_->main->set_password_change_pending(password_change_pending_); } const bool signal_online = signal_connected_.load(); - if (signal_online) { - signal_lost_at_valid_ = false; - } else if (!signal_lost_at_valid_) { - signal_lost_at_ = std::chrono::steady_clock::now(); - signal_lost_at_valid_ = true; - } - // Reconnects routinely take several seconds on a lossy path; only report - // offline once the connection has stayed down long enough to be a real - // outage rather than a blip the reconnect is already recovering from. - const bool signal_offline = - !signal_online && signal_lost_at_valid_ && - std::chrono::steady_clock::now() - signal_lost_at_ > - kSignalOfflineGrace; - ui_->main->set_signal_connected(!signal_offline); + ui_->main->set_signal_connected(signal_online); + ui_->main->set_signal_reconnecting( + signal_connection_indicator_.Update(signal_online)); ui_->main->set_signal_tls_error(signal_status_ == SignalStatus::SignalTlsCertError); ui_->main->set_update_available(update_available_); diff --git a/apps/desktop/src/gui/application/gui_application.h b/apps/desktop/src/gui/application/gui_application.h index 29c6d18d..31c2a771 100644 --- a/apps/desktop/src/gui/application/gui_application.h +++ b/apps/desktop/src/gui/application/gui_application.h @@ -14,6 +14,7 @@ #include +#include "application/signal_connection_indicator.h" #include "runtime/cursor_state_provider.h" #include "runtime/gui_runtime.h" @@ -106,11 +107,7 @@ private: cursor_delivery_states_; uint32_t cursor_state_sequence_ = 0; std::chrono::steady_clock::time_point next_video_frame_time_{}; - // The signal server is reached over a lossy path that routinely drops for the - // few seconds a reconnect takes, so the main window holds its "connected" - // indication through a brief blip instead of flickering offline. - std::chrono::steady_clock::time_point signal_lost_at_{}; - bool signal_lost_at_valid_ = false; + SignalConnectionIndicator signal_connection_indicator_; #if defined(__linux__) && !defined(__APPLE__) bool use_xwayland_gui_ = false; bool use_x11_custom_titlebar_ = false; diff --git a/apps/desktop/src/gui/application/signal_connection_indicator.h b/apps/desktop/src/gui/application/signal_connection_indicator.h new file mode 100644 index 00000000..cf78030d --- /dev/null +++ b/apps/desktop/src/gui/application/signal_connection_indicator.h @@ -0,0 +1,38 @@ +#ifndef CROSSDESK_SIGNAL_CONNECTION_INDICATOR_H_ +#define CROSSDESK_SIGNAL_CONNECTION_INDICATOR_H_ + +#include +#include + +namespace crossdesk { + +// Presentation state only. The actual connected flag remains authoritative +// for actions and other UI consumers, including the announcement inbox. +class SignalConnectionIndicator { + public: + using Clock = std::chrono::steady_clock; + + bool Update(bool connected, Clock::time_point now = Clock::now()) { + if (connected) { + disconnected_at_.reset(); + } else if (was_connected_) { + disconnected_at_ = now; + } + was_connected_ = connected; + return disconnected_at_ && + now - *disconnected_at_ < std::chrono::seconds(10); + } + + void Reset() { + was_connected_ = false; + disconnected_at_.reset(); + } + + private: + bool was_connected_ = false; + std::optional disconnected_at_; +}; + +} // namespace crossdesk + +#endif diff --git a/apps/desktop/src/gui/assets/localization/localization_data.h b/apps/desktop/src/gui/assets/localization/localization_data.h index c70262cb..52139d78 100644 --- a/apps/desktop/src/gui/assets/localization/localization_data.h +++ b/apps/desktop/src/gui/assets/localization/localization_data.h @@ -259,6 +259,8 @@ struct TranslationRow { X(signal_disconnected, u8"未连接服务器", "Disconnected", \ u8"Нет подключения к серверу") \ X(signal_reconnect, u8"重新连接", "Reconnect", u8"Переподключиться") \ + X(signal_reconnecting, u8"正在重新连接...", "Reconnecting...", \ + u8"Переподключение...") \ X(signal_tls_cert_error, u8"证书验证失败,请重新安装自托管根证书", \ "Certificate verification failed. Reinstall the self-hosted root " \ "certificate.", \ diff --git a/apps/desktop/src/gui/ui/main_window.slint b/apps/desktop/src/gui/ui/main_window.slint index 4fe4db7f..767c1f43 100644 --- a/apps/desktop/src/gui/ui/main_window.slint +++ b/apps/desktop/src/gui/ui/main_window.slint @@ -66,6 +66,7 @@ export global UiStrings { in-out property signal-disconnected: "Signal server disconnected"; in-out property tls-error: "Signal server TLS certificate error"; in-out property signal-reconnect: "Reconnect"; + in-out property signal-reconnecting: "Reconnecting..."; in-out property update-available: "A new version is available"; in-out property new-version: "New version"; in-out property check-for-updates: "Check for updates"; @@ -306,6 +307,7 @@ export component MainWindow inherits Window { in property password-visible: true; in property password-change-pending: false; in property signal-connected: false; + in property signal-reconnecting: false; in property signal-tls-error: false; in property <[RecentConnection]> recent-connections; in property <[AnnouncementItem]> announcements; @@ -1211,7 +1213,7 @@ export component MainWindow inherits Window { width: 8px; height: 8px; border-radius: 4px; - background: root.signal-tls-error ? #f59e0b : #ef4444; + background: root.signal-tls-error || root.signal-reconnecting ? #f59e0b : #ef4444; } Text { x: 28px; @@ -1219,6 +1221,7 @@ export component MainWindow inherits Window { height: 18px; text: root.signal-tls-error ? UiStrings.tls-error : root.signal-connected ? UiStrings.signal-connected + : root.signal-reconnecting ? UiStrings.signal-reconnecting : UiStrings.signal-disconnected; color: #343941; font-size: ImGuiFontStyle.base; diff --git a/apps/desktop/src/gui/ui/ui_localization.h b/apps/desktop/src/gui/ui/ui_localization.h index e4754c65..06c5426e 100644 --- a/apps/desktop/src/gui/ui/ui_localization.h +++ b/apps/desktop/src/gui/ui/ui_localization.h @@ -112,6 +112,8 @@ inline int ApplyMainWindowStrings( strings.set_tls_error(Text(localization::signal_tls_cert_error[language])); strings.set_signal_reconnect( Text(localization::signal_reconnect[language])); + strings.set_signal_reconnecting( + Text(localization::signal_reconnecting[language])); strings.set_update_available( Text(localization::new_version_available[language])); strings.set_new_version(Text(localization::new_version[language])); diff --git a/deps/submodules/minirtc b/deps/submodules/minirtc index 23b5b02c..bd664c33 160000 --- a/deps/submodules/minirtc +++ b/deps/submodules/minirtc @@ -1 +1 @@ -Subproject commit 23b5b02cc8ae0f6345841295bbeaecf51d12e792 +Subproject commit bd664c3356f5d937a4526f69fc27f793bee1d729