【免费下载链接】Ambxst
An Axtremely customizable shell.
Ambxst 是一款高度可定制的 Wayland Shell,它的原生截图工具与录屏功能不依赖任何第三方工具,而是直接基于 wlr-screencopy 协议与 SHM 共享内存实现"零拷贝"屏幕捕获,兼顾了速度、隐私与多显示器混合缩放场景的稳定性。本文带你完整看懂这套原生截图录屏的实现原理。
🎯 为什么需要"原生"屏幕捕获?
在 Wayland 环境下,传统import -window root式的 X11 截屏方案完全失效——每个应用只能访问自己的窗口。想在 Wayland 上截屏,必须让合成器主动配合,把画面"交给"客户端。
Ambxst 的解决方案分两层:
| 层 | 技术 | 作用 |
|---|---|---|
| 协议层 | wlr-screencopy-unstable-v1 | 向合成器申请抓取某块屏幕的一帧画面 |
| 传输层 | wl_shm 共享内存(memfd + mmap) | 让合成器直接写入客户端内存,避免二次拷贝 |
整个捕获引擎实现在 backend/internal/screenshot/capture.go 的Engine中:它连接 Wayland 显示服务、绑定compositor、wl_shm、wl_output与zwlr_screencopy_manager_v1四个全局对象,缺一不可。协议绑定失败时,Connect()会直接报出"compositor does not support wlr-screencopy-unstable-v1",帮你快速定位环境兼容性问题。
📡 wlr-screencopy 协议:一帧画面是怎么拿到的?
协议的核心交互在 backend/internal/proto/wlr_screencopy/screencopy.go 中生成,流程非常清晰:
- 申请:客户端调用
CaptureOutput(或CaptureOutputRegion抓取指定区域),参数overlayCursor控制是否把鼠标光标也合成进画面; - 协商:合成器回发
buffer事件,告知支持的像素格式、宽高、stride(行距); - 拷贝:客户端按规格创建一块 SHM 缓冲,发送
copy请求; - 就绪:合成器写入完成后回发
flags(例如y_invert表示画面垂直翻转)+ready事件,或回发failed。
Engine.processFrame()(capture.go 第 355 行起)用一个事件循环for !ready && !failed驱动 Wayland 事件派发,直到画面就绪才返回——没有轮询、没有忙等,完全由合成器事件驱动。
🚀 SHM 零拷贝:共享内存是核心秘诀
所谓"零拷贝",关键在于 backend/internal/wayland/shm/buffer.go 中CreateBuffer()的三步操作:
memfd_create("dms-shm")创建一个匿名共享文件描述符(见 fd_linux.go);ftruncate把文件大小撑到stride × height;mmap(MAP_SHARED)将这块内存映射进自己的地址空间。
随后通过wl_shm_pool.create_buffer把这个 fd 的句柄传给合成器。合成器把像素直接写进这块内存,客户端在同一时刻就已经"看到"了画面——不需要 socket 传像素、不需要序列化、不需要额外内存复制。用完后只需munmap+close(fd),资源即被回收。
🧊 截图工具流程:冻结预览 → 选择 → 保存
前端体验由 modules/services/Screenshot.qml 与 modules/tools/ScreenshotTool.qml 驱动,这里藏着一个很聪明的设计——冻结帧会话(freeze session):
- 打开截图工具时,
freezeScreen()对每一块显示器依次调用screenshot.frame,把整屏画面拍下来。注意 Screenshot.qml 中的注释:并发 screencopy 请求会在合成器内部竞争,因此在混合缩放布局下工具采用逐屏串行请求,保证可靠性; - 帧缓冲被保留在后端(backend/pkg/capture/session.go 的
StoreSessionFrame),你拖选区域时屏幕上显示的预览图,就是冻结的那一帧,而不是正在变动的真实屏幕——所见即所得; - 确认区域后,
screenshot.capture从冻结帧中CropBuffer裁剪出选区,坐标先按 backend/pkg/axmon 解析出的显示器缩放比例换算成物理像素; - 若 2 分钟内没有后续操作,后台清扫协程会自动释放冻结帧,避免内存长期占用;工具关闭时也会立即
screenshot.release释放。
后端服务注册在 backend/pkg/svc/screenshot/service.go,提供frame/capture/release/list/dir五个方法,截图默认保存到~/Pictures/Screenshots,可选同步写入剪贴板。最终 PNG 由自研编码器 backend/internal/screenshot/png.go 生成——它按 CPU 核心数把图像切分成多条"行带"并行压缩,速度优于常规实现。
🎬 录屏:gpu-screen-recorder 的低开销路径
录屏部分见 backend/pkg/svc/recorder/service.go,Ambxst 后端直接管理一个gpu-screen-recorder子进程,通过 IPC 暴露recorder.start / stop / status / dir四个方法,前端 modules/services/ScreenRecorder.qml 订阅recorder.state事件实时显示时长与状态。
设计上几个值得注意的点:
- 四种模式:
region(区域)、screen(指定/整屏)、portal/window(经 Portal 走用户授权,满足 Wayland 隐私要求); - 音频混录:支持同时录制输出与输入音轨(
-a default_output|default_input); - 优雅停止:
stop发送 SIGINT 让录制器正常收尾 mp4 容器,退出码 130/2 被视为正常结束;守护进程退出时若录制仍在进行,会等待 3 秒后强制终止,保证文件不损坏。
🌈 10-bit HDR 也原生支持
如果你的屏幕输出 10-bit(如XRGB2101010)或 HDR(PQ/BT.2020),捕获引擎同样能处理:SHM 层内置Convert10To8、ConvertPQ2020ToSRGB等转换(shm/buffer.go 中实现了 SMPTE ST 2084 逆 EOTF 与 BT.2087 色域矩阵),对外输出的 PNG 则是 16-bit 通道,最大限度保留色彩深度。此外Upright()会根据显示器的transform自动旋转/翻转画面,保证任何方向的屏幕截出来都是正立的。
📂 关键源码地图
想深入动手,从这几个入口入手即可:
- 协议绑定:backend/internal/proto/wlr_screencopy/screencopy.go
- 捕获引擎:backend/internal/screenshot/capture.go
- SHM 零拷贝缓冲:backend/internal/wayland/shm/buffer.go
- 区域/冻结帧封装:backend/pkg/capture/capture.go
- 截图服务(IPC):backend/pkg/svc/screenshot/service.go
- 录屏服务(IPC):backend/pkg/svc/recorder/service.go
- 前端截图工具:modules/tools/ScreenshotTool.qml
- 前端录屏服务:modules/services/ScreenRecorder.qml
✅ 小结
Ambxst 的截图与录屏是一套教科书级的 Wayland 原生实现:用wlr-screencopy 协议拿到合成器的帧、用SHM 共享内存实现零拷贝传输、用冻结帧会话解决预览一致性、用并行 PNG 编码和GPU 录制器压低落盘开销。理解了这套架构,你几乎可以为任何自研工具(OCR、取色、视频流推)复用同一个捕获底座 🛠️
【免费下载链接】Ambxst
An Axtremely customizable shell.
相关推荐
CPython copy 模块深入解析:浅拷贝、深拷贝与 replace 协议的完整指南
CPython copy 模块深入解析:浅拷贝、深拷贝与 replace 协议的完整指南 导读 copy 模块是 Python 标准库中负责"浅拷贝"与"深拷贝
编程语言语言运行时解释器标准库Java 深浅拷贝完全指南:浅拷贝、深拷贝、延迟拷贝与序列化拷贝实战解析
Java 深浅拷贝完全指南:浅拷贝、深拷贝、延迟拷贝与序列化拷贝实战解析 本文以 YCBlogs 仓库中的《Java深浅拷贝》笔记为核心,系统讲解 Java 对
教程技术博客文档JavaScript专题之从零实现 jQuery 的 extend:从浅拷贝到深拷贝的完整演进
JavaScript专题之从零实现 jQuery 的 extend:从浅拷贝到深拷贝的完整演进 导读 jQuery 的 extend 是日常开发中出场率极高的工
技术博客文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考