如何在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
相关产品推荐
相关产品推荐

