[fix] restore Windows remote keyboard shortcuts and input recovery

This commit is contained in:
dijunkun
2026-10-09 03:43:14 +08:00
parent 3cfe7cbb80
commit 06e23007ed
10 changed files with 626 additions and 312 deletions
@@ -27,8 +27,10 @@ namespace crossdesk {
namespace {
constexpr uint64_t kCaptureResumeKeyFrameGapMs = 500;
constexpr size_t kMaxCapturedKeyboardInputs = 512;
constexpr auto kFrameDeadlineTolerance = std::chrono::milliseconds(1);
#ifdef _WIN32
constexpr auto kKeyboardCaptureRetryDelay = std::chrono::seconds(1);
#endif
} // namespace
@@ -419,8 +421,12 @@ int SessionDeviceManager::StopMouseController() {
}
int SessionDeviceManager::StartKeyboardCapturer() {
ClearCapturedKeyboardInput();
captured_keyboard_inputs_.Clear();
owner_.keyboard_capturer_uses_window_events_ = false;
#ifdef _WIN32
next_keyboard_capture_retry_ =
std::chrono::steady_clock::now() + kKeyboardCaptureRetryDelay;
#endif
#ifdef __APPLE__
if (!owner_.EnsureMacAccessibilityPermission()) {
@@ -449,8 +455,10 @@ int SessionDeviceManager::StartKeyboardCapturer() {
void *user_ptr) {
if (user_ptr) {
auto *devices = static_cast<SessionDeviceManager *>(user_ptr);
devices->QueueCapturedKeyboardInput(key_code, is_down, scan_code,
extended);
// Native hooks only enqueue; forwarding and logging stay off the
// hook thread so they cannot trigger LowLevelHooksTimeout.
devices->captured_keyboard_inputs_.Push(
{key_code, is_down, scan_code, extended});
}
},
this);
@@ -468,7 +476,7 @@ int SessionDeviceManager::StartKeyboardCapturer() {
int SessionDeviceManager::StopKeyboardCapturer() {
owner_.keyboard_capturer_is_started_ = false;
if (keyboard_capturer_) keyboard_capturer_->Unhook();
ClearCapturedKeyboardInput();
captured_keyboard_inputs_.Clear();
if (owner_.keyboard_capturer_uses_window_events_) {
owner_.keyboard_capturer_uses_window_events_ = false;
LOG_INFO("Stop keyboard capturer with Slint keyboard backend");
@@ -602,7 +610,18 @@ void SessionDeviceManager::UpdateInteractions() {
#endif
}
if (owner_.start_keyboard_capturer_ && owner_.focus_on_stream_window_) {
if (owner_.start_keyboard_capturer_ && owner_.focus_on_stream_window_ &&
!owner_.privacy_.Snapshot().input_blocked) {
#ifdef _WIN32
if (owner_.keyboard_capturer_is_started_ &&
owner_.keyboard_capturer_uses_window_events_ && keyboard_capturer_ &&
std::chrono::steady_clock::now() >= next_keyboard_capture_retry_) {
// A transient installation failure or stalled hook must not leave
// system shortcuts in the window-event fallback until focus changes.
StopKeyboardCapturer();
owner_.keyboard_.ForceReleasePressedKeys();
}
#endif
if (!owner_.keyboard_capturer_is_started_ && StartKeyboardCapturer() == 0) {
owner_.keyboard_capturer_is_started_ = true;
}
@@ -613,6 +632,10 @@ void SessionDeviceManager::UpdateInteractions() {
owner_.keyboard_.ForceReleasePressedKeys();
owner_.keyboard_capturer_uses_window_events_ = true;
owner_.keyboard_capturer_is_started_ = true;
#ifdef _WIN32
next_keyboard_capture_retry_ =
std::chrono::steady_clock::now() + kKeyboardCaptureRetryDelay;
#endif
LOG_WARN("Native keyboard capture stopped; using Slint keyboard events");
}
DrainCapturedKeyboardInput();
@@ -621,43 +644,25 @@ void SessionDeviceManager::UpdateInteractions() {
} else if (owner_.keyboard_capturer_is_started_) {
owner_.keyboard_.ForceReleasePressedKeys();
StopKeyboardCapturer();
owner_.keyboard_capturer_is_started_ = false;
}
owner_.keyboard_.CheckRemoteTimeouts();
}
void SessionDeviceManager::QueueCapturedKeyboardInput(int key_code,
bool is_down,
uint32_t scan_code,
bool extended) {
std::lock_guard<std::mutex> lock(captured_keyboard_inputs_mutex_);
if (captured_keyboard_inputs_.size() >= kMaxCapturedKeyboardInputs) {
captured_keyboard_inputs_.pop_front();
LOG_WARN("Captured keyboard input queue overflow, dropping oldest event");
}
captured_keyboard_inputs_.push_back(
CapturedKeyboardInput{key_code, is_down, scan_code, extended});
}
void SessionDeviceManager::DrainCapturedKeyboardInput() {
std::deque<CapturedKeyboardInput> inputs;
{
std::lock_guard<std::mutex> lock(captured_keyboard_inputs_mutex_);
inputs.swap(captured_keyboard_inputs_);
}
const auto inputs = captured_keyboard_inputs_.Drain();
if (owner_.privacy_.Snapshot().input_blocked) return;
for (const CapturedKeyboardInput &input : inputs) {
if (inputs.resync) {
LOG_WARN("Captured keyboard queue overflow; reconciling pressed keys");
owner_.keyboard_.RecoverCapturedState(inputs.pressed);
return;
}
for (const KeyboardInput &input : inputs.events) {
owner_.keyboard_.SendKeyCommand(input.key_code, input.is_down,
input.scan_code, input.extended);
}
}
void SessionDeviceManager::ClearCapturedKeyboardInput() {
std::lock_guard<std::mutex> lock(captured_keyboard_inputs_mutex_);
captured_keyboard_inputs_.clear();
}
bool SessionDeviceManager::SendKeyboardCommand(int key_code, bool is_down,
uint32_t scan_code,
bool extended) {
@@ -7,12 +7,10 @@
#ifndef _SESSION_DEVICE_MANAGER_H_
#define _SESSION_DEVICE_MANAGER_H_
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <mutex>
#include <string>
#include <vector>
@@ -21,6 +19,7 @@
#include "device_controller.h"
#include "display_info.h"
#include "features/input/captured_keyboard_queue.h"
#include "speaker_capture_controller.h"
struct SDL_AudioStream;
@@ -83,17 +82,7 @@ public:
std::vector<HostDisplay> host_display_list() const;
private:
struct CapturedKeyboardInput {
int key_code = 0;
bool is_down = false;
uint32_t scan_code = 0;
bool extended = false;
};
void QueueCapturedKeyboardInput(int key_code, bool is_down,
uint32_t scan_code, bool extended);
void DrainCapturedKeyboardInput();
void ClearCapturedKeyboardInput();
bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now,
int fps);
bool ShouldCaptureCursor() const;
@@ -115,8 +104,10 @@ private:
mutable std::mutex display_info_mutex_;
std::vector<DisplayInfo> display_info_list_;
size_t registered_display_stream_count_ = 0;
std::deque<CapturedKeyboardInput> captured_keyboard_inputs_;
std::mutex captured_keyboard_inputs_mutex_;
CapturedKeyboardQueue captured_keyboard_inputs_;
#ifdef _WIN32
std::chrono::steady_clock::time_point next_keyboard_capture_retry_{};
#endif
std::chrono::steady_clock::time_point last_frame_time_{};
std::chrono::steady_clock::time_point next_frame_deadline_{};
std::mutex capture_metrics_mutex_;