ltk: ext-session-lock-v1 client surface mode, plus a read-only mode for text fields
Add a third Wayland surface type to the runtime so an ltk `App` can be a screen locker, alongside the existing xdg-shell window and wlr-layer-shell surfaces. A new `ShellMode::SessionLock` makes `run()` bind `ext_session_lock_manager_v1` and request the lock at startup; the lock surface itself is created in the new `SessionLockHandler::locked` callback (one surface on the first advertised output) and replaces the `SurfaceKind::PendingLock` placeholder the main surface holds until the compositor grants the lock. The `configure` event routes through the same `on_configure` path as layer and xdg surfaces, so sizing and rendering are unchanged, and `finished` (the compositor denied or ended the lock) tears the loop down. The whole thing is additive and opt-in: the `Window` and `Layer` paths are untouched and nothing enters lock mode unless an `App` returns `ShellMode::SessionLock`, so existing apps are unaffected — the only non-additive edits are the two exhaustive `match`es on `SurfaceKind` (`wl_surface` / `try_wl_surface`), which gain arms for the two new variants. Doing the locker as a first-class surface rather than compositing a static texture into an offscreen `UiSurface` is the whole point: the compositor gives the lock surface keyboard focus, so ltk's existing text-input, editing, focus and IME machinery works inside the lock exactly as on any other surface — cursor, click-to-focus, Tab, character input. A locker built on top of this is just a normal interactive ltk app that happens to be presented on the lock layer, with no special input plumbing on the compositor or the app side. `App::requested_exit()` is the new way an app asks the runtime to tear the surface down and leave the loop; it is polled after every batch of `update`s. It exists because of the one hard invariant of `ext-session-lock-v1`: a locker that disconnects without sending `unlock` leaves the compositor's outputs blanked forever — that is the protocol's deliberate anti-bypass guarantee. So when `requested_exit()` returns true and the surface is a session lock, the loop calls `session_lock.unlock()` and round-trips the connection before setting `exit_requested`, lifting the lock cleanly; for a `Window` or `Layer` surface there is no lock and it simply exits. The consequence for lock apps is that they must stop calling `process::exit` from the lock path and instead flip a flag they return from `requested_exit()`. `text_edit` gains a `read_only( bool )` builder. A read-only field still renders its box and value in the normal field style but takes no keyboard focus and accepts no input: `Element::is_focusable` and `Element::is_text_input` now return false for a read-only `TextEdit`, which keeps it out of the Tab cycle, off the keyboard-edit path, and stops the cursor from ever being drawn on it. The flag is carried through `map_msg` so it survives `Element::map`. This is for presenting a known, non-editable value in the same visual idiom as the editable fields beside it — for example the already-known user shown on a session lock, where letting that field take focus or blink a cursor would be wrong. The `shell_mode()` doc comment and the README now list the `SessionLock` surface type and point at `requested_exit()` for the unlock. Two warnings are cleared along the way: the runtime no longer stores the `SessionLockState` after requesting the lock — it has no `Drop`, so the manager object outlives the dropped handle inside the connection and the lock lifecycle runs entirely off the returned `SessionLock`, which removes a never-read field — and a pre-existing rustdoc `private_intra_doc_links` warning in `list_item` (a public doc comment linking to the private `theme::ICON_SIZE`) is downgraded to plain code formatting.
This commit is contained in:
14
src/app.rs
14
src/app.rs
@@ -33,6 +33,11 @@ pub enum ShellMode
|
||||
/// Layer-shell surface at the specified layer.
|
||||
/// Used for shell components like panels, backgrounds, overlays.
|
||||
Layer( Layer ),
|
||||
|
||||
/// `ext-session-lock-v1` lock surface. The compositor blanks the outputs
|
||||
/// and shows only this surface until the app requests exit (see
|
||||
/// [`App::requested_exit`]). Used by the screen locker.
|
||||
SessionLock,
|
||||
}
|
||||
|
||||
/// Layer-shell layer position.
|
||||
@@ -641,11 +646,20 @@ pub trait App: 'static
|
||||
///
|
||||
/// - [`ShellMode::Window`]: Normal application window (xdg-shell). **Default.**
|
||||
/// - [`ShellMode::Layer`]: System component at a specific layer (layer-shell).
|
||||
/// - [`ShellMode::SessionLock`]: `ext-session-lock-v1` lock surface (screen locker).
|
||||
///
|
||||
/// For regular applications, use the default `Window` mode.
|
||||
/// For shell components (panels, backgrounds, overlays), use `Layer`.
|
||||
/// For a screen locker, use `SessionLock` and request the unlock by
|
||||
/// returning `true` from [`requested_exit`](Self::requested_exit).
|
||||
fn shell_mode( &self ) -> ShellMode { ShellMode::Window }
|
||||
|
||||
/// 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.
|
||||
/// Polled after every batch of `update`s.
|
||||
fn requested_exit( &self ) -> bool { false }
|
||||
|
||||
/// Suggest an initial size for an xdg-shell window in logical pixels.
|
||||
///
|
||||
/// Returning `Some(( w, h ))` makes ltk call both
|
||||
|
||||
Reference in New Issue
Block a user