差点给 Hyprland 打补丁#
起因是一个很小的需求:我希望我可以快速知道 Hyprland 里面有没有窗口还打开着。
很朴素的想法,对吧?
第一反应:加一套 API#
先前我为了让 Alt+Tab 弹出一个窗口切换面板,写过一个小工具 WindowSwitcher——一个 2.6 KB 的 Python 脚本。就这么点东西,它拖着的配套却是:
- 一个独立的 Forgejo 仓库;
- 一条
.forgejo/workflows/package.yml打包流水线; - 一个 registry 包(
window-switcher-1.0.tar.zst); - 一条 ansible 的 vendored 组件登记,含
sentinel校验路径; - MANIFEST.md 里一整节说明文档与 README 里的清单条目;
- 以及
hyprland.lua里的一条快捷键绑定。
当时的思路是这样的:Hyprland 没有直接对外暴露「每个工作区里都有哪些窗口、当前聚焦的是哪个」这样一个接口,那我就在合成器里加一个。改 C++,把窗口树按工作区聚合,序列化成 JSON,再让 Waybar 去读。
这条路听起来很「彻底」,但它意味着:
- 维护一个 Hyprland 的私有分支(当时已经是
vedaru/v0.56.2-ime-popup,还要再叠一层); - 每次上游发版都要处理 rebase 冲突;
- 引入一个新的数据契约,两端一起演进;
- 而这一切,只为了在一条 24 像素高的状态栏上加几个字。
代码是能写出来的。问题在于,它值不值。
先问系统有没有这个原语#
真正把整件事推翻的,不是更聪明的架构,而是先停下来读了一遍 Waybar 的手册。
事实是:Waybar 的 hyprland/workspaces 模块早就为每个工作区按钮加好了 CSS 类——.active、.visible、.urgent,以及一个几乎没人注意的 .empty:当一个工作区没有窗口时,它会被自动打上这个类。
也就是说,「哪个工作区里有窗口」这个信息一直在那儿,只是从来没有被使用过。我不需要新 API,不需要改合成器,甚至不需要碰 Waybar 的配置文件。我要的全部东西是:
1/* 有窗口的工作区:在数字背后垫一块更亮的色阶 */2#workspaces button:not(.empty) {3 background-color: @surface-container-highest;4}5
6/* 当前聚焦的工作区:再上一层 */7#workspaces button.active,8#workspaces button.focused {9 background-color: @primary-container;10 color: @on-primary-container;11}三行。没有分支,没有数据契约,没有需要跟进的接口。上游怎么改,这段 CSS 都不需要动。
这里还有一个我中途才想明白的细节:一开始我把「有窗口」实现成了改变数字的颜色,但那是错的。占用状态应该由按钮的表面(背景色块)来表达,数字本身保持安静——这正是 GNOME 46+ 的强调色体系和 Material 3 在做的事:层级由明度台阶区分,而不是靠给文字上色。设计对了,代码反而更短。
等到这条需求本身被更好的方案取代(工作区色块已经把「哪里有事」说清楚了),我把整个自研的工具退役掉,hyprctl -j binds 里 ALT+Tab 的绑定归零。删掉的东西包括那个二进制、那段选择器函数、版本表里的一行、comps 数组里的一个名字、文档里的一节——六处配套,为了一个 2.6 KB 的脚本。
这就是复杂度的真相:它从来不是按「代码行数」计费的。一件自研工具的真实成本,是它牵出的仓库、CI、包、分发、文档和心智负担的总和。写的时候只看到那 2.6 KB,维护的时候要面对的是那六处。
为什么「加」总是先于「减」#
回头看,第一反应之所以是改合成器而不是读手册,有一个很现实的原因:写代码是有即时反馈的,而做减法没有。
加一个 API,你能看到编译通过、数据流动、界面亮起来,每一步都在给你正反馈。而「先读文档、发现人家已经做了」,它没有产出物,只有一次判断——甚至可以说,它看起来像什么都没做。于是我们本能地滑向「我要造一个」,因为造东西的手感是具体的。
但恰恰是这种手感,最容易掩盖一个事实:能被删掉的那部分,往往才是真正的解法。
几条可以带走的判断#
-
先找原语,再造轮子。 在动手加一层之前,先确认系统是否已经暴露了你需要的最小信号——它可能只是一个你没用过的 CSS 类、一个已有的环境变量、一个现成的 hook。绝大多数时候,你以为要「扩展系统」,其实只是没把系统读完。
-
状态用表面表达,而不是改写内容。 「有窗口」是按钮的状态,不是数字的属性。把它放在背景色上,语义就落在正确的层级,样式规则也随之变短、变稳。
-
按总成本给「小工具」定价。 一个脚本的重量,等于它带来的那条流水线。在写之前先问:它会不会长出自己的仓库、CI 和文档?如果会,它就不是小工具。
-
删除要趁早,且要删干净。 只删二进制而留着 registry 登记,下次部署会把它重新拉回来;只删绑定而留着文档,下一个人会以为它还在用。整套退役,比留下半截更便宜。
结语#
那次返工之后,Waybar 上什么都没有变多——合成器没被动过,分支没变长,接口没增加,代码行数反而少了一点。但工作区区块的明暗,已经把该说的话说完了。
我们总以为「更强的系统」意味着更多代码。可在这个案例里,更强的设计意味着更少的代码:不用补丁,不用 API,甚至不用那个存在了三周的自研工具。
最好的工程决策,有时是一次成功的删除。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时