event_loop: cover every output with a session-lock surface, with the app's backdrop on the secondary ones
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

ext-session-lock-v1 obliges the compositor to blank any output that has no lock surface, and the runtime only ever created one, on the first output — so on a multi-monitor session the extended screens went black the moment the lock engaged. The lock grant now creates one lock surface per advertised output: the first keeps hosting the app's main surface exactly as before, and each remaining output gets its own surface tracked in `lock_extras`, paired with its `WlOutput` so configure, scale and teardown can be routed per surface.
What those extra surfaces show is up to the app: a new `App::lock_secondary_view( width, height )` returns the content for a secondary output of that physical size, and the default `None` paints the surface with `background_color()`. The draw pass renders them without requesting frame callbacks — their content is static between invalidations, and a callback that is never re-requested would leave `frame_pending` stuck — and invalidations that touch the main surface also mark the extras, so a backdrop that follows main-surface state (wallpaper swaps, theme changes) stays in sync.
The per-surface plumbing that only ever knew about the main surface learns to route: `SessionLockHandler::configure` matches the surface against main and the extras instead of assuming main (an extra's configure used to resize the main surface's target), and the scale-change resize moves into an `apply_surface_scale` helper shared by main, overlays and the lock extras, so a HiDPI secondary gets a correctly scaled buffer instead of staying at factor 1. `new_output` creates a lock surface for an output hotplugged while locked — the compositor would blank it otherwise — and `output_destroyed` drops the matching extra.
Input on the extras is deliberately inert, with one asymmetry: pointer and touch events landing on them are discarded rather than falling back to `Main`, because their coordinates would hit-test the main surface's widgets and a click on a secondary screen could press whatever sits at those coordinates on the form — the power button included. Keyboard keeps its existing fall-through to `Main`, so typing works wherever the compositor decides to put the lock focus.
This commit is contained in:
2026-08-14 13:01:15 +02:00
parent adbbc0248b
commit 99a4767815
8 changed files with 188 additions and 34 deletions
+13
View File
@@ -794,6 +794,19 @@ pub trait App: 'static
/// returning `true` from [`requested_exit`](Self::requested_exit).
fn shell_mode( &self ) -> ShellMode { ShellMode::Window }
/// Content for the lock surfaces the runtime creates on every output
/// beyond the first while in [`ShellMode::SessionLock`]. The compositor
/// blanks any output without a lock surface, so the runtime covers each
/// one; this view is what gets painted there (typically the same
/// wallpaper as the main surface, without the interactive form).
/// `width` / `height` are that output's surface size in physical pixels.
/// Default `None`: the surface is filled with
/// [`background_color`](Self::background_color).
fn lock_secondary_view( &self, _width: u32, _height: u32 ) -> Option<Element<Self::Message>>
{
None
}
/// Return `true` to tear the surface down and exit the event loop. For a
/// [`ShellMode::SessionLock`] surface the runtime calls `unlock` first, so
/// the compositor lifts the lock instead of leaving the outputs blanked.