Windows下Python获取鼠标delta(原始移动数据)的技术方案咨询
Got it, let's tackle this problem—you need raw mouse delta data on Windows, bypassing cursor position limitations (perfect for games that lock or reset the cursor). Here's how to do it with Python, using low-level Windows APIs directly, just like that AutoHotkey implementation you referenced:
核心思路:Windows Raw Input API
Windows provides the Raw Input API, which is designed specifically to grab unprocessed input data from devices—including the exact mouse movement delta (X/Y increments)—completely bypassing the system's cursor position handling. This is exactly what you need: no matter how the game resets or locks the cursor, you'll get the real movement data.
实现代码(纯Python,无需额外库)
We'll use ctypes (a built-in Python library) to call Windows APIs directly, so you don't need to install any third-party packages:
import ctypes from ctypes import wintypes # Load Windows user32 library user32 = ctypes.WinDLL('user32', use_last_error=True) # Define required Windows API structures class RAWINPUTDEVICE(ctypes.Structure): _fields_ = [ ("usUsagePage", wintypes.WORD), ("usUsage", wintypes.WORD), ("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): _fields_ = [ ("usFlags", wintypes.WORD), ("ulButtons", wintypes.DWORD), ("usButtonFlags", wintypes.WORD), ("usButtonData", wintypes.WORD), ("ulRawButtons", wintypes.DWORD), ("lLastX", wintypes.LONG), ("lLastY", wintypes.LONG), ("ulExtraInformation", wintypes.DWORD) ] class RAWINPUT(ctypes.Structure): _fields_ = [ ("header", RAWINPUTHEADER), ("data", ctypes.Union( _fields_ = [ ("mouse", RAWMOUSE), # We only care about mouse data, ignore other device types ] )) ] # Constants for Raw Input API RIDEV_INPUTSINK = 0x00000100 # Allow window to receive input even when not in foreground RIM_TYPEMOUSE = 0x00000000 # Specify mouse input type WM_INPUT = 0x00FF # Window message for raw input def register_raw_mouse(hwnd): """Register the mouse device for raw input capture""" rid = RAWINPUTDEVICE() rid.usUsagePage = 0x01 # Generic desktop control page rid.usUsage = 0x02 # Mouse device rid.dwFlags = RIDEV_INPUTSINK rid.hwndTarget = hwnd if not user32.RegisterRawInputDevices(ctypes.byref(rid), 1, ctypes.sizeof(rid)): raise ctypes.WinError(ctypes.get_last_error()) class HiddenInputWindow: """Create a hidden window to receive raw input messages""" def __init__(self): self.hwnd = None self.running = True # Window procedure to handle messages def wndproc(hwnd, msg, wparam, lparam): if msg == WM_INPUT: # Get size of raw input data data_size = wintypes.DWORD() user32.GetRawInputData(lparam, RIM_TYPEMOUSE, None, ctypes.byref(data_size), ctypes.sizeof(RAWINPUTHEADER)) # Allocate memory and read the raw input data raw_input = RAWINPUT() user32.GetRawInputData(lparam, RIM_TYPEMOUSE, ctypes.byref(raw_input), ctypes.byref(data_size), ctypes.sizeof(RAWINPUTHEADER)) # Extract mouse delta values delta_x = raw_input.data.mouse.lLastX delta_y = raw_input.data.mouse.lLastY if delta_x != 0 or delta_y != 0: print(f"Raw Mouse Delta: X={delta_x}, Y={delta_y}") elif msg == 0x0010: # WM_CLOSE message self.running = False return 0 return user32.DefWindowProcW(hwnd, msg, wparam, lparam) # Register window class wc = wintypes.WNDCLASSEXW() wc.cbSize = ctypes.sizeof(wc) wc.lpfnWndProc = ctypes.WINFUNCTYPE(wintypes.LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)(wndproc) wc.lpszClassName = "RawMouseCaptureWindow" user32.RegisterClassExW(ctypes.byref(wc)) # Create hidden window self.hwnd = user32.CreateWindowExW( 0, wc.lpszClassName, "", 0, 0, 0, 0, 0, None, None, None, None ) if not self.hwnd: raise ctypes.WinError(ctypes.get_last_error()) # Register mouse for raw input register_raw_mouse(self.hwnd) def start(self): """Start the message loop to listen for input""" msg = wintypes.MSG() while self.running and user32.GetMessageW(ctypes.byref(msg), None, 0, 0) != 0: user32.TranslateMessage(ctypes.byref(msg)) user32.DispatchMessageW(ctypes.byref(msg)) if __name__ == "__main__": print("Listening for raw mouse delta data... Press Ctrl+C to exit") try: window = HiddenInputWindow() window.start() except KeyboardInterrupt: print("\nExiting...")
关键细节解释
- Hidden Window: Windows requires a window handle to receive input messages, so we create a fully hidden window that won't interfere with your game or other apps.
- Raw Input Registration: Using
RIDEV_INPUTSINKensures we capture mouse data even if the hidden window isn't the active foreground window (critical for games running in fullscreen). - Delta Values: The
lLastXandlLastYfields from theRAWMOUSEstruct give you the raw movement increments—positive values mean right/down, negative mean left/up—completely independent of cursor position.
注意事项
- For fullscreen exclusive games, you may need to run the Python script with administrator privileges to ensure input capture works correctly.
- This implementation uses the exact same underlying Windows API as the AutoHotkey script you referenced, so it will deliver identical raw mouse data.
内容的提问来源于stack exchange,提问作者AlexAndHisScripts

