把实体键盘当成触摸屏用。 Windows 绿色单文件工具,把主键盘区按物理拓扑映射成屏幕上的触点 —— 按下键就是手指按下,松开就是抬起,支持真多点触控。
Turn your physical keyboard into a touchscreen. The main key block is mapped onto the screen following its physical layout, so finger position ≈ screen position. Real multi-touch injection via the Windows Touch Injection API, with automatic mouse fallback. Single portable .exe, no installer, no registry, no network.
用键盘玩需要触摸/点划操作的游戏、安卓模拟器里的手游、展厅触控内容调试时,鼠标点击的效率明显不够:一次点击要走"移过去 + 按下"两步,且同一时刻只能有一个指针。
这个工具换了个思路:把键盘当成一块触摸屏。
键盘键位 屏幕触点
┌───┬───┬───┐ · · ·
│ Q │ W │ E │ ────► · · · Q 在 A 的左上方
├───┼───┼───┤ · · · 屏幕上的 Q 也在 A 的左上方
│ A │ S │ D │ ────► · · ·
└───┴───┴───┘ · · ·
按下 A,屏幕坐标 (30%, 50%) 处就有一个手指按下;A、S 同时按住,就是两根手指同时按。全程不用移光标,多个键可以同时按。
- ❌ 不是「给 PC/平板用的屏幕虚拟键盘」(方向相反:它不往屏幕画键盘,而是让键盘冒充触摸屏)
- ❌ 不是远程控制 / 自动化脚本工具
- ❌ 不注入进程、不修改目标程序、不读写目标内存 —— 只调用系统公开的输入注入 API
- 到 Releases 下载
key2touch.exe(或直接下最新版:key2touch.exe); - 双击运行,免安装,不写注册表,不联网;
- 选目标显示器 → 点右上角「点此启用」,或按
Scroll Lock; - 现在按键盘上的字母/数字键就是"触摸"了。再按一次
Scroll Lock关闭,键盘立刻恢复打字。
第一次用建议点底部蓝框的 「① 点我做自动诊断」:程序自己弹靶子、自己注入一次点击、自己判定结果, 直接告诉你"注入链路是否正常",不用你猜。
git clone https://github.com/lhr03/key2touch.git
cd key2touch
python key2touch.py运行时只依赖 Python 标准库(ctypes + tkinter),不需要 pip install 任何东西。需要 Python 3.9+ 且带 tkinter(官方安装包默认包含)。
主键盘区(不含小键盘)按物理错位关系映射到屏幕,所以「手指位置 ≈ 屏幕位置」,肌肉记忆可以直接迁移。
| 屏幕纵向位置 | 键盘行 | 按键 |
|---|---|---|
| 顶部 (0%) | 数字行 | ` 1 2 3 4 5 6 7 8 9 0 - = Backspace |
| 25% | Q 行 | Q W E R T Y U I O P [ ] \ |
| 50% | A 行 | A S D F G H J K L ; ' |
| 75% | Z 行 | Z X C V B N M , . / |
| 底部 (100%) | 空格行 | Space(正下方居中) |
- 默认四周内缩 3%,避免最外侧触点贴死在屏幕边缘(可在 0–20% 之间调)
- 勾选「启用边缘扩展键」后,
Tab / Caps / 左Shift / Enter / 右Shift也会加入映射(共 54 点) - 1920×1080 下默认 49 个触点,平均每个触点覆盖约 240×200 像素 —— 精度不足时用方向键微调(见下)
| 功能 | 默认键 | 可自定义 |
|---|---|---|
| 总开关 | Scroll Lock |
✅(Scroll/Caps/F9–F12/Insert 等) |
| 按住才生效 | 右 Alt |
✅(可设为"不使用") |
| 开关键位覆盖层 | F12 |
✅ |
| 切到上一屏 / 下一屏 | F9 / F10 |
❌(面板下拉亦可切) |
| 类别 | 功能 | 说明 |
|---|---|---|
| 注入 | 真·触摸注入 | Windows Touch Injection API(InitializeTouchInjection / InjectTouchInput),产生真实触摸事件,系统会投递 WM_TOUCH 并自动提升为点击 |
| 注入 | 鼠标事件 | SendInput,先移动再按下,任何程序都认。默认模式,最可靠 |
| 注入 | 触摸 + 鼠标双发 | 两种一起发。注意触摸本身会转成一次鼠标点击,双发等于点两次 |
| 动作 | 点击 / 长按 | 按下即触点按下,松开抬起;按住不放就是长按 |
| 动作 | 真多点触控 | 同时按住多个键 = 多指同时按下,最多 10 指,可做双指缩放 |
| 动作 | 连点 / 连发 | 按住超过设定延迟后按设定频率自动重复点击(1–60 Hz) |
| 精度 | 方向键微调 | 按住任意映射键时,方向键移动该触点(Shift 加速 5×),可当拖拽/划动用 |
| 精度 | 自定义映射区域 | 把整块映射限定在某个窗口内(如模拟器),等效精度大幅提升 |
| 显示 | 键位覆盖层 | 显示每个键对应的触点与键名,按下高亮;鼠标可穿透,不挡游戏 |
| 多屏 | 显示器切换 | 下拉选择 / F9 F10 循环 / 自动跟随鼠标所在屏幕 |
| 安全 | 防误触 | 开关 + 按住双模式;Ctrl/Alt/Win 组合键永远放行 |
| 其他 | 最短按压时长 | 防止点击过快被系统或游戏忽略(默认 80 ms) |
| 其他 | 长按心跳 | 每 0.8 s 重发一次触点状态,避免长按被判定失效 |
- 总开关关闭时,键盘 100% 正常打字,程序完全不干扰;
- 也可以只用「按住生效键」(默认右
Alt):按住时才变成触摸屏,松开立刻恢复; Ctrl/Alt/Win组合键永远放行 ——Alt+Tab、Ctrl+C不受任何影响。
- 自动枚举所有显示器,支持不同分辨率、不同 DPI、上下左右任意排列、含负坐标的副屏
- 三种切换方式:面板下拉、
F9/F10循环、勾选「自动跟随鼠标所在屏幕」 - 做了 Per-Monitor DPI 感知(
SetProcessDpiAwarenessContext(-4)),坐标按物理像素计算,缩放后不偏移 - 推荐玩法:把模拟器窗口拖到副屏 → 点「取当前窗口区域」,整块键盘映射只覆盖这个窗口。分辨率越小,等效精度越高
程序由三部分组成,都在 key2touch.py 单文件内:
① 低级键盘钩子 ② 坐标映射 ③ 输入注入
SetWindowsHookEx 物理拓扑 → 归一化 → 屏幕像素 InjectTouchInput
WH_KEYBOARD_LL ──► (支持自定义区域/内缩/多屏) ──► SendInput
│ │ │
捕获按键 算出触点坐标 产生真实输入事件
决定吞掉还是放行
- 钩子线程:独立线程跑消息循环(低级钩子必须由安装它的线程处理消息)。命中「需要吞掉」的键就返回 1,否则放行;只识别
WM_KEYDOWN/WM_KEYUP,不干扰WM_SYSKEYDOWN系列。 - 注入:触摸指针用 PID 池管理,最多 10 个并发触点;鼠标模式拆成「先移动 → 再按下」两次
SendInput,比一条指令里同时塞移动和点击更可靠。 - 配置:JSON 存 exe 同目录;目录只读时自动退到
%APPDATA%\key2touch\。
这些都是真机验证过、而不是从文档推断的结论,写在这里希望能省下后来者的时间:
1. InitializeTouchInjection 成功 ≠ 触摸可用
必须真的发一次 InjectTouchInput 才算数。远程桌面、无触摸屏设备上初始化也可能返回成功,但注入会被静默丢弃 —— 只剩下系统画的一个视觉圆点,看起来"点到了",其实什么都没发生。
2. 填 rcContact / rcContactRaw / ptPixelLocationRaw 会返回 ERROR_INVALID_PARAMETER (87)
只填 ptPixelLocation + touchMask = CONTACTAREA|ORIENTATION|PRESSURE 才通过(接触区域留空系统会自己补)。
3. 多点触控必须一帧内一次性提交全部触点
第二个手指单独 InjectTouchInput 一定失败。正确做法是每次把「所有活跃触点」组成数组一次提交(count = N);抬起一个触点后,要把它从活跃集合里移除再提交。
4. 低级键盘钩子不能传 GetModuleHandleW(None)
源码运行时正常,打包成 PyInstaller 单文件 exe 后返回 err=126(找不到模块),钩子装不上,程序界面一切正常但完全没反应。WH_KEYBOARD_LL 不需要 DLL 注入,模块句柄传 NULL 即可。
5. 分层窗口的「色键」与「全局 alpha」互斥
用色键(LWA_COLORKEY,把某颜色变透明)做透明背景时,再设 LWA_ALPHA 会把色键顶掉 —— 背景色不再透明,整块窗口以设定不透明度铺在屏幕上。两者属于同一组标志的不同分支,只能选一个。
6. 透明 ≠ 鼠标穿透
WS_EX_TRANSPARENT 需要配合 WS_EX_LAYERED 才真正生效,改完还要让系统刷新窗口样式。一个"看不见但吃掉所有点击"的覆盖层是最难排查的 bug —— 现在程序打开覆盖层时会用 WindowFromPoint 自查落点下方是不是自己,不通过就自动关闭。
7. UIPI:低权限进程不能向高权限窗口注入 目标以管理员运行时,光标照样会动,但点击被系统拦掉。程序会在状态栏提示,并提供「以管理员重启」。
# 需要 Python 3.9+(带 tkinter)
powershell -ExecutionPolicy Bypass -File .\build.ps1脚本会建 .venv、装 PyInstaller、输出 dist\key2touch.exe(约 11 MB,单文件、无控制台窗口)。也可以手动执行:
pip install -r requirements-dev.txt
python -m PyInstaller --onefile --noconsole --name key2touch --clean --noconfirm key2touch.py调试时加 --console 打包,可以看到标准输出。
先点 「① 点我做自动诊断」:程序自建靶子窗口 → 自动注入一次点击 → 自己判定结果,结论直接写在屏幕上。
- ✓ 注入完全正常 → 程序这一侧没问题,问题在目标程序(见下表第 1、2 条)
- ✗ 没有生效 → 按它给出的下一步做(改模式 / 以管理员重启)
状态栏还常驻两个有用的信息:「前台目标」(当前最前面的程序是谁、是否管理员权限)和 「落点窗口」(每按一次键就显示这个落点下面是哪个窗口)。如果落点窗口又是 key2touch.exe,说明自己的面板挡住了 —— 这是"没反应"最常见的原因。
exe 同目录会生成 key2touch_debug.log,记录每次按键的「捕获/放行」、落点坐标、落点窗口与注入结果。
| 现象 | 原因与解决 |
|---|---|
| 鼠标移过去了,却点不动东西 | ① 本程序面板/诊断画布挡在落点上(看「落点窗口」是不是自己,启用后会自动最小化);② 目标程序是管理员权限 → 点「以管理员重启」;③ 覆盖层未穿透(已内置自检,会自动关闭) |
| 完全没反应 | ① 看是不是「已启用」;② 目标显示器选错;③ 先跑「自动诊断」 |
| 触摸模式没反应,鼠标模式有 | 目标程序只认鼠标 → 改成「鼠标事件」或「双发」 |
| 提示「触摸注入不可用」 | 远程桌面 / 无触摸屏 / 部分虚拟机不支持 → 自动回退鼠标模式 |
| 点得到但位置偏 | 调「边缘内缩」;改用「取当前窗口区域」;让本程序与游戏保持一致的 DPI 行为 |
| 精度不够 | ① 缩小映射区域(最有效);② 方向键微调;③ 勾选边缘扩展键增加触点密度 |
| 打字被吞了 | 总开关开着或按住键被压着 → 按总开关热键关闭 |
| 恢复默认 | 删掉同目录的 key2touch.config.json,或点「恢复默认」 |
别被"触摸圆点"骗了:Windows 在触摸时会画一个小圆点/涟漪,那只是视觉反馈,不代表点中了任何东西。 本程序默认关闭该圆点(可在「注入方式」那一行重新打开)。
- 精度天花板:49(或 54)个触点是物理决定的,绝对定位精度天然不如真触摸屏。缓解手段是缩小映射区域 + 方向键微调。
- 滑动类操作:目前没有独立的"划动手势"快捷键,靠「按住 + 方向键微调」实现连续位移。高频划动场景(音游、切水果)建议等摇杆模式。
- 鼠标模式不支持多点(鼠标协议本身只有一个指针)。
- 注入权限:受 Windows UIPI 限制,不能向更高完整性级别的窗口注入 —— 目标以管理员运行时,本程序也必须以管理员运行。
- 反作弊:使用反作弊内核驱动的游戏可能过滤模拟输入,此类情况无法绕过。
- 精确模式:按住修饰键后进入当前区域的 3×3 细分网格,第二次按键精确落点(49 → 400+ 点)
- 摇杆模式:按住某键 = 虚拟摇杆推到某个方向,偏移量连续可调(高频划动场景)
- 配置 Profile:不同游戏各一套映射(区域、连点频率),热键秒切
- 绑定目标窗口:现在区域是静态矩形,绑定 HWND 后可跟随窗口移动/缩放
- 比例锁定:键盘与屏幕长宽比差异大(21:9、16:10)时,只铺中间同比例区域
本工具通过 Windows 公开 API 注入输入事件,不修改、不注入任何目标进程。请在合规范围内使用; 在游戏中用于自动化操作可能违反该游戏的服务条款,风险自负。