Cover every output from a background shell: the session-lock secondary machinery generalizes to layer surfaces
Some checks failed
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

The runtime already covered outputs beyond the first, but only for `ShellMode::SessionLock`: `locked` created one `ext-session-lock` surface per output, tracked in `lock_extras`, rendered from `App::lock_secondary_view`, excluded from pointer/touch routing and repainted through the shared invalidation path. A background shell had nothing equivalent, so on a multi-monitor session its wallpaper simply did not exist on the extended screens — the compositor had to fake one by cover-scaling the primary's background, wrong scale and wrong crop included.
The machinery is shared rather than duplicated. The state and the hook lose their lock-specific names — `lock_extras` becomes `secondaries`, `main_lock_output` becomes `main_output`, `App::lock_secondary_view` becomes `App::secondary_view` — and everything downstream of creation (the draw loop in frame.rs, invalidation, per-surface scale handling, the input exclusion in pointer/touch, redraw scheduling in run.rs) is the same single copy serving both kinds.
What is genuinely new is the second creation site: in `new_output`, when the app's shell mode is `Layer( Background )`, every output beyond the main surface's gets its own background layer surface — anchored to all four edges with size delegated to the compositor, exclusive zone 0, no keyboard interactivity — bound to that output at creation and torn down in `output_destroyed`. The main surface's output is now recorded when it materializes so the runtime knows which output not to cover.
Layer-shell events learn about secondaries too, and the `configure` branch is not optional: secondaries live outside the focus map, so their configure used to fall into the `None` arm and resize the main surface. `closed` on a secondary now just drops it instead of asking the app to exit.
`secondary_view` keeps its default of `None` (surface filled with `background_color`), so nothing changes for apps that do not opt in; the session-lock behaviour is unchanged aside from the renames.
This commit is contained in:
2026-08-14 23:20:58 +02:00
parent 99a4767815
commit 78053b8b01
8 changed files with 94 additions and 42 deletions

View File

@@ -320,8 +320,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
pending_size_hint_unpin,
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
overlays: std::collections::HashMap::new(),
lock_extras: Vec::new(),
main_lock_output: None,
secondaries: Vec::new(),
main_output: None,
subsurfaces: std::collections::HashMap::new(),
subsurface_gles_canvas: None,
pointer_focus: SurfaceFocus::Main,
@@ -600,7 +600,7 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
// surface qualifies we just loop back to `dispatch(None)` and sleep.
let any_drawable = ( data.main.configured && data.main.needs_redraw && !data.main.frame_pending )
|| data.overlays.values().any( |ss| ss.configured && ss.needs_redraw && !ss.frame_pending )
|| data.lock_extras.iter().any( |( _, ss )| ss.configured && ss.needs_redraw && !ss.frame_pending );
|| data.secondaries.iter().any( |( _, ss )| ss.configured && ss.needs_redraw && !ss.frame_pending );
if any_drawable
{
// Rebuild while motion is in progress and on the first frame