在Wayland上追求永恒的停靠 - lambdock (C + GTK4 + Lisp GNU Guile Scheme)
摘要
lambdock是一款原生支持Wayland的、可修改的桌面停靠应用,使用C、GTK4和GNU Guile Scheme构建,具有流畅的物理动画和运行时REPL控制。
<p><a href="https://lobste.rs/s/lvspri/quest_for_eternal_dock_on_wayland">评论</a></p>
查看缓存全文
缓存时间: 2026/08/28 11:36
# 永恒 Dock 之追寻 - lambdock
来源:https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html
这是关于`lambdock`的故事:https://codeberg.org/jjba23/lambdock
`lambdock`是一个现代的、高度可定制的、原生支持 Wayland 的桌面 Dock 应用程序(使用 C 语言与 Guile Scheme + GTK4 编写),并具备 REPL(交互式解释器)功能。
> 作为一个重度计算机用户,我大部分时间都在享受数字生活;作为一个视觉爱好者和工作流优化狂人,我长久以来都梦想着拥有终极的桌面 Dock。这就是`lambdock`背后的故事——这只原生支持 Wayland 的怪兽,拥有无限的可定制性、流畅的物理动画效果、即时的响应能力,并且自由如风。但在此之前,我将先带你了解其背后的设计理念、失败原型的“墓地”、艰难的攀登,以及最终征服 GTK4 C 运行时,并将一个活跃、呼吸的、交互式的 Lisp 心脏嵌入其中的过程。
## 永恒 Dock 之追寻(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#quest-for-eternal-dock)
多年来,从 X11 迁移到 Wayland 的 GNU/Linux 桌面用户们反复遭遇一个悲剧:失去了像 Cairo-Dock 和 Plank 这样标志性的、深度可定制的 Dock。不过,截至 2026 年,这些(以及其他)项目已经为兼容 Wayland 做出了巨大努力,这很棒。虽然像 Waybar 和 EWW 这样的状态栏引擎在状态显示方面表现出色,但要创造一个真正的、能与那些重量级应用(甚至 macOS)相媲美的应用程序 Dock,需要在布局定位、动态窗口追踪、自动隐藏以及用于动画的流畅悬停物理效果方面实现独特的融合。
`lambdock`的故事在很多方面都是一个关于坚韧的故事,因为我甚至记不清自己尝试过多少次(以不同方式)构建 Dock 了。我几乎不知道,在我的愿景成为切实现实之前,有多少种范式、想法和概念验证(PoC)会崩塌。
lambdock 展示
2026年7月30日,我获得了一个启示,并着手构建 **lambdock**:一个原生支持 Wayland 的桌面 Dock,它不仅复制 macOS 或 Cairo-Dock 的体验,更要通过完整的运行时可检查性、可定制性和 Lisp 启迪来超越它们。
在 2026 年,我发现 GTK 是 GNU/Linux 及其他平台最好的 UI 工具包。因此,对我而言这个选择非常明确,即使我也尝试过 Qt 等其他工具,但 GTK 始终是我的首选。在多个概念验证失败后,躺在床上浏览 Hacker News 时,一个念头闪过我的脑海,突然间,**豁然开朗**!
为什么不使用惯用的现代 GTK4,并采用它原生的编写语言?哦等等,那个语言,C,有`libguile.h`库,这是一个优秀的、与 Lisp(GNU Guile Scheme)互操作的库,支持双向绑定和通信。为什么不踏入这个兔子洞?这可能会让我更了解我最爱的语言(GNU Guile),同时让我能与 GTK 无缝结合并获得原生的高性能。
因此,我在床上构思了 lambdock,深受 Emacs 启发。一个强大、小巧的 C 核心驱动程序、渲染、图形和底层细节,以及一个嵌入式的 GNU Guile Scheme 引擎,后者在运行时是整个应用的核心和大脑。这种架构独特地提供了交互式套接字 REPL 控制、Lisp 元编程宏、动态多 Dock 生成、原生 Wayland foreign-toplevel 窗口追踪,以及由帧时钟驱动的悬停动画。
## 原型的墓地(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#graveyard-of-prototypes)
通往`lambdock`的道路铺满了最终夭折的雄心勃勃的概念验证(PoC)。我尝试了多种语言、方法、框架和架构实验,追逐着在不牺牲底层显示服务器控制权的前提下快速开发的梦想。
这个探索最初旨在构建一个使用 Guile GI 和其他现有 Guile GTK 绑定的纯 100% Lisp 架构。然而,这个愿景在稀缺的文档和难以理解的绑定层面前崩塌了。将 GTK 的命令式、面向对象状态与函数式的 Scheme 模式桥接起来,造成了持续的架构摩擦,而微小的社区意味着每个绑定的边缘情况都成为死胡同和兔子洞中的漫长探索。
尝试使用 Python + GTK3/4 提供了快速的原型开发和庞大的库生态系统,但撞上了严重的实时性能限制。单线程瓶颈和全局解释器锁(GIL)造成的卡顿破坏了流畅的滑出动画,除非用自定义 C 代码支持。这也促使我思考,不如干脆用 C 语言来写这一切。再加上沉重的内存占用和脆弱的 IPC 机制,证明这个运行时对于低延迟的桌面 Dock 来说过于笨重和不可预测。
使用 Rust、GTK 绑定和自定义合成器 IPC 的尝试承诺了内存安全和无畏并发,却引入了严重的冗长性和绑定摩擦。Rust 的异步事件循环与 GTK4 的主线程之间的不匹配造成了结构性问题,借用检查器(borrow checker)在与 GTK 和我想要支持的动态特性一起工作时真是个真正的麻烦(PITA),而缺少反射机制则完全(轻松)关上了嵌入一个活跃、交互式 Lisp REPL 的大门。
我做了最后的尝试,使用 JavaScript/Node 和 Layer Shell,目标是熟悉的 Web 风格样式和异步 I/O。然而,这遇到了资源消耗臃肿、与底层 Wayland 协议集成不佳的问题,并且没有清晰的 Lisp 扩展途径,因此它很快就在被遗弃原型的墓地里占有一席之地。
## 铁骨 Lisp 灵魂(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#iron-skeleton-lisp-soul)
- **C + GTK4 + gtk4-layer-shell** 是原生 Wayland 表面控制无可争议的冠军。C 提供原始速度、零开销的 GLib 集成、内存布局效率、完美的 Wayland 扫描器协议生成,以及你能找到的最好的 GTK 文档。
- **GNU Guile Scheme (`libguile`)** 是终极运行时思维。与其用静态、死板的 JSON 或 TOML 文件来配置 Dock,不如通过`libguile.h`嵌入 Lisp(Guile Scheme),这赋予了 Dock 一颗活生生的 Lisp 心脏,并将其变成了一个无限可扩展的程序。
```
int main(int argc, char **argv) {
#ifdef DEFAULT_GSK_RENDERER
g_setenv("GSK_RENDERER", DEFAULT_GSK_RENDERER, FALSE);
#endif
scm_boot_guile(argc, argv, inner_main, NULL);
return 0;
}
```
通过将`libguile`直接托管在 C 的 GTK4 主循环中,`lambdock`实现了我所认为的圣杯,很像 GNU Emacs 所做的:**无限的扩展性、渲染和动画方面毫不妥协的原生性能,以及用于用户配置和实时 REPL 检查的 Lisp 元编程超能力。**
---
## 词源(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#etymology)
名字 *lambdock* 是一个文字游戏,结合了:
- **船舶码头 ⚓**,容器和应用程序在此安全停靠。
- 函数式编程 & Lisp 启迪中的 `lambda λ` 表达式。
- **小羊 🐑**(温和、毛茸茸、轻量级且干净)。
- 作为桌面 UI 组件的 **Dock**(例如 Plank, Cairo-Dock, macOS Dock)。
`lambdock` 采用神羔羊(Agnus Dei)(https://en.wikipedia.org/wiki/Agnus_Dei) 作为其项目标志:衔着十字架和红旗的上帝羔羊。
你可以说使用这个 Dock *就像一次类似 Enrique Iglesias (https://www.youtube.com/watch?v=3DV57Y4tEAM&t=3s) 的宗教体验*
---
## 多配置 & 多 Dock 实例(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#multi-config-amp-multi-dock-instance)
`lambdock` 具有内置的多 Dock 编排功能。它不限于单个 Dock 栏,你可以同时运行多个独立的 Dock,分布在屏幕边缘或不同的显示器上。
- **自动目录监控**:`lambdock` 会监控 `~/.config/lambdock/` 目录中匹配 `settings.scm` 或 `settings-*.scm` 的文件(例如 `settings-left.scm`, `settings-bottom.scm`)。
每个配置文件定义其自己的隔离 `LambdockState`、位置(`dock-position`)、主题(`dock-theme`)、显示器目标(`dock-monitor`)和项目启动器布局(`dock-items`)。
- **动态生成**:创建一个新的 `settings-2.scm` 文件会立即在屏幕上生成一个新的 Dock 栏。
- **动态热重载**:编辑任何 `settings-*.scm` 文件会热重载该特定的 Dock 实例,而不会闪烁或重启其他正在运行的实例。
- **动态销毁**:删除一个 `settings-*.scm` 文件会安全地拆毁并销毁其对应的 Dock 窗口,移除 Wayland 句柄和 GTK 小部件,而不会使应用程序崩溃。
---
## lambdock 支持哪些系统?(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#what-systems-does-lambdock-support)
`lambdock` 是一个 GNU/Linux 优先的工具。
该工具使用 `gtk4-layer-shell` 来渲染到屏幕、定位以及实现 Dock 行为。
这意味着它在支持 `wlr-layer-shell-unstable-v1` 协议的 Wayland 合成器中运行良好,包括:
- KDE Plasma(Wayland 会话)
- 基于 Smithay 的合成器:Niri, COSMIC Desktop
- 基于 wlroots 的合成器:Sway, Hyprland, River, Wayfire
- 基于 Mir 的合成器
注意:GNOME(Mutter)目前不支持,因为它未实现该协议。
---
## 双向引擎的工作原理(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#how-the-bi-directional-engine-works)
`lambdock` 不仅仅是被 Scheme 配置。它拥有一个嵌入式的、可扩展的 Lisp 引擎和运行时环境,补充了一个高性能的 C 应用核心和图形引擎。执行流程在 C 和 Guile 之间双向进行。
二进制文件的入口点(`main.c`)使用 `scm_boot_guile` 启动 Guile 解释器。C 运行时充当主机,并调用 Scheme 过程来管理状态和提取设置。
Scheme 不限于被动的数据声明,它可以触发运行中的 C 引擎内部的操作。
lambdock 提供了一个漂亮的 Lisp DSL 来定义你的 Dock,基于项目和预设。
| Scheme 构造器 / 过程 | 返回类型 | 描述 | 关键字参数 |
|----------------------|----------|------|------------|
| `(app-item ...)` | Record | 自定义应用程序启动器定义 | `#:name`, `#:exec`, `#:icon` |
| `(dynamic-item ...)` | Record | 动态轮询小部件,显示动态文本数据 | `#:name`, `#:icon`, `#:exec`, `#:poll-fn`, `#:interval-ms`, `#:hover-animate?` |
| `(preset-launcher 'symbol)` | Record | 从内部预设列表解析的标准启动器 | Symbol (例如 `'emacs`, `'alacritty`) |
| `(preset-launchers-for 'a 'b)` | List | 批量辅助函数,返回一个预设启动器列表 | 可变符号列表 |
| `(separator-item)` | Record | 布局分隔线 | 无 |
| `(preset-icon 'symbol)` | String | 解析给定预设的默认图标名称字符串 | Symbol |
`lambdock` 的魔力在于 C 图形核心与 GNU Guile Scheme 引擎之间无缝的双向桥接。查看一个示例配置文件:
```
(define dock-auto-hide? #t)
(define dock-position 'bottom)
(define dock-icon-size 48)
(define dock-theme 'vanilla)
(define dock-monitor 'all)
(define dock-items
(append
(preset-launchers-for 'alacritty 'google-chrome 'spotify-flatpak 'nautilus)
(list (separator-item))
(preset-launchers-for 'emacs 'intellij 'bruno-flatpak)
(list (separator-item))
(preset-launchers-for 'ram 'cpu 'cpu-temp 'battery)))
(use-modules (ice-9 popen)
(ice-9 rdelim)
(srfi srfi-19)
(srfi srfi-1))
(define (sh-output cmd)
(let* ((port (open-input-pipe cmd))
(output (read-line port)))
(close-pipe port)
(if (eof-object? output) "" output)))
(define dock-auto-hide?
(string=? (string-trim-both (sh-output "hostname")) "thinkpad"))
```
---
## 读取-求值-打印循环(REPL)(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#read-eval-print-loop-repl)
出于开发、实验和实时可定制性的目的,`lambdock` 内置了 GNU Guile Scheme 运行时。你可以启用一个后台 Unix 域套接字 REPL,以实时查询或动态更改运行中 Dock 的状态。
通过 Guile 的 `(system repl server)`,`lambdock` 可以为每个 Dock 实例生成一个 Unix 域套接字,例如位于 `/tmp/lambdock-repl.sock`。你可以通过 Emacs(Geiser)、`socat` 或 `ncat` 直接连接到运行中的 Dock 进程。
由于 GTK4 要求所有 UI 修改必须在主线程上进行,而 REPL 监听在后台线程上,`lambdock` 使用 GLib 的 `g_idle_add` 来安全地调度由 Scheme 触发的 UI 更新:
```
static gboolean on_manual_reload_idle(gpointer user_data) {
(void)user_data;
LOG_C_INFO("Main", "Redrawing UI from in-memory Guile state...");
redraw_active_docks();
return G_SOURCE_REMOVE;
}
static SCM scm_reload_dock(void) {
g_idle_add(on_manual_reload_idle, NULL);
return SCM_UNSPECIFIED;
}
```
你可以在 Emacs 中编辑主题、更改图标尺寸或重定义启动器,发出 `(reload-dock!)`,然后看着你的 Dock 实时变形,无需重启或掉帧!
在你的 `~/.config/lambdock/settings.scm` 文件中,将 `#:enable-repl?` 设置为 `#t`。你还可以选择使用 `#:repl-socket-path` 自定义套接字路径(默认为 `/tmp/lambdock-repl.sock`):
```
(configure-dock!
#:position 'bottom
#:items (list ...)
#:enable-repl? #t
#:repl-socket-path "/tmp/lambdock-repl.sock")
```
然后你可以检查或修改 Dock 状态,并在需要时重新加载 Dock(使用 `reload-dock!`)。
一旦连接,你就可以对正在运行的 `lambdock` 实例求值 Scheme 表达式,可能性是无穷的,例如:
```
scheme@(guile-user)> (use-modules (lambdock core))
scheme@(guile-user)> (get-dock-theme) ;; -> 'vanilla
scheme@(guile-user)> (set-dock-theme! 'nature)
scheme@(guile-user)> (reload-dock!)
```
如果你使用带 Geiser 的 Emacs:
1. 运行 `M-x geiser-connect-local`。
2. 选择 `guile` 作为 Scheme 实现。
3. 输入套接字路径:`/tmp/lambdock-repl.sock`。
参见这里 Emacs + Geiser GIF
lambdock 展示
你也可以直接从终端使用不同的工具连接,例如 `nc`、`ncat`、`socat` 包裹 `rlwrap`,以及其他:
```
socat - UNIX-CONNECT:/tmp/lambdock-repl.sock
ncat -U /tmp/lambdock-repl.sock
nc -U /tmp/lambdock-repl.sock
rlwrap socat - UNIX-CONNECT:/tmp/lambdock-repl.sock
```
---
## 胜利:在 Guix、Nix、OCI 及其他上游\#
最初在一个本地目录中的混乱实验,现已正式登上全球舞台。
`lambdock` 已上游至 GNU Guix 并原生打包。你可以用一条孤立的命令运行或安装它:
```
guix time-machine --channels=channels.scm -- shell -f guix.scm -- lambdock
```
完全上游至 Nixpkgs(今天)并作为 Flake 提供:
```
nix --extra-experimental-features 'nix-command flakes' run .#
```
`lambdock` 二进制文件和原生软件包正在 GNU/Linux 生态系统中广泛传播:
- **openSUSE / RPM**:在 OBS 上使用原生 `lambdock.spec` 打包。
- **Debian / Ubuntu**:完整的 `debian/` 规则和未签名的 `.deb` 构建流水线。
- **Arch Linux**:在 `packaging/arch` 中有现成的 `PKGBUILD` 可供构建。
- **容器**:在 DockerHub 上提供轻量级 OCI 镜像,适用于 Podman/Docker。
---
## 奥德赛后,我们学到了什么(https://jointhefreeworld.org/blog/articles/gnu-linux/quest-for-eternal-dock/index.html#what-does-one-learn-after-the-odyssey)
- **不要对抗平台**:如果你在 GNU/Linux 上构建 Wayland 工具,考虑直接拥抱 C 和 GTK4/Layer-Shell。其清晰度、速度和可靠性无与伦比。即使你可能不得不在内存管理上把自己弄伤几次 ☺️
- **Lisp 仍然是至高无上的扩展引擎**:嵌入 `libg
相似文章
在Minecraft中作为模组实现的Wayland合成器
一个在游戏内实现了功能完整的Wayland合成器的Minecraft模组,允许用户在Minecraft世界中运行和管理应用程序。仅支持Linux。
《我的世界》中的 Wayland 合成器
一个在《我的世界》中实现完整 Wayland 合成器的模组,允许用户在游戏内启动和管理 Firefox、GIMP、VLC 等窗口。仅限 Linux。
Relm4 让开发美观的跨平台应用程序变得更地道
Relm4 是一个基于 Rust 的框架,用于开发跨平台应用程序,它采用声明式语法和 Elm 编程模型,并基于 GTK 构建以实现原生性能。
多用户 Wayland 现状
对 Linux Wayland 合成器和库中多座位(多个鼠标/键盘)支持的深入调查,并提供了已发布的工具和补丁以改善多用户计算。
Nourish - 一个具有无限缩放和平移功能的 Wayland 合成器
Nourish 是一个基于 Linux 的免费开源 Wayland 合成器,提供无限画布和缩放平移功能,使用 Rust 和 Vulkan 构建。