mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-15 00:09:39 +08:00
[fix] route unelevated Windows input through service
This commit is contained in:
@@ -19,8 +19,10 @@
|
||||
#include "runtime/gui_runtime.h"
|
||||
#include "speaker_capturer_factory.h"
|
||||
#ifdef _WIN32
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "platform/windows/screen_capturer/screen_capturer_win.h"
|
||||
#include "platform/windows/input/mouse/mouse_controller.h"
|
||||
#include "service_host.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
@@ -327,6 +329,37 @@ int SessionDeviceManager::StopScreenCapturer() {
|
||||
void SessionDeviceManager::ReleaseRemoteMouseButtons() {
|
||||
if (auto mouse = dynamic_cast<PlatformMouseController*>(mouse_controller_))
|
||||
mouse->ReleasePressedButtons();
|
||||
std::lock_guard lock(service_mouse_mutex_);
|
||||
for (int button = 0; button < 3; ++button) {
|
||||
const unsigned bit = 1u << button;
|
||||
if (!(service_mouse_buttons_ & bit)) continue;
|
||||
const auto response = SendCrossDeskSecureDesktopMouseInput(
|
||||
service_mouse_x_, service_mouse_y_, 0, (button + 1) * 2, 1000);
|
||||
const auto json = nlohmann::json::parse(response, nullptr, false);
|
||||
if (json.is_object() && json.value("ok", false)) {
|
||||
service_mouse_buttons_ &= ~bit;
|
||||
} else {
|
||||
LOG_WARN("Service mouse button release failed: button={}, response={}",
|
||||
button, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string SessionDeviceManager::SendServiceMouseCommand(int x, int y,
|
||||
int wheel, int flag) {
|
||||
std::lock_guard lock(service_mouse_mutex_);
|
||||
service_mouse_x_ = x;
|
||||
service_mouse_y_ = y;
|
||||
const unsigned bit = flag >= 1 && flag <= 6 ? 1u << ((flag - 1) / 2) : 0;
|
||||
// A timeout can follow successful injection. Keep downs for disconnect
|
||||
// cleanup even if the reply was lost; only clear a confirmed release.
|
||||
if (flag % 2 == 1) service_mouse_buttons_ |= bit;
|
||||
const auto response = SendCrossDeskSecureDesktopMouseInput(x, y, wheel, flag,
|
||||
1000);
|
||||
const auto json = nlohmann::json::parse(response, nullptr, false);
|
||||
if (flag % 2 == 0 && json.is_object() && json.value("ok", false))
|
||||
service_mouse_buttons_ &= ~bit;
|
||||
return response;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -412,6 +445,9 @@ int SessionDeviceManager::StartMouseController() {
|
||||
}
|
||||
|
||||
int SessionDeviceManager::StopMouseController() {
|
||||
#ifdef _WIN32
|
||||
ReleaseRemoteMouseButtons();
|
||||
#endif
|
||||
if (mouse_controller_) {
|
||||
mouse_controller_->Destroy();
|
||||
delete mouse_controller_;
|
||||
@@ -718,11 +754,7 @@ std::vector<HostDisplay> SessionDeviceManager::host_display_list() const {
|
||||
|
||||
void SessionDeviceManager::DestroyDevices() {
|
||||
speaker_capture_.Shutdown();
|
||||
if (mouse_controller_) {
|
||||
mouse_controller_->Destroy();
|
||||
delete mouse_controller_;
|
||||
mouse_controller_ = nullptr;
|
||||
}
|
||||
StopMouseController();
|
||||
if (screen_capturer_) {
|
||||
screen_capturer_->Destroy();
|
||||
delete screen_capturer_;
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
int StopScreenCapturer();
|
||||
#ifdef _WIN32
|
||||
void ReleaseRemoteMouseButtons();
|
||||
std::string SendServiceMouseCommand(int x, int y, int wheel, int flag);
|
||||
#endif
|
||||
// Requests are asynchronous; Stop drains/revokes audio delivery before
|
||||
// returning so the connection peer can be destroyed safely.
|
||||
@@ -107,6 +108,9 @@ private:
|
||||
CapturedKeyboardQueue captured_keyboard_inputs_;
|
||||
#ifdef _WIN32
|
||||
std::chrono::steady_clock::time_point next_keyboard_capture_retry_{};
|
||||
std::mutex service_mouse_mutex_;
|
||||
unsigned service_mouse_buttons_ = 0;
|
||||
int service_mouse_x_ = 0, service_mouse_y_ = 0;
|
||||
#endif
|
||||
std::chrono::steady_clock::time_point last_frame_time_{};
|
||||
std::chrono::steady_clock::time_point next_frame_deadline_{};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "desktop_transition_policy.h"
|
||||
#include "interactive_desktop.h"
|
||||
#include "service_host.h"
|
||||
#include "unattended_config.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
@@ -229,10 +230,13 @@ bool KeyboardController::InjectRemoteKey(int key_code, bool is_down,
|
||||
owner_.privacy_.SuspendForDesktop();
|
||||
}
|
||||
if (owner_.is_server_mode_ || !owner_.WindowsInputStage().empty()) {
|
||||
static const bool process_elevated = IsAdministratorProcess();
|
||||
return DispatchDesktopInput(
|
||||
[&] {
|
||||
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
|
||||
owner_.windows_consent_ui_.load());
|
||||
owner_.windows_consent_ui_.load(),
|
||||
owner_.local_service_available_.load(),
|
||||
process_elevated);
|
||||
},
|
||||
[&] {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "desktop_transition_policy.h"
|
||||
#include "interactive_desktop.h"
|
||||
#include "service_host.h"
|
||||
#include "unattended_config.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
@@ -394,11 +395,14 @@ void PeerEventHandler::OnReceiveDataBuffer(
|
||||
}
|
||||
#if _WIN32
|
||||
if (runtime->is_server_mode_ && remote_action.type == ControlType::mouse) {
|
||||
static const bool process_elevated = IsAdministratorProcess();
|
||||
std::string response;
|
||||
const bool delivered = DispatchDesktopInput(
|
||||
[&] {
|
||||
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
|
||||
runtime->windows_consent_ui_.load());
|
||||
return PreferUserDesktopInput(
|
||||
IsCurrentSessionUserDesktopActive(),
|
||||
runtime->windows_consent_ui_.load(),
|
||||
runtime->local_service_available_.load(), process_elevated);
|
||||
},
|
||||
[&] {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
@@ -425,9 +429,9 @@ void PeerEventHandler::OnReceiveDataBuffer(
|
||||
return {};
|
||||
}
|
||||
|
||||
response = SendCrossDeskSecureDesktopMouseInput(
|
||||
response = runtime->devices_.SendServiceMouseCommand(
|
||||
absolute_x, absolute_y, remote_action.m.s,
|
||||
static_cast<int>(remote_action.m.flag), 1000);
|
||||
static_cast<int>(remote_action.m.flag));
|
||||
auto json = nlohmann::json::parse(response, nullptr, false);
|
||||
const bool sent = json.is_object() && json.value("ok", false);
|
||||
const std::string error =
|
||||
|
||||
@@ -94,7 +94,7 @@ struct PlatformIntegrationState {
|
||||
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;
|
||||
std::atomic<bool> local_service_available_{false};
|
||||
std::string local_interactive_stage_;
|
||||
uint32_t last_local_secure_input_block_log_tick_ = 0;
|
||||
uint32_t last_windows_service_status_tick_ = 0;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "desktop_transition_policy.h"
|
||||
#include "interactive_state.h"
|
||||
#include "service_host.h"
|
||||
#include "unattended_config.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
@@ -205,7 +206,12 @@ void GuiRuntime::HandleWindowsServiceIntegration() {
|
||||
status.interactive_stage == "user-desktop";
|
||||
local_service_status_received_ =
|
||||
status_ok || previous_secure_desktop_interaction;
|
||||
local_service_available_ = status.available;
|
||||
const bool service_was_available =
|
||||
local_service_available_.exchange(status.available);
|
||||
if (status.available && !service_was_available && !IsAdministratorProcess()) {
|
||||
LOG_INFO("Unelevated host input routed through Windows service, including "
|
||||
"elevated applications on the user desktop");
|
||||
}
|
||||
if (status.available) {
|
||||
if (keep_optimistic_secure_desktop) {
|
||||
local_interactive_stage_ = "secure-desktop";
|
||||
|
||||
@@ -19,6 +19,18 @@ inline bool PreferUserDesktopInput(bool user_desktop_active,
|
||||
return user_desktop_active && !consent_ui_visible;
|
||||
}
|
||||
|
||||
inline bool PreferUserDesktopInput(bool user_desktop_active,
|
||||
bool consent_ui_visible,
|
||||
bool service_available,
|
||||
bool process_elevated) {
|
||||
// An elevated application still lives on Default. SendInput only reports
|
||||
// insertion into the input stream, not delivery to that application's UI.
|
||||
// Use the authorized service proactively for an unelevated host, including
|
||||
// clicks on an elevated window that has not become the foreground window.
|
||||
return PreferUserDesktopInput(user_desktop_active, consent_ui_visible) &&
|
||||
(process_elevated || !service_available);
|
||||
}
|
||||
|
||||
inline bool IsDesktopInputSetupPending(const std::string& error) {
|
||||
// These failures explicitly precede injection; timeouts remain ambiguous.
|
||||
return error == "secure_input_helper_not_ready" ||
|
||||
|
||||
@@ -1250,6 +1250,7 @@ LONG NormalizeAbsoluteMouseCoordinate(int value, int origin, int size) {
|
||||
INPUT BuildAbsoluteMouseMoveInput(int x, int y) {
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwExtraInfo = crossdesk::kInjectedMouseInputMarker;
|
||||
|
||||
const int virtual_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
const int virtual_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
@@ -1283,6 +1284,7 @@ InputInjectionResult InjectMouseInput(const SecureMouseRequest& request) {
|
||||
|
||||
INPUT action_input = {0};
|
||||
action_input.type = INPUT_MOUSE;
|
||||
action_input.mi.dwExtraInfo = crossdesk::kInjectedMouseInputMarker;
|
||||
switch (request.flag) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user