Skip to main content

win-mouse-listen

基于 Windows Raw Input API 的非阻塞鼠标监听模块。协议与 mouse 库兼容,但不会插入鼠标事件链,因此:

  • 回调无论多慢,都不会让鼠标卡顿;
  • 需要管理员权限;
  • 支持按键、滚轮、移动,移动事件额外附带设备原始增量 dx / dy,可用于「触顶/触底持续移动」等场景。

为什么不用 WH_MOUSE_LL 钩子

mouse 库底层使用 SetWindowsHookEx(WH_MOUSE_LL)。虽然它也能全局监听,但它位于鼠标事件链上——系统必须等你的钩子返回,才会继续把事件传给其他程序。只要你的回调里出现了锁等待、GIL 争抢、I/O 阻塞,鼠标就会卡。

Raw Input 完全不同:鼠标事件由系统正常处理,同时复制一份 WM_INPUT 消息投递给你的隐藏窗口。你的窗口处理再慢,也不会影响鼠标。


安装

pip install win-mouse-listen

或从本地 whl 安装:

pip install dist/win_mouse_listen-0.1.0-py3-none-any.whl

快速开始

import time
import win_mouse_listen as wm

# 注册回调,第一次 hook 会自动启动监听
wm.hook(lambda ev: print('event:', ev))

try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    wm.stop()

或者使用类接口:

from win_mouse_listen import Listener

with Listener() as l:
    l.hook(print)
    time.sleep(10)

事件协议

与 mouse 库兼容的 namedtuple:

ButtonEvent(event_type, button, time)
WheelEvent(delta, time)
MoveEvent(x, y, time, dx, dy)
  • event_type:'up' 或 'down'
  • button:LEFT / RIGHT / MIDDLE / X / X2
  • time:Unix 时间戳(float,秒)
  • x, y:屏幕坐标,由 GetCursorPos 获取,钳制在屏幕内
  • dx, dy:本次事件的设备原始增量,即使光标已经贴到屏幕边缘,仍会持续给出同方向的非零值

注意:MoveEvent 是 5 字段版本,与 mouse.MoveEvent 的 3 字段版本不兼容 isinstance。如果你的代码依赖 mouse 库的类型,请改用 duck typing 判断字段。


API

回调

wm.hook(callback)          # 注册回调,返回 callback
wm.unhook(callback)        # 注销单个回调
wm.unhook_all()            # 注销所有回调

回调在独立的分发线程中执行,阻塞回调不会影响鼠标。

生命周期

wm.start()                 # 显式启动(hook 会自动启动)
wm.stop()                  # 停止并重置默认监听器

非阻塞查询

wm.poll()                  # 取一个事件,没有则返回 None
wm.get_events(max_count=None)   # 批量取事件
wm.clear_buffer()          # 清空缓冲区
wm.pending_count()         # 当前缓冲的事件数量
wm.is_pressed(button=LEFT) # 指定按键是否按下
wm.position()              # 当前鼠标屏幕坐标 (x, y)

典型用法:检测「触顶 / 触底持续移动」

某些场景下需要判断鼠标已经顶到屏幕边缘,但用户仍在继续往同方向移动。传统做法是用「越界负坐标」反推增量,麻烦又容易出错。使用 dx / dy 直接判断即可:

import win_mouse_listen as wm

SCREEN_TOP_Y = 0
SCREEN_BOTTOM_Y = 1079

def on_move(ev):
    if ev.y <= SCREEN_TOP_Y and ev.dy < 0:
        print('向上越界移动', ev.dy)
    elif ev.y >= SCREEN_BOTTOM_Y and ev.dy > 0:
        print('向下越界移动', ev.dy)

wm.hook(on_move)

提示:高回报率鼠标每秒会产生数百到上千个 MoveEvent,每个事件的 dx/dy 通常只有个位数。如果需要按距离阈值触发逻辑,建议在时间窗口内累加。


限制与注意事项

  1. 绝对模式:触摸板、数位板、RDP 远程桌面等设备上报的是绝对坐标(usFlags & MOUSE_MOVE_ABSOLUTE)。此时 lLastX/lLastY 不是增量,模块会将 dx/dy 置 0,避免出现巨大数值。物理鼠标不受影响。

  2. 多显示器:x, y 来自 GetCursorPos,在虚拟屏幕坐标系下,副屏位于主屏左侧或上方时 x/y 本来就可能为负。这和「触顶越界」是两回事。

  3. 仅支持 Windows:模块依赖 user32.dll,其他平台会直接 ImportError。

  4. 需要管理员:没有管理员权限启动软件,无法做到全局监听鼠标信息。


打包

项目根目录执行:

pip install build
python -m build

产物在 dist/:

dist/
├── win_mouse_listen-0.1.0-py3-none-any.whl
└── win_mouse_listen-0.1.0.tar.gz

本地安装测试:

pip install dist/win_mouse_listen-0.1.0-py3-none-any.whl

项目结构

win_mouse_listen/
├── __init__.py        # 公共 API 与模块级单例
├── _types.py          # 事件协议(ButtonEvent / WheelEvent / MoveEvent)
├── _win32.py          # ctypes 绑定与常量
└── _listener.py       # 核心监听器实现
pyproject.toml
README.md

设计要点

  • Raw Input 而非全局钩子:鼠标事件不经过本模块,回调再慢也不会卡鼠标。
  • 隐藏消息窗口:HWND_MESSAGE 类型窗口,不出现在任务栏。
  • 双重缓冲:内部维护 deque(maxlen=N),满了自动丢弃最旧事件,永不阻塞钩子线程。
  • 无锁读取:利用 CPython 中 deque.append/popleft、list 引用替换的原子性,避免在热路径上加锁。
  • 分发线程独立:用户回调在专用线程执行,隔离用户代码。

License

MIT

Metadata

Release files for win-mouse-listener 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for win-mouse-listener 0.1.4
File Size Uploaded Provenance
win_mouse_listener-0.1.4.tar.gz 12.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for win-mouse-listener 0.1.4
File Interpreter ABI Platform
win_mouse_listener-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 24.3 kB

Release files / win_mouse_listener-0.1.4.tar.gz

Download URL win_mouse_listener-0.1.4.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
60f0f760f8f7e1c53c70faf3b7895fc3e6c753a2818d94268aacec93a3869761
BLAKE2b-256 checksum
How to use checksums
1e7a54b846cf3243e72ffe3e51367d2e1eed08160f7d2d3de0be4bc3007ec783
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release files / win_mouse_listener-0.1.4-py3-none-any.whl

Download URL win_mouse_listener-0.1.4-py3-none-any.whl
Size 11.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87604a21a88411e09021786a94f07ea78449fa898c4030f2825b7149659da507
BLAKE2b-256 checksum
How to use checksums
b943f306c0365d41d6fca47e3c95f8fe2ffb73aab9b85ea862625d1c38846b01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Security for win-mouse-listener 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page