[fix] prevent duplicate cursors during Windows window dragging

This commit is contained in:
dijunkun
2026-09-16 13:51:35 +08:00
parent 57cab3979e
commit 57bb91dba3
4 changed files with 73 additions and 3 deletions
@@ -6,6 +6,7 @@
#include <windows.h>
#include "platform/windows/screen_capturer/dxgi_cursor_state.h"
#include "runtime/cursor_position.h"
namespace crossdesk {
@@ -52,11 +53,15 @@ bool CursorStateProvider::Sample(const std::vector<DisplayInfo>& displays,
if (!GetCursorInfo(&info)) return false;
state->seq = 0;
state->visible = (info.flags & CURSOR_SHOWING) != 0;
state->shape = state->visible ? ShapeFromWindowsCursor(info.hCursor)
: RemoteCursorShape::none;
NormalizeCursorPosition(info.ptScreenPos.x, info.ptScreenPos.y, displays,
preferred_display, state);
void* cursor_monitor = state->position_valid
? displays[state->display_id].handle
: nullptr;
state->visible = SharedDxgiCursorState().ShouldDrawCursor(
(info.flags & CURSOR_SHOWING) != 0, cursor_monitor);
state->shape = state->visible ? ShapeFromWindowsCursor(info.hCursor)
: RemoteCursorShape::none;
return true;
}
@@ -0,0 +1,48 @@
#ifndef CROSSDESK_DXGI_CURSOR_STATE_H_
#define CROSSDESK_DXGI_CURSOR_STATE_H_
#include <atomic>
#include <cstdint>
namespace crossdesk {
// DXGI's Visible flag describes the separate hardware pointer, not the
// system cursor. When it is false, a visible system cursor can already be
// included in the desktop texture (for example, during window dragging).
// Share that distinction with the GUI thread without changing cursor input
// coordinates or the wire protocol.
class DxgiCursorState {
public:
void Update(int64_t last_mouse_update_time, bool separate_pointer_visible,
void* monitor) {
// PointerPosition is undefined on desktop-only updates. Retain the last
// pointer state until DXGI actually reports another pointer update.
if (last_mouse_update_time == 0) return;
embedded_monitor_.store(separate_pointer_visible ? nullptr : monitor,
std::memory_order_relaxed);
}
void Reset() {
embedded_monitor_.store(nullptr, std::memory_order_relaxed);
}
bool ShouldDrawCursor(bool system_cursor_visible, void* cursor_monitor) const {
// A hidden cursor or a pointer on another monitor also makes DXGI report
// Visible=false. Only suppress a visible cursor on the captured monitor.
return system_cursor_visible &&
(!cursor_monitor ||
embedded_monitor_.load(std::memory_order_relaxed) != cursor_monitor);
}
private:
std::atomic<void*> embedded_monitor_{nullptr};
};
inline DxgiCursorState& SharedDxgiCursorState() {
static DxgiCursorState state;
return state;
}
} // namespace crossdesk
#endif // CROSSDESK_DXGI_CURSOR_STATE_H_
@@ -7,6 +7,7 @@
#include <string>
#include <vector>
#include "dxgi_cursor_state.h"
#include "libyuv.h"
#include "rd_log.h"
@@ -232,6 +233,7 @@ void ScreenCapturerDxgi::EnumerateDisplays() {
}
bool ScreenCapturerDxgi::CreateDuplicationForMonitor(int monitor_index) {
SharedDxgiCursorState().Reset();
if (monitor_index < 0 || monitor_index >= (int)outputs_.size()) return false;
Microsoft::WRL::ComPtr<IDXGIOutput1> output1;
HRESULT hr = outputs_[monitor_index]->QueryInterface(
@@ -284,6 +286,7 @@ bool ScreenCapturerDxgi::RecreateDuplicationForCurrentMonitor() {
}
void ScreenCapturerDxgi::ReleaseDuplication() {
SharedDxgiCursorState().Reset();
++duplication_generation_;
staging_.Reset();
if (duplication_) {
@@ -344,6 +347,7 @@ void ScreenCapturerDxgi::CaptureLoop() {
// a backend rebuild. Normal error/reset handling releases optional privacy.
cached_frame_valid = false;
if (FAILED(hr)) {
SharedDxgiCursorState().Reset();
LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr);
capture_lock.unlock();
if (callback_)
@@ -352,6 +356,14 @@ void ScreenCapturerDxgi::CaptureLoop() {
continue;
}
if (frame_monitor >= 0 &&
frame_monitor < static_cast<int>(display_info_list_.size())) {
SharedDxgiCursorState().Update(
frame_info.LastMouseUpdateTime.QuadPart,
frame_info.PointerPosition.Visible != FALSE,
display_info_list_[frame_monitor].handle);
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> acquired_tex;
if (desktop_resource) {
hr = desktop_resource->QueryInterface(
@@ -16,6 +16,7 @@
#include <display_stream_id.h>
#include "captured_nv12_frame.h"
#include "dxgi_cursor_state.h"
#include "interactive_state.h"
#include "named_pipe_deadline.h"
#include "rd_log.h"
@@ -364,6 +365,7 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
return;
}
if (secure_desktop_capture_active_.load(std::memory_order_relaxed)) {
SharedDxgiCursorState().Reset();
return;
}
@@ -470,6 +472,9 @@ void ScreenCapturerWin::EmitCapturedFrame(
unsigned char* data, int size, int width, int height,
const char* stream_id, const MiniRtcNativeVideoFrame* native_frame,
bool from_secure_desktop) {
// Helper frames do not use DXGI's pointer plane. Do not carry normal-desktop
// cursor suppression into the lock screen or UAC desktop.
if (from_secure_desktop) SharedDxgiCursorState().Reset();
// Secure-desktop capture takes priority; remove privacy without dropping
// the helper frame or delaying the remote session.
if (from_secure_desktop && privacy_ && privacy_->Engaged()) {