☰
Denial架构深度剖析:Rust+Smithay+Flutter原生Wayland合成器的三层设计与8大核心不变量
2026/10/14 1:01:07 网站建设 项目流程

【免费下载链接】denial

A smooth, customizable Wayland compositor with glass effects, three window layouts, and settings you can actually click. We got a little carried away.

项目地址:https://gitcode.com/gh_mirrors/de/denial
点击查看免费下载

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 条不变量,是整个项目最容易踩坑、也最值得记住的边界:

#不变量一句话理解
1Flutter 是合成器的一部分,不是 overlay 客户端决定了一切内嵌架构的前提
2一个 Dart 场景拥有所有受管输出的 Shell 合成全局一张图集,多屏零拷贝
3Rust 拥有不安全句柄与精确的缓冲区生命周期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 给出的答案可以浓缩成三句话:

  1. 把不安全的留给 Rust——Wayland 资源、DMA-BUF、GPU fence、KMS 缓冲全部归原生核心,且主库以#![forbid(unsafe_code)]立规;
  2. 把好看的交给 Flutter——桌面、动画、玻璃材质、手势解释都在 Dart 场景里,开发者用热重载就能改桌面;
  3. 用不变量守住边界——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.

项目地址:https://gitcode.com/gh_mirrors/de/denial
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询