[fix] improve Windows secure desktop capture and input transitions

This commit is contained in:
kunkundi
2026-10-09 00:05:37 +08:00
parent 5004c91a6c
commit 3cfe7cbb80
26 changed files with 1376 additions and 582 deletions
@@ -665,11 +665,12 @@ bool SessionDeviceManager::SendKeyboardCommand(int key_code, bool is_down,
key_code, is_down, scan_code, extended) == 0;
}
void SessionDeviceManager::SendMouseCommand(const RemoteAction &action,
bool SessionDeviceManager::SendMouseCommand(const RemoteAction &action,
int selected_display) {
if (mouse_controller_) {
mouse_controller_->SendMouseCommand(action, selected_display);
return mouse_controller_->SendMouseCommand(action, selected_display) == 0;
}
return false;
}
int SessionDeviceManager::SwitchDisplay(int display_id) {
@@ -71,7 +71,7 @@ public:
bool SendKeyboardCommand(int key_code, bool is_down, uint32_t scan_code,
bool extended);
void SendMouseCommand(const RemoteAction &action, int selected_display);
bool SendMouseCommand(const RemoteAction &action, int selected_display);
int SwitchDisplay(int display_id);
void ResetToInitialDisplay();
@@ -16,7 +16,8 @@
#include "runtime/gui_runtime.h"
#include "windows_key_metadata.h"
#if _WIN32
#include "interactive_state.h"
#include "desktop_transition_policy.h"
#include "interactive_desktop.h"
#include "service_host.h"
#endif
@@ -82,12 +83,11 @@ void LogSecureDesktopInputBlocked(uint32_t* last_tick, const char* stage) {
}
bool IsTransientSecureDesktopInputFailure(const nlohmann::json& response,
const RemoteAction& action) {
bool is_down) {
return response.is_object() &&
response.value("error", std::string()) == "send_input_failed" &&
response.value("code", 0u) == ERROR_ACCESS_DENIED &&
action.type == ControlType::keyboard &&
action.k.flag == KeyFlag::key_up;
!is_down;
}
#endif
@@ -218,40 +218,51 @@ bool KeyboardController::InjectRemoteKey(int key_code, bool is_down,
if (owner_.privacy_.Engaged() && !IsWindowsPrivacyDesktopAvailable()) {
owner_.privacy_.SuspendForDesktop();
}
if (owner_.local_service_status_received_ &&
IsSecureDesktopInteractionRequired(owner_.local_interactive_stage_)) {
const std::string response = SendCrossDeskSecureDesktopKeyInput(
key_code, is_down, scan_code, extended, 1000);
const auto json = nlohmann::json::parse(response, nullptr, false);
if (json.is_discarded() || !json.value("ok", false)) {
RemoteAction action{};
action.type = ControlType::keyboard;
action.k.key_value = static_cast<size_t>(key_code);
action.k.scan_code = scan_code;
action.k.extended = extended;
action.k.flag = is_down ? KeyFlag::key_down : KeyFlag::key_up;
if (!json.is_discarded() &&
IsTransientSecureDesktopInputFailure(json, action)) {
LOG_INFO(
"Secure desktop keyboard injection transient failure, "
"key_code={}, is_down={}, response={}",
key_code, is_down, response);
return true;
}
if (owner_.is_server_mode_ || !owner_.WindowsInputStage().empty()) {
return DispatchDesktopInput(
[&] {
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
owner_.windows_consent_ui_.load());
},
[&] {
SetLastError(ERROR_SUCCESS);
const bool sent = owner_.devices_.SendKeyboardCommand(
key_code, is_down, scan_code, extended);
return DesktopInputResult{
sent, IsDesktopTransitionInputError(GetLastError())};
},
[&]() -> DesktopInputResult {
const std::string response = SendCrossDeskSecureDesktopKeyInput(
key_code, is_down, scan_code, extended, 1000);
const auto json = nlohmann::json::parse(response, nullptr, false);
if (!json.is_object() || !json.value("ok", false)) {
const std::string error =
json.is_object() ? json.value("error", "") : "";
if (IsDesktopInputSetupPending(error)) {
return {false, true};
}
if (IsTransientSecureDesktopInputFailure(json, is_down)) {
LOG_INFO(
"Secure desktop keyboard injection transient failure, "
"key_code={}, is_down={}, response={}",
key_code, is_down, response);
return {true, false};
}
LogSecureDesktopInputBlocked(
&owner_.last_local_secure_input_block_log_tick_,
owner_.local_interactive_stage_.c_str());
LOG_WARN(
"Secure desktop keyboard injection failed, key_code={}, is_down={}, "
"response={}",
key_code, is_down, response);
return false;
}
return true;
LogSecureDesktopInputBlocked(
&owner_.last_local_secure_input_block_log_tick_,
owner_.WindowsInputStage().c_str());
LOG_WARN(
"Secure desktop keyboard injection failed, key_code={}, "
"is_down={}, "
"response={}",
key_code, is_down, response);
return {};
}
return {true, false};
});
}
#endif
// Recheck after the desktop query; key-up cleanup remains allowed while paused.
return owner_.devices_.SendKeyboardCommand(key_code, is_down, scan_code,
extended);
}
@@ -87,6 +87,15 @@ class GuiRuntime : protected gui_detail::GuiState {
RemoteUnlockState GetRemoteUnlockState(const RemoteSession& props) const;
#if _WIN32
void ResetLocalWindowsServiceState(bool clear_pending_sas);
std::string WindowsInputStage() const {
std::lock_guard lock(windows_input_stage_mutex_);
return windows_input_stage_;
}
void PublishWindowsInputStage() {
std::lock_guard lock(windows_input_stage_mutex_);
windows_input_stage_ = local_service_status_received_
? local_interactive_stage_ : std::string{};
}
#endif
#ifdef __APPLE__
@@ -26,7 +26,8 @@
#include "runtime/gui_runtime.h"
#if _WIN32
#include "interactive_state.h"
#include "desktop_transition_policy.h"
#include "interactive_desktop.h"
#include "service_host.h"
#endif
@@ -392,45 +393,60 @@ void PeerEventHandler::OnReceiveDataBuffer(
std::chrono::steady_clock::now();
}
#if _WIN32
if (runtime->local_service_status_received_ &&
IsSecureDesktopInteractionRequired(runtime->local_interactive_stage_) &&
remote_action.type != ControlType::keyboard &&
remote_action.type != ControlType::keyboard_state) {
if (remote_action.type == ControlType::mouse) {
int absolute_x = 0;
int absolute_y = 0;
if (!BuildAbsoluteMousePosition(runtime->devices_.display_info_list(),
runtime->selected_display_,
remote_action.m.x, remote_action.m.y,
&absolute_x, &absolute_y)) {
LOG_WARN("Secure desktop mouse injection skipped, invalid display "
"mapping: display_index={}, x={}, y={}",
runtime->selected_display_, remote_action.m.x,
remote_action.m.y);
return;
}
if (runtime->is_server_mode_ && remote_action.type == ControlType::mouse) {
std::string response;
const bool delivered = DispatchDesktopInput(
[&] {
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
runtime->windows_consent_ui_.load());
},
[&] {
SetLastError(ERROR_SUCCESS);
const bool sent = runtime->devices_.SendMouseCommand(
remote_action, runtime->selected_display_);
return DesktopInputResult{
sent, IsDesktopTransitionInputError(GetLastError())};
},
[&]() -> DesktopInputResult {
int absolute_x = 0;
int absolute_y = 0;
if (!BuildAbsoluteMousePosition(
runtime->devices_.display_info_list(),
runtime->selected_display_, remote_action.m.x,
remote_action.m.y, &absolute_x, &absolute_y)) {
LOG_WARN(
"Secure desktop mouse injection skipped, invalid display "
"mapping: display_index={}, x={}, y={}",
runtime->selected_display_, remote_action.m.x,
remote_action.m.y);
return {};
}
const std::string response = SendCrossDeskSecureDesktopMouseInput(
absolute_x, absolute_y, remote_action.m.s,
static_cast<int>(remote_action.m.flag), 1000);
auto json = nlohmann::json::parse(response, nullptr, false);
if (json.is_discarded() || !json.value("ok", false)) {
LogSecureDesktopInputBlocked(
&runtime->last_local_secure_input_block_log_tick_, "local",
runtime->local_interactive_stage_.c_str());
LOG_WARN(
"Secure desktop mouse injection failed, x={}, y={}, wheel={}, "
"flag={}, response={}",
absolute_x, absolute_y, remote_action.m.s,
static_cast<int>(remote_action.m.flag), response);
}
return;
response = SendCrossDeskSecureDesktopMouseInput(
absolute_x, absolute_y, remote_action.m.s,
static_cast<int>(remote_action.m.flag), 1000);
auto json = nlohmann::json::parse(response, nullptr, false);
const bool sent = json.is_object() && json.value("ok", false);
const std::string error =
json.is_object() ? json.value("error", "") : "";
return {sent, IsDesktopInputSetupPending(error)};
});
if (!delivered && !response.empty()) {
LogSecureDesktopInputBlocked(
&runtime->last_local_secure_input_block_log_tick_, "local",
runtime->WindowsInputStage().c_str());
LOG_WARN(
"Secure desktop mouse injection failed, x={}, y={}, wheel={}, "
"flag={}, response={}",
remote_action.m.x, remote_action.m.y, remote_action.m.s,
static_cast<int>(remote_action.m.flag), response);
}
return;
}
#endif
if (remote_action.type == ControlType::mouse) {
runtime->devices_.SendMouseCommand(remote_action,
runtime->selected_display_);
runtime->selected_display_);
} else if (remote_action.type == ControlType::audio_capture) {
if (remote_action.a)
runtime->devices_.StartSpeakerCapturer();
@@ -29,6 +29,10 @@
#include "runtime/session_lifecycle.h"
#include "thumbnail.h"
#if _WIN32
#include "platform/windows/service/service_query_worker.h"
#endif
namespace crossdesk::gui_detail {
struct InfrastructureState {
@@ -83,6 +87,11 @@ struct PeerState {
// state so platform code does not appear to be part of the wire contract.
struct PlatformIntegrationState {
#if _WIN32
std::unique_ptr<ServiceQueryWorker> windows_service_worker_;
// Published by the UI, read by transport callbacks without sharing a mutable string.
mutable std::mutex windows_input_stage_mutex_;
std::string windows_input_stage_;
std::atomic<bool> windows_consent_ui_{false};
std::atomic<bool> pending_windows_service_sas_{false};
bool local_service_status_received_ = false;
bool local_service_available_ = false;