docs(state): [2/3] 中文注释 state.rs Wayland Dispatch trait 实现
This commit is contained in:
+121
@@ -1368,7 +1368,24 @@ impl<S: CaptureSource> State<S> {
|
|||||||
// Dispatch<WlRegistry, GlobalListContents>
|
// Dispatch<WlRegistry, GlobalListContents>
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// wayland-client 的 Dispatch trait:处理 Wayland 协议对象的事件回调。
|
||||||
|
// 泛型参数 1 = 协议对象类型(这里是 WlRegistry,compositor 全局对象广播器),
|
||||||
|
// 泛型参数 2 = 用户数据类型(GlobalListContents,附加到 registry 的 user_data,
|
||||||
|
// 类比 Go context.Context 但只读、附着在协议对象上)。
|
||||||
|
// trait method `event` 由 wayland-client 在 dispatch 阶段调用:每收到一个事件就
|
||||||
|
// 把 (&mut State, &Connection, &ProtocolObj, &UserData, Event, ...) 传进来。
|
||||||
|
// 类比 Go 的隐式 interface + callback,但 Rust 用泛型 + 显式 impl Trait for Type
|
||||||
|
// 实现编译期静态分发(每个 protocol 对应一份独立代码,零虚函数开销)。
|
||||||
|
// 本 impl 处理 wl_registry::Global / GlobalRemove 事件:当 compositor 广播新协议
|
||||||
|
// 对象时,按 interface 名选择性 bind(screencopy/dmabuf/wl_output/xdg-output/
|
||||||
|
// wlr-output-management),把 proxy 存入 ProbingOutputs stage 字段。
|
||||||
impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
||||||
|
// 注意 Dispatch::event 的 receiver 是 `state: &mut Self`(不是 self):
|
||||||
|
// wayland-client 设计为 free fn + 第一个参数 &mut State,便于事件循环统一调度。
|
||||||
|
// &mut Self 在 trait 内等价于 &mut State<S>,是当前类型的独占可变借用。
|
||||||
|
// 参数 `_data: &GlobalListContents`:registry 附带的 user_data,这里不读,前缀 `_` 标记未用。
|
||||||
|
// 参数 `qhandle: &QueueHandle<State<S>>`:事件队列句柄,bind 子对象时需传入
|
||||||
|
// (让 wayland-client 知道新 proxy 的事件继续回到这个队列)。
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
registry: &WlRegistry,
|
registry: &WlRegistry,
|
||||||
@@ -1377,25 +1394,37 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
_conn: &wayland_client::Connection,
|
_conn: &wayland_client::Connection,
|
||||||
qhandle: &QueueHandle<State<S>>,
|
qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
|
// 局部 use 把长路径重命名为短名,下面 match 用 RegistryEvent::Xxx 更清晰。
|
||||||
|
// 这是 Rust 惯用法,零运行时开销(仅作用域内类型别名)。
|
||||||
use wayland_client::protocol::wl_registry::Event as RegistryEvent;
|
use wayland_client::protocol::wl_registry::Event as RegistryEvent;
|
||||||
|
|
||||||
|
// match 枚举事件:Wayland 协议事件都是 exhaustive enum(编译期保证穷尽所有变体)。
|
||||||
|
// RegistryEvent 有两个变体:Global(新增协议对象)/ GlobalRemove(移除)。
|
||||||
match event {
|
match event {
|
||||||
|
// Global 事件:compositor 广播一个新协议对象(name=u32 ID, interface=协议名, version=版本号)。
|
||||||
RegistryEvent::Global {
|
RegistryEvent::Global {
|
||||||
name,
|
name,
|
||||||
interface,
|
interface,
|
||||||
version,
|
version,
|
||||||
} => match interface.as_str() {
|
} => match interface.as_str() {
|
||||||
|
// wlr-screencopy-manager-unstable-v1:截屏协议入口。版本取 min(server, 3)。
|
||||||
"zwlr_screencopy_manager_v1" => {
|
"zwlr_screencopy_manager_v1" => {
|
||||||
let v = version.min(3);
|
let v = version.min(3);
|
||||||
tracing::debug!("Binding zwlr_screencopy_manager_v1 v{v} (name={name})");
|
tracing::debug!("Binding zwlr_screencopy_manager_v1 v{v} (name={name})");
|
||||||
|
// registry.bind(name, version, qhandle, user_data):创建 protocol proxy。
|
||||||
|
// 返回值类型在 let mgr: 显式标注,Rust 推断不出 wayland 自动生成的类型。
|
||||||
let mgr: ZwlrScreencopyManagerV1 = registry.bind(name, v, qhandle, ());
|
let mgr: ZwlrScreencopyManagerV1 = registry.bind(name, v, qhandle, ());
|
||||||
|
// if let 模式匹配 + &mut 借用:只关心 ProbingOutputs 阶段,其它阶段忽略。
|
||||||
if let EncConstructionStage::ProbingOutputs {
|
if let EncConstructionStage::ProbingOutputs {
|
||||||
screencopy_manager, ..
|
screencopy_manager, ..
|
||||||
} = &mut state.stage
|
} = &mut state.stage
|
||||||
{
|
{
|
||||||
|
// *screencopy_manager = Some(mgr):把 Option 字段从 None 填成 Some。
|
||||||
*screencopy_manager = Some(mgr);
|
*screencopy_manager = Some(mgr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// linux-dmabuf-unstable-v1:DMA-BUF 零拷贝 buffer 协议。版本取 min(server, 4)。
|
||||||
|
// v4 起 get_default_feedback 可用,能拿到 compositor 推荐的 DRM 设备。
|
||||||
"zwp_linux_dmabuf_v1" => {
|
"zwp_linux_dmabuf_v1" => {
|
||||||
let v = version.min(4);
|
let v = version.min(4);
|
||||||
tracing::debug!("Binding zwp_linux_dmabuf_v1 v{v} (name={name})");
|
tracing::debug!("Binding zwp_linux_dmabuf_v1 v{v} (name={name})");
|
||||||
@@ -1406,16 +1435,22 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
..
|
..
|
||||||
} = &mut state.stage
|
} = &mut state.stage
|
||||||
{
|
{
|
||||||
|
// proxy.clone() 增加 wayland proxy 的引用计数(compositor 端不动)。
|
||||||
*dmabuf = Some(proxy.clone());
|
*dmabuf = Some(proxy.clone());
|
||||||
if v >= 4 {
|
if v >= 4 {
|
||||||
|
// 仅 v4+ 支持 dmabuf feedback(compositor 主动告知格式 + 设备)。
|
||||||
let feedback = proxy.get_default_feedback(qhandle, ());
|
let feedback = proxy.get_default_feedback(qhandle, ());
|
||||||
*dmabuf_feedback = Some(feedback);
|
*dmabuf_feedback = Some(feedback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// wl_output:Wayland 核心输出(显示器)协议。每个 monitor 一个 WlOutput proxy。
|
||||||
"wl_output" => {
|
"wl_output" => {
|
||||||
let v = version.min(4);
|
let v = version.min(4);
|
||||||
tracing::debug!("Binding wl_output v{v} (name={name})");
|
tracing::debug!("Binding wl_output v{v} (name={name})");
|
||||||
|
// user_data = OutputId(name):把 registry 分配的 u32 name 作为 user_data
|
||||||
|
// 附到 output proxy 上,后续 Dispatch<WlOutput, OutputId>::event 中通过
|
||||||
|
// &OutputId 拿回 name 来定位 stage.outputs 索引。
|
||||||
let output: WlOutput = registry.bind(name, v, qhandle, OutputId(name));
|
let output: WlOutput = registry.bind(name, v, qhandle, OutputId(name));
|
||||||
if let EncConstructionStage::ProbingOutputs {
|
if let EncConstructionStage::ProbingOutputs {
|
||||||
outputs,
|
outputs,
|
||||||
@@ -1425,15 +1460,19 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
..
|
..
|
||||||
} = &mut state.stage
|
} = &mut state.stage
|
||||||
{
|
{
|
||||||
|
// 4 个并行 Vec 用相同下标对齐:第 i 个 output 的 info/proxy/name 共享 idx i。
|
||||||
outputs.push(PartialOutputInfo::default());
|
outputs.push(PartialOutputInfo::default());
|
||||||
bound_outputs.push(output.clone());
|
bound_outputs.push(output.clone());
|
||||||
output_names.push(name);
|
output_names.push(name);
|
||||||
|
// 若 xdg-output manager 已 bind(顺序无关),立刻请求 xdg-output 信息
|
||||||
|
// (logical_position / name 等高元数据只有 xdg-output 才有)。
|
||||||
if let Some(xdg_mgr) = xdg_output_manager {
|
if let Some(xdg_mgr) = xdg_output_manager {
|
||||||
let output_id = OutputId(name);
|
let output_id = OutputId(name);
|
||||||
xdg_mgr.get_xdg_output(&output, qhandle, output_id);
|
xdg_mgr.get_xdg_output(&output, qhandle, output_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// xdg-output-unstable-v1:扩展 wl_output 的逻辑坐标/名称(Sway/Hyprland 用)。
|
||||||
"zxdg_output_manager_v1" => {
|
"zxdg_output_manager_v1" => {
|
||||||
let v = version.min(3);
|
let v = version.min(3);
|
||||||
tracing::debug!("Binding zxdg_output_manager_v1 v{v} (name={name})");
|
tracing::debug!("Binding zxdg_output_manager_v1 v{v} (name={name})");
|
||||||
@@ -1445,7 +1484,10 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
..
|
..
|
||||||
} = &mut state.stage
|
} = &mut state.stage
|
||||||
{
|
{
|
||||||
|
// 回填:之前已 bind 的 wl_output 现在补请 xdg-output(处理乱序到达)。
|
||||||
|
// .enumerate() 把 iter 转成 (idx, &item),类比 Go 的 for i, o := range。
|
||||||
for (i, output) in bound_outputs.iter().enumerate() {
|
for (i, output) in bound_outputs.iter().enumerate() {
|
||||||
|
// .copied() 把 Option<&u32> 转 Option<u32>(Copy 类型专用,零开销)。
|
||||||
let oname = output_names.get(i).copied().unwrap_or(0);
|
let oname = output_names.get(i).copied().unwrap_or(0);
|
||||||
let output_id = OutputId(oname);
|
let output_id = OutputId(oname);
|
||||||
xdg_mgr.get_xdg_output(output, qhandle, output_id);
|
xdg_mgr.get_xdg_output(output, qhandle, output_id);
|
||||||
@@ -1453,6 +1495,7 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
*xdg_output_manager = Some(xdg_mgr);
|
*xdg_output_manager = Some(xdg_mgr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// wlr-output-management-unstable-v1:niri 用的输出管理协议(替代 xdg-output)。
|
||||||
"zwlr_output_manager_v1" => {
|
"zwlr_output_manager_v1" => {
|
||||||
let v = version.min(4);
|
let v = version.min(4);
|
||||||
tracing::debug!("Binding zwlr_output_manager_v1 v{v} (name={name})");
|
tracing::debug!("Binding zwlr_output_manager_v1 v{v} (name={name})");
|
||||||
@@ -1464,11 +1507,14 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
*wlr_output_manager = Some(mgr);
|
*wlr_output_manager = Some(mgr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 其它未关心的协议:忽略(compositor 还会广播 wl_seat/wl_data_device 等)。
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
// GlobalRemove:compositor 移除某协议对象(如显示器热插拔)。截屏启动期忽略。
|
||||||
RegistryEvent::GlobalRemove { name } => {
|
RegistryEvent::GlobalRemove { name } => {
|
||||||
tracing::debug!("Global removed: name={name}");
|
tracing::debug!("Global removed: name={name}");
|
||||||
}
|
}
|
||||||
|
// 兜底 arm:未来 Wayland 新增事件变体时不会编译失败(trait 兼容性预留)。
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1478,6 +1524,10 @@ impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
|
|||||||
// Dispatch<WlOutput, ()>
|
// Dispatch<WlOutput, ()>
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 处理 wl_output 协议事件:compositor 推送显示器元数据(geometry/mode/name/done)。
|
||||||
|
// user_data = OutputId(name):bind 时传入的 registry name,这里用来反查 stage 中的索引。
|
||||||
|
// 本 impl 把事件数据累计到 PartialOutputInfo(在 ProbingOutputs 阶段),
|
||||||
|
// Done 事件触发 try_finalize_output 尝试切到 EverythingButFmt。
|
||||||
impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
@@ -1487,29 +1537,40 @@ impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
|||||||
_conn: &wayland_client::Connection,
|
_conn: &wayland_client::Connection,
|
||||||
_qhandle: &QueueHandle<State<S>>,
|
_qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
|
// 引入 wl_output 的三个 enum 别名:事件 / 模式 flag / 旋转 transform。
|
||||||
|
// WEnum 是 wayland-client 的 enum 包装:协议允许 Unknown 值,所以是 Value + Unknown。
|
||||||
use wayland_client::protocol::wl_output::Event as OutputEvent;
|
use wayland_client::protocol::wl_output::Event as OutputEvent;
|
||||||
use wayland_client::protocol::wl_output::Mode as WlMode;
|
use wayland_client::protocol::wl_output::Mode as WlMode;
|
||||||
use wayland_client::protocol::wl_output::Transform as WlTransform;
|
use wayland_client::protocol::wl_output::Transform as WlTransform;
|
||||||
|
|
||||||
|
// 解构 OutputId tuple struct:拿回 u32 name。`data` 是 &OutputId,所以这里 target_name 是 &u32。
|
||||||
let OutputId(target_name) = data;
|
let OutputId(target_name) = data;
|
||||||
|
// 在 output_names 中找索引:用 iter().position(...) 类似 Go 的 for-loop + index。
|
||||||
|
// 只在 ProbingOutputs 阶段处理;其它阶段(Streaming)的 wl_output 事件忽略。
|
||||||
let idx = match &state.stage {
|
let idx = match &state.stage {
|
||||||
EncConstructionStage::ProbingOutputs { output_names, .. } => {
|
EncConstructionStage::ProbingOutputs { output_names, .. } => {
|
||||||
output_names.iter().position(|&n| n == *target_name)
|
output_names.iter().position(|&n| n == *target_name)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
// 早期 return:找不到 idx 说明该 output 已被丢弃或阶段已过,直接退出。
|
||||||
|
// Rust 中 fn 内 `return;` 等价于返回 ()(本函数返回类型就是 ())。
|
||||||
let idx = match idx {
|
let idx = match idx {
|
||||||
Some(i) => i,
|
Some(i) => i,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
// Geometry:compositor 推送显示器物理属性 + 旋转方向。
|
||||||
|
// `..` 表示忽略其它字段(protocol 可能有 x/y/subpixel/manufacturer 等)。
|
||||||
OutputEvent::Geometry {
|
OutputEvent::Geometry {
|
||||||
transform,
|
transform,
|
||||||
physical_width,
|
physical_width,
|
||||||
physical_height,
|
physical_height,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
// WEnum::Value(...) 模式:取出协议枚举的已知值;Unknown 值落到 `_` 默认分支。
|
||||||
|
// Transform 是本程序自定义的 enum(去掉 wayland 的 WEnum 包装,方便后续匹配)。
|
||||||
let t = match transform {
|
let t = match transform {
|
||||||
wayland_client::WEnum::Value(WlTransform::Normal) => Transform::Normal,
|
wayland_client::WEnum::Value(WlTransform::Normal) => Transform::Normal,
|
||||||
wayland_client::WEnum::Value(WlTransform::_90) => Transform::Normal90,
|
wayland_client::WEnum::Value(WlTransform::_90) => Transform::Normal90,
|
||||||
@@ -1522,18 +1583,22 @@ impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
|||||||
_ => Transform::Normal,
|
_ => Transform::Normal,
|
||||||
};
|
};
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
|
// .get_mut(idx) 返回 Option<&mut T>:越界时返回 None 而非 panic。
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
info.transform = Some(t);
|
info.transform = Some(t);
|
||||||
info.physical_size = Some((physical_width, physical_height));
|
info.physical_size = Some((physical_width, physical_height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Mode:显示器分辨率模式。flags 标识 Current/Preferred(位掩码)。
|
||||||
OutputEvent::Mode {
|
OutputEvent::Mode {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
flags,
|
flags,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
// matches! 宏:等价于 if let + bool,简洁判断 flags 是不是 Current。
|
||||||
|
// 不用 == 是因为 WEnum 是 enum,需要模式匹配。
|
||||||
let is_current = matches!(flags, wayland_client::WEnum::Value(WlMode::Current));
|
let is_current = matches!(flags, wayland_client::WEnum::Value(WlMode::Current));
|
||||||
if is_current {
|
if is_current {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
@@ -1543,16 +1608,21 @@ impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Done:compositor 一次性告知所有元数据已发完(wl_output v2+)。
|
||||||
|
// done_count += 1 后立即尝试 finalize——若所有必填字段就绪,切到 EverythingButFmt。
|
||||||
OutputEvent::Done => {
|
OutputEvent::Done => {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
info.done_count += 1;
|
info.done_count += 1;
|
||||||
if info.done_count >= 1 {
|
if info.done_count >= 1 {
|
||||||
|
// try_finalize_output 内部会判断阶段 + 字段完备性,可能 return false。
|
||||||
state.try_finalize_output(idx);
|
state.try_finalize_output(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Name:显示器可读名(v4+,如 "eDP-1" / "HDMI-A-1")。
|
||||||
|
// 注意:本字段 info.wl_name 与 bind 时的 OutputId(name) 是同一来源(registry name 的字符串化版本)。
|
||||||
OutputEvent::Name { name } => {
|
OutputEvent::Name { name } => {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
@@ -1569,6 +1639,11 @@ impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
|
|||||||
// Dispatch<ZxdgOutputV1, OutputId>
|
// Dispatch<ZxdgOutputV1, OutputId>
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 处理 xdg-output 协议事件:compositor 推送显示器的逻辑坐标 + 名称(Sway/Hyprland)。
|
||||||
|
// xdg-output 是 wl_output 的扩展协议:wl_output 只给物理像素,
|
||||||
|
// xdg-output 补充 logical_position / logical_size(HiDPI scaled 后的逻辑值)+ name。
|
||||||
|
// user_data = OutputId(name):与 Dispatch<WlOutput> 同源,用 registry name 反查索引。
|
||||||
|
// Done 事件与 wl_output::Done 二选一触发 try_finalize_output(双向保险)。
|
||||||
impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
@@ -1578,7 +1653,9 @@ impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
|||||||
_conn: &wayland_client::Connection,
|
_conn: &wayland_client::Connection,
|
||||||
_qhandle: &QueueHandle<State<S>>,
|
_qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
|
// data.0 直接取出 OutputId 内的 u32(不用解构,因为 OutputId 是 transparent tuple struct)。
|
||||||
let target_name = data.0;
|
let target_name = data.0;
|
||||||
|
// 同 Dispatch<WlOutput>:找索引,找不到就 return(不在 ProbingOutputs 阶段时也忽略)。
|
||||||
let idx = match &state.stage {
|
let idx = match &state.stage {
|
||||||
EncConstructionStage::ProbingOutputs { output_names, .. } => {
|
EncConstructionStage::ProbingOutputs { output_names, .. } => {
|
||||||
output_names.iter().position(|&n| n == target_name)
|
output_names.iter().position(|&n| n == target_name)
|
||||||
@@ -1591,6 +1668,7 @@ impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
// Name:xdg-output 提供的可读名(比 wl_output::Name 更可靠,v3+ 必有)。
|
||||||
XdgOutputEvent::Name { name } => {
|
XdgOutputEvent::Name { name } => {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
@@ -1598,6 +1676,7 @@ impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// LogicalPosition:scaled 后的逻辑坐标(用于多显示器拼接顺序)。
|
||||||
XdgOutputEvent::LogicalPosition { x, y } => {
|
XdgOutputEvent::LogicalPosition { x, y } => {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
@@ -1605,7 +1684,10 @@ impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// LogicalSize 故意忽略:截屏用物理像素 size(mode_size),不用 logical(会被 scale 缩放)。
|
||||||
XdgOutputEvent::LogicalSize { .. } => {}
|
XdgOutputEvent::LogicalSize { .. } => {}
|
||||||
|
// Done:xdg-output 的元数据批次结束。done_count 在 wl_output 和 xdg-output 两边都自增,
|
||||||
|
// try_finalize_output 内部 has_xdg 分支要求 done_count >= 2(wl_output + xdg-output 各一次)。
|
||||||
XdgOutputEvent::Done => {
|
XdgOutputEvent::Done => {
|
||||||
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
|
||||||
if let Some(info) = outputs.get_mut(idx) {
|
if let Some(info) = outputs.get_mut(idx) {
|
||||||
@@ -1625,6 +1707,11 @@ impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
|
|||||||
// Dispatch<ZwpLinuxDmabufV1, ()>
|
// Dispatch<ZwpLinuxDmabufV1, ()>
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 处理 linux-dmabuf v1 基础协议事件(Format/Modifier 广播)。
|
||||||
|
// 故意全部留空:我们用的是更现代的 dmabuf feedback(ZwpLinuxDmabufFeedbackV1),
|
||||||
|
// feedback 协议在 v4+ 才有,更准确(compositor 主动告知主设备 + tranche 优先级)。
|
||||||
|
// 这里只实现空 match 是因为 wayland-client 要求每个 bind 的协议都实现 Dispatch;
|
||||||
|
// legacy Format/Modifier 广播在 feedback 存在时是冗余信息,忽略以省 log。
|
||||||
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S> {
|
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S> {
|
||||||
fn event(
|
fn event(
|
||||||
_state: &mut Self,
|
_state: &mut Self,
|
||||||
@@ -1635,13 +1722,19 @@ impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S> {
|
|||||||
_qhandle: &QueueHandle<State<S>>,
|
_qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
|
// Format:legacy "支持 DRM format X" 广播(无 modifier 信息,已被 feedback 取代)。
|
||||||
DmabufEvent::Format { .. } => {}
|
DmabufEvent::Format { .. } => {}
|
||||||
|
// Modifier:v3+ 的 "支持 format X with modifier Y" 广播(仍然没有设备/tranche 优先级)。
|
||||||
DmabufEvent::Modifier { .. } => {}
|
DmabufEvent::Modifier { .. } => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理 linux-dmabuf feedback 事件(v4+):compositor 主动告知 DMA-BUF 偏好。
|
||||||
|
// 关键事件 MainDevice:compositor 推荐的 DRM 设备(dev_t 编码),
|
||||||
|
// 解析为 /dev/dri/renderD{minor} 路径存入 state.drm_device_from_compositor,
|
||||||
|
// 供后续 VAAPI 初始化用作硬件设备上下文。
|
||||||
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
|
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
@@ -1652,11 +1745,20 @@ impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
|
|||||||
_qhandle: &QueueHandle<State<S>>,
|
_qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
|
// MainDevice:dev 是 8 字节 dev_t(Linux 内核设备号编码,little-endian)。
|
||||||
DmabufFeedbackEvent::MainDevice { device } => {
|
DmabufFeedbackEvent::MainDevice { device } => {
|
||||||
|
// 防御性长度检查:协议规定 8 字节,但服务器实现可能违反。
|
||||||
if device.len() >= 8 {
|
if device.len() >= 8 {
|
||||||
|
// device[..8].try_into() 把 &[u8] 转 [u8; 8](固定大小数组)。
|
||||||
|
// unwrap_or([0u8; 8]):try_into 失败时回退到 0(理论上不会触发,因已检查 len)。
|
||||||
let dev_bytes: [u8; 8] = device[..8].try_into().unwrap_or([0u8; 8]);
|
let dev_bytes: [u8; 8] = device[..8].try_into().unwrap_or([0u8; 8]);
|
||||||
|
// u64::from_ne_bytes:本机字节序(little-endian on x86/ARM)解析为 u64。
|
||||||
let dev = u64::from_ne_bytes(dev_bytes);
|
let dev = u64::from_ne_bytes(dev_bytes);
|
||||||
|
// 解码 Linux dev_t:低 8 位 + 高 12 位组合成 minor(renderD{minor} 用 minor+128)。
|
||||||
|
// dev_t 编码:bits 0-7 = minor low, bits 8-19 = major, bits 20-31 = minor high.
|
||||||
|
// 这里用位掩码重组 minor = (dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)。
|
||||||
let minor = ((dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)) as u32;
|
let minor = ((dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)) as u32;
|
||||||
|
// renderD{minor}:Linux DRM render 节点命名规则(renderD128 = card0, renderD129 = card1 ...)。
|
||||||
let path = PathBuf::from(format!("/dev/dri/renderD{}", minor));
|
let path = PathBuf::from(format!("/dev/dri/renderD{}", minor));
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
@@ -1679,6 +1781,7 @@ impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 其它 feedback 事件(格式表 / tranche)暂不消费:我们只用主设备做启发式。
|
||||||
DmabufFeedbackEvent::FormatTable { .. } => {}
|
DmabufFeedbackEvent::FormatTable { .. } => {}
|
||||||
DmabufFeedbackEvent::Done => {}
|
DmabufFeedbackEvent::Done => {}
|
||||||
DmabufFeedbackEvent::TrancheDone => {}
|
DmabufFeedbackEvent::TrancheDone => {}
|
||||||
@@ -1694,6 +1797,12 @@ impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
|
|||||||
// Dispatch<ZwpLinuxBufferParamsV1, ()>
|
// Dispatch<ZwpLinuxBufferParamsV1, ()>
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 处理 linux-dmabuf buffer_params 协议事件:compositor 反馈 DMA-BUF buffer 创建结果。
|
||||||
|
// 每次 capture 创建一个 ZwpLinuxBufferParamsV1 proxy,add 添加 plane 后 create_immed
|
||||||
|
// 触发 buffer 创建;Created 成功 / Failed 失败。
|
||||||
|
// Failed 分支是关键的错误恢复点:把 in_flight_surface 状态机回退到 None,
|
||||||
|
// 释放持有的 dmabuf buffer(drop 释放文件描述符),并通过 cap.on_done_with_frame
|
||||||
|
// 把 frame 还给 capture 后端(让 CaptureSource 内部状态保持一致),最后 errored=true。
|
||||||
impl<S: CaptureSource> Dispatch<ZwpLinuxBufferParamsV1, ()> for State<S> {
|
impl<S: CaptureSource> Dispatch<ZwpLinuxBufferParamsV1, ()> for State<S> {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
@@ -1704,23 +1813,35 @@ impl<S: CaptureSource> Dispatch<ZwpLinuxBufferParamsV1, ()> for State<S> {
|
|||||||
_qhandle: &QueueHandle<State<S>>,
|
_qhandle: &QueueHandle<State<S>>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
|
// Created:buffer 成功创建。本程序路径里不消费这个事件
|
||||||
|
// (streaming 后立即用 on_frame_allocd 路径,不依赖 Created 触发后续动作)。
|
||||||
BufferParamsEvent::Created { .. } => {
|
BufferParamsEvent::Created { .. } => {
|
||||||
tracing::debug!("DMA-BUF buffer created");
|
tracing::debug!("DMA-BUF buffer created");
|
||||||
}
|
}
|
||||||
|
// Failed:buffer 创建失败(format/modifier 不支持、内存不足等)。
|
||||||
BufferParamsEvent::Failed => {
|
BufferParamsEvent::Failed => {
|
||||||
tracing::error!("DMA-BUF buffer creation failed");
|
tracing::error!("DMA-BUF buffer creation failed");
|
||||||
|
// mem::replace:把 in_flight_surface 当前值取出(替换为 None),
|
||||||
|
// 类比 Go 的 swap pattern —— 先拿走所有权再处理,避免后续逻辑还看到旧状态。
|
||||||
let taken = mem::replace(&mut state.in_flight_surface, InFlightSurface::None);
|
let taken = mem::replace(&mut state.in_flight_surface, InFlightSurface::None);
|
||||||
match taken {
|
match taken {
|
||||||
|
// CopyQueued:截屏已排队但 buffer 失败。需要回收 frame 资源。
|
||||||
InFlightSurface::CopyQueued { buffer, frame, .. } => {
|
InFlightSurface::CopyQueued { buffer, frame, .. } => {
|
||||||
|
// drop(buffer):显式释放 dmabuf buffer(关闭文件描述符)。
|
||||||
|
// Rust 默认会在作用域结束 drop,这里提前 drop 释放 FD。
|
||||||
drop(buffer);
|
drop(buffer);
|
||||||
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
|
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
|
||||||
|
// cap.on_done_with_frame(frame):把失败但未释放的 frame 还给 capture 后端,
|
||||||
|
// CaptureSource 内部状态机才知道这帧"结束了"。
|
||||||
cap.on_done_with_frame(frame);
|
cap.on_done_with_frame(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 其它 in_flight 状态(AllocQueued/None):原样放回,不动状态机。
|
||||||
other => {
|
other => {
|
||||||
state.in_flight_surface = other;
|
state.in_flight_surface = other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// proxy.destroy():销毁 buffer_params 对象,避免 compositor 端资源泄漏。
|
||||||
proxy.destroy();
|
proxy.destroy();
|
||||||
state.errored = true;
|
state.errored = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user