chore: design cleanup, dead-code purge, README/doc refresh

Audit-driven follow-up after the SAFETY-debt commit (Oracle steps 6-7).
End state: cargo clippy --release --all-targets still 0 errors; private_interfaces
and type_complexity warnings cleared.

Design cleanups (Oracle step 6):
  - cap_portal.rs: introduce PortalFormatInfo struct to replace the
    Rc<Cell<Option<(u32,u32,u32,u64)>>> cross-callback hand-off. Self-
    documenting struct fields replace positional tuple access at the
    format-change and process callbacks.
  - avhw.rs: import_dma_buf_to_vaapi signature collapses from 8 args
    (fd/width/height/drm_format/modifier/stride/offset) to
    (*mut AVBufferRef, &PwDmaBufFrame). Callers in avhw.rs,
    state_portal.rs, and vaapi_import_bench.rs now pass the frame by
    reference instead of unpacking 7 fields just to repack them. Drops
    the unused width parameter and the too_many_arguments(8/7) warning.
  - state.rs: visibility hygiene. EncConstructionStage and WlrHeadInfo
    downgrade pub -> pub(crate); State.stage field downgrades to
    pub(crate). These are internal state-machine types not exposed
    across the crate boundary; making them pub(crate) clears all
    private_interfaces warnings without leaking more types.

Dead-code purge (Oracle step 7):
  - transform.rs: remove unused Rect struct, transform_basis,
    screen_to_frame, fit_inside_bounds helpers and their 18 dedicated
    tests. Transform enum and transpose_if_transform_transposed remain
    (both are actively used by state.rs and avhw.rs). File shrinks
    from 409 -> 109 lines.

Repository housekeeping (Oracle step 7):
  - .gitignore: add review.json (stray review-tool output that
    regenerates per run).
  - README.md: refresh CLI table to match src/args.rs (now lists
    --backend, --no-persist, --port-as-WebRTC-signaling, --max-bitrate,
    --stats). Add capture-backend explainer + 4 new usage examples.
    Note in README points readers at src/args.rs as the authoritative
    source. Remove stale 'WebTransport, unused in MVP' description.

avhw.rs: AsRawFd import annotated with a rustc-quirk explanation — the
import triggers a false 'unused_imports' warning but E0599 if removed.
Left as-is with explanatory comment rather than chasing the lint.

All 79 remaining unit tests + 3 integration tests still pass. Cargo
build --release clean.
This commit is contained in:
dailz
2026-06-28 13:58:52 +08:00
parent 30f8fe51f2
commit 145b5d3e7e
8 changed files with 118 additions and 397 deletions
+8 -310
View File
@@ -1,8 +1,12 @@
/// Coordinate transformation module for Wayland output transforms.
///
/// Handles the 8 `wl_output` transform variants (rotation + reflection)
/// and ROI clipping for screen capture.
///
//
// Historically exposed a family of `Rect`/`screen_to_frame`/`fit_inside_bounds`
// helpers for ROI-based capture clipping. Those were never wired into the
// capture pipeline (we capture full frames and let FFmpeg's filter graph handle
// any scaling/rotation); they have been removed. Only `Transform` and the
// `transpose_if_transform_transposed` helper remain — both are actively used by
// `state.rs` and `avhw.rs`.
/// Wayland output transform enum, matching `wl_output::Transform`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Transform {
@@ -16,68 +20,6 @@ pub enum Transform {
Flipped270,
}
/// Axis-aligned rectangle in integer coordinates.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
/// Returns the 2×2 basis matrix (a, b, c, d) for the given transform.
///
/// The matrix represents the affine mapping from screen coordinates to
/// frame coordinates:
///
/// ```text
/// [new_x] [a b] [x]
/// [new_y] = [c d] [y]
/// ```
pub fn transform_basis(transform: Transform) -> (i32, i32, i32, i32) {
match transform {
Transform::Normal => (1, 0, 0, 1),
Transform::Normal90 => (0, 1, -1, 0),
Transform::Normal180 => (-1, 0, 0, -1),
Transform::Normal270 => (0, -1, 1, 0),
Transform::Flipped => (-1, 0, 0, 1),
Transform::Flipped90 => (0, 1, 1, 0),
Transform::Flipped180 => (1, 0, 0, -1),
Transform::Flipped270 => (0, -1, -1, 0),
}
}
/// Transform a rectangle from screen space to frame space.
///
/// Applies the 2×2 basis matrix and computes offsets so the result
/// fits within the frame dimensions `(frame_w, frame_h)`.
///
/// ```text
/// new_x = a * x + b * y + offset_x
/// new_y = c * x + d * y + offset_y
/// ```
pub fn screen_to_frame(transform: Transform, rect: Rect, frame_w: i32, frame_h: i32) -> Rect {
let (a, b, c, d) = transform_basis(transform);
// Compute the offset so that the transformed origin maps correctly.
// For transforms with negative components, we need to shift by the
// frame dimension to keep coordinates in [0, frame_w) × [0, frame_h).
let offset_x = if a + b < 0 { frame_w } else { 0 };
let offset_y = if c + d < 0 { frame_h } else { 0 };
let new_x = a * rect.x + b * rect.y + offset_x;
let new_y = c * rect.x + d * rect.y + offset_y;
let new_w = a * rect.w + b * rect.h;
let new_h = c * rect.w + d * rect.h;
Rect {
x: new_x,
y: new_y,
w: new_w.abs(),
h: new_h.abs(),
}
}
/// Swap width and height for 90° or 270° rotations.
///
/// After a quarter-turn rotation the output dimensions are transposed
@@ -93,140 +35,10 @@ pub fn transpose_if_transform_transposed(transform: Transform, w: i32, h: i32) -
}
}
/// Clip a rectangle so it stays inside `(0, 0) .. (bounds_w, bounds_h)`.
///
/// The resulting rectangle has non-negative origin and its extent does
/// not exceed the bounds.
pub fn fit_inside_bounds(rect: Rect, bounds_w: i32, bounds_h: i32) -> Rect {
let x = rect.x.clamp(0, bounds_w);
let y = rect.y.clamp(0, bounds_h);
let right = (rect.x + rect.w).min(bounds_w);
let bottom = (rect.y + rect.h).min(bounds_h);
let w = (right - x).max(0);
let h = (bottom - y).max(0);
Rect { x, y, w, h }
}
#[cfg(test)]
mod tests {
use super::*;
// ── transform_basis ───────────────────────────────────────────
#[test]
fn basis_normal_is_identity() {
assert_eq!(transform_basis(Transform::Normal), (1, 0, 0, 1));
}
#[test]
fn basis_90_cw_rotation() {
assert_eq!(transform_basis(Transform::Normal90), (0, 1, -1, 0));
}
#[test]
fn basis_180_rotation() {
assert_eq!(transform_basis(Transform::Normal180), (-1, 0, 0, -1));
}
#[test]
fn basis_270_cw_rotation() {
assert_eq!(transform_basis(Transform::Normal270), (0, -1, 1, 0));
}
#[test]
fn basis_flipped_horizontal() {
assert_eq!(transform_basis(Transform::Flipped), (-1, 0, 0, 1));
}
#[test]
fn basis_flipped_90() {
assert_eq!(transform_basis(Transform::Flipped90), (0, 1, 1, 0));
}
#[test]
fn basis_flipped_180() {
assert_eq!(transform_basis(Transform::Flipped180), (1, 0, 0, -1));
}
#[test]
fn basis_flipped_270() {
assert_eq!(transform_basis(Transform::Flipped270), (0, -1, -1, 0));
}
// ── screen_to_frame ───────────────────────────────────────────
#[test]
fn screen_to_frame_identity_unchanged() {
let rect = Rect {
x: 10,
y: 20,
w: 100,
h: 50,
};
let result = screen_to_frame(Transform::Normal, rect, 1920, 1080);
assert_eq!(
result,
Rect {
x: 10,
y: 20,
w: 100,
h: 50
}
);
}
#[test]
fn screen_to_frame_90_rotates_origin() {
// 90° CW: top-left (0,0) in screen should map to bottom-left in frame
let rect = Rect {
x: 0,
y: 0,
w: 100,
h: 50,
};
let result = screen_to_frame(Transform::Normal90, rect, 1080, 1920);
// a=0,b=1,c=-1,d=0 => offset_x=0, offset_y=1920 (c+d=-1<0)
// new_x = 0*0 + 1*0 + 0 = 0
// new_y = -1*0 + 0*0 + 1920 = 1920
assert_eq!(result.x, 0);
assert_eq!(result.y, 1920);
// w' = 0*100 + 1*50 = 50, h' = -1*100 + 0*50 = -100 -> abs=100
assert_eq!(result.w, 50);
assert_eq!(result.h, 100);
}
#[test]
fn screen_to_frame_180_rotates() {
let rect = Rect {
x: 100,
y: 200,
w: 300,
h: 400,
};
let result = screen_to_frame(Transform::Normal180, rect, 1920, 1080);
// a=-1,b=0,c=0,d=-1, offset_x=1920, offset_y=1080
assert_eq!(result.x, -100 + 1920);
assert_eq!(result.y, -200 + 1080);
assert_eq!(result.w, 300);
assert_eq!(result.h, 400);
}
#[test]
fn screen_to_frame_flipped_horizontal() {
let rect = Rect {
x: 50,
y: 30,
w: 200,
h: 100,
};
let result = screen_to_frame(Transform::Flipped, rect, 1920, 1080);
// a=-1,b=0,c=0,d=1, offset_x=1920, offset_y=0
assert_eq!(result.x, -50 + 1920);
assert_eq!(result.y, 30);
assert_eq!(result.w, 200);
assert_eq!(result.h, 100);
}
// ── transpose_if_transform_transposed ─────────────────────────
#[test]
@@ -292,118 +104,4 @@ mod tests {
(1080, 1920)
);
}
// ── fit_inside_bounds ─────────────────────────────────────────
#[test]
fn fit_inside_already_fits() {
let rect = Rect {
x: 10,
y: 20,
w: 100,
h: 50,
};
let result = fit_inside_bounds(rect, 1920, 1080);
assert_eq!(result, rect);
}
#[test]
fn fit_inside_clips_right_and_bottom() {
let rect = Rect {
x: 1800,
y: 1000,
w: 200,
h: 200,
};
let result = fit_inside_bounds(rect, 1920, 1080);
assert_eq!(
result,
Rect {
x: 1800,
y: 1000,
w: 120,
h: 80
}
);
}
#[test]
fn fit_inside_clips_negative_origin() {
let rect = Rect {
x: -50,
y: -30,
w: 200,
h: 200,
};
let result = fit_inside_bounds(rect, 1920, 1080);
assert_eq!(
result,
Rect {
x: 0,
y: 0,
w: 150,
h: 170
}
);
}
#[test]
fn fit_inside_completely_out_of_bounds() {
let rect = Rect {
x: 2000,
y: 2000,
w: 100,
h: 100,
};
let result = fit_inside_bounds(rect, 1920, 1080);
assert_eq!(
result,
Rect {
x: 1920,
y: 1080,
w: 0,
h: 0
}
);
}
#[test]
fn fit_inside_zero_size_rect() {
let rect = Rect {
x: 100,
y: 100,
w: 0,
h: 0,
};
let result = fit_inside_bounds(rect, 1920, 1080);
assert_eq!(
result,
Rect {
x: 100,
y: 100,
w: 0,
h: 0
}
);
}
#[test]
fn fit_inside_zero_bounds() {
let rect = Rect {
x: 0,
y: 0,
w: 100,
h: 100,
};
let result = fit_inside_bounds(rect, 0, 0);
assert_eq!(
result,
Rect {
x: 0,
y: 0,
w: 0,
h: 0
}
);
}
}