返回 Windows:
Linux:
获取鼠标原始输入:进阶方法与实现
python
2025-01-20 01:17:10
获取鼠标原始输入:一种进阶方法
常见场景中,开发者常常需要读取鼠标输入,用于交互、自动化测试或游戏开发。诸如PyAutoGUI和Pynput等库,它们能读取鼠标在屏幕上的位置坐标。但是,某些应用场景,例如游戏宏录制,则需要直接捕获鼠标的原始输入信号——并非屏幕上的位置变化,而是设备本身发送的数据流。本文将讨论几种直接获取鼠标原始输入的方法,及其优缺点。
系统级事件捕获
操作系统层提供了底层的事件监听机制,可以捕获包括鼠标在内的多种输入设备的数据。Windows系统使用win32api
或pywin32
模块可以实现此功能。在Linux或macOS系统中,则可以利用Xlib
或相应的系统调用实现。
Windows:pywin32
方法
pywin32
允许Python直接调用Windows API,访问更底层的硬件信息。可以注册一个Windows钩子,截取鼠标事件。这种方法通常能提供非常精确且低延迟的数据,但是它较为复杂,并且对操作系统高度依赖。
import win32api
import win32con
import time
import struct
import ctypes
class RawInput:
def __init__(self):
self.mouse_x = 0
self.mouse_y = 0
self.buffer = bytearray(ctypes.sizeof(win32con.RAWINPUT))
self.handle = win32api.GetRawInputDeviceList()[1].hDevice # 获取第一个鼠标设备
# 注册鼠标设备
win32api.RegisterRawInputDevices([
(win32con.RIM_TYPEHID,win32con.RIDEV_INPUTSINK | win32con.RIDEV_NOLEGACY,0) # 如果没有这个可能会造成鼠标无法使用
])
def update_mouse(self):
while True:
try:
headerSize = win32api.GetRawInputData(self.handle, win32con.RID_INPUT, None, win32api.GetRawInputDeviceInfo(self.handle, win32con.RIDI_RAWDATA_SIZE), ctypes.sizeof(win32con.RAWINPUTHEADER))
if headerSize >= 0: # headerSize > 0 可能存在兼容性问题,
size = win32api.GetRawInputData(self.handle,win32con.RID_INPUT, self.buffer,headerSize, ctypes.sizeof(win32con.RAWINPUTHEADER))
# unpack的格式与 RAWINPUT结构体需要对应 https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-rawinput
_, mouse_header_type,_,_,mouse_buttons,mouse_x_raw,mouse_y_raw,mouse_wheel = struct.unpack('<HHIiIIi', self.buffer[:44])
self.mouse_x = mouse_x_raw
self.mouse_y = mouse_y_raw
except Exception:
pass #某些情况下,无法获取鼠标输入可以忽略
def get_data(self):
return self.mouse_x,self.mouse_y
if __name__ == "__main__":
input_handle = RawInput()
import threading
t = threading.Thread(target=input_handle.update_mouse)
t.start()
while True:
print(input_handle.get_data())
time.sleep(0.01)
- 操作步骤:
- 安装
pywin32
:pip install pywin32
。 - 复制并运行提供的代码,输出为鼠标的相对位置变化。
- 根据具体需求处理
mouse_x
,mouse_y
。
- 安装
- 原理: 通过Windows API注册鼠标输入设备,实时获取设备的原始输入数据。
Linux:evdev
方法
在Linux环境中, /dev/input/
下的事件设备文件提供了访问鼠标原始输入的入口。可以使用evdev
模块,它可以直接读取这些事件文件,捕获鼠标的移动、按钮点击等动作。这种方式直接、高效。
from evdev import InputDevice, categorize, ecodes
# 列出所有输入设备,选择鼠标设备 (如果鼠标是多个,注意识别)
devices = [InputDevice(path) for path in '/dev/input/' in os.listdir() if path.startswith('event') ]
for d in devices:
print(d.path, d.name, d.phys)
# 例如 mouse0 = InputDevice('/dev/input/event2') 请替换成你自己的鼠标的event序号
mouse = InputDevice('/dev/input/event2')
for event in mouse.read_loop():
if event.type == ecodes.EV_REL:
if event.code == ecodes.REL_X:
print("相对X坐标: ", event.value)
elif event.code == ecodes.REL_Y:
print("相对Y坐标:", event.value)
if event.type == ecodes.EV_KEY:
if event.code == ecodes.BTN_LEFT:
print("左键:", event.value) # 1是按下,0是弹起
if event.code == ecodes.BTN_RIGHT:
print("右键:", event.value) # 1是按下,0是弹起
- 操作步骤:
- 安装
evdev
模块:pip install evdev
。 - 找出你的鼠标对应的
/dev/input/eventX
文件, 将其替换代码中mouse = InputDevice('/dev/input/event2')
,运行代码。 - 根据需要处理鼠标移动和按钮事件。
- 安装
- 原理:
evdev
直接与 Linux 内核的输入子系统交互,捕获设备的原始数据流。
优缺点比较
系统级事件捕获的优势:
- 精度高: 可以获取设备原始数据,避免了窗口系统等上层抽象造成的偏差。
- 延迟低: 与系统底层交互,降低延迟。
系统级事件捕获的缺点:
- 平台依赖性强: 代码不能在不同操作系统之间通用。需要为不同的操作系统编写特定的代码。
- 复杂度较高: 需要更深入的系统知识。
注意事项和安全建议
- 权限管理: 某些情况下,捕获原始输入需要管理员权限或使用特殊的用户组。
- 设备冲突: 读取原始输入可能与其他使用同一设备的应用发生冲突,需要谨慎使用。
- 安全问题: 读取鼠标输入可能暴露隐私信息, 特别在执行鼠标移动和点击模拟时。需要充分理解相关API的权限,避免滥用,谨慎操作。
综上所述,在需要获取精确和低延迟的鼠标原始输入时,系统级的事件捕获提供了一种有效的手段。开发者可以根据具体应用场景选择合适的方案,理解其原理和限制。务必谨慎操作,尤其是在模拟用户行为时。