Mac 手机触控板开源项目 mouse-control:用 Node.js + CGEvent 把手机变触控板

Mac 手机触控板开源项目 mouse-control:用 Node.js + CGEvent 把手机变触控板
摘要:本文介绍一个开源项目 mouse-control,通过 Node.js cliclick CGEvent 的组合,将手机浏览器变成 macOS 原生风格的触控板。纯局域网方案,无需云服务,无需 pip 依赖。一、背景:站立办公的隐藏痛点升降桌 站立办公已经成为程序员的标配,但有一个痛点常常被忽略:笔记本放高 → 触控板位置也高 → 手臂长时间悬空桌面纵深不够 → 鼠标垫放不下外接 Apple Trackpad → ¥499 起,且占用桌面理想的方案:把输入设备放到低处,手臂自然下垂即可操作。mouse-control 就是这个思路的实现:手机浏览器扫码连接 Mac,变成触控板,手机可以放在键盘托架层。二、核心架构手机浏览器 (HTML/CSS/JS) ──WebSocket──▶ Node 服务 (Mac) ├─ cliclick → 移动/点击/拖拽/控制键 └─ helper.py(ctypes)→ 滚动/Unicode文本/快捷键2.1 为什么有两个后端?cliclick 5.1 存在三处不足:没有 scroll 命令t:无法输入中文(只能发 key code)kp:不支持字符键组合(如⌘)解决方案:滚动、Unicode 文本输入、修饰键快捷键统一走ctypes调 CoreGraphics 的CGEvent,无需 pip 安装 pyobjc。2.2 高频合并策略触摸移动事件是 60Hz 的,如果每次触摸都 spawn cliclick 进程,必然卡顿。控制器的做法:把增量累积,采用单进程在飞合并 flush:所有 cliclick 调用串行化按执行速度自然限频延迟有界2.3 CGEvent 文本输入实现# 核心调用CGEventKeyboardSetUnicodeString(event,length,buffer)直接投递 Unicode 串(按 ≤20 码元分块),支持中文、emoji,绕过 cliclick 的 key code 限制。2.4 CGEvent 快捷键实现CGEventCreateKeyboardEvent(None,keycode,True)CGEventSetFlags(event,kCGEventFlagMaskCommand)keycode 修饰键 flag,如 ⌘ keycode 24 Cmd Shift。三、快速开始3.1 环境准备macOSNode.js ≥ 18Homebrew3.2 安装与启动gitclone https://github.com/askxiaozhang/mouse-control.gitcdmouse-control brewinstallcliclicknpminstallnpmstart终端会打印 Mac 名、局域网 URL、PIN 和二维码。3.3 授予辅助功能权限openx-apple-systempreferences:com.apple.preference.security?Privacy_Accessibility将运行npm start的终端 App(Terminal / iTerm / VS Code)加入列表,改动后重启终端。3.4 手机连接手机与 Mac 连同一 Wi-Fi手机相机扫终端二维码顶部绿灯亮 已连接可选:Safari → 分享 → 添加到主屏幕四、手势速查表手势动作一指移动移动光标一指轻点单击连续两次轻点双击一指按住后移动拖拽双指轻点右键双指移动滚动双指张开/捏合放大/缩小(⌘/⌘−)三指左/右滑切换桌面空间三指上滑调度中心三指下滑App Exposé五、键盘与语音输入点 Dock 的 ⌨️ 进入键盘视图:输入框聚焦后用手机系统键盘打字,文字实时打到 Mac 当前光标位置中文候选词期间不发送,选词后才发,避免发拼音控制键:Esc / Tab / ←↑↓→ / ⌫ / ⏎ 语音:HTTP 走系统键盘听写;HTTPS 用 Web Speech 直接听写六、安全考虑WebSocket 必须 PIN 鉴权后才接受控制指令默认监听0.0.0.0:8787(局域网可见)PIN 在二维码 URL 里以?pin明文带出便于扫码仅用于可信局域网,不建议暴露公网七、配置参数参数默认说明PORT8787服务端口--open-settings—启动时打开辅助功能设置面板PORT9000npmstartnpmrun setup# node server.js --open-settings八、常见问题光标不动→ 授予辅助功能权限并重启终端中文打不出→ 确认走键盘视图(⌨️),不是触控板滚动方向反了→ 手机 ⚙️ 里切换自然滚动三指滑动没反应→ 系统设置 → 键盘快捷键 → 调度中心,确认快捷键为默认连不上→ 确认同 Wi-Fi,IP 是 Mac 当前局域网 IP,防火墙放行端口九、项目结构mouse-control/ ├── server.js # HTTP WebSocket PIN 鉴权 二维码 ├── lib/ │ ├── controller.js # cliclick 封装 高频合并 路由 │ ├── helper.py # ctypes 助手 │ ├── net.js # 局域网 IP 探测 │ └── pin.js # PIN 生成 / 校验 ├── public/ # 手机端 UI(无构建) │ ├── index.html │ ├── styles.css │ └── app.js └── test-controller.js # 自检脚本十、总结mouse-control的核心价值:零安装客户端:手机浏览器扫码即用macOS 原生手势:肌肉记忆零成本迁移纯局域网 无云:隐私可控无 pip 依赖:只用标准库 ctypes项目开源,MIT 协议,欢迎 Star / Issue / PR: github.com/askxiaozhang/mouse-control