diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 35b0b0fa..80d8683d 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1971,6 +1971,7 @@ void GuiApplication::Tick() { devices_.UpdateInteractions(); ShareLocalCursorState(); + CloseRemoteClosedTabs(); UpdateLocalization(); SyncMainWindow(); SyncConnectionDialog(); @@ -2761,6 +2762,7 @@ void GuiApplication::SyncStreamWindow() { std::shared_lock lock(remote_sessions_mutex_); has_sessions = !remote_sessions_.empty(); } + if (!has_sessions) need_to_create_stream_window_ = false; if (need_to_create_stream_window_ && !ui_->stream) { ui_->stream.emplace(ui::StreamWindow::create()); #if defined(__linux__) @@ -3688,6 +3690,21 @@ void GuiApplication::ReorderStreamTab(int from, float drop_x, float tab_width) { SelectStreamTab(target); } +void GuiApplication::CloseRemoteClosedTabs() { + std::vector closed; + { + std::shared_lock lock(remote_sessions_mutex_); + for (const auto& [id, props] : remote_sessions_) { + if (props && props->remote_close_pending_.exchange(false)) { + closed.push_back(id); + } + } + } + // CloseRemoteSession drains transport callbacks and takes the session map + // lock. Do this on the UI thread, outside both the callback and map lock. + for (const auto& id : closed) CloseStreamTab(id); +} + void GuiApplication::CloseStreamTab(const std::string& remote_id) { std::shared_ptr props; { diff --git a/apps/desktop/src/gui/application/gui_application.h b/apps/desktop/src/gui/application/gui_application.h index c1dc9075..47f66f6c 100644 --- a/apps/desktop/src/gui/application/gui_application.h +++ b/apps/desktop/src/gui/application/gui_application.h @@ -87,6 +87,7 @@ private: void SelectStreamTab(int index); void ReorderStreamTab(int from, float drop_x, float tab_width); void CloseStreamTab(const std::string &remote_id); + void CloseRemoteClosedTabs(); void SendPointerInput(int button, int kind, float x, float y); void SendScrollInput(float delta_x, float delta_y, float x, float y); void SendKeyInput(const std::string &text, bool pressed, bool control, diff --git a/apps/desktop/src/gui/runtime/connection_runtime.cpp b/apps/desktop/src/gui/runtime/connection_runtime.cpp index 37b47f57..85b6d172 100644 --- a/apps/desktop/src/gui/runtime/connection_runtime.cpp +++ b/apps/desktop/src/gui/runtime/connection_runtime.cpp @@ -308,7 +308,13 @@ void GuiRuntime::CloseServerController(const std::string& remote_id) { if (!peer_ || remote_id.empty() || controller_cleanup_tasks_.count(remote_id)) return; controller_cleanup_tasks_[remote_id] = session_cleanup_queue_.PostTask( - [peer = peer_, remote_id] { LeaveConnection(peer, remote_id.c_str()); }); + [peer = peer_, transmission_id = std::string(client_id_), remote_id] { + if (DisconnectPeerConnection(peer, transmission_id.c_str(), + remote_id.c_str()) != 0) { + LOG_WARN("[{}] Could not queue controller disconnect notification", + remote_id); + } + }); } void GuiRuntime::CloseConnectionPeer() { diff --git a/apps/desktop/src/gui/runtime/peer_event_handler.cpp b/apps/desktop/src/gui/runtime/peer_event_handler.cpp index f27915dd..7b9fd044 100644 --- a/apps/desktop/src/gui/runtime/peer_event_handler.cpp +++ b/apps/desktop/src/gui/runtime/peer_event_handler.cpp @@ -379,6 +379,10 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status, runtime->focus_on_stream_window_ = false; + if (status == ConnectionStatus::Closed) { + props->remote_close_pending_.store(true, std::memory_order_release); + } + break; } case ConnectionStatus::IncorrectPassword: { diff --git a/apps/desktop/src/gui/runtime/remote_session.h b/apps/desktop/src/gui/runtime/remote_session.h index c720f281..f9bccb84 100644 --- a/apps/desktop/src/gui/runtime/remote_session.h +++ b/apps/desktop/src/gui/runtime/remote_session.h @@ -73,6 +73,8 @@ struct RemoteSession { PeerPtr* peer_ = nullptr; std::shared_ptr peer_events_; std::atomic closing_{false}; + // Transport callbacks request tab closure; only the UI thread destroys it. + std::atomic remote_close_pending_{false}; std::string audio_label_ = kAudioStream; std::string data_label_ = kDataStream; std::string mouse_label_ = kMouseStream; diff --git a/deps/submodules/minirtc b/deps/submodules/minirtc index 61778b1f..d3bac046 160000 --- a/deps/submodules/minirtc +++ b/deps/submodules/minirtc @@ -1 +1 @@ -Subproject commit 61778b1f395cb87b570316185587bbf2d444b669 +Subproject commit d3bac046d116166b51c72cdc586e10b0d0d80d0f