Skip to content

Commit

Permalink
fix: 正确还原原始光标裁剪区域,以及添加注释
Browse files Browse the repository at this point in the history
  • Loading branch information
Blinue committed Jun 16, 2024
1 parent 3fb3e5c commit 636df3e
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 43 deletions.
105 changes: 62 additions & 43 deletions src/Magpie.Core/CursorManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,49 +81,57 @@ static POINT ScalingToSrc(POINT pt) noexcept {
// 避免了并发问题。如果设置不成功则多次尝试。这里旨在尽最大努力,我怀疑是否有完美
// 的解决方案。
static void ReliableSetCursorPos(POINT pos) noexcept {
const int screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
const int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);

INPUT input{
.type = INPUT_MOUSE,
.mi{
.dx = (pos.x * 65535) / (screenWidth - 1),
.dy = (pos.y * 65535) / (screenHeight - 1),
.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK
}
};
// 检查光标的限制区域,如果要设置的位置不在限制区域内则回落到 SetCursorPos
RECT clipRect;
GetClipCursor(&clipRect);

if (PtInRect(&clipRect, pos)) {
const int screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
const int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);

INPUT input{
.type = INPUT_MOUSE,
.mi{
.dx = (pos.x * 65535) / (screenWidth - 1),
.dy = (pos.y * 65535) / (screenHeight - 1),
.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK
}
};

// 如果设置不成功则多次尝试
for (int i = 0; i < 10; ++i) {
if (!SendInput(1, &input, sizeof(input))) {
Logger::Get().Win32Error("SendInput 失败");
break;
}
// 如果设置不成功则多次尝试
for (int i = 0; i < 10; ++i) {
if (!SendInput(1, &input, sizeof(input))) {
Logger::Get().Win32Error("SendInput 失败");
break;
}

// 等待系统处理
Sleep(0);
// 等待系统处理
Sleep(0);

POINT curCursorPos;
if (!GetCursorPos(&curCursorPos)) {
Logger::Get().Win32Error("GetCursorPos 失败");
break;
}
POINT curCursorPos;
if (!GetCursorPos(&curCursorPos)) {
Logger::Get().Win32Error("GetCursorPos 失败");
break;
}

if (curCursorPos == pos) {
// 已成功,但保险起见再设置一次
SendInput(1, &input, sizeof(input));
return;
if (curCursorPos == pos) {
// 已成功,但保险起见再设置一次
SendInput(1, &input, sizeof(input));
return;
}
}
// 多次不成功回落到 SetCursorPos
}

// 回落到 SetCursorPos
SetCursorPos(pos.x, pos.y);
}

CursorManager::~CursorManager() noexcept {
_ShowSystemCursor(true, true);

ClipCursor(nullptr);
if (_lastClip.left != std::numeric_limits<LONG>::max()) {
_RestoreClipCursor();
}

if (_isUnderCapture && !ScalingWindow::Get().Options().IsDebugMode()) {
POINT cursorPos;
Expand All @@ -142,6 +150,8 @@ bool CursorManager::Initialize() noexcept {
_shouldDrawCursor = true;
_isUnderCapture = true;
} else if (options.Is3DGameMode()) {
ClipCursor(&ScalingWindow::Get().Renderer().SrcRect());

POINT cursorPos;
GetCursorPos(&cursorPos);
_StartCapture(cursorPos);
Expand Down Expand Up @@ -653,32 +663,31 @@ void CursorManager::_UpdateCursorClip() noexcept {
RECT curClip;
GetClipCursor(&curClip);

// 如果当前光标裁剪区域与我们之前设置的不同,肯定是其他程序更改了,记录此原始裁剪区域
// 用于后续计算和还原。
// 如果我们没有裁剪光标区域,则 _lastClip.left == std::numeric_limits<LONG>::max(),
// curClip != _lastClip 始终为真。
// 但如果其他程序恰好设置了和我们相同的裁剪区域怎么办?
if (curClip != _lastClip) {
_originClip = curClip;
}

// 为了尊重原始裁剪区域,计算和我们的裁剪区域的交集
RECT targetClip;
if (!IntersectRect(&targetClip, &_originClip, &clips)) {
// 不相交则不再尊重原始裁剪区域,这不太可能发生
targetClip = clips;
}

// 裁剪区域变化了才调用 ClipCursor。每次调用 ClipCursor 都会向前台窗口发送 WM_MOUSEMOVE
// 消息,一些程序无法正确处理。见 GH#920
if (targetClip != _lastClip) {
ClipCursor(&targetClip);
OutputDebugString(fmt::format(L"{},{},{},{}", targetClip.left, targetClip.top, targetClip.right, targetClip.bottom).c_str());
_lastClip = targetClip;
}
} else {
if (_lastClip.left != std::numeric_limits<LONG>::max()) {
RECT curClip;
GetClipCursor(&curClip);

if (curClip == _lastClip && curClip != _originClip) {
ClipCursor(&_originClip);
OutputDebugString(fmt::format(L"{},{},{},{}", _originClip.left, _originClip.top, _originClip.right, _originClip.bottom).c_str());
}

_lastClip = { std::numeric_limits<LONG>::max() };
}
} else if (_lastClip.left != std::numeric_limits<LONG>::max()) {
_RestoreClipCursor();
_lastClip = { std::numeric_limits<LONG>::max() };
}

// SetCursorPos 应在 ClipCursor 之后,否则会受到上一次 ClipCursor 的影响
Expand Down Expand Up @@ -762,4 +771,14 @@ bool CursorManager::_StopCapture(POINT& cursorPos, bool onDestroy) noexcept {
}
}

void CursorManager::_RestoreClipCursor() const noexcept {
RECT curClip;
GetClipCursor(&curClip);

// 如果 curClip != _lastClip,则其他程序已经更改光标裁剪区域,我们放弃更改
if (curClip == _lastClip && curClip != _originClip) {
ClipCursor(&_originClip);
}
}

}
2 changes: 2 additions & 0 deletions src/Magpie.Core/CursorManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class CursorManager {

bool _StopCapture(POINT& cursorPos, bool onDestroy = false) noexcept;

void _RestoreClipCursor() const noexcept;

HCURSOR _hCursor = NULL;
POINT _cursorPos { std::numeric_limits<LONG>::max(),std::numeric_limits<LONG>::max() };

Expand Down

0 comments on commit 636df3e

Please sign in to comment.