mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-09 07:40:17 +08:00
[fix] fix stuck connecting state when remote device is offline, refs #57
This commit is contained in:
@@ -249,6 +249,11 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
|
|||||||
// std::shared_lock read_lock(client_properties_mutex_);
|
// std::shared_lock read_lock(client_properties_mutex_);
|
||||||
auto props = client_properties_[remote_id];
|
auto props = client_properties_[remote_id];
|
||||||
if (!props->connection_established_) {
|
if (!props->connection_established_) {
|
||||||
|
props->connection_status_.store(ConnectionStatus::Connecting);
|
||||||
|
props->connection_attempt_active_.store(true);
|
||||||
|
props->connection_attempt_started_at_ = std::chrono::steady_clock::now();
|
||||||
|
show_connection_status_window_ = true;
|
||||||
|
|
||||||
props->remember_password_ = remember_password;
|
props->remember_password_ = remember_password;
|
||||||
if (strcmp(password, "") != 0 &&
|
if (strcmp(password, "") != 0 &&
|
||||||
strcmp(password, props->remote_password_) != 0) {
|
strcmp(password, props->remote_password_) != 0) {
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ namespace crossdesk {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint64_t kCaptureResumeKeyFrameGapMs = 500;
|
constexpr uint64_t kCaptureResumeKeyFrameGapMs = 500;
|
||||||
|
constexpr auto kPresenceProbeTimeout = std::chrono::seconds(5);
|
||||||
|
constexpr auto kConnectionAttemptTimeout = std::chrono::seconds(20);
|
||||||
|
|
||||||
|
bool IsConnectionAttemptPending(ConnectionStatus status) {
|
||||||
|
return status == ConnectionStatus::Connecting ||
|
||||||
|
status == ConnectionStatus::Gathering;
|
||||||
|
}
|
||||||
|
|
||||||
const ImWchar* GetMultilingualGlyphRanges() {
|
const ImWchar* GetMultilingualGlyphRanges() {
|
||||||
static std::vector<ImWchar> glyph_ranges;
|
static std::vector<ImWchar> glyph_ranges;
|
||||||
@@ -2078,6 +2085,7 @@ void Render::MainLoop() {
|
|||||||
HandleRecentConnections();
|
HandleRecentConnections();
|
||||||
HandleConnectionStatusChange();
|
HandleConnectionStatusChange();
|
||||||
HandlePendingPresenceProbe();
|
HandlePendingPresenceProbe();
|
||||||
|
HandleConnectionTimeouts();
|
||||||
HandleStreamWindow();
|
HandleStreamWindow();
|
||||||
HandleServerWindow();
|
HandleServerWindow();
|
||||||
HandleWindowsServiceIntegration();
|
HandleWindowsServiceIntegration();
|
||||||
@@ -2398,6 +2406,72 @@ void Render::HandlePendingPresenceProbe() {
|
|||||||
show_offline_warning_window_ = true;
|
show_offline_warning_window_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render::HandleConnectionTimeouts() {
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
bool presence_probe_timed_out = false;
|
||||||
|
std::string presence_remote_id;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(pending_presence_probe_mutex_);
|
||||||
|
if (pending_presence_probe_ && !pending_presence_result_ready_ &&
|
||||||
|
now - pending_presence_probe_started_at_ >= kPresenceProbeTimeout) {
|
||||||
|
presence_probe_timed_out = true;
|
||||||
|
presence_remote_id = pending_presence_remote_id_;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presence_probe_timed_out) {
|
||||||
|
offline_warning_text_ =
|
||||||
|
localization::device_offline[localization_language_index_];
|
||||||
|
show_offline_warning_window_ = true;
|
||||||
|
LOG_WARN("Presence probe timed out for [{}]", presence_remote_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rejoin_state_changed = false;
|
||||||
|
for (auto& [_, props] : client_properties_) {
|
||||||
|
if (!props || !props->connection_attempt_active_.load()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConnectionStatus status = props->connection_status_.load();
|
||||||
|
if (!IsConnectionAttemptPending(status)) {
|
||||||
|
props->connection_attempt_active_.store(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - props->connection_attempt_started_at_ <
|
||||||
|
kConnectionAttemptTimeout) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN("Connection to [{}] timed out, status={}", props->remote_id_,
|
||||||
|
static_cast<int>(status));
|
||||||
|
props->connection_attempt_active_.store(false);
|
||||||
|
props->connection_established_ = false;
|
||||||
|
props->rejoin_ = false;
|
||||||
|
props->connection_status_.store(ConnectionStatus::Failed);
|
||||||
|
focused_remote_id_ = props->remote_id_;
|
||||||
|
show_connection_status_window_ = true;
|
||||||
|
rejoin_state_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rejoin_state_changed) {
|
||||||
|
need_to_rejoin_ = false;
|
||||||
|
for (const auto& [_, props] : client_properties_) {
|
||||||
|
if (props && props->rejoin_) {
|
||||||
|
need_to_rejoin_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Render::RequestSingleDevicePresence(const std::string& remote_id,
|
int Render::RequestSingleDevicePresence(const std::string& remote_id,
|
||||||
const char* password,
|
const char* password,
|
||||||
bool remember_password) {
|
bool remember_password) {
|
||||||
@@ -2410,6 +2484,7 @@ int Render::RequestSingleDevicePresence(const std::string& remote_id,
|
|||||||
pending_presence_probe_ = true;
|
pending_presence_probe_ = true;
|
||||||
pending_presence_result_ready_ = false;
|
pending_presence_result_ready_ = false;
|
||||||
pending_presence_online_ = false;
|
pending_presence_online_ = false;
|
||||||
|
pending_presence_probe_started_at_ = std::chrono::steady_clock::now();
|
||||||
pending_presence_remote_id_ = remote_id;
|
pending_presence_remote_id_ = remote_id;
|
||||||
pending_presence_password_ = password ? password : "";
|
pending_presence_password_ = password ? password : "";
|
||||||
pending_presence_remember_password_ = remember_password;
|
pending_presence_remember_password_ = remember_password;
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ class Render {
|
|||||||
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
||||||
bool connection_established_ = false;
|
bool connection_established_ = false;
|
||||||
bool rejoin_ = false;
|
bool rejoin_ = false;
|
||||||
|
std::atomic<bool> connection_attempt_active_ = false;
|
||||||
|
std::chrono::steady_clock::time_point connection_attempt_started_at_;
|
||||||
bool net_traffic_stats_button_pressed_ = false;
|
bool net_traffic_stats_button_pressed_ = false;
|
||||||
bool enable_mouse_control_ = true;
|
bool enable_mouse_control_ = true;
|
||||||
bool mouse_controller_is_started_ = false;
|
bool mouse_controller_is_started_ = false;
|
||||||
@@ -220,6 +222,7 @@ class Render {
|
|||||||
void HandleRecentConnections();
|
void HandleRecentConnections();
|
||||||
void HandleConnectionStatusChange();
|
void HandleConnectionStatusChange();
|
||||||
void HandlePendingPresenceProbe();
|
void HandlePendingPresenceProbe();
|
||||||
|
void HandleConnectionTimeouts();
|
||||||
void HandleStreamWindow();
|
void HandleStreamWindow();
|
||||||
void HandleServerWindow();
|
void HandleServerWindow();
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
@@ -833,6 +836,7 @@ class Render {
|
|||||||
bool pending_presence_probe_ = false;
|
bool pending_presence_probe_ = false;
|
||||||
bool pending_presence_result_ready_ = false;
|
bool pending_presence_result_ready_ = false;
|
||||||
bool pending_presence_online_ = false;
|
bool pending_presence_online_ = false;
|
||||||
|
std::chrono::steady_clock::time_point pending_presence_probe_started_at_;
|
||||||
std::string pending_presence_remote_id_ = "";
|
std::string pending_presence_remote_id_ = "";
|
||||||
std::string pending_presence_password_ = "";
|
std::string pending_presence_password_ = "";
|
||||||
bool pending_presence_remember_password_ = false;
|
bool pending_presence_remember_password_ = false;
|
||||||
|
|||||||
@@ -1507,6 +1507,10 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
|||||||
render->is_client_mode_ = true;
|
render->is_client_mode_ = true;
|
||||||
render->show_connection_status_window_ = true;
|
render->show_connection_status_window_ = true;
|
||||||
props->connection_status_.store(status);
|
props->connection_status_.store(status);
|
||||||
|
if (status != ConnectionStatus::Connecting &&
|
||||||
|
status != ConnectionStatus::Gathering) {
|
||||||
|
props->connection_attempt_active_.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus::Connected: {
|
case ConnectionStatus::Connected: {
|
||||||
@@ -1608,7 +1612,8 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectionStatus::NoSuchTransmissionId: {
|
case ConnectionStatus::NoSuchTransmissionId:
|
||||||
|
case ConnectionStatus::RemoteUnavailable: {
|
||||||
if (render->connect_button_pressed_) {
|
if (render->connect_button_pressed_) {
|
||||||
props->connection_established_ = false;
|
props->connection_established_ = false;
|
||||||
render->connect_button_label_ =
|
render->connect_button_label_ =
|
||||||
|
|||||||
@@ -180,6 +180,19 @@ bool Render::ConnectionStatusWindow(
|
|||||||
DestroyPeer(&props->peer_);
|
DestroyPeer(&props->peer_);
|
||||||
ret_flag = true;
|
ret_flag = true;
|
||||||
}
|
}
|
||||||
|
} else if (ConnectionStatus::RemoteUnavailable == status) {
|
||||||
|
text = localization::device_offline[localization_language_index_];
|
||||||
|
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||||
|
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||||
|
// ok
|
||||||
|
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
|
show_connection_status_window_ = false;
|
||||||
|
re_enter_remote_id_ = true;
|
||||||
|
DestroyPeer(&props->peer_);
|
||||||
|
ret_flag = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||||
@@ -193,4 +206,4 @@ bool Render::ConnectionStatusWindow(
|
|||||||
|
|
||||||
return ret_flag;
|
return ret_flag;
|
||||||
}
|
}
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|||||||
+1
-1
Submodule submodules/minirtc updated: 0d9a9d6a32...9e5ce698df
@@ -0,0 +1,100 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::filesystem::path FindRepoRoot() {
|
||||||
|
std::filesystem::path current = std::filesystem::current_path();
|
||||||
|
while (!current.empty()) {
|
||||||
|
if (std::filesystem::exists(current / "xmake.lua") &&
|
||||||
|
std::filesystem::exists(current / "submodules/minirtc/src/api/minirtc.h")) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current.parent_path();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadFile(const std::filesystem::path& path) {
|
||||||
|
std::ifstream file(path, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << file.rdbuf();
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectContains(const char* name, const std::string& value,
|
||||||
|
const std::string& expected) {
|
||||||
|
if (value.find(expected) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << name << " missing expected text: " << expected << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectNotContains(const char* name, const std::string& value,
|
||||||
|
const std::string& unexpected) {
|
||||||
|
if (value.find(unexpected) == std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << name << " contains unexpected text: " << unexpected << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::filesystem::path repo_root = FindRepoRoot();
|
||||||
|
if (repo_root.empty()) {
|
||||||
|
std::cerr << "failed to locate repository root\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string minirtc_api =
|
||||||
|
ReadFile(repo_root / "submodules/minirtc/src/api/minirtc.h");
|
||||||
|
const std::string peer_connection =
|
||||||
|
ReadFile(repo_root / "submodules/minirtc/src/pc/peer_connection.cpp");
|
||||||
|
const std::string connection_status_window =
|
||||||
|
ReadFile(repo_root / "src/gui/windows/connection_status_window.cpp");
|
||||||
|
const std::string render_h = ReadFile(repo_root / "src/gui/render.h");
|
||||||
|
const std::string render_cpp = ReadFile(repo_root / "src/gui/render.cpp");
|
||||||
|
const std::string remote_peer_panel =
|
||||||
|
ReadFile(repo_root / "src/gui/panels/remote_peer_panel.cpp");
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
ok &= ExpectContains("minirtc.h", minirtc_api, "RemoteUnavailable");
|
||||||
|
ok &= ExpectNotContains("minirtc.h", minirtc_api, "DeviceOffline");
|
||||||
|
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||||
|
"\"Remote unavailable\"");
|
||||||
|
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||||
|
"ConnectionStatus::RemoteUnavailable");
|
||||||
|
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
||||||
|
"\"Device offline\"");
|
||||||
|
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
||||||
|
"ConnectionStatus::DeviceOffline");
|
||||||
|
ok &= ExpectContains("connection_status_window.cpp",
|
||||||
|
connection_status_window,
|
||||||
|
"localization::device_offline");
|
||||||
|
ok &= ExpectContains("render.h", render_h,
|
||||||
|
"pending_presence_probe_started_at_");
|
||||||
|
ok &= ExpectContains("render.h", render_h,
|
||||||
|
"connection_attempt_started_at_");
|
||||||
|
ok &= ExpectContains("render.cpp", render_cpp,
|
||||||
|
"HandleConnectionTimeouts");
|
||||||
|
ok &= ExpectContains("render.cpp", render_cpp,
|
||||||
|
"kPresenceProbeTimeout");
|
||||||
|
ok &= ExpectContains("render.cpp", render_cpp,
|
||||||
|
"kConnectionAttemptTimeout");
|
||||||
|
ok &= ExpectContains("remote_peer_panel.cpp", remote_peer_panel,
|
||||||
|
"connection_attempt_started_at_");
|
||||||
|
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -50,6 +50,11 @@ function setup_targets()
|
|||||||
add_includedirs("src/device_controller", "src/common")
|
add_includedirs("src/device_controller", "src/common")
|
||||||
add_files("tests/keyboard_state_protocol_test.cpp")
|
add_files("tests/keyboard_state_protocol_test.cpp")
|
||||||
|
|
||||||
|
target("connection_status_protocol_test")
|
||||||
|
set_kind("binary")
|
||||||
|
set_default(false)
|
||||||
|
add_files("tests/connection_status_protocol_test.cpp")
|
||||||
|
|
||||||
target("windows_manifest_resource_test")
|
target("windows_manifest_resource_test")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user