refactor: decompose oversized modules into directory form (avhw + state + cap_portal + state_portal + webrtc + bench bins) #26

Merged
dailz merged 16 commits from refactor/split-avhw into master 2026-07-14 11:44:56 +08:00
8 changed files with 600 additions and 604 deletions
Showing only changes of commit a17f809d9f - Show all commits
+19
View File
@@ -0,0 +1,19 @@
use wayland_client::protocol::wl_buffer::WlBuffer;
use wayland_client::{Dispatch, Proxy, QueueHandle};
use crate::state::{CaptureSource, State};
impl<S: CaptureSource> Dispatch<WlBuffer, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &WlBuffer,
event: <WlBuffer as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
if let wayland_client::protocol::wl_buffer::Event::Release = event {
tracing::trace!("WlBuffer released");
}
}
}
+115
View File
@@ -0,0 +1,115 @@
use std::mem;
use std::path::PathBuf;
use wayland_client::{Dispatch, Proxy, QueueHandle};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::{
Event as BufferParamsEvent, ZwpLinuxBufferParamsV1,
};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::{
Event as DmabufFeedbackEvent, ZwpLinuxDmabufFeedbackV1,
};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::{
Event as DmabufEvent, ZwpLinuxDmabufV1,
};
use crate::state::{CaptureSource, EncConstructionStage, InFlightSurface, State};
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwpLinuxDmabufV1,
event: <ZwpLinuxDmabufV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
DmabufEvent::Format { .. } => {}
DmabufEvent::Modifier { .. } => {}
_ => {}
}
}
}
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZwpLinuxDmabufFeedbackV1,
event: <ZwpLinuxDmabufFeedbackV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
DmabufFeedbackEvent::MainDevice { device } => {
if device.len() >= 8 {
let dev_bytes: [u8; 8] = device[..8].try_into().unwrap_or([0u8; 8]);
let dev = u64::from_ne_bytes(dev_bytes);
let minor = ((dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)) as u32;
let path = PathBuf::from(format!("/dev/dri/renderD{}", minor));
if path.exists() {
tracing::info!(
"Compositor DRM device: {} (dev_t: {})",
path.display(),
dev
);
state.drm_device_from_compositor = Some(path);
} else {
tracing::warn!(
"Compositor reported DRM device {} (dev_t: {}) but path does not exist",
path.display(),
dev
);
}
} else {
tracing::warn!(
"main_device event with unexpected data length: {}",
device.len()
);
}
}
DmabufFeedbackEvent::FormatTable { .. } => {}
DmabufFeedbackEvent::Done => {}
DmabufFeedbackEvent::TrancheDone => {}
DmabufFeedbackEvent::TrancheTargetDevice { .. } => {}
DmabufFeedbackEvent::TrancheFormats { .. } => {}
DmabufFeedbackEvent::TrancheFlags { .. } => {}
_ => {}
}
}
}
impl<S: CaptureSource> Dispatch<ZwpLinuxBufferParamsV1, ()> for State<S> {
fn event(
state: &mut Self,
proxy: &ZwpLinuxBufferParamsV1,
event: <ZwpLinuxBufferParamsV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
BufferParamsEvent::Created { .. } => {
tracing::debug!("DMA-BUF buffer created");
}
BufferParamsEvent::Failed => {
tracing::error!("DMA-BUF buffer creation failed");
let taken = mem::replace(&mut state.in_flight_surface, InFlightSurface::None);
match taken {
InFlightSurface::CopyQueued { buffer, frame, .. } => {
drop(buffer);
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
cap.on_done_with_frame(frame);
}
}
other => {
state.in_flight_surface = other;
}
}
proxy.destroy();
state.errored = true;
}
_ => {}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
mod buffer;
mod dmabuf;
mod output_mgr;
mod registry;
mod screencopy;
mod wl_output;
+109
View File
@@ -0,0 +1,109 @@
use wayland_client::{event_created_child, Dispatch, Proxy, QueueHandle};
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_head_v1::{
self, Event as WlrHeadEvent, ZwlrOutputHeadV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::{
self, Event as WlrOutputManagerEvent, ZwlrOutputManagerV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_mode_v1::ZwlrOutputModeV1;
use crate::state::{CaptureSource, EncConstructionStage, State, WlrHeadInfo};
impl<S: CaptureSource> Dispatch<ZxdgOutputManagerV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZxdgOutputManagerV1,
_event: <ZxdgOutputManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
impl<S: CaptureSource> Dispatch<ZwlrOutputManagerV1, ()> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZwlrOutputManagerV1,
event: <ZwlrOutputManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
WlrOutputManagerEvent::Head { head } => {
let _head: ZwlrOutputHeadV1 = head;
tracing::debug!("wlr output head advertised");
}
WlrOutputManagerEvent::Done { .. } => {
if let EncConstructionStage::ProbingOutputs {
wlr_manager_done,
outputs,
..
} = &mut state.stage
{
*wlr_manager_done = true;
let count = outputs.len();
for idx in 0..count {
state.try_finalize_output(idx);
}
}
}
WlrOutputManagerEvent::Finished => {
tracing::warn!("zwlr_output_manager_v1::Finished received during probing");
}
_ => {}
}
}
event_created_child!(State<S>, ZwlrOutputManagerV1, [
zwlr_output_manager_v1::EVT_HEAD_OPCODE => (ZwlrOutputHeadV1, ()),
]);
}
impl<S: CaptureSource> Dispatch<ZwlrOutputHeadV1, ()> for State<S> {
fn event(
state: &mut Self,
proxy: &ZwlrOutputHeadV1,
event: <ZwlrOutputHeadV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
WlrHeadEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs {
wlr_heads,
wlr_head_proxy_to_name,
..
} = &mut state.stage
{
wlr_heads.entry(name.clone()).or_insert(WlrHeadInfo {});
wlr_head_proxy_to_name.insert(proxy.id(), name);
}
}
WlrHeadEvent::Position { .. } => {}
WlrHeadEvent::Finished => {
tracing::debug!("zwlr_output_head_v1::Finished received");
}
_ => {}
}
}
event_created_child!(State<S>, ZwlrOutputHeadV1, [
zwlr_output_head_v1::EVT_MODE_OPCODE => (ZwlrOutputModeV1, ()),
]);
}
impl<S: CaptureSource> Dispatch<ZwlrOutputModeV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwlrOutputModeV1,
_event: <ZwlrOutputModeV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
+116
View File
@@ -0,0 +1,116 @@
use wayland_client::globals::GlobalListContents;
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::protocol::wl_registry::WlRegistry;
use wayland_client::{Dispatch, QueueHandle};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::ZwlrOutputManagerV1;
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use crate::state::{CaptureSource, EncConstructionStage, OutputId, PartialOutputInfo, State};
impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
fn event(
state: &mut Self,
registry: &WlRegistry,
event: wayland_client::protocol::wl_registry::Event,
_data: &GlobalListContents,
_conn: &wayland_client::Connection,
qhandle: &QueueHandle<State<S>>,
) {
use wayland_client::protocol::wl_registry::Event as RegistryEvent;
match event {
RegistryEvent::Global {
name,
interface,
version,
} => match interface.as_str() {
"zwlr_screencopy_manager_v1" => {
let v = version.min(3);
tracing::debug!("Binding zwlr_screencopy_manager_v1 v{v} (name={name})");
let mgr: ZwlrScreencopyManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
screencopy_manager, ..
} = &mut state.stage
{
*screencopy_manager = Some(mgr);
}
}
"zwp_linux_dmabuf_v1" => {
let v = version.min(4);
tracing::debug!("Binding zwp_linux_dmabuf_v1 v{v} (name={name})");
let proxy: ZwpLinuxDmabufV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
dmabuf,
dmabuf_feedback,
..
} = &mut state.stage
{
*dmabuf = Some(proxy.clone());
if v >= 4 {
let feedback = proxy.get_default_feedback(qhandle, ());
*dmabuf_feedback = Some(feedback);
}
}
}
"wl_output" => {
let v = version.min(4);
tracing::debug!("Binding wl_output v{v} (name={name})");
let output: WlOutput = registry.bind(name, v, qhandle, OutputId(name));
if let EncConstructionStage::ProbingOutputs {
outputs,
bound_outputs,
output_names,
xdg_output_manager,
..
} = &mut state.stage
{
outputs.push(PartialOutputInfo::default());
bound_outputs.push(output.clone());
output_names.push(name);
if let Some(xdg_mgr) = xdg_output_manager {
let output_id = OutputId(name);
xdg_mgr.get_xdg_output(&output, qhandle, output_id);
}
}
}
"zxdg_output_manager_v1" => {
let v = version.min(3);
tracing::debug!("Binding zxdg_output_manager_v1 v{v} (name={name})");
let xdg_mgr: ZxdgOutputManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
bound_outputs,
xdg_output_manager,
output_names,
..
} = &mut state.stage
{
for (i, output) in bound_outputs.iter().enumerate() {
let oname = output_names.get(i).copied().unwrap_or(0);
let output_id = OutputId(oname);
xdg_mgr.get_xdg_output(output, qhandle, output_id);
}
*xdg_output_manager = Some(xdg_mgr);
}
}
"zwlr_output_manager_v1" => {
let v = version.min(4);
tracing::debug!("Binding zwlr_output_manager_v1 v{v} (name={name})");
let mgr: ZwlrOutputManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
wlr_output_manager, ..
} = &mut state.stage
{
*wlr_output_manager = Some(mgr);
}
}
_ => {}
},
RegistryEvent::GlobalRemove { name } => {
tracing::debug!("Global removed: name={name}");
}
_ => {}
}
}
}
+93
View File
@@ -0,0 +1,93 @@
use wayland_client::{Dispatch, Proxy, QueueHandle};
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{
Event as ScreencopyFrameEvent, ZwlrScreencopyFrameV1,
};
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use crate::cap_wlr_screencopy::CapWlrScreencopy;
use crate::state::{EncConstructionStage, InFlightSurface, State};
impl Dispatch<ZwlrScreencopyFrameV1, ()> for State<CapWlrScreencopy> {
fn event(
state: &mut Self,
proxy: &ZwlrScreencopyFrameV1,
event: <ZwlrScreencopyFrameV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<CapWlrScreencopy>>,
) {
match event {
// SHM buffer offer — in v3 the compositor enumerates supported buffer
// types (buffer and/or linux_dmabuf) before buffer_done. We only
// support DMA-BUF, so just log and wait for linux_dmabuf / buffer_done.
ScreencopyFrameEvent::Buffer { .. } => {
tracing::debug!("Received SHM Buffer offer — only DMA-BUF capture is supported");
}
ScreencopyFrameEvent::LinuxDmabuf {
format,
width,
height,
} => {
tracing::debug!("Screencopy LinuxDmabuf: format={format}, {width}x{height}");
if !matches!(state.in_flight_surface, InFlightSurface::AllocQueued) {
tracing::warn!("Received LinuxDmabuf while no frame allocation was queued");
return;
}
if matches!(state.stage, EncConstructionStage::EverythingButFmt { .. }) {
state.negotiate_format(format, width, height);
if state.errored {
return;
}
}
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
cap.current_frame = Some(proxy.clone());
}
state.on_frame_allocd((), format, width, height);
}
// v3 terminal event: all buffer offers have been enumerated.
// If still AllocQueued, the compositor never sent linux_dmabuf —
// DMA-BUF screencopy is unsupported, so we must error out.
ScreencopyFrameEvent::BufferDone => {
if matches!(state.in_flight_surface, InFlightSurface::AllocQueued) {
tracing::error!(
"Compositor did not offer DMA-BUF screencopy (only SHM); \
DMA-BUF capture is required"
);
state.in_flight_surface = InFlightSurface::None;
proxy.destroy();
state.errored = true;
}
}
ScreencopyFrameEvent::Ready {
tv_sec_hi,
tv_sec_lo,
tv_nsec,
} => {
let tv_sec = (tv_sec_hi as u64) << 32 | tv_sec_lo as u64;
let tv_usec = tv_nsec / 1000;
tracing::trace!("Screencopy ready: tv_sec={tv_sec}, tv_usec={tv_usec}");
state.on_copy_complete(tv_sec, tv_usec);
}
ScreencopyFrameEvent::Failed => {
tracing::error!("Screencopy frame failed");
state.on_copy_fail();
}
ScreencopyFrameEvent::Damage { .. } => {}
_ => {}
}
}
}
impl<S: crate::state::CaptureSource> Dispatch<ZwlrScreencopyManagerV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwlrScreencopyManagerV1,
_event: <ZwlrScreencopyManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
+133
View File
@@ -0,0 +1,133 @@
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::{Dispatch, Proxy, QueueHandle};
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::{
Event as XdgOutputEvent, ZxdgOutputV1,
};
use crate::state::{CaptureSource, EncConstructionStage, OutputId, State, Transform};
impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
fn event(
state: &mut Self,
_proxy: &WlOutput,
event: wayland_client::protocol::wl_output::Event,
data: &OutputId,
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
use wayland_client::protocol::wl_output::Event as OutputEvent;
use wayland_client::protocol::wl_output::Mode as WlMode;
use wayland_client::protocol::wl_output::Transform as WlTransform;
let OutputId(target_name) = data;
let idx = match &state.stage {
EncConstructionStage::ProbingOutputs { output_names, .. } => {
output_names.iter().position(|&n| n == *target_name)
}
_ => None,
};
let idx = match idx {
Some(i) => i,
None => return,
};
match event {
OutputEvent::Geometry { transform, .. } => {
let t = match transform {
wayland_client::WEnum::Value(WlTransform::Normal) => Transform::Normal,
wayland_client::WEnum::Value(WlTransform::_90) => Transform::Normal90,
wayland_client::WEnum::Value(WlTransform::_180) => Transform::Normal180,
wayland_client::WEnum::Value(WlTransform::_270) => Transform::Normal270,
wayland_client::WEnum::Value(WlTransform::Flipped) => Transform::Flipped,
wayland_client::WEnum::Value(WlTransform::Flipped90) => Transform::Flipped90,
wayland_client::WEnum::Value(WlTransform::Flipped180) => Transform::Flipped180,
wayland_client::WEnum::Value(WlTransform::Flipped270) => Transform::Flipped270,
_ => Transform::Normal,
};
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.transform = Some(t);
}
}
}
OutputEvent::Mode {
width,
height,
flags,
..
} => {
let is_current = matches!(flags, wayland_client::WEnum::Value(WlMode::Current));
if is_current {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.mode_size = Some((width, height));
}
}
}
}
OutputEvent::Done => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.done_count += 1;
if info.done_count >= 1 {
state.try_finalize_output(idx);
}
}
}
}
OutputEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.wl_name = Some(name);
}
}
}
_ => {}
}
}
}
impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZxdgOutputV1,
event: <ZxdgOutputV1 as Proxy>::Event,
data: &OutputId,
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
let target_name = data.0;
let idx = match &state.stage {
EncConstructionStage::ProbingOutputs { output_names, .. } => {
output_names.iter().position(|&n| n == target_name)
}
_ => None,
};
let idx = match idx {
Some(i) => i,
None => return,
};
match event {
XdgOutputEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.name = Some(name);
}
}
}
XdgOutputEvent::LogicalSize { .. } => {}
XdgOutputEvent::Done => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.done_count += 1;
if info.done_count >= 1 {
state.try_finalize_output(idx);
}
}
}
}
_ => {}
}
}
}
+9 -604
View File
@@ -9,34 +9,16 @@ use std::time::Instant;
use anyhow::Result;
use wayland_client::backend::ObjectId;
use wayland_client::globals::{GlobalList, GlobalListContents};
use wayland_client::globals::GlobalList;
use wayland_client::protocol::wl_buffer::WlBuffer;
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::protocol::wl_registry::WlRegistry;
use wayland_client::{event_created_child, Dispatch, Proxy, QueueHandle};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::{
Event as BufferParamsEvent, Flags as BufferParamsFlags, ZwpLinuxBufferParamsV1,
};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::{
Event as DmabufFeedbackEvent, ZwpLinuxDmabufFeedbackV1,
};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::{
Event as DmabufEvent, ZwpLinuxDmabufV1,
};
use wayland_client::{Dispatch, QueueHandle};
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::Flags as BufferParamsFlags;
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1;
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1;
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::{
Event as XdgOutputEvent, ZxdgOutputV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_head_v1::{
self, Event as WlrHeadEvent, ZwlrOutputHeadV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::{
self, Event as WlrOutputManagerEvent, ZwlrOutputManagerV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_mode_v1::ZwlrOutputModeV1;
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{
Event as ScreencopyFrameEvent, ZwlrScreencopyFrameV1,
};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::ZwlrOutputManagerV1;
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1;
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use ffmpeg_next as ff;
@@ -44,12 +26,13 @@ use ffmpeg_next::ffi;
use crate::args::Args;
use crate::avhw::{AvHwDevCtx, EncState, EncodedH264Frame, SwEncState};
use crate::cap_wlr_screencopy::CapWlrScreencopy;
use crate::fps_limit::FpsLimit;
use crate::stats::{FrameTimings, PipelineStats};
use crate::transform::{transpose_if_transform_transposed, Transform};
use crate::webrtc::WebRtcState;
mod dispatch;
// ---------------------------------------------------------------------------
// CaptureSource trait
// ---------------------------------------------------------------------------
@@ -1014,581 +997,3 @@ impl<S: CaptureSource> State<S> {
true
}
}
// ---------------------------------------------------------------------------
// Dispatch<WlRegistry, GlobalListContents>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<WlRegistry, GlobalListContents> for State<S> {
fn event(
state: &mut Self,
registry: &WlRegistry,
event: wayland_client::protocol::wl_registry::Event,
_data: &GlobalListContents,
_conn: &wayland_client::Connection,
qhandle: &QueueHandle<State<S>>,
) {
use wayland_client::protocol::wl_registry::Event as RegistryEvent;
match event {
RegistryEvent::Global {
name,
interface,
version,
} => match interface.as_str() {
"zwlr_screencopy_manager_v1" => {
let v = version.min(3);
tracing::debug!("Binding zwlr_screencopy_manager_v1 v{v} (name={name})");
let mgr: ZwlrScreencopyManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
screencopy_manager, ..
} = &mut state.stage
{
*screencopy_manager = Some(mgr);
}
}
"zwp_linux_dmabuf_v1" => {
let v = version.min(4);
tracing::debug!("Binding zwp_linux_dmabuf_v1 v{v} (name={name})");
let proxy: ZwpLinuxDmabufV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
dmabuf,
dmabuf_feedback,
..
} = &mut state.stage
{
*dmabuf = Some(proxy.clone());
if v >= 4 {
let feedback = proxy.get_default_feedback(qhandle, ());
*dmabuf_feedback = Some(feedback);
}
}
}
"wl_output" => {
let v = version.min(4);
tracing::debug!("Binding wl_output v{v} (name={name})");
let output: WlOutput = registry.bind(name, v, qhandle, OutputId(name));
if let EncConstructionStage::ProbingOutputs {
outputs,
bound_outputs,
output_names,
xdg_output_manager,
..
} = &mut state.stage
{
outputs.push(PartialOutputInfo::default());
bound_outputs.push(output.clone());
output_names.push(name);
if let Some(xdg_mgr) = xdg_output_manager {
let output_id = OutputId(name);
xdg_mgr.get_xdg_output(&output, qhandle, output_id);
}
}
}
"zxdg_output_manager_v1" => {
let v = version.min(3);
tracing::debug!("Binding zxdg_output_manager_v1 v{v} (name={name})");
let xdg_mgr: ZxdgOutputManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
bound_outputs,
xdg_output_manager,
output_names,
..
} = &mut state.stage
{
for (i, output) in bound_outputs.iter().enumerate() {
let oname = output_names.get(i).copied().unwrap_or(0);
let output_id = OutputId(oname);
xdg_mgr.get_xdg_output(output, qhandle, output_id);
}
*xdg_output_manager = Some(xdg_mgr);
}
}
"zwlr_output_manager_v1" => {
let v = version.min(4);
tracing::debug!("Binding zwlr_output_manager_v1 v{v} (name={name})");
let mgr: ZwlrOutputManagerV1 = registry.bind(name, v, qhandle, ());
if let EncConstructionStage::ProbingOutputs {
wlr_output_manager, ..
} = &mut state.stage
{
*wlr_output_manager = Some(mgr);
}
}
_ => {}
},
RegistryEvent::GlobalRemove { name } => {
tracing::debug!("Global removed: name={name}");
}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<WlOutput, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<WlOutput, OutputId> for State<S> {
fn event(
state: &mut Self,
_proxy: &WlOutput,
event: wayland_client::protocol::wl_output::Event,
data: &OutputId,
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
use wayland_client::protocol::wl_output::Event as OutputEvent;
use wayland_client::protocol::wl_output::Mode as WlMode;
use wayland_client::protocol::wl_output::Transform as WlTransform;
let OutputId(target_name) = data;
let idx = match &state.stage {
EncConstructionStage::ProbingOutputs { output_names, .. } => {
output_names.iter().position(|&n| n == *target_name)
}
_ => None,
};
let idx = match idx {
Some(i) => i,
None => return,
};
match event {
OutputEvent::Geometry { transform, .. } => {
let t = match transform {
wayland_client::WEnum::Value(WlTransform::Normal) => Transform::Normal,
wayland_client::WEnum::Value(WlTransform::_90) => Transform::Normal90,
wayland_client::WEnum::Value(WlTransform::_180) => Transform::Normal180,
wayland_client::WEnum::Value(WlTransform::_270) => Transform::Normal270,
wayland_client::WEnum::Value(WlTransform::Flipped) => Transform::Flipped,
wayland_client::WEnum::Value(WlTransform::Flipped90) => Transform::Flipped90,
wayland_client::WEnum::Value(WlTransform::Flipped180) => Transform::Flipped180,
wayland_client::WEnum::Value(WlTransform::Flipped270) => Transform::Flipped270,
_ => Transform::Normal,
};
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.transform = Some(t);
}
}
}
OutputEvent::Mode {
width,
height,
flags,
..
} => {
let is_current = matches!(flags, wayland_client::WEnum::Value(WlMode::Current));
if is_current {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.mode_size = Some((width, height));
}
}
}
}
OutputEvent::Done => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.done_count += 1;
if info.done_count >= 1 {
state.try_finalize_output(idx);
}
}
}
}
OutputEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.wl_name = Some(name);
}
}
}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZxdgOutputV1, OutputId>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZxdgOutputV1, OutputId> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZxdgOutputV1,
event: <ZxdgOutputV1 as Proxy>::Event,
data: &OutputId,
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
let target_name = data.0;
let idx = match &state.stage {
EncConstructionStage::ProbingOutputs { output_names, .. } => {
output_names.iter().position(|&n| n == target_name)
}
_ => None,
};
let idx = match idx {
Some(i) => i,
None => return,
};
match event {
XdgOutputEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.name = Some(name);
}
}
}
XdgOutputEvent::LogicalSize { .. } => {}
XdgOutputEvent::Done => {
if let EncConstructionStage::ProbingOutputs { outputs, .. } = &mut state.stage {
if let Some(info) = outputs.get_mut(idx) {
info.done_count += 1;
if info.done_count >= 1 {
state.try_finalize_output(idx);
}
}
}
}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZwpLinuxDmabufV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwpLinuxDmabufV1,
event: <ZwpLinuxDmabufV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
DmabufEvent::Format { .. } => {}
DmabufEvent::Modifier { .. } => {}
_ => {}
}
}
}
impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZwpLinuxDmabufFeedbackV1,
event: <ZwpLinuxDmabufFeedbackV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
DmabufFeedbackEvent::MainDevice { device } => {
if device.len() >= 8 {
let dev_bytes: [u8; 8] = device[..8].try_into().unwrap_or([0u8; 8]);
let dev = u64::from_ne_bytes(dev_bytes);
let minor = ((dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)) as u32;
let path = PathBuf::from(format!("/dev/dri/renderD{}", minor));
if path.exists() {
tracing::info!(
"Compositor DRM device: {} (dev_t: {})",
path.display(),
dev
);
state.drm_device_from_compositor = Some(path);
} else {
tracing::warn!(
"Compositor reported DRM device {} (dev_t: {}) but path does not exist",
path.display(),
dev
);
}
} else {
tracing::warn!(
"main_device event with unexpected data length: {}",
device.len()
);
}
}
DmabufFeedbackEvent::FormatTable { .. } => {}
DmabufFeedbackEvent::Done => {}
DmabufFeedbackEvent::TrancheDone => {}
DmabufFeedbackEvent::TrancheTargetDevice { .. } => {}
DmabufFeedbackEvent::TrancheFormats { .. } => {}
DmabufFeedbackEvent::TrancheFlags { .. } => {}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZwpLinuxBufferParamsV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwpLinuxBufferParamsV1, ()> for State<S> {
fn event(
state: &mut Self,
proxy: &ZwpLinuxBufferParamsV1,
event: <ZwpLinuxBufferParamsV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
BufferParamsEvent::Created { .. } => {
tracing::debug!("DMA-BUF buffer created");
}
BufferParamsEvent::Failed => {
tracing::error!("DMA-BUF buffer creation failed");
let taken = mem::replace(&mut state.in_flight_surface, InFlightSurface::None);
match taken {
InFlightSurface::CopyQueued { buffer, frame, .. } => {
drop(buffer);
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
cap.on_done_with_frame(frame);
}
}
other => {
state.in_flight_surface = other;
}
}
proxy.destroy();
state.errored = true;
}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZwlrScreencopyFrameV1, ()> for CapWlrScreencopy
// ---------------------------------------------------------------------------
impl Dispatch<ZwlrScreencopyFrameV1, ()> for State<CapWlrScreencopy> {
fn event(
state: &mut Self,
proxy: &ZwlrScreencopyFrameV1,
event: <ZwlrScreencopyFrameV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<CapWlrScreencopy>>,
) {
match event {
// SHM buffer offer — in v3 the compositor enumerates supported buffer
// types (buffer and/or linux_dmabuf) before buffer_done. We only
// support DMA-BUF, so just log and wait for linux_dmabuf / buffer_done.
ScreencopyFrameEvent::Buffer { .. } => {
tracing::debug!("Received SHM Buffer offer — only DMA-BUF capture is supported");
}
ScreencopyFrameEvent::LinuxDmabuf {
format,
width,
height,
} => {
tracing::debug!("Screencopy LinuxDmabuf: format={format}, {width}x{height}");
if !matches!(state.in_flight_surface, InFlightSurface::AllocQueued) {
tracing::warn!("Received LinuxDmabuf while no frame allocation was queued");
return;
}
if matches!(state.stage, EncConstructionStage::EverythingButFmt { .. }) {
state.negotiate_format(format, width, height);
if state.errored {
return;
}
}
if let EncConstructionStage::Streaming { cap, .. } = &mut state.stage {
cap.current_frame = Some(proxy.clone());
}
state.on_frame_allocd((), format, width, height);
}
// v3 terminal event: all buffer offers have been enumerated.
// If still AllocQueued, the compositor never sent linux_dmabuf —
// DMA-BUF screencopy is unsupported, so we must error out.
ScreencopyFrameEvent::BufferDone => {
if matches!(state.in_flight_surface, InFlightSurface::AllocQueued) {
tracing::error!(
"Compositor did not offer DMA-BUF screencopy (only SHM); \
DMA-BUF capture is required"
);
state.in_flight_surface = InFlightSurface::None;
proxy.destroy();
state.errored = true;
}
}
ScreencopyFrameEvent::Ready {
tv_sec_hi,
tv_sec_lo,
tv_nsec,
} => {
let tv_sec = (tv_sec_hi as u64) << 32 | tv_sec_lo as u64;
let tv_usec = tv_nsec / 1000;
tracing::trace!("Screencopy ready: tv_sec={tv_sec}, tv_usec={tv_usec}");
state.on_copy_complete(tv_sec, tv_usec);
}
ScreencopyFrameEvent::Failed => {
tracing::error!("Screencopy frame failed");
state.on_copy_fail();
}
ScreencopyFrameEvent::Damage { .. } => {}
_ => {}
}
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZxdgOutputManagerV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZxdgOutputManagerV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZxdgOutputManagerV1,
_event: <ZxdgOutputManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZwlrOutputManagerV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwlrOutputManagerV1, ()> for State<S> {
fn event(
state: &mut Self,
_proxy: &ZwlrOutputManagerV1,
event: <ZwlrOutputManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
WlrOutputManagerEvent::Head { head } => {
let _head: ZwlrOutputHeadV1 = head;
tracing::debug!("wlr output head advertised");
}
WlrOutputManagerEvent::Done { .. } => {
if let EncConstructionStage::ProbingOutputs {
wlr_manager_done,
outputs,
..
} = &mut state.stage
{
*wlr_manager_done = true;
let count = outputs.len();
for idx in 0..count {
state.try_finalize_output(idx);
}
}
}
WlrOutputManagerEvent::Finished => {
tracing::warn!("zwlr_output_manager_v1::Finished received during probing");
}
_ => {}
}
}
event_created_child!(State<S>, ZwlrOutputManagerV1, [
zwlr_output_manager_v1::EVT_HEAD_OPCODE => (ZwlrOutputHeadV1, ()),
]);
}
// ---------------------------------------------------------------------------
// Dispatch<ZwlrOutputHeadV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwlrOutputHeadV1, ()> for State<S> {
fn event(
state: &mut Self,
proxy: &ZwlrOutputHeadV1,
event: <ZwlrOutputHeadV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
match event {
WlrHeadEvent::Name { name } => {
if let EncConstructionStage::ProbingOutputs {
wlr_heads,
wlr_head_proxy_to_name,
..
} = &mut state.stage
{
wlr_heads.entry(name.clone()).or_insert(WlrHeadInfo {});
wlr_head_proxy_to_name.insert(proxy.id(), name);
}
}
WlrHeadEvent::Position { .. } => {}
WlrHeadEvent::Finished => {
tracing::debug!("zwlr_output_head_v1::Finished received");
}
_ => {}
}
}
event_created_child!(State<S>, ZwlrOutputHeadV1, [
zwlr_output_head_v1::EVT_MODE_OPCODE => (ZwlrOutputModeV1, ()),
]);
}
// ---------------------------------------------------------------------------
// Dispatch<ZwlrOutputModeV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwlrOutputModeV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwlrOutputModeV1,
_event: <ZwlrOutputModeV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
// ---------------------------------------------------------------------------
// Dispatch<ZwlrScreencopyManagerV1, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<ZwlrScreencopyManagerV1, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &ZwlrScreencopyManagerV1,
_event: <ZwlrScreencopyManagerV1 as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
}
}
// ---------------------------------------------------------------------------
// Dispatch<WlBuffer, ()>
// ---------------------------------------------------------------------------
impl<S: CaptureSource> Dispatch<WlBuffer, ()> for State<S> {
fn event(
_state: &mut Self,
_proxy: &WlBuffer,
event: <WlBuffer as Proxy>::Event,
_data: &(),
_conn: &wayland_client::Connection,
_qhandle: &QueueHandle<State<S>>,
) {
if let wayland_client::protocol::wl_buffer::Event::Release = event {
tracing::trace!("WlBuffer released");
}
}
}