【免费下载链接】denial
A smooth, customizable Wayland compositor with glass effects, three window layouts, and settings you can actually click. We got a little carried away.
Denial是一款基于 Rust + Smithay + Flutter 构建的开源 Wayland 合成器(Wayland compositor):它拥有流畅的窗口动画、可定制的磨砂玻璃(Glass)效果、三种窗口布局,以及真正可以点开的图形化设置面板。与传统"合成器套壳"桌面不同,Denial 把 Flutter 直接内嵌进合成器进程内部,让桌面 UI 和 Wayland 应用窗口在同一个场景里合成。本文带你完整读懂它的三层架构设计与 8 条核心不变量。
一、Denial 是什么:先看看最终效果
Denial 的定位是一站式的 Linux 桌面环境。它支持 Stacking(自由堆叠)、Dwindle(平铺)和 Scrolling(滚动平铺)三种窗口布局,支持多显示器、Xwayland 兼容 X11 应用、截图与屏幕共享,外观上主打半透明玻璃材质。
二、三层架构总览:Rust 核心 + Flutter 场景 + Portal 后端
Denial 的运行时由三个角色组成,官方架构文档在 docs/architecture.md:
| 层 | 进程 | 职责 |
|---|---|---|
| 原生核心 | deniald(Rust) | Wayland 前端、DRM/KMS 显示、输入、GBM/EGL 渲染、窗口与缓冲区生命周期 |
| Flutter 宿主 | deniald内嵌(Rust) | 动态加载 Flutter Engine ABI、外部纹理、帧调度与图集缓冲所有权 |
| Flutter 引擎 | deniald进程内 | Dart Shell、一整张逻辑桌面场景、把客户端窗口作为外部纹理导入 |
| Portal 后端 | denial-portal(独立进程) | D-Bus 激活的 Settings portal,只读缓存外观状态 |
其中最反直觉的一点:Flutter 引擎和 Dart isolate 直接运行在deniald进程内部。桌面 Shell 不是 Wayland 客户端,也不是第二个合成器——它就是合成器的一部分。这个决定催生了整套架构的 8 条不变量。
三、第一层:Rust + Smithay 原生核心
3.1 Smithay 打地基
Wayland 协议、DRM/KMS、libinput、libseat 和 udev 这些"硬骨头"全部由 Smithay 提供,另可选集成 Xwayland。项目自有的原生代码集中在 compositor/ 目录,并刻意分成四个独立层:
- 纯拓扑/图集(atlas)模型,带原子热插拔事务;
- 用于在别的合成器内做开发的嵌套(nested)测试装置;
- 真正的 Smithay DRM/KMS 合成器后端;
- 面向版本化 per-user IPC 的
denialctl控制客户端。
3.2 一张桌面级 GBM 图集:多屏零拷贝
KMS 后端只分配一张桌面级 XRGB8888 GBM 图集,每个连接的 CRTC 从同一帧缓冲的不同源矩形做扫描输出——多显示器不产生任何合成器拷贝。病态布局会在 GBM 分配之前被直接拒绝:任一图集轴超过 16,384 像素、或整个图集池超过 1 GiB 都会失败。
拓扑模型实现在 compositor/src/topology.rs,其中RenderViewId用负数命名空间映射物理输出,避免依赖连接器枚举顺序。
3.3 Volition:原子 KMS 呈现的"预判"
帧同步由内库denial_core::volition负责(源码见 compositor/src/volition/mod.rs)。它持有 DRM 文件描述符和可复用的 plane 提交,用有界截止期调度器提前逼近每一帧的物理垂直同步边沿,全程不阻塞在 DRM ioctl 上。分工非常明确:KMS 提交时序的变化归 Volition 管,而 Shell、Flutter、Wayland、DPMS 和截图策略归调用方(输出调度器)管。
四、第二层:Flutter 为什么"晋升"成了合成器的一部分
传统做法是把桌面 Shell 做成一个 Wayland 客户端(overlay client),Denial 拒绝了这条路。它的设计是:
- 一个 Dart 场景(scene)横跨所有受管输出:Flutter 渲染整张桌面图集,应用窗口(Wayland 客户端表面)作为外部纹理导入同一场景;
- 引擎是定制构建:Denial 的 Flutter 引擎是 Slimpeller 构建,只跑 Impeller GLES,Skia 的 Ganesh/Graphite 渲染器在引擎生成期就被关闭;锁定的引擎 fork 让 Impeller 直接对接合成器持有的轮转 FBO,保留图集损伤(atlas damage)追踪;
- Rust 拥有不安全资源,Dart 只拥有策略:Dart 拿到的是稳定的数字身份和不可变元数据,它从不持有 Wayland 资源、文件描述符、EGL image、GPU fence 或 KMS 缓冲。
这个分界的原文表述很直白:
Rust owns everything whose correctness depends on native resource lifetime. (正确性依赖原生资源生命周期的一切,都归 Rust 管。)
Dart 侧的 Shell 代码在 dart_shell/,可复用 SDK 在 packages/denial_flutter_sdk/,UI 组件库(包括桌面、启动器、设置应用)在 plugins/denial_desktop/。因为 Dart 只是"策略层",开发者改一个 widget 保存后热重载,桌面即刻更新,而所有 Wayland 应用照常运行——这就是 Denial 主打的"活体 Shell 编辑"能力,指南见 docs/UI_DEVELOPMENT.md。
窗口布局:协议无关的WindowLayouttrait
三种布局(stacking / dwindle / scrolling)是WindowLayouttrait 的协议无关实现,位于 compositor/src/bin/deniald/window_layout.rs。想加新布局?实现 trait、注册到WindowLayoutKind即可——布局实现只管 ID 和返回逻辑矩形,Smithay 适配器负责窗口资格、工作区和协议 configure。
五、第三层:denial-portal 与设置体系
5.1 设置:Rust 是唯一持久化权威
配置文件$XDG_CONFIG_HOME/denial/settings.json由 Rust 侧全权管理:版本化、迁移、保护原生独占段落、每次变更做 revision 检查,并经由0600临时文件 + 原子 rename 落盘。Flutter 侧的设置应用是独立的 Wayland 进程denial-settings,只拥有自己的 widget 树,通过控制协议 v1 与deniald对话——因此它自己的 UI/栅格线程永远不会给合成器的桌面帧增加构建、布局、绘制或栅格开销。
5.2 denial-portal:一个"只读缓存"型后端
denial-portal(源码在 compositor/denial-portal/)实现org.freedesktop.impl.portal.Settings,把 Denial 的亮/暗配色与强调色发布给普通应用。它的关键约束:
- 不读写设置文件、不在合成器事件循环里跑 D-Bus 工作;
- 合成器通过
$XDG_RUNTIME_DIR/denial/portal.sock发布带版本号的DesktopThemeSnapshot,事件循环只做"替换缓存 + 唤醒 worker",从不等待 D-Bus; - 门户状态只在设置提交之后才发布,绝不从 Flutter 的乐观预览里推断。
5.3 平台桥:FlatBuffers 线协议
结构化通信使用 protocol/denial.fbs 定义的 FlatBuffers schema(文件标识DENW),生成的 Dart/Rust 代码直接入库,构建无需网络或flatc。高频小消息则用 80 字节的定长二进制包(DENPmagic)。线协议细节见 docs/protocol/WIRE_FORMAT.md,通道清单见 docs/protocol/CHANNEL_INVENTORY.md。外部控制走$XDG_RUNTIME_DIR下0600权限的 Unix socket,denialctl通过版本化请求/响应协议查询状态、检查输出、恢复打包 Shell,参考 docs/DENIALCTL.md。
六、8 大核心不变量:架构的"宪法"
docs/architecture.md 末尾列出的 8 条不变量,是整个项目最容易踩坑、也最值得记住的边界:
| # | 不变量 | 一句话理解 |
|---|---|---|
| 1 | Flutter 是合成器的一部分,不是 overlay 客户端 | 决定了一切内嵌架构的前提 |
| 2 | 一个 Dart 场景拥有所有受管输出的 Shell 合成 | 全局一张图集,多屏零拷贝 |
| 3 | Rust 拥有不安全句柄与精确的缓冲区生命周期 | Dart 从不碰 fd / EGL image / fence / KMS buffer |
| 4 | 客户端呈现只随物理输出进度推进 | frame 回传统绑物理刷新,禁止"旧帧确认新提交" |
| 5 | 输入路由消费一份不可变布局快照 | Dart 发布的布局整体原子替换,无竞态 |
| 6 | 内嵌 Dart 永不启动进程或高频 CLI 工具 | 命令走原生桥,控制走持久连接 |
| 7 | 运行时路径不依赖另一份源码 checkout | 构建产物自包含,可复现 |
| 8 | 门户状态只在设置提交后发布 | 拒绝乐观 UI 污染系统级外观状态 |
其中第 4 条的细节最能体现合成器工程的深度:呈现反馈(presentation feedback)随每个已发布的客户端缓冲被捕获,跟着 Flutter 实际采样的纹理代数一起存活,并被渲染它的输出保留到 KMS page-flip 完成——后续的 surface commit 绝不能用一个更早排队中的桌面帧来确认。
七、源码导航:按模块速查
- 架构总纲:docs/architecture.md
- Rust 合成器工作区:compositor/(含 compositor/README.md 的构建与运行方式)
- Volition 帧同步库:compositor/src/volition/mod.rs
- 拓扑与图集模型:compositor/src/topology.rs
- Flutter 宿主(引擎 ABI 绑定):compositor/flutter-engine/
- Portal 后端:compositor/denial-portal/
- Shell 前端:dart_shell/ · plugins/denial_desktop/
- 线协议 schema:protocol/denial.fbs
- 构建指南:docs/BUILDING.md · 会话与渲染器选项:docs/SESSION_STARTUP.md
八、总结:为什么这套架构值得学
Denial 给出的答案可以浓缩成三句话:
- 把不安全的留给 Rust——Wayland 资源、DMA-BUF、GPU fence、KMS 缓冲全部归原生核心,且主库以
#![forbid(unsafe_code)]立规; - 把好看的交给 Flutter——桌面、动画、玻璃材质、手势解释都在 Dart 场景里,开发者用热重载就能改桌面;
- 用不变量守住边界——8 条不变量把"谁拥有什么、谁何时能推进"写得明明白白,让多显示器、热插拔、Xwayland、输入法和截图各归其位。
这就是一个"有点上头"的开源合成器的底层逻辑:表面是丝滑的玻璃桌面,底下是原子 KMS 提交、GPU fence 和不可变输入快照支撑的严谨状态机。
【免费下载链接】denial
A smooth, customizable Wayland compositor with glass effects, three window layouts, and settings you can actually click. We got a little carried away.
相关推荐
rust-analyzer 架构深度解析:IDE 编译器前端的 crate 分层与设计不变量
rust analyzer 架构深度解析:IDE 编译器前端的 crate 分层与设计不变量 导读 本文基于 docs/book/src/contributin
开发工具StyleGAN2架构深度剖析:生成器与判别器设计原理
StyleGAN2架构深度剖析:生成器与判别器设计原理 本文深入解析了StyleGAN2的核心架构设计,重点剖析了生成器中映射网络与合成网络的协同工作机制,以及
人工智能深度学习计算机视觉媒体生成预训练深度剖析:Envoy Gateway核心架构与组件设计原理
深度剖析:Envoy Gateway核心架构与组件设计原理 引言:为什么现代网关需要新的架构范式? 你是否还在为服务网格配置复杂度过高而烦恼?是否经历过网关规则
API网关后端云原生微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考