event_loop: cover every output with a session-lock surface, with the app's backdrop on the secondary ones
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:
@@ -17,6 +17,7 @@ use smithay_client_toolkit::reexports::client::
|
||||
protocol::
|
||||
{
|
||||
wl_keyboard::WlKeyboard,
|
||||
wl_output::WlOutput,
|
||||
wl_pointer::WlPointer,
|
||||
wl_surface::WlSurface,
|
||||
wl_touch::WlTouch,
|
||||
@@ -218,6 +219,15 @@ pub struct AppData<A: App>
|
||||
/// Populated and reconciled by the run loop from [`App::overlays`]. Always
|
||||
/// empty until overlay diffing is wired up.
|
||||
pub overlays: HashMap<OverlayId, SurfaceState<A::Message>>,
|
||||
/// Session-lock surfaces covering every output beyond the first, paired
|
||||
/// with the output each one belongs to. The compositor blanks any output
|
||||
/// without a lock surface, so one is created per output; these render
|
||||
/// [`App::lock_secondary_view`] and take no input (the input handlers
|
||||
/// find no focus for them, and keyboard falls through to `Main`).
|
||||
pub lock_extras: Vec<( WlOutput, SurfaceState<A::Message> )>,
|
||||
/// Output the main lock surface was created on, so a later `new_output`
|
||||
/// for the same output does not add a duplicate lock surface.
|
||||
pub main_lock_output: Option<WlOutput>,
|
||||
/// Input-transparent child surfaces keyed by their stable
|
||||
/// [`SubsurfaceId`]. Reconciled each frame from [`App::subsurfaces`].
|
||||
pub subsurfaces: HashMap<SubsurfaceId, SubsurfaceSlot>,
|
||||
@@ -285,6 +295,16 @@ impl<A: App> AppData<A>
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether `wl` is one of the secondary session-lock surfaces. Pointer and
|
||||
/// touch events landing on them must be dropped rather than falling back
|
||||
/// to `Main` — their coordinates would hit-test the main surface's
|
||||
/// widgets. Keyboard keeps the `Main` fallback so typing works wherever
|
||||
/// the compositor puts the lock focus.
|
||||
pub( crate ) fn is_lock_extra_surface( &self, wl: &WlSurface ) -> bool
|
||||
{
|
||||
self.lock_extras.iter().any( |( _, ss )| ss.surface.try_wl_surface() == Some( wl ) )
|
||||
}
|
||||
|
||||
/// Borrow the [`SurfaceState`] identified by `focus`. Panics if `focus`
|
||||
/// refers to an overlay that is not currently registered — callers must
|
||||
/// only pass focus values obtained from [`focus_for_surface`] or from the
|
||||
|
||||
Reference in New Issue
Block a user