mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-29 03:08:42 +08:00
fix: close server window after controller disconnects
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#ifndef CROSSDESK_GUI_APPLICATION_STATE_H_
|
#ifndef CROSSDESK_GUI_APPLICATION_STATE_H_
|
||||||
#define CROSSDESK_GUI_APPLICATION_STATE_H_
|
#define CROSSDESK_GUI_APPLICATION_STATE_H_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -119,8 +120,11 @@ struct StreamWindowState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ServerWindowState {
|
struct ServerWindowState {
|
||||||
bool need_to_create_server_window_ = false;
|
// Transport callbacks run outside the UI thread. Keep the two lifecycle
|
||||||
bool need_to_destroy_server_window_ = false;
|
// requests atomic so a disconnect cannot be lost while the Slint timer is
|
||||||
|
// deciding whether to create or destroy the controlled-side window.
|
||||||
|
std::atomic<bool> need_to_create_server_window_{false};
|
||||||
|
std::atomic<bool> need_to_destroy_server_window_{false};
|
||||||
bool server_window_created_ = false;
|
bool server_window_created_ = false;
|
||||||
bool server_window_inited_ = false;
|
bool server_window_inited_ = false;
|
||||||
int server_window_width_default_ = 250;
|
int server_window_width_default_ = 250;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ bool HasNonEmptyEnvironmentVariable(const char* name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ScopedUnsetEnvironmentVariable {
|
class ScopedUnsetEnvironmentVariable {
|
||||||
public:
|
public:
|
||||||
explicit ScopedUnsetEnvironmentVariable(const char* name) : name_(name) {
|
explicit ScopedUnsetEnvironmentVariable(const char* name) : name_(name) {
|
||||||
if (const char* value = std::getenv(name_)) {
|
if (const char* value = std::getenv(name_)) {
|
||||||
original_value_ = value;
|
original_value_ = value;
|
||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
ScopedUnsetEnvironmentVariable& operator=(
|
ScopedUnsetEnvironmentVariable& operator=(
|
||||||
const ScopedUnsetEnvironmentVariable&) = delete;
|
const ScopedUnsetEnvironmentVariable&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* name_;
|
const char* name_;
|
||||||
std::optional<std::string> original_value_;
|
std::optional<std::string> original_value_;
|
||||||
};
|
};
|
||||||
@@ -101,8 +101,7 @@ private:
|
|||||||
&bytes_after, &data) == Success &&
|
&bytes_after, &data) == Success &&
|
||||||
actual_type == XA_WINDOW && actual_format == 32 && item_count == 1 &&
|
actual_type == XA_WINDOW && actual_format == 32 && item_count == 1 &&
|
||||||
data) {
|
data) {
|
||||||
const ::Window active_window =
|
const ::Window active_window = *reinterpret_cast<const ::Window*>(data);
|
||||||
*reinterpret_cast<const ::Window*>(data);
|
|
||||||
XFree(data);
|
XFree(data);
|
||||||
return active_window;
|
return active_window;
|
||||||
}
|
}
|
||||||
@@ -165,9 +164,8 @@ bool X11WindowMatches(Display* display, ::Window x11_window,
|
|||||||
int root_x = 0;
|
int root_x = 0;
|
||||||
int root_y = 0;
|
int root_y = 0;
|
||||||
::Window child = 0;
|
::Window child = 0;
|
||||||
if (!XTranslateCoordinates(display, x11_window,
|
if (!XTranslateCoordinates(display, x11_window, DefaultRootWindow(display), 0,
|
||||||
DefaultRootWindow(display), 0, 0, &root_x,
|
0, &root_x, &root_y, &child)) {
|
||||||
&root_y, &child)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,17 +267,15 @@ std::vector<ParsedReleaseNoteBlock> ParseReleaseNotesMarkdownForSlint(
|
|||||||
size_t line_start = 0;
|
size_t line_start = 0;
|
||||||
while (line_start < markdown.size()) {
|
while (line_start < markdown.size()) {
|
||||||
const size_t newline = markdown.find('\n', line_start);
|
const size_t newline = markdown.find('\n', line_start);
|
||||||
const size_t line_end = newline == std::string_view::npos
|
const size_t line_end =
|
||||||
? markdown.size()
|
newline == std::string_view::npos ? markdown.size() : newline;
|
||||||
: newline;
|
|
||||||
std::string_view line = markdown.substr(line_start, line_end - line_start);
|
std::string_view line = markdown.substr(line_start, line_end - line_start);
|
||||||
if (!line.empty() && line.back() == '\r') {
|
if (!line.empty() && line.back() == '\r') {
|
||||||
line.remove_suffix(1);
|
line.remove_suffix(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::all_of(line.begin(), line.end(), [](unsigned char ch) {
|
if (std::all_of(line.begin(), line.end(),
|
||||||
return std::isspace(ch) != 0;
|
[](unsigned char ch) { return std::isspace(ch) != 0; })) {
|
||||||
})) {
|
|
||||||
section_gap = !blocks.empty();
|
section_gap = !blocks.empty();
|
||||||
if (newline == std::string_view::npos) {
|
if (newline == std::string_view::npos) {
|
||||||
break;
|
break;
|
||||||
@@ -306,11 +302,11 @@ std::vector<ParsedReleaseNoteBlock> ParseReleaseNotesMarkdownForSlint(
|
|||||||
|
|
||||||
const std::string block_markdown(line);
|
const std::string block_markdown(line);
|
||||||
ParsedReleaseNoteBlock block;
|
ParsedReleaseNoteBlock block;
|
||||||
if (const auto parsed =
|
if (const auto parsed = slint::StyledText::from_markdown(block_markdown)) {
|
||||||
slint::StyledText::from_markdown(block_markdown)) {
|
|
||||||
block.content = *parsed;
|
block.content = *parsed;
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("Failed to parse a release-note Markdown block; using plain text");
|
LOG_WARN(
|
||||||
|
"Failed to parse a release-note Markdown block; using plain text");
|
||||||
block.content = slint::StyledText::from_plain_text(block_markdown);
|
block.content = slint::StyledText::from_plain_text(block_markdown);
|
||||||
}
|
}
|
||||||
block.section_gap = section_gap;
|
block.section_gap = section_gap;
|
||||||
@@ -958,13 +954,14 @@ bool GuiApplication::InitializeSDL() {
|
|||||||
const bool video_driver_allows_x11 =
|
const bool video_driver_allows_x11 =
|
||||||
requested_video_driver == nullptr || requested_video_driver[0] == '\0' ||
|
requested_video_driver == nullptr || requested_video_driver[0] == '\0' ||
|
||||||
std::strcmp(requested_video_driver, "x11") == 0;
|
std::strcmp(requested_video_driver, "x11") == 0;
|
||||||
use_xwayland_gui_ =
|
use_xwayland_gui_ = IsWaylandSession() &&
|
||||||
IsWaylandSession() && HasNonEmptyEnvironmentVariable("DISPLAY") &&
|
HasNonEmptyEnvironmentVariable("DISPLAY") &&
|
||||||
video_driver_allows_x11;
|
video_driver_allows_x11;
|
||||||
if (use_xwayland_gui_) {
|
if (use_xwayland_gui_) {
|
||||||
setenv("SDL_VIDEODRIVER", "x11", 1);
|
setenv("SDL_VIDEODRIVER", "x11", 1);
|
||||||
LOG_INFO("Wayland session detected; using XWayland for positioned GUI "
|
LOG_INFO(
|
||||||
"windows while retaining Wayland portal capture and input");
|
"Wayland session detected; using XWayland for positioned GUI "
|
||||||
|
"windows while retaining Wayland portal capture and input");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
||||||
@@ -1011,8 +1008,7 @@ void GuiApplication::InitializeUi() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<ui::ReleaseNoteBlock> release_note_blocks;
|
std::vector<ui::ReleaseNoteBlock> release_note_blocks;
|
||||||
for (const auto& parsed :
|
for (const auto& parsed : ParseReleaseNotesMarkdownForSlint(release_notes_)) {
|
||||||
ParseReleaseNotesMarkdownForSlint(release_notes_)) {
|
|
||||||
ui::ReleaseNoteBlock block;
|
ui::ReleaseNoteBlock block;
|
||||||
block.content = parsed.content;
|
block.content = parsed.content;
|
||||||
block.section_gap = parsed.section_gap;
|
block.section_gap = parsed.section_gap;
|
||||||
@@ -1159,10 +1155,10 @@ void GuiApplication::InitializeUi() {
|
|||||||
#endif
|
#endif
|
||||||
BindStreamCallbacks();
|
BindStreamCallbacks();
|
||||||
(*ui_->stream)->show();
|
(*ui_->stream)->show();
|
||||||
PositionWindowAtCenter((*ui_->stream)->window(), ui_->main->window(),
|
PositionWindowAtCenter(
|
||||||
kStreamWindowLogicalWidth,
|
(*ui_->stream)->window(), ui_->main->window(),
|
||||||
StreamWindowLogicalHeight(
|
kStreamWindowLogicalWidth,
|
||||||
(*ui_->stream)->get_custom_titlebar()));
|
StreamWindowLogicalHeight((*ui_->stream)->get_custom_titlebar()));
|
||||||
ui_->main->hide();
|
ui_->main->hide();
|
||||||
} else if (ui_->capture_mode && ui_->capture_page == "server") {
|
} else if (ui_->capture_mode && ui_->capture_page == "server") {
|
||||||
ui_->server.emplace(ui::ServerWindow::create());
|
ui_->server.emplace(ui::ServerWindow::create());
|
||||||
@@ -2235,8 +2231,7 @@ void GuiApplication::SyncXWaylandWindowActivation() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto process_id =
|
if (const auto process_id = X11GetWindowProcessId(display, active_window);
|
||||||
X11GetWindowProcessId(display, active_window);
|
|
||||||
process_id && *process_id != static_cast<unsigned long>(getpid())) {
|
process_id && *process_id != static_cast<unsigned long>(getpid())) {
|
||||||
ui_->main->set_window_active(false);
|
ui_->main->set_window_active(false);
|
||||||
if (ui_->stream) {
|
if (ui_->stream) {
|
||||||
@@ -2249,8 +2244,8 @@ void GuiApplication::SyncXWaylandWindowActivation() {
|
|||||||
X11WindowMatches(display, active_window, ui_->main->window()));
|
X11WindowMatches(display, active_window, ui_->main->window()));
|
||||||
if (ui_->stream) {
|
if (ui_->stream) {
|
||||||
(*ui_->stream)
|
(*ui_->stream)
|
||||||
->set_window_active(X11WindowMatches(
|
->set_window_active(
|
||||||
display, active_window, (*ui_->stream)->window()));
|
X11WindowMatches(display, active_window, (*ui_->stream)->window()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2289,8 +2284,7 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
if (!ui_->stream) {
|
if (!ui_->stream) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(*ui_->stream)
|
(*ui_->stream)->set_window_maximized((*ui_->stream)->window().is_maximized());
|
||||||
->set_window_maximized((*ui_->stream)->window().is_maximized());
|
|
||||||
(*ui_->stream)->set_srtp_enabled(enable_srtp_);
|
(*ui_->stream)->set_srtp_enabled(enable_srtp_);
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
if (ui_->stream_live_resize_configuration_attempts > 0) {
|
if (ui_->stream_live_resize_configuration_attempts > 0) {
|
||||||
@@ -2302,10 +2296,10 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ui_->stream_initial_position_attempts > 0) {
|
if (ui_->stream_initial_position_attempts > 0) {
|
||||||
if (PositionWindowAtCenter((*ui_->stream)->window(), ui_->main->window(),
|
if (PositionWindowAtCenter(
|
||||||
kStreamWindowLogicalWidth,
|
(*ui_->stream)->window(), ui_->main->window(),
|
||||||
StreamWindowLogicalHeight(
|
kStreamWindowLogicalWidth,
|
||||||
(*ui_->stream)->get_custom_titlebar()))) {
|
StreamWindowLogicalHeight((*ui_->stream)->get_custom_titlebar()))) {
|
||||||
--ui_->stream_initial_position_attempts;
|
--ui_->stream_initial_position_attempts;
|
||||||
} else {
|
} else {
|
||||||
ui_->stream_initial_position_attempts = 0;
|
ui_->stream_initial_position_attempts = 0;
|
||||||
@@ -2539,7 +2533,22 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::SyncServerWindow() {
|
void GuiApplication::SyncServerWindow() {
|
||||||
if (need_to_create_server_window_ && !ui_->server) {
|
// The connection map is authoritative. Lifecycle flags record callback
|
||||||
|
// intent, but callbacks for different controllers can cross each other, so
|
||||||
|
// the final decision must reflect the current connected-controller set.
|
||||||
|
need_to_create_server_window_.exchange(false, std::memory_order_acq_rel);
|
||||||
|
need_to_destroy_server_window_.exchange(false, std::memory_order_acq_rel);
|
||||||
|
bool has_connected_controller = false;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(connection_status_mutex_);
|
||||||
|
has_connected_controller =
|
||||||
|
std::any_of(connection_status_.begin(), connection_status_.end(),
|
||||||
|
[](const auto& entry) {
|
||||||
|
return entry.second == ConnectionStatus::Connected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_connected_controller && !ui_->server) {
|
||||||
ui_->server.emplace(ui::ServerWindow::create());
|
ui_->server.emplace(ui::ServerWindow::create());
|
||||||
RegisterFontAwesome((*ui_->server)->window());
|
RegisterFontAwesome((*ui_->server)->window());
|
||||||
(*ui_->server)->set_controllers(ui_->controller_model);
|
(*ui_->server)->set_controllers(ui_->controller_model);
|
||||||
@@ -2559,15 +2568,13 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
ui_->server_initial_position_attempts = 2;
|
ui_->server_initial_position_attempts = 2;
|
||||||
server_window_created_ = true;
|
server_window_created_ = true;
|
||||||
server_window_inited_ = true;
|
server_window_inited_ = true;
|
||||||
need_to_create_server_window_ = false;
|
|
||||||
}
|
}
|
||||||
if (need_to_destroy_server_window_ && ui_->server) {
|
if (!has_connected_controller && ui_->server) {
|
||||||
(*ui_->server)->hide();
|
(*ui_->server)->hide();
|
||||||
ui_->server.reset();
|
ui_->server.reset();
|
||||||
server_window_created_ = false;
|
server_window_created_ = false;
|
||||||
server_window_inited_ = false;
|
server_window_inited_ = false;
|
||||||
ui_->server_initial_position_attempts = 0;
|
ui_->server_initial_position_attempts = 0;
|
||||||
need_to_destroy_server_window_ = false;
|
|
||||||
}
|
}
|
||||||
if (!ui_->server) {
|
if (!ui_->server) {
|
||||||
return;
|
return;
|
||||||
@@ -2868,9 +2875,8 @@ void GuiApplication::SendPointerInput(int button, int kind, float x, float y) {
|
|||||||
: 0.0f;
|
: 0.0f;
|
||||||
const float available_height =
|
const float available_height =
|
||||||
size.height / scale - titlebar_height -
|
size.height / scale - titlebar_height -
|
||||||
(fullscreen_button_pressed_
|
(fullscreen_button_pressed_ ? 0.0f
|
||||||
? 0.0f
|
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
||||||
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
|
||||||
float render_width = available_width;
|
float render_width = available_width;
|
||||||
float render_height = available_height;
|
float render_height = available_height;
|
||||||
float offset_x = 0;
|
float offset_x = 0;
|
||||||
@@ -2944,9 +2950,8 @@ void GuiApplication::SendScrollInput(float delta_x, float delta_y, float x,
|
|||||||
: 0.0f;
|
: 0.0f;
|
||||||
const float height =
|
const float height =
|
||||||
size.height / scale - titlebar_height -
|
size.height / scale - titlebar_height -
|
||||||
(fullscreen_button_pressed_
|
(fullscreen_button_pressed_ ? 0.0f
|
||||||
? 0.0f
|
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
||||||
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#include "runtime/gui_runtime.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -12,6 +10,7 @@
|
|||||||
|
|
||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
#include "rd_log.h"
|
#include "rd_log.h"
|
||||||
|
#include "runtime/gui_runtime.h"
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ bool IsConnectionAttemptPending(ConnectionStatus status) {
|
|||||||
return status == ConnectionStatus::Connecting ||
|
return status == ConnectionStatus::Connecting ||
|
||||||
status == ConnectionStatus::Gathering;
|
status == ConnectionStatus::Gathering;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GuiRuntime::HandleConnectionStatusChange() {
|
void GuiRuntime::HandleConnectionStatusChange() {
|
||||||
if (signal_connected_ && peer_ && need_to_send_recent_connections_) {
|
if (signal_connected_ && peer_ && need_to_send_recent_connections_) {
|
||||||
@@ -31,7 +30,7 @@ void GuiRuntime::HandleConnectionStatusChange() {
|
|||||||
j["type"] = "recent_connections_presence";
|
j["type"] = "recent_connections_presence";
|
||||||
j["user_id"] = client_id_;
|
j["user_id"] = client_id_;
|
||||||
j["devices"] = nlohmann::json::array();
|
j["devices"] = nlohmann::json::array();
|
||||||
for (const auto &id : recent_connection_ids_) {
|
for (const auto& id : recent_connection_ids_) {
|
||||||
std::string pure_id = id;
|
std::string pure_id = id;
|
||||||
size_t pos_y = pure_id.find('Y');
|
size_t pos_y = pure_id.find('Y');
|
||||||
size_t pos_n = pure_id.find('N');
|
size_t pos_n = pure_id.find('N');
|
||||||
@@ -123,7 +122,7 @@ void GuiRuntime::HandleConnectionTimeouts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool rejoin_state_changed = false;
|
bool rejoin_state_changed = false;
|
||||||
for (auto &[_, props] : remote_sessions_) {
|
for (auto& [_, props] : remote_sessions_) {
|
||||||
if (!props || !props->connection_attempt_active_.load()) {
|
if (!props || !props->connection_attempt_active_.load()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,7 @@ void GuiRuntime::HandleConnectionTimeouts() {
|
|||||||
|
|
||||||
if (rejoin_state_changed) {
|
if (rejoin_state_changed) {
|
||||||
need_to_rejoin_ = false;
|
need_to_rejoin_ = false;
|
||||||
for (const auto &[_, props] : remote_sessions_) {
|
for (const auto& [_, props] : remote_sessions_) {
|
||||||
if (props && props->rejoin_) {
|
if (props && props->rejoin_) {
|
||||||
need_to_rejoin_ = true;
|
need_to_rejoin_ = true;
|
||||||
break;
|
break;
|
||||||
@@ -161,8 +160,65 @@ void GuiRuntime::HandleConnectionTimeouts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GuiRuntime::RequestSingleDevicePresence(const std::string &remote_id,
|
void GuiRuntime::HandleServerControllerDisconnected(
|
||||||
const char *password,
|
const std::string& remote_id, const char* reason) {
|
||||||
|
keyboard_.ReleaseRemotePressedKeys(remote_id, reason);
|
||||||
|
|
||||||
|
bool has_connected_controller = false;
|
||||||
|
bool has_web_controller = false;
|
||||||
|
std::string remaining_controller_id;
|
||||||
|
{
|
||||||
|
std::unique_lock lock(connection_status_mutex_);
|
||||||
|
connection_status_.erase(remote_id);
|
||||||
|
connection_host_names_.erase(remote_id);
|
||||||
|
for (const auto& [id, status] : connection_status_) {
|
||||||
|
if (status != ConnectionStatus::Connected) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
has_connected_controller = true;
|
||||||
|
has_web_controller =
|
||||||
|
has_web_controller || id.find("web") != std::string::npos;
|
||||||
|
if (remaining_controller_id.empty()) {
|
||||||
|
remaining_controller_id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show_cursor_ = has_web_controller;
|
||||||
|
if (has_connected_controller) {
|
||||||
|
remote_client_id_ = remaining_controller_id;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
need_to_create_server_window_.store(false, std::memory_order_release);
|
||||||
|
need_to_destroy_server_window_.store(true, std::memory_order_release);
|
||||||
|
is_server_mode_ = false;
|
||||||
|
#if defined(__linux__) && !defined(__APPLE__)
|
||||||
|
if (IsWaylandSession()) {
|
||||||
|
// Keep Wayland capture session warm to avoid black screen on subsequent
|
||||||
|
// reconnects.
|
||||||
|
start_screen_capturer_ = true;
|
||||||
|
LOG_INFO(
|
||||||
|
"Keeping Wayland screen capturer running after disconnect to "
|
||||||
|
"preserve reconnect stability");
|
||||||
|
} else {
|
||||||
|
start_screen_capturer_ = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
start_screen_capturer_ = false;
|
||||||
|
#endif
|
||||||
|
start_speaker_capturer_ = false;
|
||||||
|
start_mouse_controller_ = false;
|
||||||
|
start_keyboard_capturer_ = false;
|
||||||
|
remote_client_id_.clear();
|
||||||
|
if (audio_capture_) {
|
||||||
|
devices_.StopSpeakerCapturer();
|
||||||
|
audio_capture_ = false;
|
||||||
|
}
|
||||||
|
devices_.ResetToInitialDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id,
|
||||||
|
const char* password,
|
||||||
bool remember_password) {
|
bool remember_password) {
|
||||||
if (!signal_connected_ || !peer_) {
|
if (!signal_connected_ || !peer_) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -221,7 +277,7 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
|
|||||||
std::thread save_thread([buffer_copy, video_width, video_height, remote_id,
|
std::thread save_thread([buffer_copy, video_width, video_height, remote_id,
|
||||||
remote_host_name, password,
|
remote_host_name, password,
|
||||||
thumbnail = thumbnail_]() {
|
thumbnail = thumbnail_]() {
|
||||||
thumbnail->SaveToThumbnail((char *)buffer_copy.data(), video_width,
|
thumbnail->SaveToThumbnail((char*)buffer_copy.data(), video_width,
|
||||||
video_height, remote_id, remote_host_name,
|
video_height, remote_id, remote_host_name,
|
||||||
password);
|
password);
|
||||||
});
|
});
|
||||||
@@ -255,7 +311,7 @@ void GuiRuntime::CloseAllRemoteSessions() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// std::shared_lock lock(remote_sessions_mutex_);
|
// std::shared_lock lock(remote_sessions_mutex_);
|
||||||
for (auto &it : remote_sessions_) {
|
for (auto& it : remote_sessions_) {
|
||||||
auto props = it.second;
|
auto props = it.second;
|
||||||
CloseRemoteSession(props);
|
CloseRemoteSession(props);
|
||||||
}
|
}
|
||||||
@@ -279,7 +335,7 @@ void GuiRuntime::WaitForThumbnailSaveTasks() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &thread : threads_to_join) {
|
for (auto& thread : threads_to_join) {
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
@@ -300,9 +356,8 @@ void GuiRuntime::ResetRemoteSessionResources(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<GuiRuntime::RemoteSession>
|
std::shared_ptr<GuiRuntime::RemoteSession> GuiRuntime::FindRemoteSession(
|
||||||
GuiRuntime::FindRemoteSession(
|
const std::string& remote_id) {
|
||||||
const std::string &remote_id) {
|
|
||||||
if (remote_id.empty()) {
|
if (remote_id.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -315,8 +370,7 @@ GuiRuntime::FindRemoteSession(
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
if (!bypass_presence_check && !device_presence_cache_.IsOnline(remote_id)) {
|
if (!bypass_presence_check && !device_presence_cache_.IsOnline(remote_id)) {
|
||||||
int ret =
|
int ret =
|
||||||
@@ -336,19 +390,17 @@ int GuiRuntime::ConnectTo(const std::string &remote_id, const char *password,
|
|||||||
focused_remote_id_ = remote_id;
|
focused_remote_id_ = remote_id;
|
||||||
|
|
||||||
// std::shared_lock shared_lock(remote_sessions_mutex_);
|
// std::shared_lock shared_lock(remote_sessions_mutex_);
|
||||||
bool exists =
|
bool exists = (remote_sessions_.find(remote_id) != remote_sessions_.end());
|
||||||
(remote_sessions_.find(remote_id) != remote_sessions_.end());
|
|
||||||
// shared_lock.unlock();
|
// shared_lock.unlock();
|
||||||
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
PeerPtr *peer_to_init = nullptr;
|
PeerPtr* peer_to_init = nullptr;
|
||||||
std::string local_id;
|
std::string local_id;
|
||||||
|
|
||||||
{
|
{
|
||||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||||
if (remote_sessions_.find(remote_id) == remote_sessions_.end()) {
|
if (remote_sessions_.find(remote_id) == remote_sessions_.end()) {
|
||||||
remote_sessions_[remote_id] =
|
remote_sessions_[remote_id] = std::make_shared<RemoteSession>();
|
||||||
std::make_shared<RemoteSession>();
|
|
||||||
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;
|
||||||
@@ -371,7 +423,7 @@ int GuiRuntime::ConnectTo(const std::string &remote_id, const char *password,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &display_info : devices_.display_info_list()) {
|
for (const auto& display_info : devices_.display_info_list()) {
|
||||||
AddVideoStream(props->peer_, display_info.name.c_str());
|
AddVideoStream(props->peer_, display_info.name.c_str());
|
||||||
}
|
}
|
||||||
AddAudioStream(props->peer_, props->audio_label_.c_str());
|
AddAudioStream(props->peer_, props->audio_label_.c_str());
|
||||||
@@ -429,4 +481,4 @@ int GuiRuntime::ConnectTo(const std::string &remote_id, const char *password,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|||||||
@@ -8,16 +8,16 @@
|
|||||||
#include "features/devices/session_device_manager.h"
|
#include "features/devices/session_device_manager.h"
|
||||||
#include "features/file_transfer/file_transfer_manager.h"
|
#include "features/file_transfer/file_transfer_manager.h"
|
||||||
#include "features/input/keyboard_controller.h"
|
#include "features/input/keyboard_controller.h"
|
||||||
|
#include "features/settings/settings_manager.h"
|
||||||
#include "runtime/gui_state.h"
|
#include "runtime/gui_state.h"
|
||||||
#include "runtime/peer_event_handler.h"
|
#include "runtime/peer_event_handler.h"
|
||||||
#include "features/settings/settings_manager.h"
|
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
// Shared GUI runtime. It owns subsystem controllers and cross-cutting session
|
// Shared GUI runtime. It owns subsystem controllers and cross-cutting session
|
||||||
// state, but no window lifecycle, ImGui view, or transport callback methods.
|
// state, but no window lifecycle, ImGui view, or transport callback methods.
|
||||||
class GuiRuntime : protected gui_detail::GuiState {
|
class GuiRuntime : protected gui_detail::GuiState {
|
||||||
protected:
|
protected:
|
||||||
using FileTransferState = gui_detail::FileTransferState;
|
using FileTransferState = gui_detail::FileTransferState;
|
||||||
using RemoteSession = gui_detail::RemoteSession;
|
using RemoteSession = gui_detail::RemoteSession;
|
||||||
|
|
||||||
@@ -32,35 +32,35 @@ protected:
|
|||||||
GuiRuntime();
|
GuiRuntime();
|
||||||
~GuiRuntime();
|
~GuiRuntime();
|
||||||
|
|
||||||
static void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
|
static void SdlCaptureAudioIn(void* userdata, Uint8* stream, int len);
|
||||||
static void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
|
static void SdlCaptureAudioOut(void* userdata, Uint8* stream, int len);
|
||||||
|
|
||||||
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);
|
||||||
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);
|
||||||
|
|
||||||
void UpdateLabels();
|
void UpdateLabels();
|
||||||
void HandleRecentConnections();
|
void HandleRecentConnections();
|
||||||
void HandleConnectionStatusChange();
|
void HandleConnectionStatusChange();
|
||||||
void HandlePendingPresenceProbe();
|
void HandlePendingPresenceProbe();
|
||||||
void HandleConnectionTimeouts();
|
void HandleConnectionTimeouts();
|
||||||
|
void HandleServerControllerDisconnected(const std::string& remote_id,
|
||||||
|
const char* reason);
|
||||||
void HandleWindowsServiceIntegration();
|
void HandleWindowsServiceIntegration();
|
||||||
|
|
||||||
void CloseRemoteSession(std::shared_ptr<RemoteSession> props);
|
void CloseRemoteSession(std::shared_ptr<RemoteSession> props);
|
||||||
void CloseAllRemoteSessions();
|
void CloseAllRemoteSessions();
|
||||||
void ResetRemoteSessionResources(
|
void ResetRemoteSessionResources(std::shared_ptr<RemoteSession> props);
|
||||||
std::shared_ptr<RemoteSession> props);
|
|
||||||
void WaitForThumbnailSaveTasks();
|
void WaitForThumbnailSaveTasks();
|
||||||
std::shared_ptr<RemoteSession>
|
std::shared_ptr<RemoteSession> FindRemoteSession(
|
||||||
FindRemoteSession(const std::string &remote_id);
|
const std::string& remote_id);
|
||||||
|
|
||||||
void ResetRemoteServiceStatus(RemoteSession &props);
|
void ResetRemoteServiceStatus(RemoteSession& props);
|
||||||
void ApplyRemoteServiceStatus(RemoteSession &props,
|
void ApplyRemoteServiceStatus(RemoteSession& props,
|
||||||
const ServiceStatus &status);
|
const ServiceStatus& status);
|
||||||
RemoteUnlockState
|
RemoteUnlockState GetRemoteUnlockState(const RemoteSession& props) const;
|
||||||
GetRemoteUnlockState(const RemoteSession &props) const;
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
void ResetLocalWindowsServiceState(bool clear_pending_sas);
|
void ResetLocalWindowsServiceState(bool clear_pending_sas);
|
||||||
#endif
|
#endif
|
||||||
@@ -82,7 +82,7 @@ protected:
|
|||||||
KeyboardController keyboard_;
|
KeyboardController keyboard_;
|
||||||
PeerEventHandler peer_events_;
|
PeerEventHandler peer_events_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ClipboardController;
|
friend class ClipboardController;
|
||||||
friend class SessionDeviceManager;
|
friend class SessionDeviceManager;
|
||||||
friend class FileTransferManager;
|
friend class FileTransferManager;
|
||||||
@@ -91,6 +91,6 @@ private:
|
|||||||
friend class PeerEventHandler;
|
friend class PeerEventHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|
||||||
#endif // CROSSDESK_GUI_RUNTIME_H_
|
#endif // CROSSDESK_GUI_RUNTIME_H_
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
PeerEventHandler::PeerEventHandler(GuiRuntime &owner) : owner_(owner) {}
|
PeerEventHandler::PeerEventHandler(GuiRuntime& owner) : owner_(owner) {}
|
||||||
|
|
||||||
void PeerEventHandler::OnSignalMessage(const char *message, size_t size,
|
void PeerEventHandler::OnSignalMessage(const char* message, size_t size,
|
||||||
void *user_data) {
|
void* user_data) {
|
||||||
auto *handler = static_cast<PeerEventHandler *>(user_data);
|
auto* handler = static_cast<PeerEventHandler*>(user_data);
|
||||||
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
|
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
|
||||||
if (!runtime || !message || size == 0) {
|
if (!runtime || !message || size == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ void PeerEventHandler::OnSignalMessage(const char *message, size_t size,
|
|||||||
std::string type = j["type"].get<std::string>();
|
std::string type = j["type"].get<std::string>();
|
||||||
if (type == "presence") {
|
if (type == "presence") {
|
||||||
if (j.contains("devices") && j["devices"].is_array()) {
|
if (j.contains("devices") && j["devices"].is_array()) {
|
||||||
for (auto &dev : j["devices"]) {
|
for (auto& dev : j["devices"]) {
|
||||||
if (!dev.is_object()) {
|
if (!dev.is_object()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -88,11 +88,10 @@ void PeerEventHandler::OnSignalMessage(const char *message, size_t size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PeerEventHandler::OnSignalStatus(SignalStatus status, const char* user_id,
|
||||||
void PeerEventHandler::OnSignalStatus(SignalStatus status, const char *user_id,
|
size_t user_id_size, void* user_data) {
|
||||||
size_t user_id_size, void *user_data) {
|
auto* handler = static_cast<PeerEventHandler*>(user_data);
|
||||||
auto *handler = static_cast<PeerEventHandler *>(user_data);
|
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
|
||||||
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
|
|
||||||
if (!runtime) {
|
if (!runtime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,13 +149,12 @@ void PeerEventHandler::OnSignalStatus(SignalStatus status, const char *user_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
||||||
const char *user_id,
|
const char* user_id,
|
||||||
const size_t user_id_size,
|
const size_t user_id_size,
|
||||||
void *user_data) {
|
void* user_data) {
|
||||||
auto *handler = static_cast<PeerEventHandler *>(user_data);
|
auto* handler = static_cast<PeerEventHandler*>(user_data);
|
||||||
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
|
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
|
||||||
if (!runtime)
|
if (!runtime) return;
|
||||||
return;
|
|
||||||
|
|
||||||
std::string remote_id(user_id, user_id_size);
|
std::string remote_id(user_id, user_id_size);
|
||||||
std::shared_ptr<GuiRuntime::RemoteSession> props;
|
std::shared_ptr<GuiRuntime::RemoteSession> props;
|
||||||
@@ -178,107 +176,110 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus::Connected: {
|
case ConnectionStatus::Connected: {
|
||||||
runtime->ResetRemoteServiceStatus(*props);
|
runtime->ResetRemoteServiceStatus(*props);
|
||||||
{
|
{
|
||||||
RemoteAction remote_action;
|
RemoteAction remote_action;
|
||||||
remote_action.i.display_num =
|
remote_action.i.display_num =
|
||||||
runtime->devices_.display_info_list().size();
|
runtime->devices_.display_info_list().size();
|
||||||
remote_action.i.display_list =
|
remote_action.i.display_list =
|
||||||
(char **)malloc(remote_action.i.display_num * sizeof(char *));
|
(char**)malloc(remote_action.i.display_num * sizeof(char*));
|
||||||
remote_action.i.left =
|
remote_action.i.left =
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
remote_action.i.top =
|
remote_action.i.top =
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
remote_action.i.right =
|
remote_action.i.right =
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
remote_action.i.bottom =
|
remote_action.i.bottom =
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
for (int i = 0; i < remote_action.i.display_num; i++) {
|
for (int i = 0; i < remote_action.i.display_num; i++) {
|
||||||
LOG_INFO("Local display [{}:{}]", i + 1,
|
LOG_INFO("Local display [{}:{}]", i + 1,
|
||||||
runtime->devices_.display_info_list()[i].name);
|
runtime->devices_.display_info_list()[i].name);
|
||||||
remote_action.i.display_list[i] = (char *)malloc(
|
remote_action.i.display_list[i] = (char*)malloc(
|
||||||
runtime->devices_.display_info_list()[i].name.length() + 1);
|
runtime->devices_.display_info_list()[i].name.length() + 1);
|
||||||
strncpy(remote_action.i.display_list[i],
|
strncpy(remote_action.i.display_list[i],
|
||||||
runtime->devices_.display_info_list()[i].name.c_str(),
|
runtime->devices_.display_info_list()[i].name.c_str(),
|
||||||
runtime->devices_.display_info_list()[i].name.length());
|
runtime->devices_.display_info_list()[i].name.length());
|
||||||
remote_action.i.display_list
|
remote_action.i
|
||||||
[i][runtime->devices_.display_info_list()[i].name.length()] = '\0';
|
.display_list[i][runtime->devices_.display_info_list()[i]
|
||||||
remote_action.i.left[i] =
|
.name.length()] = '\0';
|
||||||
runtime->devices_.display_info_list()[i].left;
|
remote_action.i.left[i] =
|
||||||
remote_action.i.top[i] = runtime->devices_.display_info_list()[i].top;
|
runtime->devices_.display_info_list()[i].left;
|
||||||
remote_action.i.right[i] =
|
remote_action.i.top[i] =
|
||||||
runtime->devices_.display_info_list()[i].right;
|
runtime->devices_.display_info_list()[i].top;
|
||||||
remote_action.i.bottom[i] =
|
remote_action.i.right[i] =
|
||||||
runtime->devices_.display_info_list()[i].bottom;
|
runtime->devices_.display_info_list()[i].right;
|
||||||
|
remote_action.i.bottom[i] =
|
||||||
|
runtime->devices_.display_info_list()[i].bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string host_name = GetHostName();
|
||||||
|
remote_action.type = ControlType::host_infomation;
|
||||||
|
memcpy(&remote_action.i.host_name, host_name.data(),
|
||||||
|
host_name.size());
|
||||||
|
remote_action.i.host_name[host_name.size()] = '\0';
|
||||||
|
remote_action.i.host_name_size = host_name.size();
|
||||||
|
|
||||||
|
std::string msg = remote_action.to_json();
|
||||||
|
int ret = SendReliableDataFrame(props->peer_, msg.data(), msg.size(),
|
||||||
|
runtime->control_data_label_.c_str());
|
||||||
|
remote_action_codec::Free(remote_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string host_name = GetHostName();
|
if (!runtime->need_to_create_stream_window_ &&
|
||||||
remote_action.type = ControlType::host_infomation;
|
!runtime->remote_sessions_.empty()) {
|
||||||
memcpy(&remote_action.i.host_name, host_name.data(), host_name.size());
|
runtime->need_to_create_stream_window_ = true;
|
||||||
remote_action.i.host_name[host_name.size()] = '\0';
|
}
|
||||||
remote_action.i.host_name_size = host_name.size();
|
props->connection_established_ = true;
|
||||||
|
runtime->start_keyboard_capturer_ = true;
|
||||||
std::string msg = remote_action.to_json();
|
break;
|
||||||
int ret = SendReliableDataFrame(props->peer_, msg.data(), msg.size(),
|
|
||||||
runtime->control_data_label_.c_str());
|
|
||||||
remote_action_codec::Free(remote_action);
|
|
||||||
}
|
}
|
||||||
|
case ConnectionStatus::Disconnected:
|
||||||
if (!runtime->need_to_create_stream_window_ &&
|
case ConnectionStatus::Failed:
|
||||||
!runtime->remote_sessions_.empty()) {
|
case ConnectionStatus::Closed: {
|
||||||
runtime->need_to_create_stream_window_ = true;
|
runtime->keyboard_.ReleaseRemotePressedKeys(remote_id,
|
||||||
}
|
"connection_closed");
|
||||||
props->connection_established_ = true;
|
|
||||||
runtime->start_keyboard_capturer_ = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ConnectionStatus::Disconnected:
|
|
||||||
case ConnectionStatus::Failed:
|
|
||||||
case ConnectionStatus::Closed: {
|
|
||||||
runtime->keyboard_.ReleaseRemotePressedKeys(remote_id,
|
|
||||||
"connection_closed");
|
|
||||||
props->connection_established_ = false;
|
|
||||||
props->enable_mouse_control_ = false;
|
|
||||||
runtime->ResetRemoteServiceStatus(*props);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
|
||||||
props->front_frame_.reset();
|
|
||||||
props->back_frame_.reset();
|
|
||||||
props->video_width_ = 0;
|
|
||||||
props->video_height_ = 0;
|
|
||||||
props->video_size_ = 0;
|
|
||||||
props->render_rect_dirty_ = true;
|
|
||||||
props->stream_cleanup_pending_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime->focus_on_stream_window_ = false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ConnectionStatus::IncorrectPassword: {
|
|
||||||
runtime->password_validating_ = false;
|
|
||||||
runtime->password_validating_time_++;
|
|
||||||
if (runtime->connect_button_pressed_) {
|
|
||||||
runtime->connect_button_pressed_ = false;
|
|
||||||
props->connection_established_ = false;
|
props->connection_established_ = false;
|
||||||
runtime->connect_button_label_ =
|
props->enable_mouse_control_ = false;
|
||||||
localization::connect[runtime->localization_language_index_];
|
runtime->ResetRemoteServiceStatus(*props);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||||
|
props->front_frame_.reset();
|
||||||
|
props->back_frame_.reset();
|
||||||
|
props->video_width_ = 0;
|
||||||
|
props->video_height_ = 0;
|
||||||
|
props->video_size_ = 0;
|
||||||
|
props->render_rect_dirty_ = true;
|
||||||
|
props->stream_cleanup_pending_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime->focus_on_stream_window_ = false;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case ConnectionStatus::IncorrectPassword: {
|
||||||
}
|
runtime->password_validating_ = false;
|
||||||
case ConnectionStatus::NoSuchTransmissionId:
|
runtime->password_validating_time_++;
|
||||||
case ConnectionStatus::RemoteUnavailable: {
|
if (runtime->connect_button_pressed_) {
|
||||||
if (runtime->connect_button_pressed_) {
|
runtime->connect_button_pressed_ = false;
|
||||||
props->connection_established_ = false;
|
props->connection_established_ = false;
|
||||||
runtime->connect_button_label_ =
|
runtime->connect_button_label_ =
|
||||||
localization::connect[runtime->localization_language_index_];
|
localization::connect[runtime->localization_language_index_];
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case ConnectionStatus::NoSuchTransmissionId:
|
||||||
}
|
case ConnectionStatus::RemoteUnavailable: {
|
||||||
default:
|
if (runtime->connect_button_pressed_) {
|
||||||
break;
|
props->connection_established_ = false;
|
||||||
|
runtime->connect_button_label_ =
|
||||||
|
localization::connect[runtime->localization_language_index_];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runtime->is_client_mode_ = false;
|
runtime->is_client_mode_ = false;
|
||||||
@@ -289,155 +290,107 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus::Connected: {
|
case ConnectionStatus::Connected: {
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
runtime->last_windows_service_status_tick_ = 0;
|
runtime->last_windows_service_status_tick_ = 0;
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
RemoteAction remote_action;
|
|
||||||
remote_action.i.display_num =
|
|
||||||
runtime->devices_.display_info_list().size();
|
|
||||||
remote_action.i.display_list =
|
|
||||||
(char **)malloc(remote_action.i.display_num * sizeof(char *));
|
|
||||||
remote_action.i.left =
|
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
|
||||||
remote_action.i.top =
|
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
|
||||||
remote_action.i.right =
|
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
|
||||||
remote_action.i.bottom =
|
|
||||||
(int *)malloc(remote_action.i.display_num * sizeof(int));
|
|
||||||
for (int i = 0; i < remote_action.i.display_num; i++) {
|
|
||||||
LOG_INFO("Local display [{}:{}]", i + 1,
|
|
||||||
runtime->devices_.display_info_list()[i].name);
|
|
||||||
remote_action.i.display_list[i] = (char *)malloc(
|
|
||||||
runtime->devices_.display_info_list()[i].name.length() + 1);
|
|
||||||
strncpy(remote_action.i.display_list[i],
|
|
||||||
runtime->devices_.display_info_list()[i].name.c_str(),
|
|
||||||
runtime->devices_.display_info_list()[i].name.length());
|
|
||||||
remote_action.i.display_list
|
|
||||||
[i][runtime->devices_.display_info_list()[i].name.length()] = '\0';
|
|
||||||
remote_action.i.left[i] =
|
|
||||||
runtime->devices_.display_info_list()[i].left;
|
|
||||||
remote_action.i.top[i] = runtime->devices_.display_info_list()[i].top;
|
|
||||||
remote_action.i.right[i] =
|
|
||||||
runtime->devices_.display_info_list()[i].right;
|
|
||||||
remote_action.i.bottom[i] =
|
|
||||||
runtime->devices_.display_info_list()[i].bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string host_name = GetHostName();
|
|
||||||
remote_action.type = ControlType::host_infomation;
|
|
||||||
memcpy(&remote_action.i.host_name, host_name.data(), host_name.size());
|
|
||||||
remote_action.i.host_name[host_name.size()] = '\0';
|
|
||||||
remote_action.i.host_name_size = host_name.size();
|
|
||||||
|
|
||||||
std::string msg = remote_action.to_json();
|
|
||||||
int ret = SendReliableDataFrame(runtime->peer_, msg.data(), msg.size(),
|
|
||||||
runtime->control_data_label_.c_str());
|
|
||||||
remote_action_codec::Free(remote_action);
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime->need_to_create_server_window_ = true;
|
|
||||||
runtime->is_server_mode_ = true;
|
|
||||||
runtime->start_screen_capturer_ = true;
|
|
||||||
runtime->start_speaker_capturer_ = true;
|
|
||||||
runtime->remote_client_id_ = remote_id;
|
|
||||||
runtime->start_mouse_controller_ = true;
|
|
||||||
{
|
|
||||||
std::shared_lock lock(runtime->connection_status_mutex_);
|
|
||||||
if (std::all_of(runtime->connection_status_.begin(),
|
|
||||||
runtime->connection_status_.end(), [](const auto &kv) {
|
|
||||||
return kv.first.find("web") != std::string::npos;
|
|
||||||
})) {
|
|
||||||
runtime->show_cursor_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ConnectionStatus::Disconnected:
|
|
||||||
case ConnectionStatus::Failed:
|
|
||||||
case ConnectionStatus::Closed: {
|
|
||||||
runtime->keyboard_.ReleaseRemotePressedKeys(remote_id,
|
|
||||||
"connection_closed");
|
|
||||||
bool all_disconnected = false;
|
|
||||||
{
|
|
||||||
std::shared_lock lock(runtime->connection_status_mutex_);
|
|
||||||
all_disconnected =
|
|
||||||
std::all_of(runtime->connection_status_.begin(),
|
|
||||||
runtime->connection_status_.end(), [](const auto &kv) {
|
|
||||||
return kv.second == ConnectionStatus::Closed ||
|
|
||||||
kv.second == ConnectionStatus::Failed ||
|
|
||||||
kv.second == ConnectionStatus::Disconnected;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (all_disconnected) {
|
|
||||||
runtime->need_to_destroy_server_window_ = true;
|
|
||||||
runtime->is_server_mode_ = false;
|
|
||||||
#if defined(__linux__) && !defined(__APPLE__)
|
|
||||||
if (IsWaylandSession()) {
|
|
||||||
// Keep Wayland capture session warm to avoid black screen on
|
|
||||||
// subsequent reconnects.
|
|
||||||
runtime->start_screen_capturer_ = true;
|
|
||||||
LOG_INFO("Keeping Wayland screen capturer running after "
|
|
||||||
"disconnect to preserve reconnect stability");
|
|
||||||
} else {
|
|
||||||
runtime->start_screen_capturer_ = false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
runtime->start_screen_capturer_ = false;
|
|
||||||
#endif
|
|
||||||
runtime->start_speaker_capturer_ = false;
|
|
||||||
runtime->start_mouse_controller_ = false;
|
|
||||||
runtime->start_keyboard_capturer_ = false;
|
|
||||||
runtime->remote_client_id_ = "";
|
|
||||||
if (props)
|
|
||||||
props->connection_established_ = false;
|
|
||||||
if (runtime->audio_capture_) {
|
|
||||||
runtime->devices_.StopSpeakerCapturer();
|
|
||||||
runtime->audio_capture_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(runtime->connection_status_mutex_);
|
RemoteAction remote_action;
|
||||||
runtime->connection_status_.erase(remote_id);
|
remote_action.i.display_num =
|
||||||
runtime->connection_host_names_.erase(remote_id);
|
runtime->devices_.display_info_list().size();
|
||||||
}
|
remote_action.i.display_list =
|
||||||
runtime->devices_.ResetToInitialDisplay();
|
(char**)malloc(remote_action.i.display_num * sizeof(char*));
|
||||||
}
|
remote_action.i.left =
|
||||||
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
|
remote_action.i.top =
|
||||||
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
|
remote_action.i.right =
|
||||||
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
|
remote_action.i.bottom =
|
||||||
|
(int*)malloc(remote_action.i.display_num * sizeof(int));
|
||||||
|
for (int i = 0; i < remote_action.i.display_num; i++) {
|
||||||
|
LOG_INFO("Local display [{}:{}]", i + 1,
|
||||||
|
runtime->devices_.display_info_list()[i].name);
|
||||||
|
remote_action.i.display_list[i] = (char*)malloc(
|
||||||
|
runtime->devices_.display_info_list()[i].name.length() + 1);
|
||||||
|
strncpy(remote_action.i.display_list[i],
|
||||||
|
runtime->devices_.display_info_list()[i].name.c_str(),
|
||||||
|
runtime->devices_.display_info_list()[i].name.length());
|
||||||
|
remote_action.i
|
||||||
|
.display_list[i][runtime->devices_.display_info_list()[i]
|
||||||
|
.name.length()] = '\0';
|
||||||
|
remote_action.i.left[i] =
|
||||||
|
runtime->devices_.display_info_list()[i].left;
|
||||||
|
remote_action.i.top[i] =
|
||||||
|
runtime->devices_.display_info_list()[i].top;
|
||||||
|
remote_action.i.right[i] =
|
||||||
|
runtime->devices_.display_info_list()[i].right;
|
||||||
|
remote_action.i.bottom[i] =
|
||||||
|
runtime->devices_.display_info_list()[i].bottom;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
std::string host_name = GetHostName();
|
||||||
std::shared_lock lock(runtime->connection_status_mutex_);
|
remote_action.type = ControlType::host_infomation;
|
||||||
if (std::all_of(runtime->connection_status_.begin(),
|
memcpy(&remote_action.i.host_name, host_name.data(),
|
||||||
runtime->connection_status_.end(), [](const auto &kv) {
|
host_name.size());
|
||||||
return kv.first.find("web") == std::string::npos;
|
remote_action.i.host_name[host_name.size()] = '\0';
|
||||||
})) {
|
remote_action.i.host_name_size = host_name.size();
|
||||||
runtime->show_cursor_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
std::string msg = remote_action.to_json();
|
||||||
}
|
int ret =
|
||||||
default:
|
SendReliableDataFrame(runtime->peer_, msg.data(), msg.size(),
|
||||||
break;
|
runtime->control_data_label_.c_str());
|
||||||
|
remote_action_codec::Free(remote_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime->need_to_destroy_server_window_.store(
|
||||||
|
false, std::memory_order_release);
|
||||||
|
runtime->need_to_create_server_window_.store(true,
|
||||||
|
std::memory_order_release);
|
||||||
|
runtime->is_server_mode_ = true;
|
||||||
|
runtime->start_screen_capturer_ = true;
|
||||||
|
runtime->start_speaker_capturer_ = true;
|
||||||
|
runtime->remote_client_id_ = remote_id;
|
||||||
|
runtime->start_mouse_controller_ = true;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(runtime->connection_status_mutex_);
|
||||||
|
if (std::all_of(runtime->connection_status_.begin(),
|
||||||
|
runtime->connection_status_.end(),
|
||||||
|
[](const auto& kv) {
|
||||||
|
return kv.first.find("web") != std::string::npos;
|
||||||
|
})) {
|
||||||
|
runtime->show_cursor_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConnectionStatus::Disconnected:
|
||||||
|
case ConnectionStatus::Failed:
|
||||||
|
case ConnectionStatus::Closed: {
|
||||||
|
runtime->HandleServerControllerDisconnected(remote_id,
|
||||||
|
"connection_closed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerEventHandler::OnNetStatusReport(
|
void PeerEventHandler::OnNetStatusReport(
|
||||||
const char *client_id, size_t client_id_size, TraversalMode mode,
|
const char* client_id, size_t client_id_size, TraversalMode mode,
|
||||||
const XNetTrafficStats *net_traffic_stats, const char *user_id,
|
const XNetTrafficStats* net_traffic_stats, const char* user_id,
|
||||||
const size_t user_id_size, void *user_data) {
|
const size_t user_id_size, void* user_data) {
|
||||||
auto *handler = static_cast<PeerEventHandler *>(user_data);
|
auto* handler = static_cast<PeerEventHandler*>(user_data);
|
||||||
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
|
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
|
||||||
if (!runtime) {
|
if (!runtime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strchr(client_id, '@') != nullptr && strchr(user_id, '-') == nullptr) {
|
if (strchr(client_id, '@') != nullptr && strchr(user_id, '-') == nullptr) {
|
||||||
std::string id, password;
|
std::string id, password;
|
||||||
const char *at_pos = strchr(client_id, '@');
|
const char* at_pos = strchr(client_id, '@');
|
||||||
if (at_pos == nullptr) {
|
if (at_pos == nullptr) {
|
||||||
id = client_id;
|
id = client_id;
|
||||||
password.clear();
|
password.clear();
|
||||||
@@ -510,4 +463,4 @@ void PeerEventHandler::OnNetStatusReport(
|
|||||||
props->net_traffic_stats_ = *net_traffic_stats;
|
props->net_traffic_stats_ = *net_traffic_stats;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|||||||
+1
-1
Submodule submodules/minirtc updated: da4b0f2e9a...e6d796d7b7
@@ -19,7 +19,7 @@ std::filesystem::path FindRepoRoot() {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ReadFile(const std::filesystem::path &path) {
|
std::string ReadFile(const std::filesystem::path& path) {
|
||||||
std::ifstream file(path, std::ios::binary);
|
std::ifstream file(path, std::ios::binary);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return {};
|
return {};
|
||||||
@@ -30,8 +30,8 @@ std::string ReadFile(const std::filesystem::path &path) {
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExpectContains(const char *name, const std::string &value,
|
bool ExpectContains(const char* name, const std::string& value,
|
||||||
const std::string &expected) {
|
const std::string& expected) {
|
||||||
if (value.find(expected) != std::string::npos) {
|
if (value.find(expected) != std::string::npos) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -40,8 +40,8 @@ bool ExpectContains(const char *name, const std::string &value,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExpectNotContains(const char *name, const std::string &value,
|
bool ExpectNotContains(const char* name, const std::string& value,
|
||||||
const std::string &unexpected) {
|
const std::string& unexpected) {
|
||||||
if (value.find(unexpected) == std::string::npos) {
|
if (value.find(unexpected) == std::string::npos) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,10 @@ int main() {
|
|||||||
ReadFile(repo_root / "src/gui/runtime/remote_session.h");
|
ReadFile(repo_root / "src/gui/runtime/remote_session.h");
|
||||||
const std::string connection_runtime_cpp =
|
const std::string connection_runtime_cpp =
|
||||||
ReadFile(repo_root / "src/gui/runtime/connection_runtime.cpp");
|
ReadFile(repo_root / "src/gui/runtime/connection_runtime.cpp");
|
||||||
|
const std::string peer_event_handler_cpp =
|
||||||
|
ReadFile(repo_root / "src/gui/runtime/peer_event_handler.cpp");
|
||||||
|
const std::string application_state_h =
|
||||||
|
ReadFile(repo_root / "src/gui/application/application_state.h");
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
ok &= ExpectContains("minirtc.h", minirtc_api, "RemoteUnavailable");
|
ok &= ExpectContains("minirtc.h", minirtc_api, "RemoteUnavailable");
|
||||||
@@ -81,6 +85,11 @@ int main() {
|
|||||||
"\"Remote unavailable\"");
|
"\"Remote unavailable\"");
|
||||||
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||||
"ConnectionStatus::RemoteUnavailable");
|
"ConnectionStatus::RemoteUnavailable");
|
||||||
|
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||||
|
"IsCurrentPeerConnection");
|
||||||
|
ok &= ExpectContains(
|
||||||
|
"peer_connection.cpp", peer_connection,
|
||||||
|
"on_connection_status_(ConnectionStatus::Closed, remote_user_id.data()");
|
||||||
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
||||||
"\"Device offline\"");
|
"\"Device offline\"");
|
||||||
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
ok &= ExpectNotContains("peer_connection.cpp", peer_connection,
|
||||||
@@ -105,6 +114,14 @@ int main() {
|
|||||||
"kConnectionAttemptTimeout");
|
"kConnectionAttemptTimeout");
|
||||||
ok &= ExpectContains("connection_runtime.cpp", connection_runtime_cpp,
|
ok &= ExpectContains("connection_runtime.cpp", connection_runtime_cpp,
|
||||||
"connection_attempt_started_at_");
|
"connection_attempt_started_at_");
|
||||||
|
ok &= ExpectContains("connection_runtime.cpp", connection_runtime_cpp,
|
||||||
|
"HandleServerControllerDisconnected");
|
||||||
|
ok &= ExpectContains("peer_event_handler.cpp", peer_event_handler_cpp,
|
||||||
|
"HandleServerControllerDisconnected(remote_id");
|
||||||
|
ok &= ExpectNotContains("connection_runtime.cpp", connection_runtime_cpp,
|
||||||
|
"ControllerHeartbeat");
|
||||||
|
ok &= ExpectContains("application_state.h", application_state_h,
|
||||||
|
"std::atomic<bool> need_to_destroy_server_window_");
|
||||||
|
|
||||||
return ok ? 0 : 1;
|
return ok ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user