[fix] correct WGC and GDI cursor capture and positioning

This commit is contained in:
dijunkun
2026-09-16 23:03:36 +08:00
parent d9116200fe
commit ce7a3c178e
20 changed files with 309 additions and 56 deletions
@@ -67,7 +67,6 @@ struct InteractionState {
bool start_screen_capturer_ = false;
bool screen_capturer_is_started_ = false;
bool start_keyboard_capturer_ = false;
bool show_cursor_ = false;
bool keyboard_capturer_is_started_ = false;
bool keyboard_capturer_uses_window_events_ = false;
bool foucs_on_main_window_ = false;
@@ -271,6 +271,15 @@ int SessionDeviceManager::InitializeScreenCapturer() {
return -1;
}
bool SessionDeviceManager::ShouldCaptureCursor() const {
std::shared_lock lock(owner_.connection_status_mutex_);
return std::any_of(owner_.connection_status_.begin(),
owner_.connection_status_.end(), [](const auto& entry) {
return entry.second == ConnectionStatus::Connected &&
entry.first.find("web") != std::string::npos;
});
}
int SessionDeviceManager::StartScreenCapturer() {
#ifdef __APPLE__
if (!owner_.EnsureMacScreenRecordingPermission()) {
@@ -286,8 +295,11 @@ int SessionDeviceManager::StartScreenCapturer() {
}
}
LOG_INFO("Start screen capturer, show cursor: {}", owner_.show_cursor_);
const int ret = screen_capturer_->Start(owner_.show_cursor_);
// Clear before sampling, so a change arriving after the sample remains pending.
cursor_capture_dirty_.store(false, std::memory_order_relaxed);
const bool show_cursor = ShouldCaptureCursor();
LOG_INFO("Start screen capturer, show cursor: {}", show_cursor);
const int ret = screen_capturer_->Start(show_cursor);
if (ret != 0) {
LOG_ERROR("Start screen capturer failed: {}", ret);
}
@@ -503,6 +515,15 @@ void SessionDeviceManager::PushAudio(const char *data, size_t size) {
}
void SessionDeviceManager::UpdateInteractions() {
#ifdef _WIN32
if (owner_.screen_capturer_is_started_ &&
cursor_capture_dirty_.load(std::memory_order_relaxed)) {
if (auto* windows = dynamic_cast<ScreenCapturerWin*>(screen_capturer_)) {
cursor_capture_dirty_.store(false, std::memory_order_relaxed);
windows->SetCursorCapture(ShouldCaptureCursor());
}
}
#endif
#if defined(__linux__) && !defined(__APPLE__)
const bool is_wayland_session = IsWaylandSession();
const bool stop_wayland_mouse_before_screen =
@@ -10,6 +10,7 @@
#include <SDL3/SDL.h>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
@@ -40,6 +41,11 @@ public:
void Initialize();
void UpdateInteractions();
// Call after changing the controller map. UpdateInteractions consumes this
// notification on the UI thread before reading the map under its mutex.
void OnControllerConnectionsChanged() {
cursor_capture_dirty_.store(true, std::memory_order_relaxed);
}
void DestroyDevices();
void DestroyFactories();
@@ -84,6 +90,7 @@ private:
void ClearCapturedKeyboardInput();
bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now,
int fps);
bool ShouldCaptureCursor() const;
void RecordCaptureCadence(std::chrono::steady_clock::time_point now, int fps,
bool from_secure_desktop);
@@ -91,6 +98,7 @@ private:
SDL_AudioStream *output_stream_ = nullptr;
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
ScreenCapturer *screen_capturer_ = nullptr;
std::atomic<bool> cursor_capture_dirty_{true};
SpeakerCaptureController speaker_capture_;
DeviceControllerFactory *device_controller_factory_ = nullptr;
MouseController *mouse_controller_ = nullptr;
@@ -140,11 +140,11 @@ void GuiRuntime::HandleServerControllerDisconnected(
}
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);
devices_.OnControllerConnectionsChanged();
connection_host_names_.erase(remote_id);
privacy_sessions_.Disconnected(remote_id);
for (const auto& [id, status] : connection_status_) {
@@ -152,14 +152,11 @@ void GuiRuntime::HandleServerControllerDisconnected(
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;
@@ -7,6 +7,7 @@
#include <windows.h>
#include "platform/windows/input/windows_cursor_state.h"
#include "platform/windows/screen_capturer/captured_cursor_state.h"
#include "platform/windows/screen_capturer/dxgi_cursor_state.h"
#include "platform/windows/screen_capturer/secure_desktop_cursor_state.h"
#include "runtime/cursor_position.h"
@@ -53,8 +54,9 @@ bool CursorStateProvider::Sample(const std::vector<DisplayInfo>& displays,
void* cursor_monitor = state->position_valid
? displays[state->display_id].handle
: nullptr;
const bool embedded = (info.flags & CURSOR_SHOWING) != 0 &&
!SharedDxgiCursorState().ShouldDrawCursor(true, cursor_monitor);
const bool embedded = SharedCapturedCursorState().IsEmbedded(cursor_monitor) ||
((info.flags & CURSOR_SHOWING) != 0 &&
!SharedDxgiCursorState().ShouldDrawCursor(true, cursor_monitor));
impl_->cursor.Sample(info, embedded, state);
return true;
}
@@ -419,6 +419,7 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
}
}
runtime->connection_status_[remote_id] = status;
runtime->devices_.OnControllerConnectionsChanged();
}
switch (status) {
@@ -485,16 +486,6 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
runtime->devices_.StartSpeakerCapturer();
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;
}