# imlock **Repository Path**: lingxing/imlock ## Basic Information - **Project Name**: imlock - **Description**: mac 锁定输入法工具 - **Primary Language**: Rust - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-04 - **Last Updated**: 2026-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # imlock —— macOS 输入法锁定工具 一个用 Rust 编写的小工具,把输入法「锁」住:一旦被切换,会在 150ms 内自动切回你指定的输入法。 适合被微信键盘等输入法反复闪烁/跳变困扰的场景,也适合需要长期固定某种输入法的用户。 ## 功能 - 菜单栏图标,一眼看到锁定状态(🔒 启用中 / 🔓 未启用) - **「✅ 启用锁定」勾选项**:一键开关锁定(开关状态持久化) - **「锁定目标」子菜单**:列出全部输入法,单选;选定即把该输入法锁为锁定目标(前面 ✓ 标记当前目标) - 设置(是否启用 + 锁定目标)持久化到 `NSUserDefaults`,重启后自动恢复 - 启动即读取设置:若上次开启了锁定,自动把输入法切到锁定目标 - 全局热键 **Ctrl + Cmd + L** 一键切换「启用锁定」 - **单实例守卫**:重复启动时弹窗提示并退出,避免多个实例抢菜单栏图标、监控线程互殴 - 后台监控线程,输入法被改就立刻切回 ## 原理 macOS 没有「禁止切换输入法」的公开 API,因此做法是 **监控 + 回滚**: - 通过 Carbon 框架的 Text Input Source (TIS) API 读取/枚举/切换输入法: - `TISCopyCurrentKeyboardInputSource` 读当前输入法 - `TISCreateInputSourceList` 枚举全部键盘输入源 - `TISSelectInputSource` 强制选中某个输入法 - 一个独立线程每 150ms 检查一次当前输入法,若处于锁定状态且发现被改,立即调用 `TISSelectInputSource` 切回。 > 这种方式不注入任何按键事件,因此**不需要**「辅助功能 / 输入监控」权限即可工作。 > 如果你希望连「切换输入法的快捷键」都彻底失效,可在系统设置里把多余输入法移除,只保留一个。 ## 构建 ```bash # 需要 Rust 与 Xcode 命令行工具 xcode-select --install cargo build --release # 产物:target/release/imlock ``` ## 运行 ```bash cargo run # 开发模式 # 或 ./target/release/imlock ``` 运行后菜单栏会出现红色圆点图标,点击即可看到设置菜单。 ## 设置界面(菜单栏内嵌) imlock 没有独立的设置窗口,所有配置都在菜单栏下拉里完成: | 菜单项 | 作用 | |---|---| | ✅ 启用锁定 / ⬜ 启用锁定 | 开关「锁定」总开关;开启时若尚未选目标,默认锁定「当前输入法」 | | 锁定目标 ▸(某个输入法) | 把该输入法设为锁定目标并立即启用锁定;当前目标前以 ✓ 标记 | | 锁定目标(当前:xxx) | 父菜单标题会显示当前锁定的输入法名 | | 退出 | 退出程序 | - 所有改动即时生效,并写入 `NSUserDefaults`(键 `imlock.enabled`、`imlock.target`)。 - 想要「彻底固定某输入法」:打开「锁定目标」选它 → 它立刻被锁死,之后被切走会在 150ms 内自动拉回。 ## 打包成 .app(推荐,便于常驻) 编译出的二进制是命令行程序,运行时图标会出现在菜单栏,但关闭终端就会退出。 用 `cargo-bundle` 生成 .app(`Cargo.toml` 里已配好 `[package.metadata.bundle]`,identifier `com.example.imlock`): ```bash cargo install cargo-bundle cargo bundle --release # 生成:target/release/bundle/osx/imlock.app (以及可选的 imlock.dmg) ``` 安装到用户应用目录(无需 sudo,避免占用系统 /Applications): ```bash mkdir -p ~/Applications cp -R target/release/bundle/osx/imlock.app ~/Applications/imlock.app ``` > 菜单栏图标的点击行为:点击图标会用 `popUpStatusItemMenu:` 显式弹出菜单(比把菜单挂在按钮上更稳,所有 macOS 版本都会显示)。菜单内含「退出」项,点击即退出程序。 ## 开机自启(登录启动) 已写好 `~/Library/LaunchAgents/com.example.imlock.plist`(`RunAtLoad`、`LimitLoadToSessionType=Aqua`),指向 `~/Applications/imlock.app/Contents/MacOS/imlock`。 - **下次登录**会自动启动(plist 在 `~/Library/LaunchAgents/` 下即可,无需手动 load)。 - 想**立即**生效,在终端执行(需处于 GUI 登录会话): ```bash launchctl load ~/Library/LaunchAgents/com.example.imlock.plist # 新版 macOS 若上述报错,改用: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.imlock.plist ``` - 停止自启:`launchctl unload ~/Library/LaunchAgents/com.example.imlock.plist`(或删除该 plist)。 也可手写一个最小的 .app 结构(见下),或用「系统设置 → 通用 → 登录项」把 `~/Applications/imlock.app` 加进去作为替代方案。 ## 最小 .app 结构(手写) ``` imlock.app/ └── Contents/ ├── Info.plist └── MacOS/ └── imlock # 把 target/release/imlock 复制到这里 ``` `Info.plist` 示例: ```xml CFBundleName imlock CFBundleIdentifier com.example.imlock CFBundleVersion 0.1.0 CFBundleExecutable imlock LSMinimumSystemVersion11.0 LSUIElement ``` ## 已知限制 - 锁定是「软锁定」(监控回滚),不是内核级拦截;在 150ms 检测窗口内可能有一瞬跳变,但基本不可感知。 - 系统升级、注销/切换用户后需重新运行。 - 部分 App(如某些远程桌面、游戏)会绕过 TIS 自行管理输入,可能不受影响。 - 单实例守卫通过占用本地端口 `127.0.0.1:19834` 实现;进程退出端口即释放,不会留下陈锁。若端口被其它程序占用,imlock 会误判「已在运行」而退出(极少见)。 ## 许可 MIT