【开源自荐】KeySteer:用键盘移动、点击和快速定位鼠标(Windows / macOS)

KeySteer——用键盘移动、点击和快速定位鼠标

应用平台

Windows 10/11(x64、ARM64),macOS 14+(Apple Silicon、Intel)

开源

基于 GPL-3.0-or-later 开源。

一句简介

KeySteer 是一个原生、轻量、可配置的键盘鼠标控制工具:日常可以用 hjkl 移动和点击鼠标,距离较远时则用网格或界面标签快速定位。

应用介绍

我平时使用电脑时不想反复把手移到鼠标上,鼠标用久了手也容易疼,于是先去试了一圈同类开源工具。

  • Mousemaster
    功能很全,不过我当时用的 Java 版本约 120 MB,常驻内存到了 200 MB 左右,最关键的时第一次显示网格会掉帧,对我来说这不能忍,而且没有mac版本

  • Neru
    常驻内存也有约 100 MB,而且网格划分方式以及不能随意移动的鼠标不太符合我的习惯。

于是我用 Rust 写了 KeySteer,同时感谢这两个项目对我的参考。

当前发布文件约 2 MB,日常使用中常驻内存约 20 MB。

启动后安静地待在系统托盘或菜单栏,按 Primary+E 才进入操作模式;按 Esc 后立即回到正常打字。

第一次只需要记住这一条操作链:

Primary+E 开始 → h j k l 移动 → ; 左键点击 → Esc 结束

这里的 Primary 在 Windows 上默认是左 Alt,在 macOS 上默认是 Command,也可以通过 TOML 配置修改。

目前主要有四种使用方式:

  • Normal:用 hjkl 平滑移动鼠标,支持加速、精确/慢速/快速移动、滚动、左中右键点击和拖拽。
  • Grid:先显示大字母区域,并在其中预览第二级小字母;连续按两个键即可快速定位。
  • Recursive Grid:不断细分选中的区域,适合较小目标和精确落点。
  • UI Hint:扫描按钮、链接、菜单和输入框,为可操作控件显示键盘标签。加入了OCR本地识别,更精准的跳转

还支持多显示器切换、点击状态指示、长按转为持续按下,以及键位、颜色、字号和网格样式配置。程序本身不依赖外置配置文件,下载后即可使用;需要自定义时,可以在网页配置与模拟器中直观看效果并导出 TOML。

更新日志

v0.9.x: Windows端加入本地OCR识别,更多场景可以识别到跳转标签了

视频介绍

Normal:移动、滚动与点击

Grid:二键快速定位

Recursive Grid:递归细分定位

UI Hint:按界面控件定位

开源与隐私

KeySteer 使用 Rust 编写,输入处理、屏幕定位和界面识别都在本机完成,不会上传键盘输入或识别结果。只有在用户主动检查或下载更新时才会访问网络。

项目采用 GPL-3.0-or-later:可以自由使用、研究、修改和再发布;对外分发的修改版或衍生作品需要继续提供对应源码。

下载与安装

Windows 下载对应架构的 ZIP,解压后在合适的文件夹中运行 KeySteer.exe 即可。

macOS 下载对应架构的 ZIP,将 KeySteer.app 移到 /Applications,并授予“辅助功能”和“屏幕录制”权限。如果 Gatekeeper 阻止打开,请确认文件来自官方 Release,然后执行:

sudo xattr -cr /Applications/KeySteer.app

目前暂不支持 Linux

欢迎试用和反馈。问题和建议可以直接回复本帖,或提交到 GitHub Issues

1 个赞

神器级产品,设计很不错。

初步使用发现两个问题:

  1. 原有的 Alt + 右键 快捷键会失效。(补充:后续发现,更多快捷键受影响而失效,关掉 KeySteer 就正常了,之前的快捷键来自 AHK、Quicker 等工具。)
  2. ; 左键点击无效。

猜你在找:ThinkPad小红点 :doge:

谢谢使用,感谢反馈

1 好像懂你意思了,当前的设计是 进入 Normal模式之后就会 独占,截获所有输入,所以和别的软件的兼容性的确可能有问题

2 请问是什么情况下的?难道是别的程序冲突了 :sad_but_relieved_face:

所有情况,目前还没左键点击成功过,只能移动光标。

系统是 Windows 10

:joy:用过,好像只能移动,不方便点击

谢谢试用,请问同目录下有 keysteer.log 吗,里面有什么报错吗?

keysteer.log有报错:

2026-08-09T06:46:48.786Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:46:58.667Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:49:14.152Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:50:05.068Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:30.381Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:41.199Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:52.498Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:52:08.965Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:06.229Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:15.161Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable

谢谢反馈,只看这个的话,貌似是权限的问题?我自己测试时有的窗口是不允许注入的,比如任务管理器。

可以试试以管理员身份运行看看吗?

测试用管理员身份运行也无法模拟左键点击

更新了v0.8.1版本,可以麻烦尝试一下吗?右键菜单可以自动更新。如果还是不行,日志请发给我,谢谢

测试v0.8.1左击还是不行:

2026-08-09T06:46:48.786Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:46:58.667Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:49:14.152Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:50:05.068Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:30.381Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:41.199Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:52.498Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:52:08.965Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:06.229Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:15.161Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:23:57.357Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:14.780Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:31.869Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:52.471Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:25:14.095Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T10:37:17.933Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x000000CB (操作系统找不到已输入的环境选项。 (os error 203)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-09T10:37:36.917Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-09T10:38:51.304Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x1701210, thread=26336, pid=2656, exe="vivaldi.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset

没辙了,可以麻烦试试看 v0.8.2 可以吗?:joy: 再次感谢:folded_hands:

Mousemaster是我之前用过的(除了UIA相关功能外)功能最全面的键盘控制鼠标的工具,不过也是觉得软件体积有点太大了。而UIA这块体验最好的是jwno(【开发者自荐】 Jwno 平铺窗口管理器 - #11,来自 agentkilo ),虽然这个功能完全不是它的重心。期待楼主的项目能综合二者的优点。

测试了v0.8.2还是不行:

2026-08-09T06:46:48.786Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:46:58.667Z [ERROR] [input] [thread=main ThreadId(1)] keyboard chord: SendInput sent 0 of 2 keyboard events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:49:14.152Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:50:05.068Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:30.381Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:41.199Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:51:52.498Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:52:08.965Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:06.229Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T06:55:15.161Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:23:57.357Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:14.780Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:31.869Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:24:52.471Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T08:25:14.095Z [ERROR] [input] [thread=main ThreadId(1)] mouse button action: SendInput sent 0 of 2 events; Windows UIPI may be blocking input to a higher-integrity window; action ignored because synthetic input is temporarily unavailable
2026-08-09T10:37:17.933Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x000000CB (操作系统找不到已输入的环境选项。 (os error 203)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-09T10:37:36.917Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-09T10:38:51.304Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=44300, session=1, integrity=medium(0x2000), elevated=false, ui_access=false}, foreground={hwnd=0x1701210, thread=26336, pid=2656, exe="vivaldi.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-10T03:47:08.208Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=24336, session=1, integrity=high(0x3000), elevated=true, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-10T03:47:18.186Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=24336, session=1, integrity=high(0x3000), elevated=true, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-10T03:47:35.086Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=24336, session=1, integrity=high(0x3000), elevated=true, ui_access=false}, foreground={hwnd=0x20700, thread=21560, pid=20136, exe="dopus.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset
2026-08-10T03:48:43.863Z [ERROR] [input] [thread=main ThreadId(1)] mouse button Left Click: SendInput inserted 0 of 2 events; last_error=0x00000000 (not set (UIPI may leave it unset)), input_size=40, pointer_width=64, current={pid=24336, session=1, integrity=high(0x3000), elevated=true, ui_access=false}, foreground={hwnd=0x50E4E, thread=19524, pid=22640, exe="EverEdit.exe", session=1, integrity=medium(0x2000), elevated=false, ui_access=false}; this action was rejected and runtime input state was reset

可以麻烦试试看 v0.8.4 可以吗?添加了日志和重试, :joy: 再次感谢 :folded_hands:

体积大就算了,主要是我使用 mousemaster时,第一帧grid那几百个网格出现在我的机器上掉帧 :joy:
jwno 我搜到是窗口管理器,我平时用的不多,意思是想可以窗口分屏的控制吗?

jwno确实是窗口管理器,但是我说的部分不是它的主要功能,只是里面很小的一个模块,跟窗口管理没什么关系,也是用键盘控制鼠标点击屏幕上的元素。

UIA这一块每个程序能枚举到的对象都不太一样,我没有相关的开发经验,不太理解其中的原因。这个软件的优点首先是能枚举的对象比较全。其次还有一些特色的功能:

一个是按UI对象的树状关系逐步筛选对象,一个是按照对象的类型(按钮、超链接、输入框等)过滤,这种设计我在其他类似软件中都没见过。

相关的功能在这一部分:

不过具体的体验还是得实际用过才知道。(虽然实际上我因为没有窗口管理的需求并没有在用jwno)

原来如此。我看看实现方式
过滤的话,浏览器上的surfingkeys也有,输入框什么的。不过这样快捷键更多了啊。我怕难上手 :joy:

其实只要根据所处的状态,实时显示当前可用的快捷键,就不怕上手门槛高了。用户熟悉快捷键之后也可以关掉。