news 2026/9/11 1:31:41

niri 录屏(Screencasting)实战指南:Portal/PipeWire 采集、窗口遮挡、动态采集目标与镜像

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
niri 录屏(Screencasting)实战指南:Portal/PipeWire 采集、窗口遮挡、动态采集目标与镜像

niri 录屏(Screencasting)实战指南:Portal/PipeWire 采集、窗口遮挡、动态采集目标与镜像

【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niri

本指南围绕 niri(一款可滚动平铺的 Wayland 合成器)的录屏功能展开,系统讲解如何通过 xdg-desktop-portal 与 PipeWire 对显示器或单个窗口进行采集,并深入介绍block-out-from窗口/图层遮挡规则、25.05 引入的动态采集目标(Dynamic Cast Target)、is-window-cast-target采集指示、窗口化全屏(windowed fullscreen)以及输出镜像等专为录屏场景设计的特性。读完本文,你将能够配置一套"既能录屏、又能保护敏感窗口"的完整 niri 录屏方案,并理解其底层(ScreenCast D-Bus 协议 + PipeWire 流)是如何工作的。

录屏的两种主流方式

niri 主要的录屏接口走Portal + PipeWire这条标准链路,并且已经得到 OBS、Firefox、Chromium、Electron、Telegram 等大量应用的开箱支持。你可以采集整个显示器,也可以只采集某个单独的窗口。

要使用它,需要满足以下运行条件:

  • 一个可用的 D-Bus 会话;
  • 已安装并运行 PipeWire;
  • 已安装xdg-desktop-portal-gnome
  • niri 以会话方式运行(即通过niri-session或显示管理器启动,参见 Getting-Started)。

在主流发行版上,这些依赖通常"开箱即用"。除了 Portal 方式,niri 也支持依赖wlr-screencopy协议的第三方工具(例如许多截图/录屏小工具),因此选择面很广。

从源码结构看,这条链路的实现集中在 src/screencasting/mod.rs:Screencasting结构体维护casts(正在进行的采集)、pending_dynamic_casts(等待首个目标的动态采集)、mapped_cast_output(每个已映射窗口对应的输出)以及pipewire实例。Portal 端的请求通过 mutter 的 ScreenCast D-Bus 协议(src/dbus/mutter_screen_cast.rs)以ScreenCastToNiri::StartCast/StopCast消息进入on_screen_cast_msg,随后 niri 初始化 GBM 设备与渲染格式、启动 PipeWire 流,并在每个帧周期通过render_for_screen_cast(整屏采集)或render_windows_for_screen_cast(单窗口采集)把渲染元素送往采集缓冲区。

从录屏中遮挡窗口(block-out-from)

录屏演示时,你可能不希望密码管理器、聊天窗口等内容出现在画面里。niri 提供block-out-from窗口规则,把被匹配到的窗口在录屏中替换为纯黑色矩形

// 将密码管理器从录屏中遮挡掉。 window-rule { match app-id=r#"^org\.keepassxc\.KeePassXC$"# match app-id=r#"^org\.gnome\.World\.Secrets$"# block-out-from "screencast" }

同样的思路也适用于 layer-shell 表面(比如通知弹窗),只需改用图层规则:

// 将 mako 的通知从录屏中遮挡掉。 layer-rule { match namespace="^notifications$" block-out-from "screencast" }

在配置解析层面,block-out-from对应的值由 niri-config/src/appearance.rs 中的BlockOutFrom枚举定义,目前支持两个取值:

  • "screencast":仅从 xdg-desktop-portal 录屏中遮挡;
  • "screen-capture":从所有屏幕捕获中遮挡,包括第三方截图工具。

Configuration:-Window-Rules.md 对这两种取值做了更详细的对比:

  • "screencast"不会影响第三方截图工具——如果你在录屏过程中打开了带预览的截图工具,被遮挡的窗口仍会出现在录屏里。niri 内置的交互式截图 UI 不受此问题影响:录屏时打开截图 UI,你仍能看到所有窗口并正常框选区域,而录屏画面上截图选区 UI 会以"窗口被遮挡"的状态呈现。
  • "screen-capture"会额外把窗口从第三方面截图中也遮挡掉,避免"录屏时误开截图预览导致敏感内容泄露"的尴尬;它仍然允许使用内置的交互式截图 UI,但会自动屏蔽screenshot-screenscreenshot-window这类全自动截图动作——因为交互式框选时你可以自行避开敏感内容。

调试这类规则时,可以利用配置中 debug 段的preview-render选项来预览遮挡效果。

[!CAUTION] 请小心基于动态变化的窗口标题来做遮挡。例如下面这个"遮挡 Firefox 的 Gmail 标签页"的写法:

window-rule { // 并不能完美工作!尝试遮挡 Gmail 标签页。 match app-id="firefox$" title="- Gmail " block-out-from "screencast" }

它虽然能生效,但从敏感标签页切回普通标签页的瞬间,敏感标签页的内容仍会在录屏上闪现一瞬。原因在于 Wayland 协议中窗口标题(以及 app-id)不是双缓冲的,无法与特定窗口内容严格绑定,Firefox 也没有可靠手段让"可见标签页切换"与"标题变更"完全同步。

动态采集目标(Dynamic Cast Target)

自 25.05 起提供

niri 提供一个可以动态切换内容的特殊录屏流,它在录屏窗口选择对话框中显示为 "niri Dynamic Cast Target"。选择它之后,再用下面的绑定键来切换它展示的内容:

  • set-dynamic-cast-window:采集当前聚焦的窗口;
  • set-dynamic-cast-monitor:采集当前聚焦的显示器;
  • clear-dynamic-cast-target:重置为空白视频流。

注意:在你做出第一次目标选择之前,该视频流不会启动(保持空流)。

也可以从命令行触发这些动作,例如配合pick-window交互式挑选要采集的窗口:

$ niri msg action set-dynamic-cast-window --id $(niri msg --json pick-window | jq .id)

从实现上看(src/screencasting/mod.rs),niri 会为 Portal 端合成一个特殊的窗口 IDdynamic_cast_id_for_portal,当 ScreenCast 请求落在该 ID 上时,请求不会立即启动,而是进入pending_dynamic_casts队列等待首个目标;set_dynamic_cast_target会把目标(CastTarget::Window/CastTarget::Output/CastTarget::Nothing)应用到所有动态采集流上,并据目标输出刷新率同步调整流的 FPS。行为细节如下:

  • 如果采集目标消失(例如目标窗口被关闭),视频流会自动回到空白
  • 所有动态采集共享同一个目标,但新建的动态采集会从空白开始,直到你下一次显式切换目标(这是为了避免新会话一上来就意外共享敏感内容);
  • 从源码可以推断,动态采集在目标缺失/输出断开时不会像普通采集那样直接停止,而是切回Nothing状态等待下次切换(见stop_casts_for_target中 "We don't stop dynamic casts, instead we switch them to Nothing" 的注释)。

标记正在被采集的窗口

自 25.02 起提供

is-window-cast-target=true窗口规则(详见 Configuration:-Window-Rules.md)用于匹配正处于窗口采集目标的窗口。典型用法是给它配上醒目的边框/阴影颜色,让观众一眼看出哪些窗口正在被录屏,例如:

// 用红色系标记正在被采集的窗口。 window-rule { match is-window-cast-target=true focus-ring { active-color "#f38ba8" inactive-color "#7d0d2d" } border { inactive-color "#7d0d2d" } shadow { color "#7d0d2d70" } tab-indicator { active-color "#f38ba8" inactive-color "#7d0d2d" } }

需要注意它的适用范围:

  • 动态采集(Dynamic Cast Target)锁定的窗口同样生效;
  • 仅仅"恰好显示在整屏采集画面里"的窗口不生效——只有被显式指定为窗口采集目标时才会被标记。

底层支撑在Niri::refresh_mapped_cast_window_rules(src/screencasting/mod.rs):每当采集列表变化时,niri 会遍历所有映射窗口,检查是否存在CastTarget::Window { id }与之匹配,并把结果写入mapped.set_is_window_cast_target(...),供渲染与规则匹配阶段使用。

窗口化(伪/分离)全屏

自 25.05 起提供

录屏基于浏览器的演示(如 Google Slides)时,通常希望隐藏浏览器 UI,这往往要求浏览器进入全屏。但全屏并不总是方便:比如你用的是超宽屏显示器,或者只想让浏览器保持一个小窗口、不想占满整块屏幕。

toggle-windowed-fullscreen绑定键就是为此设计的:它告诉应用"你已经全屏了",但实际上窗口仍是普通窗口,你可以随意调整大小、放到任意位置。

binds { Mod+Ctrl+Shift+F { toggle-windowed-fullscreen; } }

需要注意:并非所有应用都会响应"全屏"请求,所以有时按下绑定键看起来什么都没发生——这属于应用侧的行为差异,而非 niri 的问题。

屏幕镜像(Screen Mirroring)

做演示时,把一块输出镜像到另一块输出往往很有用。niri 目前没有内置输出镜像,但可以借助第三方工具wl-mirror把某块输出镜像到一个窗口里。下面这条绑定还依赖jq命令:

binds { Mod+P repeat=false { spawn-sh "wl-mirror $(niri msg --json focused-output | jq -r .name)"; } }

操作流程:

  1. 聚焦想要镜像的那块输出;
  2. 按下ModP
  3. 把弹出的wl-mirror窗口移动到目标输出;
  4. 将该窗口全屏(默认是ModShiftF)。

小结与建议配置

把上述特性组合起来,你可以得到一套完整的 niri 录屏配置:

// 遮挡敏感窗口与通知。 window-rule { match app-id=r#"^org\.keepassxc\.KeePassXC$"# block-out-from "screencast" } layer-rule { match namespace="^notifications$" block-out-from "screencast" } // 标记正在被采集的窗口。 window-rule { match is-window-cast-target=true border { inactive-color "#7d0d2d" } shadow { color "#7d0d2d70" } } binds { // 演示浏览器内容时隐藏浏览器 UI。 Mod+Ctrl+Shift+F { toggle-windowed-fullscreen; } // 动态采集目标。 Mod+Ctrl+C { set-dynamic-cast-window; } Mod+Ctrl+M { set-dynamic-cast-monitor; } Mod+Ctrl+X { clear-dynamic-cast-target; } // 输出镜像。 Mod+P repeat=false { spawn-sh "wl-mirror $(niri msg --json focused-output | jq -r .name)"; } }

更完整的规则说明与示例可继续参阅 Configuration:-Window-Rules.md 与 Configuration:-Layer-Rules.md;关于录屏实现细节(流管理、尺寸/刷新率同步、光标合成等)可深入阅读 src/screencasting/mod.rs。

【免费下载链接】niriA scrollable-tiling Wayland compositor.项目地址: https://gitcode.com/GitHub_Trending/ni/niri

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 1:30:28

怎么在浏览器里看到地下:Cesium地下空间可视化完整指南

怎么在浏览器里看到地下:Cesium地下空间可视化完整指南 【免费下载链接】cesium An open-source JavaScript library for world-class 3D globes and maps :earth_americas: 项目地址: https://gitcode.com/GitHub_Trending/ce/cesium 做地质研究、管地下管线…

作者头像 李华
网站建设 2026/9/11 1:29:02

AngularJS中$q.when()的异步编程实践与优化

1. 理解$q.when()的核心定位在AngularJS的异步编程体系中,$q.when()是一个常被忽视但极具实用价值的工具函数。它的核心作用是"规范化处理值或承诺",简单说就是无论你传入的是普通值还是Promise对象,它都能统一返回一个Promise。这…

作者头像 李华
网站建设 2026/9/11 1:28:14

Linux Mint 22 部署 Vosk-API 安装避坑:离线语音识别一次跑通

Linux Mint 22 部署 Vosk-API 安装避坑:离线语音识别一次跑通 【免费下载链接】vosk-api Offline speech recognition API for Android, iOS, Raspberry Pi and servers with Python, Java, C# and Node 项目地址: https://gitcode.com/GitHub_Trending/vo/vosk-a…

作者头像 李华
网站建设 2026/9/11 1:27:43

electerm 完整使用指南:一个终端搞定 SSH、SFTP 与多机切换

electerm 完整使用指南:一个终端搞定 SSH、SFTP 与多机切换 【免费下载链接】electerm 📻Free and open-sourced terminal/ssh/sftp/ftp/telnet/serialport/RDP/VNC/Spice client(Linux, Mac, Windows, Android, HarmonyOS, iOS) 项目地址: https://gi…

作者头像 李华
网站建设 2026/9/11 1:25:48

云服务X1的擎天架构与技术民主化实践

1. 云服务X1的颠覆性定位当大多数云服务商还在比拼资源规模和价格战时,X1选择了一条截然不同的道路——它本质上是一场关于"技术民主化"的运动。我在实际测试中发现,这套基于擎天架构的系统,通过三个维度重构了传统云服务的价值链条…

作者头像 李华