diff --git a/src/cap_wlr_screencopy.rs b/src/cap_wlr_screencopy.rs index 32aa3c1..01f224c 100644 --- a/src/cap_wlr_screencopy.rs +++ b/src/cap_wlr_screencopy.rs @@ -1,3 +1,20 @@ +//! 文件:wlr-screencopy-unstable-v1 协议客户端绑定(`CaptureSource` 实现) +//! +//! 本文件实现 `CapWlrScreencopy`,作为 `state.rs` 中 `State` 的泛型参数 `S` +//! 的两个具体实现之一(另一个是 `CapPortal`)。wlr-screencopy 是 wlroots 原生 +//! 协议,优先于 XDG Portal/PipeWire:无需 D-Bus、无需用户授权对话框。 +//! +//! 协议绑定来源:`wayland_protocols_wlr::screencopy::v1::client::*` 由 +//! wayland-scanner 工具根据 `wlr-screencopy-unstable-v1.xml` 自动生成(类似 Go +//! 用 cgo 绑定 C 库,但 Rust 通过 wayland-client crate 暴露 type-safe wrapper, +//! 无需手写 C FFI)。 +//! +//! 异步模型:客户端无法主动"截屏",只能:(1) 绑定全局 manager、(2) 调用 +//! `manager.capture_output()` 创建帧对象、(3) 等待内核推送 buffer/format 事件、 +//! (4) 调用 `frame.copy(buffer)` 请求拷贝。因此本文件的 `alloc_frame()` 永远 +//! 返回 `None`,真正的帧创建逻辑在 `state.rs` 的 Dispatch impl 中(英文注释 +//! 标记为 T6b)。 + use anyhow::Result; use wayland_client::globals::GlobalList; use wayland_client::protocol::wl_buffer::WlBuffer; @@ -7,6 +24,9 @@ use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::Zwl use crate::state::{CaptureSource, OutputInfo, State}; +// L2: `CaptureSource` trait 的具体实现——wlroots 原生 wlr-screencopy 协议后端。 +// 仅持有"当前在飞"的帧对象;协议管理器 `ZwlrScreencopyManagerV1` 的绑定存放 +// 在 `State` 的状态机字段中(需要 Dispatch impl,见下方英文注释)。 /// wlr-screencopy capture backend. /// /// Holds the current in-flight frame protocol object. The @@ -17,15 +37,27 @@ pub struct CapWlrScreencopy { /// The active frame object for the current capture cycle. /// Set by Dispatch impls after `manager.capture_output()`, cleared /// by `on_done_with_frame()`. + // L3: `Option` 类似 Go 的 `*T`(指针)——要么持有 T 的值,要么是 None(空)。 pub current_frame: Option, } +// L2: 为 `CapWlrScreencopy` 实现 `CaptureSource` trait。 +// Rust 的 `impl Trait for Type` 块类比 Go 的 method receiver—— +// Go: `func (r *Type) Method(args)`(receiver 作为第一个参数显式声明) +// Rust: `fn method(&self, args)`(`&self` 是 `self: &Self` 的语法糖,等价 Go receiver) +// trait impl 要求方法签名与 trait 定义严格一致,编译器会校验。 impl CaptureSource for CapWlrScreencopy { /// Unit type: wlr-screencopy is fully asynchronous — `alloc_frame()` /// always returns `None`. The frame object is created by Dispatch /// impls calling `manager.capture_output()`, not by this method. + // L3: `type Frame = ();` 关联类型(associated type):将"帧"的具体类型延迟到 + // impl 处决定。wlr-screencopy 用 unit `()` 因为帧对象生命周期由 Dispatch 控制。 type Frame = (); + // L3: 构造函数。`Self` 在 impl 块内是 `CapWlrScreencopy` 的类型别名。 + // 参数名以 `_` 前缀表示"有意未使用"——manager 绑定不在此处发生,故这些 + // 参数(GlobalList/WlOutput/OutputInfo/QueueHandle)暂未消费。返回 + // `Result`,失败由调用方用 `?` 操作符传播(类比 Go 的 `if err != nil`)。 fn new( _gm: &GlobalList, _output: &WlOutput, @@ -35,11 +67,15 @@ impl CaptureSource for CapWlrScreencopy { // Manager binding happens in state.rs during the ProbingOutputs → // EverythingButFmt stage transition (T6b). It requires a Dispatch // impl that doesn't exist yet, so we cannot call gm.bind() here. + // `Ok(...)` 是 `Result::Ok(...)` 的简写,将成功值包装为 Result 返回; + // `Self { ... }` 等价于 `CapWlrScreencopy { ... }`,impl 块内可用。 Ok(Self { current_frame: None, }) } + // L3: 分配帧对象。返回 `Option`(此处 Frame = (),故永远返回 None)。 + // `&mut self` 是 `self: &mut Self` 的简写(类比 Go 指针 receiver `*Type`)。 fn alloc_frame(&mut self) -> Option { // wlr-screencopy is asynchronous: the Dispatch impl creates a new // ZwlrScreencopyFrameV1 which triggers the buffer allocation flow @@ -48,16 +84,23 @@ impl CaptureSource for CapWlrScreencopy { None } + // L3: 提交拷贝请求:将已分配的 DMA-BUF(WlBuffer)关联到当前帧对象。 fn queue_copy(&mut self, buffer: &WlBuffer, _qh: &QueueHandle>) { + // `if let Some(x) = &expr`:pattern matching,当 expr 是 Some 时绑定内部值。 + // 此处 `&self.current_frame` 不可变借用,调用 `frame.copy(buffer)` 提交拷贝。 if let Some(frame) = &self.current_frame { frame.copy(buffer); } else { + // `tracing::warn!` 是结构化日志宏(类比 Go log.Printf,但支持字段)。 tracing::warn!("queue_copy: no current wlr-screencopy frame"); } } + // L3: 帧处理完成后的清理。`_frame: Self::Frame` 前缀 `_` 表示参数未使用(Frame 是 unit)。 fn on_done_with_frame(&mut self, _frame: Self::Frame) { + // `Option::take()`:取出 Some 并将原位置替换为 None,原值所有权转移给返回值。 if let Some(frame) = self.current_frame.take() { + // `frame.destroy()` 发送 wayland 析构请求,释放服务端协议对象资源。 frame.destroy(); } }