
niri 设计原则深度解析可滚动平铺 Wayland 合成器的架构取舍与实践指南【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niriniri 是一款可滚动平铺scrollable-tiling的 Wayland 合成器其独特交互体验并非偶然而是源于一整套明确的设计原则。本文以仓库中的 Development:-Design-Principles.md 为骨架结合 src/layout 与 resources/default-config.kdl 等源码实现系统讲解 niri 的通用设计原则、窗口布局核心决策以及默认配置的设计哲学帮助你理解为什么 niri 用起来是这个感觉以及如何基于这些原则评估和调优自己的配置。通用设计原则niri 的开发遵循一组通用原则它们构成了整个合成器行为的底层逻辑。这些原则不是教条——在具体场景下有充分理由时可以适当偏离但理解它们能让你预测 niri 的行为、读懂它的配置选项。原则一新窗口的打开不应影响任何现有窗口的大小这是传统平铺窗口管理器tiling WM最令人恼火的问题当你正在浏览一个浏览器或图片编辑器这样的大窗口想临时开一个终端时平铺逻辑会把大窗口压缩到不可用或触发内容重排、部分裁剪。传统平铺 WM 的常见解决思路是多用工作区——需要新窗口时跳到空工作区打开顺便还能获得整个屏幕。但 niri 用可滚动平铺给出了另一条路临时窗口直接在当前视图旁打开用完即关完全不打扰其他窗口也无需切换工作区相关窗口聚合把使用频率较低的窗口滚动到视图之外让同工作区的多个相关窗口自然归组。从源码看这一原则直接落实在布局逻辑中新窗口通过 add_tile、add_tile_to_column、add_column 等路径加入工作区而视口偏移view offset的调整只发生在激活/滚动等明确动作中不会因插入窗口而回挤既有列宽。这正是打开窗口不动其他窗口的代码级保证。原则二聚焦窗口不应自行移动聚焦窗口是用户正在工作的窗口视图之外发生的事情不应该干扰它。具体而言聚焦窗口左侧的窗口打开、关闭、改变大小时不应导致聚焦窗口产生视觉位移。这意味着 niri 的视口锚定在聚焦窗口上而不是简单地左对齐全部内容。与之呼应的是工作区对视图位置view position的保留机制见下文警惕不可见状态两者共同保证了编辑/阅读时的稳定感。原则三动作应当立即生效这一原则同时服务于合成器响应性、可预测性和代码健康避免大量边界情况和不必要的异步调整大小、并入列consume into column等操作立即生效即使窗口内容需要时间追赶动画化的工作区切换输入焦点立刻落到目标工作区和窗口无需等待动画播完打开总览Overview带缩放动画后可立即拖拽窗口关闭总览后输入立即回到窗口不等缩放动画回放。这一原则也解释了 niri 为何用事务 即时状态更新的模型而不是把动作排队到动画结束状态先行、动画只是视觉层用户永远不会因为动画而卡住输入。原则四被禁用时花哨特性不应影响性能动画、自定义着色器等eye-candy特性在关闭时不会运行也不出现在渲染树render tree中并且避免额外的离屏offscreen渲染。关于动画源码给出了非常具体的实现证据在 src/animation/mod.rs 的replace_config中当config.off为真时动画的duration和clamped_duration被直接置为Duration::ZERO。也就是说禁用动画时动画对象仍然被启动但时长为零时间一推进就立即结束。这既不影响性能又避免了代码中大量有没有动画的分支边界情况——动画代码路径始终存在且行为一致。原则五eye-candy 特性不应造成不合理的过度渲染即使开启了视觉特性也要控制渲染成本clip-to-geometry在很多情况下会阻止直接扫描输出direct scanout因为窗口表面并非完全可见但如果表面或子表面完全位于裁剪区域内仍然允许 direct scanout不无谓牺牲性能动画可以每帧造成 damage 甚至绘制到离屏缓冲因为动画预期是短暂的且可以被禁用圆角着色器rounded corners shader这类长期持续生效的效果则不应该每帧离屏渲染或产生过量 damage。这体现了 niri 对临时性效果与常驻效果的差异化性能预算。原则六警惕不可见状态invisible state不可见状态指无法直接从屏幕画面看出来的 niri 内部状态。它本身不是坏事但需要认真设计以减少意外感surprise factor。文档给出两个典型例子1. 工作区的原始显示器original output当一台显示器断开时它的所有工作区会迁移到另一台已连接的显示器上为了将来这台显示器重新接入时能还原工作区保留了自己原来属于哪台显示器的记忆。但只看屏幕你完全无法察觉这种状态——于是可能出现在家拔掉显示器、去公司把窗口重新排布、回家后某些工作区突然跳回家里那台显示器的意外。从源码看这一字段在 workspace.rs 中被定义为original_output: OutputId并在创建工作区时初始化src/layout/workspace.rs。为了降低意外感niri 采取了一个巧妙的策略每当工作区上出现新窗口就把该工作区的 original output 重置为当前所在显示器src/layout/workspace.rs 附近的归一化逻辑。这样你实际动手工作过的工作区会留在当前显示器只有从未主动使用的工作区才会在重新插回时回到老家。2. 视图位置的保存与遗忘niri 会在窗口出现、或窗口进入全屏时保存当前视图位置以便之后恢复——处理临时对话框的开合、全屏切换时就不会把视口弄得乱七八糟。但这也是一种看不见的状态屏幕上看不出关掉某个窗口后视图会恢复到哪。如果极端到为每个打开的窗口永远保存位置长时间运行后关闭窗口会导致视图近乎随机地跳动。为此 niri 采取克制策略每个工作区只记住最近一个视图位置并且一旦焦点切换就遗忘该记录。这样恢复只发生在紧邻的临时场景中不会积累成长期的意外行为。窗口布局三条核心设计决策除了通用原则文档还专门总结了窗口布局逻辑的三条设计考量它们直接塑造了 niri 的观感与操作方式。决策一大于屏幕的窗口/弹窗左上角对齐如果窗口或弹窗比屏幕还大niri 将其对齐到左上角。原因是窗口的左上区域更可能承载重要内容标题、工具栏、关键操作应保证始终可见。决策二固定像素尺寸作用于窗口本身比例尺寸作用于tile含边框这是 niri 配置中一个容易混淆、但逻辑严谨的设计固定像素尺寸如set-column-width 1280或default-column-width { fixed 1280; }设置的是窗口本身的大小——用户此时多半是在测试特定客户端尺寸或截取特定大小的截图所以边框不应计入比例尺寸如set-column-width 50%设置的是tile的大小包含 niri 添加的边框——用户用比例时是在规划多个窗口平铺边框当然要算进去否则实际排布对不上设置完成后niri 内部统一转换为包含边框的值以保持代码逻辑的单一性。因此set-column-width 1000之后再去修改 niri 的边框宽度窗口会被相应调整保证设了 1000 就是 1000。从配置解析源码看这一设计对应 niri-config/src/layout.rs 中的PresetSize::Proportion(f64)与PresetSize::Fixed(i32)两个变体比例值通过SetProportion(prop * 100.)转成百分比固定值通过SetFixed(fixed)直接传递——两者在后续布局管线中按上述规则换算为含边框的宽度。决策三全屏窗口是可滚动布局中的普通一员这是 niri 最独特、也最有代表性的一条设计全屏窗口不是盖在一切之上的特殊层而是普通 tile可以随时切换走而不破坏其全屏状态。可滚动平铺天然适合实现这一模型。当然聚焦在全屏窗口上时你确实想要整个显示器被覆盖——这一行为被显式硬编码进逻辑当视图静止停留在聚焦的全屏窗口上时顶层 layer-shell 层和浮动窗口会隐藏。这也解释了为什么把浮动窗口全屏化时它会进入滚动布局——全屏不是浮在上面而是成为滚动空间里的一格。默认配置为新人设计而非晒配置niri 的默认配置resources/default-config.kdl目标是为新用户提供熟悉、有帮助、不突兀的体验。它不是一份建议的炫酷配置——不会用彩虹边框和疯狂着色器吓到新人。定位与原则niri 不是完整的桌面环境也没有足够的贡献者规模去成为桌面环境无法提供全集成体验发行版定制版更适合做这件事。因此新用户被预期要通读并动手改默认配置。基于此默认配置详尽注释并链接到 wiki 对应章节不罗列所有选项避免信息过载过于特定或冷门的配置留在 wiki 上总原则是包含用户合理预期会想改或想知道的东西会主动宣传 niri 的独特特性例如截屏遮蔽block-out-from示例见默认配置中注释掉的 KeePassXC/Secrets 规则 resources/default-config.kdl。具体设计取舍主题默认值设计理由窗口装饰默认 CSDprefer-no-csd被注释掉给新用户熟悉易用的标题栏来移动/关闭窗口尤其 niri 目前没有服务端标题栏焦点环绘制完全绘制在窗口后面只绕窗口绘制会在 CSD 圆角内部留下空洞缺点是不透明窗口会透出焦点环FAQ 有解释。理想方案是向 Wayland 提出协议让窗口报告圆角半径焦点环宽度4 px与 CSD 窗口搭配观感好、足够显眼间距gaps16 px与默认焦点环宽度搭配协调见 layout 区块触控板默认开启tap点按与natural-scroll自然滚动作者认为多数人期望这样的使用方式阴影默认关闭阴影是性能开销较大的着色器且许多 CSD 窗口自带阴影截图路径~/Pictures/Screenshots/...与 GNOME Shell 一致用户熟悉、迁移成本低默认窗口规则仅两条修复 WezTerm 初始 configure bugdefault-column-width {}见 resources/default-config.kdl让 Firefox 画中画窗口默认浮动resources/default-config.kdl只修已知严重问题、只做绝大多数人想要的事按键绑定的组织逻辑默认 binds 大部分来自作者使用 PaperWM 的经验和其他合成器假定 QWERTY 布局且按逐步引入不同绑定配置概念的顺序排列。整体系统非常规律某个按键负责切换focus/move/workspace时加Ctrl就是把聚焦窗口或列一起移动过去加Shift执行替代动作聚焦/移动类变为跨显示器操作缩放类变为操作窗口高度而非宽度工作区切换放在ModU/I正好是窗口切换ModJ/K的上一行——纵向相邻手指位置自然延伸Alt相关绑定主要是只能在宿主机上用的如唤醒屏幕锁屏因为Alt在嵌套 niriniri 作为 winit 窗口运行时中是 Mod 修饰键。完整的默认绑定可查阅 resources/default-config.kdl从show-hotkey-overlay、spawn到focus-column-left、switch-preset-column-width、toggle-overview、quit带确认对话框一应俱全且每条都配有说明注释。从原则到实践如何读懂并调整 niri理解上述设计原则后你的配置决策会更有依据想临时开窗口不打断大窗口直接开即可——可滚动平铺会把它放在旁边用完关掉视图位置由每工作区单视图位置 焦点切换即遗忘的机制兜底调列宽前先想清楚set-column-width 1000是窗口净宽set-column-width 50%是含边框的 tile 宽度混用时注意换算全屏窗口想切走直接切全屏状态保留回到它时顶层面板与浮动窗口自动隐藏关闭动画/着色器animations { off; }见 resources/default-config.kdl等配置会让这些效果完全退出渲染路径性能零开销改默认配置默认配置就是最好的带注释教程每个区块都链接到 wiki按需取消注释或增删即可。结语niri 的好用不是玄学而是一组可追溯、可验证的设计原则的产物不打扰既有窗口的滚动平铺、立即生效的动作模型、可预测的不可见状态管理以及一套克制而亲和的默认配置。当你遇到 niri 的某个行为时回看这些原则并对照 src/layout/scrolling.rs 等布局源码与 resources/default-config.kdl 默认配置通常都能找到为什么这样设计的答案也就能更有底气地写出真正属于自己的配置。【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niri创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考