Skip to content

fix: 修复 WithWindowPos 模式鼠标干扰并移除阻塞限制#1147

Merged
MistEO merged 6 commits intoMaaXYZ:mainfrom
ZeroAd-06:fix/win32-windowpos-tracking
Feb 26, 2026
Merged

fix: 修复 WithWindowPos 模式鼠标干扰并移除阻塞限制#1147
MistEO merged 6 commits intoMaaXYZ:mainfrom
ZeroAd-06:fix/win32-windowpos-tracking

Conversation

@ZeroAd-06
Copy link
Contributor

@ZeroAd-06 ZeroAd-06 commented Feb 24, 2026

概述

本次 PR 主要为了解决在使用 WithWindowPos 模式时,玩家移动物理鼠标导致模拟点击发生错位的问题。随着在底层实现零延迟的追踪机制后,原有的通过 block_input 强行剥夺玩家鼠标控制权的临时做法也被一并移除。

终于可以毫无顾忌地边挂机边用鼠标操作其他区域了! 🎉

变更内容

  1. 核心底层追踪 (MessageInput.cpp)
    • 引入了 WH_MOUSE_LL 鼠标钩子来拦截由玩家产生的真实硬件级别输入,将其提取并累加成内部增量偏移量,保证物理输入和程序的模拟点击彻底隔离。
    • 使用后台线程(得益于 timeBeginPeriod(1) 定时器的高精度),以极低延迟平滑同步光标中心与目标客户端位置。
    • 引入目标进程控制机制:在执行 SetWindowPosSetCursorPos 进行重新对齐的极致瞬间,通过底层挂起和恢复操作(Suspend/Resume Process)防范由于异步操作导致的游戏内渲染出光标未对齐的中间失效帧,实现完美的“瞬移”效果。
  2. 多屏自适应
    • 更新系统虚拟桌面矩阵坐标边界限制(SM_XVIRTUALSCREEN 等),防范由于拉扯导致目标和光标完全飞出不同屏幕引起的死锁或目标丢失问题。
  3. 取消阻塞设定 (Win32ControlUnitMgr.cpp)
    • 得益于上述底层追踪保护,常规的鼠标干扰已被彻底避免。本次更新正式将相关控制器的 block_input 构造默认参数下调为了 false,真正实现了无感的后台控制。

Summary by Sourcery

改进 WithWindowPos 的鼠标输入处理,将模拟点击与物理鼠标移动解耦,并消除对全局输入阻塞的需求。

Bug 修复:

  • 修复在 WithWindowPos 模式下,当用户移动物理鼠标时,模拟点击位置错位的问题。
  • 防止在 WithWindowPos 交互过程中,在多显示器环境下目标窗口与光标发生不同步或位置漂移。

功能增强:

  • 引入带有底层鼠标钩子的后台跟踪线程,用于汇总真实鼠标移动,并以低延迟保持窗口与光标的对齐。
  • 在窗口和光标重新定位前后增加进程挂起/恢复处理,以避免出现可见的中间帧,并确保对齐操作具有原子性。
  • 将光标移动限制在虚拟桌面范围内,避免在光标触及多显示器边缘时导致窗口丢失或死锁。

杂项:

  • 修改默认配置,使基于消息的、依赖光标或窗口位置的输入方式不再阻塞用户输入。
Original summary in English

Summary by Sourcery

Improve WithWindowPos mouse input handling to decouple simulated clicks from physical mouse movement and remove the need for global input blocking.

Bug Fixes:

  • Fix misaligned simulated clicks in WithWindowPos mode when the user moves the physical mouse.
  • Prevent the target window and cursor from desynchronizing or drifting across multi-monitor setups during WithWindowPos interactions.

Enhancements:

  • Introduce a background tracking thread with a low-level mouse hook to aggregate real mouse movement and keep the window and cursor aligned with low latency.
  • Add process suspend/resume handling around window and cursor repositioning to avoid visible intermediate frames and ensure atomic alignment.
  • Clamp cursor movement to the virtual desktop bounds to avoid window loss or deadlock when the cursor hits multi-monitor edges.

Chores:

  • Change default configurations to no longer block user input for message-based input methods using cursor or window position.

Z_06 added 2 commits February 25, 2026 01:42
解决在 `WithWindowPos` 模式执行任务时,玩家移动物理鼠标可能导致点击位置偏移、单击变成滑动等干扰问题。

修复方案与核心重构:
- 引入 WH_MOUSE_LL 底层钩子:拦截真实的硬件鼠标移动并转化为内部位移累加,防止与程序的模拟点击产生位置冲突。
- 采用独立追踪线程(60fps 节流):平滑同步光标和窗口位置,彻底消除异步调用拉扯造成的累积误差。
- 引入目标进程挂起机制(NtSuspendProcess):在重新对齐窗口和光标的极短瞬间双向挂起目标进程,避免游戏捕获到重置过程中的失效帧。
- 完善锚点算法与多显示器适配:基于系统视区边缘限制坐标,提升了多屏或屏幕边缘拖拽时的精确度和鲁棒性。
由于底层已经实现了零延迟的窗口和光标同步追踪,彻底解决了鼠标移动带来的干扰和点击错位问题。
因此不再需要通过 `block_input = true` 来强制阻塞玩家的物理鼠标输入,恢复完整的后台无感体验。
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • 新增的 WH_MOUSE_LL 钩子以及静态全局变量 s_active_instance_ / hook_block_mouse_ 假设同一时间只有一个激活的 MessageInput;如果将来同时创建多个带有 with_window_pos 的实例,行为可能会变得不可预测——建议将钩子管理显式设计为单例,或增加对多实例使用场景的防护。
  • tracking_thread_func 中对目标进程的挂起/恢复逻辑不是基于 RAII 的,如果之后引入了提前返回或类似异常的失败路径,有可能导致进程被一直挂起;可以考虑用一个小的守卫对象封装挂起/恢复,或者使用类似 ScopeGuard 的辅助工具来让这部分逻辑更健壮。
  • 跟踪线程当前在安装 WH_MOUSE_LL 钩子时使用 GetModuleHandleW(NULL);为了在代码以后迁移到 DLL 时依然保持清晰和正确,建议显式传入包含 MouseHookProc 的模块句柄,而不是默认假设为 EXE 模块。
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- The new WH_MOUSE_LL hook and static `s_active_instance_`/`hook_block_mouse_` globals assume only a single active `MessageInput` at a time; if multiple instances with `with_window_pos` are ever created concurrently this will behave unpredictably—consider making the hook management explicitly singleton or guarding against multi-instance use.
- The suspend/resume logic for the target process in `tracking_thread_func` is non-RAII and can leave the process suspended if an early return or exception-like failure path is introduced later; wrapping suspend/resume in a small guard object or using `ScopeGuard`-style helpers would make this more robust.
- The tracking thread currently uses `GetModuleHandleW(NULL)` when installing the WH_MOUSE_LL hook; for clarity and correctness if this code ever moves to a DLL, consider explicitly passing the module handle that contains `MouseHookProc` rather than assuming the EXE module.

## Individual Comments

### Comment 1
<location path="source/MaaWin32ControlUnit/Input/MessageInput.cpp" line_range="311-319" />
<code_context>
+    // 预先打开目标进程句柄(用于挂起/恢复)
+    open_target_process();
+
+    HHOOK hHook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, GetModuleHandleW(NULL), 0);
+
+    // 60fps 节流:每帧间隔 ~16.67ms
+    using clock = std::chrono::steady_clock;
+    auto last_frame = clock::now();
+    const auto frame_interval = std::chrono::microseconds(16667);
+
+    MSG msg;
+    while (!tracking_exit_) {
+        // 消息泵:WH_MOUSE_LL 钩子回调在此线程上被系统调度,必须保持消息循环活跃
+        DWORD res = MsgWaitForMultipleObjects(0, NULL, FALSE, 1, QS_ALLINPUT);
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Handle SetWindowsHookExW failure explicitly to avoid a busy tracking loop that never receives mouse events.

If `SetWindowsHookExW` fails and `hHook` is null, this loop will still run at high priority with the 60fps timing but never receive mouse events, wasting resources and appearing to do nothing. Please check `hHook` after the call and either log and exit the thread early or disable tracking when the hook cannot be installed.

Suggested implementation:

```cpp
    // 预先打开目标进程句柄(用于挂起/恢复)
    open_target_process();

    HHOOK hHook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, GetModuleHandleW(NULL), 0);
    if (!hHook) {
        // 钩子安装失败时避免进入忙循环:记录失败并退出跟踪线程
        // TODO: 根据项目日志系统替换为合适的日志输出
        // e.g. LOG_ERROR("SetWindowsHookExW(WH_MOUSE_LL) failed: {}", GetLastError());
        tracking_exit_ = true;
        timeEndPeriod(1);
        return;
    }

    // 60fps 节流:每帧间隔 ~16.67ms

```

1. Replace the TODO comment with the project's actual logging / error-reporting macro or mechanism (for example, `LOG_ERROR`, `spdlog::error`, or your existing trace facility).
2. If there is a centralized cleanup path for this thread (e.g., unhooking and restoring timer precision), you may prefer to factor the `timeEndPeriod(1); return;` into that path instead of returning early here, while still ensuring the thread exits when the hook cannot be installed.
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The new WH_MOUSE_LL hook and static s_active_instance_/hook_block_mouse_ globals assume only a single active MessageInput at a time; if multiple instances with with_window_pos are ever created concurrently this will behave unpredictably—consider making the hook management explicitly singleton or guarding against multi-instance use.
  • The suspend/resume logic for the target process in tracking_thread_func is non-RAII and can leave the process suspended if an early return or exception-like failure path is introduced later; wrapping suspend/resume in a small guard object or using ScopeGuard-style helpers would make this more robust.
  • The tracking thread currently uses GetModuleHandleW(NULL) when installing the WH_MOUSE_LL hook; for clarity and correctness if this code ever moves to a DLL, consider explicitly passing the module handle that contains MouseHookProc rather than assuming the EXE module.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new WH_MOUSE_LL hook and static `s_active_instance_`/`hook_block_mouse_` globals assume only a single active `MessageInput` at a time; if multiple instances with `with_window_pos` are ever created concurrently this will behave unpredictably—consider making the hook management explicitly singleton or guarding against multi-instance use.
- The suspend/resume logic for the target process in `tracking_thread_func` is non-RAII and can leave the process suspended if an early return or exception-like failure path is introduced later; wrapping suspend/resume in a small guard object or using `ScopeGuard`-style helpers would make this more robust.
- The tracking thread currently uses `GetModuleHandleW(NULL)` when installing the WH_MOUSE_LL hook; for clarity and correctness if this code ever moves to a DLL, consider explicitly passing the module handle that contains `MouseHookProc` rather than assuming the EXE module.

## Individual Comments

### Comment 1
<location path="source/MaaWin32ControlUnit/Input/MessageInput.cpp" line_range="311-319" />
<code_context>
+    // 预先打开目标进程句柄(用于挂起/恢复)
+    open_target_process();
+
+    HHOOK hHook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, GetModuleHandleW(NULL), 0);
+
+    // 60fps 节流:每帧间隔 ~16.67ms
+    using clock = std::chrono::steady_clock;
+    auto last_frame = clock::now();
+    const auto frame_interval = std::chrono::microseconds(16667);
+
+    MSG msg;
+    while (!tracking_exit_) {
+        // 消息泵:WH_MOUSE_LL 钩子回调在此线程上被系统调度,必须保持消息循环活跃
+        DWORD res = MsgWaitForMultipleObjects(0, NULL, FALSE, 1, QS_ALLINPUT);
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Handle SetWindowsHookExW failure explicitly to avoid a busy tracking loop that never receives mouse events.

If `SetWindowsHookExW` fails and `hHook` is null, this loop will still run at high priority with the 60fps timing but never receive mouse events, wasting resources and appearing to do nothing. Please check `hHook` after the call and either log and exit the thread early or disable tracking when the hook cannot be installed.

Suggested implementation:

```cpp
    // 预先打开目标进程句柄(用于挂起/恢复)
    open_target_process();

    HHOOK hHook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, GetModuleHandleW(NULL), 0);
    if (!hHook) {
        // 钩子安装失败时避免进入忙循环:记录失败并退出跟踪线程
        // TODO: 根据项目日志系统替换为合适的日志输出
        // e.g. LOG_ERROR("SetWindowsHookExW(WH_MOUSE_LL) failed: {}", GetLastError());
        tracking_exit_ = true;
        timeEndPeriod(1);
        return;
    }

    // 60fps 节流:每帧间隔 ~16.67ms

```

1. Replace the TODO comment with the project's actual logging / error-reporting macro or mechanism (for example, `LOG_ERROR`, `spdlog::error`, or your existing trace facility).
2. If there is a centralized cleanup path for this thread (e.g., unhooking and restoring timer precision), you may prefer to factor the `timeEndPeriod(1); return;` into that path instead of returning early here, while still ensuring the thread exits when the hook cannot be installed.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fundamentally redesigns the WithWindowPos input mechanism for Win32 control to eliminate mouse interference. The old approach temporarily blocked all user input during operations; the new implementation uses a sophisticated background tracking system that continuously monitors and compensates for physical mouse movement, allowing users to move their mouse freely while automation runs.

Changes:

  • Replaced input blocking with a continuous tracking thread that uses a low-level mouse hook to intercept and accumulate real mouse movements
  • Implemented process suspend/resume mechanism to prevent visual glitches during window repositioning
  • Changed default block_input parameter from true to false for all WithWindowPos and WithCursorPos input methods
  • Added multi-monitor awareness with virtual screen boundary clamping

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 15 comments.

File Description
Win32ControlUnitMgr.cpp Changed default block_input from true to false for WithCursorPos and WithWindowPos input methods
MessageInput.h Added tracking thread infrastructure, hook management, process suspension members, and atomic synchronization primitives
MessageInput.cpp Implemented background tracking thread with WH_MOUSE_LL hook, 60fps mouse delta processing, process suspend/resume, enhanced error handling, and redesigned window alignment algorithm

Comment on lines +398 to +403
HHOOK hHook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, GetModuleHandleW(NULL), 0);
if (!hHook) {
LogError << "SetWindowsHookExW failed, tracking disabled" << VAR(GetLastError());
return;
}
OnScopeLeave([&]() { UnhookWindowsHookEx(hHook); });
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hook lifetime issue: The WH_MOUSE_LL hook is installed on the tracking thread at line 398, but the static callback MouseHookProc accesses the global s_active_instance_. If multiple MessageInput instances exist simultaneously (even if only one is tracking at a time), the hook from the first instance's thread will still be active when a second instance is created and starts its own tracking thread with its own hook.

This means there could be multiple hooks installed simultaneously, all sharing the same s_active_instance_. When the first instance is destroyed and unhooks, the second instance's hook is still active. This design appears intentional (only one instance can be active at a time via s_active_instance_), but it's not immediately clear and could cause issues if assumptions change.

Consider adding documentation explaining this single-active-instance design, or implementing explicit checks to prevent multiple tracking instances.

Copilot uses AI. Check for mistakes.
Comment on lines +203 to 206
s_active_instance_ = this;
tracking_active_ = true;

move_window_to_align_cursor(x, y);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential race condition: Multiple MessageInput instances could be created simultaneously (from different threads or sequentially), and the global s_active_instance_ will be overwritten. If one instance starts tracking after another has already set s_active_instance_, the hook will suddenly start processing mouse events for the new instance while the old instance may still have tracking_active_ set to true.

Consider adding a check to prevent multiple instances from being active simultaneously, or implement proper instance management to handle this scenario safely.

Suggested change
s_active_instance_ = this;
tracking_active_ = true;
move_window_to_align_cursor(x, y);
// 仅在当前没有其他活动实例(或已经是当前实例)时,才设置为活动实例
MessageInput* expected = nullptr;
MessageInput* desired = this;
if (s_active_instance_.compare_exchange_strong(expected, desired, std::memory_order_acq_rel, std::memory_order_acquire) ||
expected == this) {
// 成功成为活动实例(或已经是活动实例),启动追踪
tracking_active_ = true;
move_window_to_align_cursor(x, y);
} else {
// 已经有其他活动实例,避免抢占其追踪权
LogWarn << "MessageInput::prepare_mouse_position skipped activating new instance because another instance is already active."
<< VAR(expected) << VAR(this);
}

Copilot uses AI. Check for mistakes.
Comment on lines +279 to +295
template <typename Fn>
static Fn resolve_nt_function(const char* name)
{
static Fn func = nullptr;
static bool resolved = false;
if (!resolved) {
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
if (ntdll) {
func = reinterpret_cast<Fn>(GetProcAddress(ntdll, name));
}
if (!func) {
LogWarn << "Failed to resolve ntdll function" << VAR(name);
}
resolved = true;
}
return func;
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thread-local static can cause issues: The resolve_nt_function template uses function-local static variables that are initialized once per template instantiation. However, this is being called from a background thread, and C++11 guarantees thread-safe initialization of function-local statics. This is correct, but if multiple threads call this simultaneously during first use, it could block.

More critically, if GetModuleHandleW or GetProcAddress fail silently (func remains null), subsequent calls won't retry. Consider logging a warning only on the first resolution attempt, not on every call where func is null.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +31
if (config_.with_window_pos) {
tracking_thread_ = std::thread(&MessageInput::tracking_thread_func, this);
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing synchronization on thread startup: The constructor starts the tracking thread at line 30, but the thread immediately begins executing and may attempt to use hwnd_ before the constructor has fully initialized all member variables. While hwnd_ is initialized in the member initializer list before the constructor body, there's no guarantee that the thread won't start executing before the object is fully constructed.

Consider using a separate start() method or ensuring all initialization is complete in the member initializer list before starting the thread. Alternatively, use a flag to signal when initialization is complete.

Copilot uses AI. Check for mistakes.
restore_cursor_pos();
}
else if (config_.with_window_pos) {
tracking_active_ = false;
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical race condition: s_active_instance_ is set to nullptr without synchronization with the tracking thread. The tracking thread may still be running and accessing inst from s_active_instance_ in MouseHookProc (line 227-243) when it's set to nullptr here. This can cause undefined behavior if the hook callback reads the nullptr and then this instance is destroyed before the callback completes.

The tracking thread should be signaled to stop and joined before clearing s_active_instance_, or proper synchronization is needed to ensure the hook callback isn't accessing the instance during destruction.

Suggested change
tracking_active_ = false;
tracking_active_ = false;
if (tracking_thread_.joinable()) {
tracking_thread_.join();
}

Copilot uses AI. Check for mistakes.
return CallNextHookEx(NULL, nCode, wParam, lParam);
}

MessageInput* inst = s_active_instance_.load(std::memory_order_acquire);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory order inconsistency: The hook callback uses memory_order_acquire to load s_active_instance_ (line 227), but restore_pos() sets it to nullptr without any memory order specification (line 144 uses default memory_order_seq_cst). For consistency and to match the acquire-release semantics, line 144 should use memory_order_release when setting to nullptr:
s_active_instance_.store(nullptr, std::memory_order_release);

Similarly, line 203 should use memory_order_release when storing the instance pointer.

Copilot uses AI. Check for mistakes.
Comment on lines +370 to +381
// 1. 挂起目标进程,使其看不到中间态
suspend_target_process();

// 2. 移动窗口(SWP_ASYNCWINDOWPOS 避免阻塞 + SWP_NOSENDCHANGING 跳过同步通知)
SetWindowPos(hwnd_, nullptr, new_left, new_top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_ASYNCWINDOWPOS);

// 3. 释放光标到累积后的真实目标位置
SetCursorPos(mx, my);

// 4. 恢复目标进程(它醒来时看到的窗口和光标已完美对齐)
resume_target_process();
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential deadlock: The tracking thread runs with THREAD_PRIORITY_HIGHEST (line 386) and calls suspend_target_process() which suspends all threads in the target process. If the target process holds system-wide locks (e.g., a window manager lock) when suspended, and the tracking thread needs to acquire that same lock through a Win32 API call (e.g., SetWindowPos, GetCursorPos), this could cause a deadlock.

This is a known issue with process suspension in general. Consider adding a timeout mechanism or reconsidering the suspend/resume approach if target processes commonly hold system locks.

Suggested change
// 1. 挂起目标进程,使其看不到中间态
suspend_target_process();
// 2. 移动窗口(SWP_ASYNCWINDOWPOS 避免阻塞 + SWP_NOSENDCHANGING 跳过同步通知)
SetWindowPos(hwnd_, nullptr, new_left, new_top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_ASYNCWINDOWPOS);
// 3. 释放光标到累积后的真实目标位置
SetCursorPos(mx, my);
// 4. 恢复目标进程(它醒来时看到的窗口和光标已完美对齐)
resume_target_process();
// 直接移动窗口(SWP_ASYNCWINDOWPOS 避免阻塞 + SWP_NOSENDCHANGING 跳过同步通知)
SetWindowPos(hwnd_, nullptr, new_left, new_top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_ASYNCWINDOWPOS);
// 释放光标到累积后的真实目标位置
SetCursorPos(mx, my);

Copilot uses AI. Check for mistakes.
MSG msg;
while (!tracking_exit_) {
// 消息泵:WH_MOUSE_LL 钩子回调在此线程上被系统调度,必须保持消息循环活跃
DWORD res = MsgWaitForMultipleObjects(0, NULL, FALSE, 1, QS_ALLINPUT);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance concern: The tracking thread calls MsgWaitForMultipleObjects with a 1ms timeout and then checks frame timing with a 16.67ms target. This means the thread will wake up approximately every 1ms, check the frame timing, and go back to sleep, resulting in high CPU usage for no benefit during the ~15ms between actual frame processing.

Consider using a longer wait timeout (e.g., 15ms) or implementing a smarter wait strategy that wakes only when messages are available or when the frame interval has elapsed.

Copilot uses AI. Check for mistakes.
@ZeroAd-06
Copy link
Contributor Author

ZeroAd-06 commented Feb 25, 2026

啊?在我这边能过编译啊
没事了。

@MistEO MistEO merged commit a57b19a into MaaXYZ:main Feb 26, 2026
38 of 41 checks passed
MistEO pushed a commit that referenced this pull request Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants