)
niri 配置指南用 switch-events 绑定笔记本开关事件合盖/开盖与平板模式【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niri开关事件Switch Event是 niri 自 0.1.10 起提供的一类特殊输入绑定用于响应笔记本电脑的物理状态变化——合盖/开盖、进入/退出平板模式。本文围绕 Configuration:-Switch-Events.md 展开完整讲解switch-events {}配置节的四个事件、语法规则、锁定会话下仍会执行等关键行为并结合 niri 配置解析与输入分发源码niri-config/src/binds.rs、src/input/mod.rs、src/niri.rs说明其底层实现。读完本文你将能在自己的 niri 配置中实现合盖通知、开盖恢复、进入平板模式自动调起屏幕键盘等实战方案。概述switch-events 是什么switch-events {}是 niri 配置文件中用于声明开关事件绑定的顶级配置节对应 niri-config/src/lib.rs 中Config结构体的switch_events: SwitchBinds字段。该特性自niri 0.1.10起提供。目前可以绑定的事件共有四个一眼概览如下事件触发时机lid-close笔记本屏幕合盖lid 关闭lid-open笔记本屏幕开盖lid 打开tablet-mode-on可变形笔记本进入平板模式tablet-mode-off可变形笔记本退出平板模式完整的示例配置switch-events { lid-close { spawn notify-send The laptop lid is closed!; } lid-open { spawn notify-send The laptop lid is open!; } tablet-mode-on { spawn bash -c gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true; } tablet-mode-off { spawn bash -c gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false; } }语法规则与键位绑定相似但动作有限开关事件绑定的语法与键位绑定Configuration:-Key-Bindings.md非常相似每个事件是一个子节点节点内以action 参数...;的形式书写动作。目前switch-events只支持spawn动作。spawn后的每个参数作为独立参数传给要执行的程序因此带空格或需要 shell 特性的命令应显式拆成参数列表或像上面的示例那样使用bash -c ...。在源码层面这一限制体现在SwitchAction结构体上niri-config/src/binds.rs#[derive(knuffel::Decode, Debug, Clone, PartialEq)] pub struct SwitchAction { #[knuffel(child, unwrap(arguments))] pub spawn: VecString, }而SwitchBinds则把四个事件定义为四个可选的SwitchAction字段#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] pub struct SwitchBinds { #[knuffel(child)] pub lid_open: OptionSwitchAction, #[knuffel(child)] pub lid_close: OptionSwitchAction, #[knuffel(child)] pub tablet_mode_on: OptionSwitchAction, #[knuffel(child)] pub tablet_mode_off: OptionSwitchAction, }注意与键位绑定不同开关事件绑定总是会被执行即使会话处于锁定状态。这一点在官方文档中有明确标注其实现依据是 src/input/mod.rs 中分发动作时传入的allow_when_locked: true见下文源码级实现原理。lid-close 与 lid-open合盖与开盖事件lid-close与lid-open分别对应笔记本屏幕的合上与打开。最常见的用法是合盖时发送系统通知switch-events { lid-close { spawn notify-send The laptop lid is closed!; } lid-open { spawn notify-send The laptop lid is open!; } }请务必理解一个重要前提niri 本身就会根据笔记本盖子的状态自动关闭和打开内置显示器。也就是说合盖/开盖对内置屏幕的开关行为是 niri 自动完成的与switch-events配置无关。你在这里绑定的命令只是额外的自定义动作比如通知、电源管理等用于在盖子状态变化时做自己想做的事。这个自动行为可以在 src/niri.rs 的set_lid_closed实现中看到// We monitor both libinput and logind: libinput is always there (including without DBus), but // it misses some switch events (e.g. after unsuspend) on some systems. pub fn set_lid_closed(mut self, is_closed: bool) { if self.niri.is_lid_closed is_closed { return; } debug!(laptop lid {}, if is_closed { closed } else { opened }); self.niri.is_lid_closed is_closed; self.backend.on_output_config_changed(mut self.niri); }从这段代码还可以推断两个细节niri 会同时监听libinput 与 logind两个来源的开关事件libinput 始终可用包括没有 DBus 的环境但在某些系统上会漏掉部分开关事件例如从挂起恢复之后同时监听 logind 可以补上这些遗漏。盖子状态变化会触发on_output_config_changed从而驱动内置显示器的开关这就是文档所说niri 会自动开关内置笔记本屏幕的机制来源。tablet-mode-on 与 tablet-mode-off平板模式事件当一台可变形笔记本convertible laptop进入或退出平板模式时会分别触发tablet-mode-on和tablet-mode-off。在平板模式下键盘和鼠标通常不可用因此这两个事件最常见的用途是自动启用/禁用屏幕键盘on-screen keyboard。官方文档给出的示例是配合 GNOME 的无障碍屏幕键盘设置switch-events { tablet-mode-on { spawn bash -c gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true; } tablet-mode-off { spawn bash -c gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false; } }提示上述命令只是示例你需要准备自己的屏幕键盘程序例如 sysboard 或 wvkbd。gsettings 方案只适用于使用 GNOME 无障碍设置的应用栈你可以根据桌面环境换成自己的启动命令例如直接spawn wvkbd或使用其他方式切换。源码级实现原理从开关事件到动作执行为了帮助你判断事件是否真正触发、以及理解配置如何生效下面梳理从内核输入到动作执行的完整链路。1. 配置解析与 include 合并switch-events节点在配置解析时被读入Config.switch_eventsniri-config/src/lib.rs 中switch-events m_merge!(switch_events)。由于SwitchBinds实现了MergeWithimpl MergeWithSwitchBinds for SwitchBinds { fn merge_with(mut self, part: SwitchBinds) { merge_clone_opt!( (self, part), lid_open, lid_close, tablet_mode_on, tablet_mode_off, ); } }因此switch-events也可以配合 Configuration:-Include.md 的 include 机制把开关事件绑定拆分到多个配置文件片段中按需合并方便不同设备共用同一份主配置。2. 输入事件接收与分发开关事件来自 libinput 的 switch 输入设备。在 src/input/mod.rs 的on_switch_toggle回调中niri 会先根据事件中的 switch 类型与状态更新内部盖子状态再查找并执行配置好的动作fn on_switch_toggleI: InputBackend(mut self, evt: I::SwitchToggleEvent) { let Some(switch) evt.switch() else { return; }; if switch Switch::Lid { let is_closed evt.state() SwitchState::On; trace!(lid switch {}, if is_closed { closed } else { opened }); self.set_lid_closed(is_closed); } let action { let bindings self.niri.config.borrow().switch_events; find_configured_switch_action(bindings, switch, evt.state()) }; if let Some(action) action { self.do_action(action, true); } }其中的find_configured_switch_action负责把 (switch, state) 二元组映射到你在配置中写入的事件字段fn find_configured_switch_action( bindings: SwitchBinds, switch: Switch, state: SwitchState, ) - OptionAction { let switch_action match (switch, state) { (Switch::Lid, SwitchState::Off) bindings.lid_open, (Switch::Lid, SwitchState::On) bindings.lid_close, (Switch::TabletMode, SwitchState::Off) bindings.tablet_mode_off, (Switch::TabletMode, SwitchState::On) bindings.tablet_mode_on, _ unreachable!(), }; switch_action .as_ref() .map(|switch_action| Action::Spawn(switch_action.spawn.clone())) }注意这里do_action(action, true)的第二个参数allow_when_locked被固定传为true。在do_action的实现中pub fn do_action(mut self, action: Action, allow_when_locked: bool) { if self.niri.is_locked() !(allow_when_locked || allowed_when_locked(action)) { return; } ...即即使会话处于锁定状态开关事件绑定的spawn依然会被执行——这正是文档中总是执行声明的代码依据。这在实用上很有价值例如锁屏状态下合盖/开盖你依然可以收到通知或执行电源管理命令。spawn动作本身会为子进程创建外部激活令牌后启动Action::Spawn(command) { let (token, _) self.niri.activation_state.create_external_token(None); spawn(command, Some(token.clone())); }这也意味着你 spawn 的程序可以正常请求 Wayland 激活例如弹出通知、打开窗口。实战建议与注意事项综合文档与源码使用switch-events时有几点值得注意四个事件均可选SwitchBinds中四个字段都是Option只配置你需要的事件即可未配置的事件触发时 niri 只做内置行为如自动开关内置屏幕不会报错。不要重复实现内屏开关合盖自动关闭内置屏幕是 niri 内置行为不需要也不应在switch-events里再 spawn 命令去关闭显示器否则可能造成冲突或重复处理。锁定会话下照常执行这是与键位绑定最大的行为差异。如果你的命令包含敏感操作请自行评估安全性。屏幕键盘命令要按自己的环境调整示例中的gsettings命令依赖 GNOME 无障碍设置栈在非 GNOME 环境请直接 spawn 你自己的屏幕键盘程序。结合 include 按设备拆分由于switch-events支持MergeWith合并桌面机与笔记本可以共享主配置再通过 include 片段分别为笔记本加入 lid/tablet 绑定。进一步阅读键位绑定语法与完整的动作列表Configuration:-Key-Bindings.md配置总览与 include 机制Configuration:-Introduction.md、Configuration:-Include.md配置解析实现niri-config/src/binds.rs、niri-config/src/lib.rs输入事件处理与动作分发src/input/mod.rs盖子状态与内置显示器联动src/niri.rs【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niri创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考