BongoCat:实时追踪键盘鼠标的低占用开源桌宠
前阵子把桌面彻底翻新了一遍任务栏精简、壁纸换成纯色结果发现桌面空得有点难受。试了几款桌宠要么是观赏型的——猫自己在那儿睡觉、伸懒腰、来回走跟我在干什么毫无关系要么就是套一个重型外壳开机之后内存曲线肉眼可见地上抬一截用两天就忍不住卸了。后来把BongoCat跑起来才算找到我想要的那种它不自己演戏而是把我真实的键盘敲击和鼠标移动实时追踪下来映射成猫爪敲键盘、猫头跟着光标转。开源、多款皮肤、内存占用低、完全免费这几条凑在一起确实值得单独拿出来聊一次。这篇内容我打算按一个真跑过、也改过源码的人的视角来写。适合三类人想让桌面有点活气但受不了重外壳的普通用户想拿它当入门项目练手的前端/客户端开发者以及打算自己做皮肤、改行为逻辑的折腾党。涉及代码和配置的地方我都会给出可直接照做的路径涉及取舍的地方我会把为什么这么选讲透。1. 一只跟着你敲键盘的猫到底解决了什么需求桌宠这个品类其实存在很久了从早期的桌面小人到后来的虚拟主播挂件技术路线一直在变但用户真正想要的从来没变过在不干扰工作的前提下给屏幕加一点活的东西。1.1 桌宠的三条实现路线差别比想象中大我观察下来这类工具基本分三派。第一派是观赏型猫有自己的行为状态机定时切换动作跟用户输入完全解耦实现简单但久了会觉得假第二派是装饰型本质是一张动图或者视频循环内存友好但没有任何交互第三派是输入镜像型把用户的键盘鼠标事件当作驱动信号猫的动作和你手上的动作一一对应也就是 BongoCat 走的这条路。第三条路最难也最值钱。难在哪难在你得全局监听输入事件——注意是全局不管焦点在哪个窗口、哪一行代码编辑器、哪一个聊天框里你的每一次按下和抬起都得被捕获到然后再实时地翻译成动画帧。这在 Windows、macOS、Linux 三套系统上的做法完全不同权限模型也完全不同。1.2 BongoCat 的定位它是输入镜像不是电子宠物我特别喜欢它的一点是克制。它没有做喂食、成长、成就系统那一套养成逻辑就是一个纯粹的镜像你打字它敲键盘你点鼠标它拍爪子你移动光标它眼睛跟着转。这种克制的价值在于它的资源开销是被约束住的——没有复杂的状态机、没有后台定时任务、没有网络请求轮询整机负担主要就是渲染一帧动画加接一个事件流。提示如果你之前用过那种会自己乱跑、会弹出对话框的桌宠切换到 BongoCat 的第一感受会是安静。它不是功能变少了而是把预算全花在了实时追踪的准确度上。还有一点容易被忽略因为它是开源的模型资源和行为参数都是明面上的文件你可以随时替换、随时魔改。闭源桌宠最让人难受的地方就是想改一点东西无从下手而这一类项目把扩展点直接摆在你面前。2. 实时追踪鼠标键盘全局输入监听的实现逻辑实时追踪这四个字听起来很轻巧但它是整个项目技术含量最高的部分。我拆开讲你把这一节看明白其它桌宠类项目基本也能举一反三。2.1 全局钩子是怎么看到你每一次按键的操作系统层面键盘和鼠标事件在到达目标窗口之前会经过一层系统级的输入队列。想全局拿到这些事件常见有两种做法钩子Hook机制在输入处理链路上挂一个回调事件流经时先经过你的函数。Windows 上是SetWindowsHookEx配合WH_KEYBOARD_LL/WH_MOUSE_LL这类低级钩子macOS 上是 Quartz 的CGEventTap挂在kCGHIDEventTap位置。原始输入Raw Input机制Windows 上通过WM_INPUT消息拿到设备级原始数据好处是不受焦点影响、能区分具体是哪个设备坏处是需要自己处理窗口消息循环。Rust 生态里有一批成熟的封装库把上面这套跨平台差异抹平了暴露出来的 API 基本就是注册一个回调回调里给我事件类型和键码。项目里真正要做的其实是在回调之上再包一层状态管理按下要记状态、抬起要清状态、长按要有持续动作、组合键要有互斥判断。这里有个小细节值得说键盘事件是按下/抬起成对出现的但动画是按下触发、抬起复位。如果你直接把事件透传给渲染层快速连打的时候会出现动画抖动。我自己改代码时看到项目里做了一层节流和最小帧间隔保护就是为了解决这个。2.2 三个平台在权限和兼容上完全是三套逻辑这是新手最容易翻车的地方——同一份代码在 Windows 上丝滑在 macOS 上装了没反应在 Linux 上甚至起不来。差异集中在权限和输入系统两处平台全局输入监听的实现需要的授权典型坑Windows低级钩子 / Raw Input通常无需额外授权被杀软或安全软件拦截管理员权限进程的事件有时收不到macOSCGEventTap辅助功能Accessibility权限授权后需重启应用系统升级后授权可能被重置LinuxX11XRecord 扩展 / 输入设备节点读取输入设备节点的权限需要把用户加入对应用户组或调整 udev 规则LinuxWayland无标准全局监听接口依赖合成器提供的受限接口大概率拿不到完整按键流只能响应有限事件macOS 那条我踩得最实。第一次装完打开猫一动不动我以为是程序坏了翻了半天日志才发现是辅助功能权限没给。而且这个权限的入口藏得比较深系统设置 → 隐私与安全性 → 辅助功能把应用加进去并且勾选然后必须完全退出再重新打开热切换不生效。第二次坑是系统大版本升级之后授权列表里的勾选还在但实际已经不生效了取消勾选再重新勾上才恢复。Wayland 那条要有心理预期。Wayland 的设计理念就是应用之间互不可见全局键盘监听天然和这个理念冲突所以现阶段在 Wayland 会话下只能做到部分可用。如果你用的是比较新的发行版并且默认走 Wayland遇到猫不跟着动先别怀疑代码登录界面切回 X11 会话验证一下最快。2.3 从输入事件到猫爪动画中间隔着一层映射表事件拿到了怎么变成动作项目里的做法是维护一张映射关系把物理键位划分成区域左手区、右手区、功能键区、数字区等每个区域对应模型里的一个动作名。鼠标移动走的是另一条路——通常是取光标相对位移换算成头部或眼球的偏移角度鼠标点击则直接触发对应爪子的拍击动作。这套设计的好处是可扩展性极强。你想加一个打字时尾巴摆动的效果不需要动输入监听的代码只要在映射表里挂一个新的动作名再让模型支持这个动作就行。我自己动手加过一个长时间不操作就进入打盹状态的小逻辑实现方式就是记录最后一次事件的时间戳超过阈值后切换到 idle 动作分支。注意改映射表的时候动作名必须和模型文件里定义的动作名严格一致大小写和分隔符都不能错。这类错误不会报错只会表现为某几个键按下去没反应非常难排查。建议改完先用一个显示当前动作名的调试面板验证一遍。3. 内存占用为什么能压到这个水平超低内存占用是这个项目宣传里最吸引人的一条也是最容易被误读的一条。我把这件事从头说清楚。3.1 外壳选型决定了内存的下限桌面应用的外壳大致两条路一类是把整个浏览器内核打包进应用运行时体积大、冷启动慢但一致性极好另一类是把渲染交给系统自带的 WebView应用本体只包含业务代码和 Rust 后端。BongoCat 走的是第二条路。这意味着安装包很小本体进程的内存占用也主要落在Rust 后端 一点前端逻辑上那部分通常就是几十 MB 的量级。而系统 WebView 那一坨内存是共享的理论上可以被多个应用复用不会因为你多开一个桌宠就整体翻倍。提示这也是为什么它敢说自己占用低——不是它做了什么神奇的内存优化而是它把最重的那部分甩给了操作系统。3.2 常驻内存的大头其实是模型和贴图真正吃内存的不是框架是皮肤。一套精细的模型包含若干个高分辨率贴图文件加载之后会常驻显存和内存。同一个程序换一套轻量皮肤和换一套超精细皮肤占用能差出一倍以上。我的经验是如果你在意占用优先挑贴图尺寸小、图层少的模型。判断方法很直接——看皮肤文件夹的体积几十 KB 到几百 KB 的一般很轻几 MB 到十几 MB 的就要有心理准备。另外动效复杂度也有影响如果一个皮肤包含大量高帧率动作切换动作时会有额外的解码和纹理上传开销。3.3 任务管理器里的数字别只看一行这是我想重点澄清的一个误读。因为渲染跑在系统 WebView 里你在任务管理器里看到的往往是一组进程应用自身进程后端 主逻辑数字通常最小WebView 的一到多个子进程数字往往更大且名字可能完全看不出跟这个应用有关加上共享内存的部分任务管理器默认不显示完整。所以我只占了 40MB这种说法严格讲只是应用自身进程的私有工作集。真实感受要看整组进程加起来以及你开它之前和之后的系统可用内存差值。我在 Windows 11 上用同一套默认皮肤粗测过应用自身进程私有工作集约在几十 MBWebView 那组进程加起来在几十到一百 MB 之间浮动具体数字跟皮肤复杂度和同时开着多少个浏览器标签强相关因为共享的 WebView 运行时可能有复用。换一套极简皮肤之后整组数字能明显下降。桌子上还有别的重型应用时这点差异基本感觉不出来。对比参照我早先用过的某些桌宠单个进程就能到两三百 MB开机自启之后每次切窗口都能感觉到一点黏滞。BongoCat 这方面确实清爽但我不建议把它理解成零成本——准确的表述是在同类里成本很低且成本主要可控地花在皮肤上。4. 皮肤系统的玩法资源结构、换装与自制多款皮肤是很多人入坑的直接理由。但大部分人只停留在下载、导入、换一个其实这个系统的可玩性远不止于此。4.1 一套皮肤的文件结构长什么样模型类资源常见的组织方式是一个描述文件记录模型结构、贴图引用、动作和表情清单若干个贴图文件以及动作、物理演算等附加数据。描述文件是这套资源的总入口导入的时候程序读的就是它。所以你换皮肤的时候本质上做的是三件事把资源目录放到指定的模型目录下、让程序读到描述文件、把当前选中的模型标识写进配置。这三步里最容易出问题的是第二步——资源目录的层级不能乱。很多人解压完之后多了一层文件夹导致程序找不到描述文件表现就是导入成功但列表里没有或者选上了显示空白。把目录拍平一层通常就好了。配置一般落在系统的应用数据目录下以应用标识命名。想快速定位直接搜应用名或者标识串就行。里面通常是可读的 JSON保存了当前模型、窗口位置、缩放、快捷键等。4.2 换装之后这几个参数值得手动调一遍默认参数不一定适合你的屏幕。我换完皮肤第一件事是调这几项缩放比例高分辨率屏幕上默认值往往偏小猫会显得很mini窗口位置多显示器场景下默认可能落在主屏建议直接拖到目标屏幕再让它记住置顶层级要压在普通窗口上面但不要挡住输入法候选框和系统托盘透明度/被遮挡时的表现有些皮肤可以设置成鼠标悬停时才完全不透明平时半透减少干扰。注意置顶和点击穿透是两个独立开关。开了穿透之后你的点击会落到下面的窗口上猫就点不到了不开穿透猫身覆盖的那一小块区域会挡住你的操作。我一般只留一小块可交互区域其余部分穿透。4.3 想自己做一套皮肤要准备什么自制皮肤的门槛主要在建模工具不在这个项目本身。路径大致是用支持该类模型格式的编辑工具做好模型 → 按约定命名动作 → 导出成程序能读的格式 → 放进模型目录 → 在配置里选中。这里的关键是动作命名约定。程序不认识你的美术它只认名字。你需要把左手按下右手松开鼠标左键点击这些语义映射到模型里真实存在的动作名上。命名对不上模型再好看也不会动。我给想做皮肤的朋友一个建议先拿官方皮肤拆开当模板把描述文件里的动作清单抄下来照着这份清单去做动作比自己凭空设计一套命名体系省事得多。做完之后先用最简单的几个动作验证链路通不通全部跑通了再去补细节动作和表情。5. 跑起来才会遇到的坑完整排查链路前面讲的是设计层面的东西这一节讲实战。我把自己遇到过的几类问题按排查顺序整理出来你可以照着这条链路走。5.1 猫完全不动或者只动半个身子第一步确认权限。macOS 看辅助功能授权Linux 看有没有读输入设备的权限。这一步能解决绝大部分完全不动。第二步确认事件到底有没有进来。项目一般有日志输出如果能看到按键按下之类的记录说明监听是通的问题在渲染侧如果日志里一条都没有问题在监听侧。第三步区分全部不响应和部分不响应。如果是某些键没反应八成是映射表的问题回去看第 2.3 节讲的动作名一致性。如果只有鼠标不动而键盘正常重点看鼠标事件那条分支以及是不是被系统里的其它辅助功能软件截走了事件。第四步确认是不是被别的进程抢了钩子。安全软件、输入法、录屏工具、剪贴板管理工具都可能挂输入钩子钩子链是有顺序的抢在前面的一方有可能吃掉事件。解决方式是临时退出可疑软件再测一遍二分法定位。5.2 多显示器、DPI 缩放下的位置错位这个坑很隐蔽。我一开始用笔记本外接了一块屏副屏缩放比例和主屏不一样结果猫的位置总是偏一点越靠边偏得越明显。原因是坐标系的换算。窗口位置记录的是逻辑坐标而实际渲染发生在物理像素上两块屏缩放不同的时候同一个逻辑坐标对应的物理位置不一样。排查思路是先把所有屏幕的缩放设成一致看问题是否消失如果消失就确认是缩放换算的问题然后检查项目有没有正确处理每个显示器的缩放因子。提示跨屏拖动的场景建议把猫固定在一块屏上不要让它跟随性价比更高。跟随意味着每一帧都要重新算位置既增加开销也更容易出偏差。5.3 全屏游戏、远程桌面、虚拟机里的异常表现独占全屏的游戏会接管整个显示输出桌宠被压在下面看不见这是正常现象不用怀疑。无边框全屏的游戏桌宠会浮在上面可能挡视野也可能因为多了一层持续渲染而影响帧率。我的建议是打游戏前直接退出桌宠或者给它设一个检测到全屏应用就自动隐藏的开关。远程桌面场景下会话切换时输入钩子可能失效或者重复注册表现为鼠标一顿一顿、或者动画卡住不动。断开会话重连一次通常恢复。虚拟机里要额外注意如果宿主机的输入没被正确转发到虚拟机虚拟机里的程序自然什么都收不到这时候要检查的是虚拟机软件的输入捕获设置而不是桌宠本身。6. 从源码跑起来到改一版自己的对开发者来说这个项目最大的价值是它是一份完整的跨平台桌面应用参考实现而且体量不大适合通读。6.1 环境准备与构建链路构建这类项目需要三块工具链包管理器、系统 WebView 的开发依赖、以及 Rust 工具链。以 Linux 为例除了常规的 Node 环境和 Rust 环境还需要系统层面的 WebView 开发库和相关依赖缺任何一项都会在编译期报错而且报错信息往往指向一个看起来毫不相干的包。我的做法是先把官方文档里的依赖清单一次性装全别一个个试。装完之后按顺序走先装依赖再装项目依赖最后跑开发模式。开发模式下改前端代码会热更新改 Rust 后端会触发重新编译后者慢得多所以调样式的时候尽量少动后端。第一次编译会比较久因为要把整个 Rust 依赖树拉下来编译一遍。后面增量编译会快很多。如果编译卡住不动先看是不是在下载依赖网络慢的话换个镜像源。6.2 想改行为逻辑该从哪个文件下手按职责分代码大致是三块输入监听层负责注册钩子、接收原始事件、做节流和状态去重。想改响应灵敏度长按判定忽略某些按键改这里。映射与状态机层负责把事件翻译成动作名维护当前该播哪个动作。想加打盹连击特效特定组合键彩蛋改这里。渲染层负责加载模型、播放动作、处理窗口透明和置顶。想改缩放透明度多屏行为改这里。我改过的一个小功能是按空格时尾巴单独摇一下。实现方式就是在前两层加一条规则然后在模型里确认有对应的尾巴动作。整个过程没碰渲染层说明这套分层是清晰的。6.3 打包分发和开机自启打包出来的产物体积通常不大这是走系统 WebView 路线最直接的收益。要注意的是不同平台的打包产物不能通用各自平台的产物只能在自己平台上构建。开机自启这件事我建议谨慎开。虽然这个应用本身很轻但它是置顶窗口开机就出现有时候会干扰你进入工作状态。我现在是手动开需要的时候点一下。真要自启记得检查有没有延迟启动的选项避开开机那一堆进程抢资源的时段。7. 用了一段时间之后我的几点真实体会写到这里其实已经把我能想到的都说完了剩下几条零碎的经验当成朋友之间的提醒。第一别为了桌宠去折腾一整天。我第一周就把皮肤目录翻了个遍、把配置项改了个遍最后发现最舒服的状态其实就是默认参数加一套干净的皮肤什么都不用调。工具的价值在于它安静地在那儿而不是让你一直围着它转。第二把输入镜像这个思路记下来。这套监听 → 映射 → 渲染的三段式结构稍微抽象一下可以套用到很多地方做一个跟着你打字跳动的音乐可视化、做一个根据你工作节奏变色的状态灯、做一个统计你一天敲了多少键的极简面板。输入事件是一份很便宜也很好玩的数据源。第三遇到问题先怀疑权限和环境最后才怀疑代码。全局输入监听这件事对系统环境太敏感了绝大多数不工作的案例压根不是 bug。我现在的排查习惯是先看权限再看日志最后才去翻代码。这个顺序能省掉大量无效时间。第四控制好你对低占用的预期。它确实同类里很轻但轻不等于没有。你要是开着几十个浏览器标签再加上它系统该卡还是卡。把它当成一个加分的小工具而不是一个能拯救低配机器的方案心态会舒服很多。