BongoCat:实时追踪键盘鼠标的低占用开源桌宠
2026/9/18 15:03:05 网站建设 项目流程

前阵子把桌面彻底翻新了一遍,任务栏精简、壁纸换成纯色,结果发现桌面空得有点难受。试了几款桌宠,要么是观赏型的——猫自己在那儿睡觉、伸懒腰、来回走,跟我在干什么毫无关系;要么就是套一个重型外壳,开机之后内存曲线肉眼可见地上抬一截,用两天就忍不住卸了。后来把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)权限授权后需重启应用;系统升级后授权可能被重置
Linux(X11)XRecord 扩展 / 输入设备节点读取输入设备节点的权限需要把用户加入对应用户组,或调整 udev 规则
Linux(Wayland)无标准全局监听接口依赖合成器提供的受限接口大概率拿不到完整按键流,只能响应有限事件

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 上用同一套默认皮肤粗测过:应用自身进程私有工作集约在几十 MB,WebView 那组进程加起来在几十到一百 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。我现在的排查习惯是:先看权限,再看日志,最后才去翻代码。这个顺序能省掉大量无效时间。

第四,控制好你对"低占用"的预期。它确实同类里很轻,但"轻"不等于"没有"。你要是开着几十个浏览器标签再加上它,系统该卡还是卡。把它当成一个加分的小工具,而不是一个能拯救低配机器的方案,心态会舒服很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询