【免费下载链接】jay
A Wayland Compositor
Jay 是一款用 Rust 编写的 Wayland 合成器,除了官方推荐的 TOML 配置文件外,还提供了jay-config共享库,允许你用真正的 Rust 代码来定义快捷键、窗口规则、启动行为和定时任务。这篇教程带你从零搭建第一个config.so配置库,并逐步掌握bind_masked、窗口匹配器、Command进程管理、定时器和异步任务等高级模式。
jay-config 是什么?它和 TOML 配置有什么区别?
Jay 的默认配置方式是把 config.toml 放在~/.config/jay/目录下。TOML 覆盖了绝大多数场景,但当你需要以下能力时,就该切换到 Rust 编程配置了:
- 🧠条件逻辑:根据窗口标题、client 类型等动态决定行为
- ⏱️定时器与轮询:周期性刷新状态栏、监控文件系统
- 🚀后台异步任务:用 Rust 的
Future写守护逻辑 - 🔌复杂快捷键:按键释放(latch)、忽略修饰键掩码等精细控制
jay-config位于 jay-config/ 目录,是一个约 6400 行、结构清晰的 crate(jay-config/Cargo.toml),模块按职责拆分:input(输入设备)、window(窗口)、client(客户端)、video(显卡/显示器)、keyboard(键盘布局)、timer(定时器)、tasks(异步任务)、status(状态栏)、exec(进程)等。
一步到位:搭建你的第一个 Rust 配置项目
创建配置项目只需要三步:新建 crate、声明cdylib、注册入口函数。
第一步,新建一个库项目:
cargo new jay-my-config cd jay-my-config第二步,在 Cargo.toml 中声明cdylib并引入jay-config依赖:
[lib] crate-type = ["cdylib"] [dependencies] jay-config = "1.13"cdylib是关键:它让编译器产出共享库(.so文件),Jay 启动时会把它dlopen进来。
第三步,编写入口函数并用config!宏注册。config!宏定义在 jay-config/src/macros.rs,它会在库中生成一个名为JAY_CONFIG_ENTRY_V1的静态符号,Jay 靠它找到你的配置入口:
use jay_config::{config, quit, reload}; use jay_config::input::get_default_seat; use jay_config::keyboard::mods::ALT; use jay_config::keyboard::syms::{SYM_q, SYM_r}; fn configure() { let seat = get_default_seat(); seat.bind(ALT | SYM_q, || quit()); // Alt+Q 退出 seat.bind(ALT | SYM_r, || reload()); // Alt+R 热加载 } config!(configure);最后一步,编译并把产物复制到配置目录,然后重启 Jay:
cargo build --release cp target/release/libjay_my_config.so ~/.config/jay/config.so从此以后修改配置只需重新编译、复制,再按Alt+R触发reload()即可热加载,无需重启合成器。官方文档 lib.rs 开头的示例正是这个最小骨架。
快捷键高级技巧:bind、bind_masked 与 latch
Seat::bind是最常用的入口,它把按键组合(修饰键用|位或拼接)映射到一个闭包。除了bind,jay-config/src/input.rs 还提供了几个强大的"进阶旋钮":
| 方法 | 作用 | 典型场景 |
|---|---|---|
bind_masked | 只考虑指定修饰键掩码 | 让音量键在任何修饰键下都生效 |
set_repeat_bind | 允许按键持续触发 | 按住方向键连续移动焦点 |
set_bind_allow_locked | 锁屏时也能触发 | 锁屏状态下调节音量 |
latch | 在按键释放时执行闭包 | 推而不误触(press-to-talk) |
unbind | 解绑某个快捷键 | 覆盖默认绑定 |
比如实现"推而不误触"语音功能:按下Alt+X解除静音,松手时自动恢复静音——这正是latch的用武之地:
seat.bind(ALT | SYM_x, || { Command::new("pactl").arg("set-sink-mute").arg("0").arg("0").spawn(); // 注册一个松手即执行的回调 seat.latch(|| { Command::new("pactl").arg("set-sink-mute").arg("0").arg("1").spawn(); }); });两个容易踩坑的细节(源码注释中有明确提示):
- 按键释放事件触发时,keysym 是不带修饰键计算的,所以
SHIFT+k应写成SHIFT | SYM_k而不是SHIFT | SYM_K; CapsLock和NumLock在修饰键求值时被忽略,包含它们的绑定永远不会触发。
对应地,TOML 配置里也能表达部分复杂快捷键,参考官方文档 book/src/configuration/shortcuts.md 中的mod-mask与latch说明。
窗口规则:用 WindowCriterion 精确匹配窗口
TOML 的窗口规则是声明式的,而jay-config的WindowCriterion(jay-config/src/window.rs)支持完整的布尔组合:All、Any、Not、Exactly,以及按标题、app-id、X class 的正则匹配。
例如让所有标题以Vim开头的窗口初始平铺、不抢焦点,并绑定一个回调:
use jay_config::window::WindowCriterion; WindowCriterion::All(&[ &WindowCriterion::TitleRegex("^Vim"), &WindowCriterion::Types(WindowType::XDG_TOPLEVEL), ]) .set_initial_tile_state(TileState::Tiled) .set_auto_focus(false) .bind(|w| log::info!("Vim 窗口出现: {}", w.title()));Window句柄本身也提供了丰富的操作:move_(四向移动)、create_split(按轴向拆分)、set_fullscreen、set_floating、resize、workspace()等,方便在回调中做程序化窗口管理。
同理,ClientCriterion(jay-config/src/client.rs)可以按进程名、沙箱 app-id、UID 等匹配客户端,并用set_capabilities控制其可访问的 Wayland 全局对象(如截屏、图层 shell 等)。
定时与自动化:get_timer、tasks::spawn 与异步 IO
jay-config内建了三类自动化原语,正好构成"延迟执行 / 周期执行 / 异步监听"的完整闭环:
1. 命名定时器(jay-config/src/timer.rs)
定时器按名字创建,生命周期绑定配置,reload()会销毁全部旧定时器:
use jay_config::timer::get_timer; use std::time::Duration; let t = get_timer("status_clock"); t.repeated(Duration::from_secs(1), Duration::from_secs(1)); t.on_tick(|| { jay_config::status::set_status(&format!("本地时间: {:?}", std::time::SystemTime::now())); });配合duration_until_wall_clock_is_multiple_of还能让定时器与墙钟对齐(例如整分钟触发)。
2. 异步任务(jay-config/src/tasks.rs)
tasks::spawn接受任意Future,在合成器的事件循环中后台运行,返回JoinHandle用于await结果或abort()中止。
3. 异步文件 IO(jay-config/src/io.rs)
Async<T>把任意实现了AsFd的文件描述符适配为AsyncRead/AsyncWrite,底层自动设为非阻塞并接入合成器的 poll 循环。这意味着你可以在配置里监控管道、设备文件甚至网络套接字,实现自定义状态栏插件——set_status_command(jay-config/src/status.rs)其实就是这个机制的封装,支持纯文本、Pango markup 和 i3bar 三种输出格式。
生命周期钩子:让配置"感知"系统状态
lib.rs 顶层提供了一组回调钩子,是高级配置的核心:
on_idle(f):屏幕进入空闲时触发,适合触发锁屏或熄屏逻辑;on_locked(f):屏幕锁定/解锁时触发(配置热加载时若已锁定会立即回调);on_unload(f):配置被卸载前触发,用于清理线程和资源;on_devices_enumerated(f):所有设备枚举完成后回调一次,适合选择渲染 GPU;is_reload():判断本次执行是启动还是热加载,常用语决定是否启动开机程序——热加载时不应重复spawn常驻进程。
再配合set_idle(空闲超时,默认 10 分钟)、set_idle_grace_period(黑屏宽限期,默认 5 秒),就能完整定制待机行为。
显示器与键盘布局编程
video模块(jay-config/src/video.rs)暴露了connectors()、get_connector等接口,可按 PCI ID 或名称精确定位显卡接口,设置缩放、旋转与模式;keyboard模块(jay-config/src/keyboard/mod.rs)则提供parse_keymap(直接解析 XKB 文本)、keymap_from_names(按 RMLVO 名称构建)和链式Keymap::builder(),还能用shortcuts_group固定快捷键查询的键盘组——对多语言键盘用户非常实用。
此外,xwayland模块(jay-config/src/xwayland.rs)可开关 XWayland 并选择 X 窗口的缩放模式(DEFAULT或高性能损耗更高的DOWNSCALED);workspace模块(jay-config/src/workspace.rs)可设置工作区排序策略(手动拖动或字母排序)。
常见问题速查
Q:修改 config.so 后要重启 Jay 吗?不需要。重新编译覆盖~/.config/jay/config.so后,绑定一个reload()快捷键即可热加载;注意所有定时器、匹配器都会被销毁重建。
Q:TOML 和 config.so 能共存吗?可以。两者是互补关系:config.toml负责声明式配置,config.so负责编程式扩展。TOML 中还有reload-config-so动作可手动触发共享库重载。
Q:编译报错找不到符号?检查 Cargo.toml 中crate-type是否为["cdylib"],以及config!(configure)是否放在了入口函数之后。
总结
jay-config让 Jay 的配置能力从"声明式"跃升到"编程式":一个约百行的 Rust crate,就能拥有条件窗口规则、进程管理、定时任务与异步 IO 的全部能力。推荐的学习路径是——先用 TOML 熟悉基础操作(参考 book/src/configuration/),遇到表达力不够的场景再切换到config.so,对照 jay-config/src/ 下各模块的源码注释,几乎所有 API 的行为都写得清清楚楚。
【免费下载链接】jay
A Wayland Compositor
相关推荐
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考