You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中获取鼠标锁定状态下的dx和dy值?

获取游戏锁定鼠标时的光标Delta值(Python实现)

要在游戏锁定鼠标位置时获取光标delta值,你需要绕开系统层面的鼠标位置监听,直接读取原始鼠标输入数据——因为游戏锁定鼠标后,系统会固定光标坐标,但底层的输入设备仍会上报位移变化量。下面是不同平台的实现方案:

Windows平台方案(用ctypes调用系统API)

Windows的Raw Input机制可以直接捕获鼠标原始位移,不受系统光标锁定影响。以下是基于ctypes的实现:

import ctypes
from ctypes import wintypes

# 定义Raw Input相关结构体
class RAWINPUTDEVICE(ctypes.Structure):
    _fields_ = [
        ("usUsagePage", wintypes.USHORT),
        ("usUsage", wintypes.USHORT),
        ("dwFlags", wintypes.DWORD),
        ("hwndTarget", wintypes.HWND)
    ]

class RAWINPUTHEADER(ctypes.Structure):
    _fields_ = [
        ("dwType", wintypes.DWORD),
        ("dwSize", wintypes.DWORD),
        ("hDevice", wintypes.HANDLE),
        ("wParam", wintypes.WPARAM)
    ]

class RAWMOUSE(ctypes.Structure):
    class _MOUSEDATA(ctypes.Union):
        _fields_ = [
            ("ulButtons", wintypes.ULONG),
            ("usButtonFlags", wintypes.USHORT),
            ("usButtonData", wintypes.USHORT)
        ]
    _fields_ = [
        ("usFlags", wintypes.USHORT),
        ("union", _MOUSEDATA),
        ("ulRawButtons", wintypes.ULONG),
        ("lLastX", wintypes.LONG),
        ("lLastY", wintypes.LONG),
        ("ulExtraInformation", wintypes.ULONG)
    ]

class RAWINPUT(ctypes.Structure):
    _fields_ = [
        ("header", RAWINPUTHEADER),
        ("data", ctypes.Union(
            _fields_ = [("mouse", RAWMOUSE)]
        ))
    ]

# 注册Raw Input设备
def register_raw_input():
    rid = RAWINPUTDEVICE()
    rid.usUsagePage = 0x01  # 通用桌面控件类别
    rid.usUsage = 0x02       # 鼠标设备
    rid.dwFlags = 0x00000001  # 接收所有输入
    rid.hwndTarget = ctypes.windll.user32.GetForegroundWindow()
    if not ctypes.windll.user32.RegisterRawInputDevices(ctypes.byref(rid), 1, ctypes.sizeof(rid)):
        raise RuntimeError("注册Raw Input失败")

# 监听并输出delta值
def listen_mouse_delta():
    register_raw_input()
    msg = wintypes.MSG()
    while True:
        if ctypes.windll.user32.GetMessageW(ctypes.byref(msg), None, 0, 0) > 0:
            if msg.message == 0x00FF:  # WM_INPUT事件
                data_size = wintypes.DWORD()
                ctypes.windll.user32.GetRawInputData(
                    wintypes.HRAWINPUT(msg.lParam),
                    0x10000003,
                    None,
                    ctypes.byref(data_size),
                    ctypes.sizeof(RAWINPUTHEADER)
                )
                raw_input = RAWINPUT()
                ctypes.windll.user32.GetRawInputData(
                    wintypes.HRAWINPUT(msg.lParam),
                    0x10000003,
                    ctypes.byref(raw_input),
                    ctypes.byref(data_size),
                    ctypes.sizeof(RAWINPUTHEADER)
                )
                if raw_input.header.dwType == 0:  # 鼠标事件
                    delta_x = raw_input.data.mouse.lLastX
                    delta_y = raw_input.data.mouse.lLastY
                    print(f"Delta X: {delta_x}, Delta Y: {delta_y}")
            ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
            ctypes.windll.user32.DispatchMessageW(ctypes.byref(msg))

if __name__ == "__main__":
    listen_mouse_delta()

注意:运行时可能需要管理员权限,避免被游戏的输入拦截机制屏蔽。

Mac平台方案(用Quartz框架)

Mac通过Quartz的CGEventTap可以监听原始鼠标事件,以下是实现代码:

import Quartz
import time

def mouse_tap_callback(proxy, event_type, event, refcon):
    if event_type == Quartz.kCGEventMouseMoved:
        # 获取原始位移量
        delta_x = Quartz.CGEventGetIntegerValueField(event, Quartz.kCGMouseEventDeltaX)
        delta_y = Quartz.CGEventGetIntegerValueField(event, Quartz.kCGMouseEventDeltaY)
        print(f"Delta X: {delta_x}, Delta Y: {delta_y}")
    return event

def listen_mouse_delta():
    tap = Quartz.CGEventTapCreate(
        Quartz.kCGHIDEventTap,
        Quartz.kCGHeadInsertEventTap,
        Quartz.kCGEventTapOptionDefault,
        Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved),
        mouse_tap_callback,
        None
    )
    if not tap:
        raise RuntimeError("创建Event Tap失败")
    
    run_loop_source = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
    Quartz.CFRunLoopAddSource(Quartz.CFRunLoopGetCurrent(), run_loop_source, Quartz.kCFRunLoopDefaultMode)
    Quartz.CGEventTapEnable(tap, True)
    
    try:
        while True:
            time.sleep(0.01)
    except KeyboardInterrupt:
        Quartz.CGEventTapEnable(tap, False)

if __name__ == "__main__":
    listen_mouse_delta()

运行前需要在系统偏好设置的「安全性与隐私」中,给Python赋予「辅助功能」权限,否则无法捕获游戏内的鼠标事件。

为什么pynput无法满足需求?

pynput监听的是系统层面的鼠标位置变化事件,当游戏锁定光标时,系统会强制把光标坐标固定在窗口中心(或指定位置),所以pynput只能读到固定的坐标值,无法获取实际的位移量。而上面的方案直接读取设备的原始输入数据,不受系统光标位置的限制。

内容的提问来源于stack exchange,提问作者KrystosTheOverlord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 20:10:24