App: claim raw touch per press with claims_raw_touch_at, and toggle fullscreen at runtime with fullscreen
CI / build + test (push) Canceled after 0s
CI / cargo audit (push) Canceled after 0s

Two additions to the `App` trait, both needed by an application that embeds a self-contained input consumer next to regular widgets. The case at hand is trust, the Eydos browser: an ltk-webkit `WebView` below a toolbar built from stock widgets, with an F11 fullscreen toggle.
Raw touch per press. `App::claims_raw_touch` is all or nothing: once it returns `true` every finger, the primary included, bypasses the gesture machine, so widget presses, taps and swipes stop working on touch. That suits an app whose whole surface is one WebView (the eydos-settings portal), but not one that shows the view under its own chrome: claiming raw touch made the address box and the toolbar buttons dead to the finger, and not claiming it fed the page a mouse-like primary slot, so a drag selected text instead of scrolling and pinch-zoom was impossible. The new `App::claims_raw_touch_at( x, y )` is consulted on `wl_touch.down` with the press position in physical pixels of the main surface; a finger landing where it returns `true` goes through the raw `on_touch_down` / `on_touch_move` / `on_touch_up` stream while fingers landing elsewhere keep driving the gesture machine. The default is `false`, so existing apps are unaffected, and it is only consulted for the main surface: overlays have their own coordinate space and the hook carries no surface argument.
The implementation reuses the auxiliary-slot path instead of adding a second notion of raw ownership. In `TouchHandler::down` the position is now resolved before the takeover check; a claimed finger is recorded in `touch_slots` without becoming `primary_touch_id`, which is precisely the state of a second finger today, so `motion`, `up` and `reset_touch_state` already route it to the app and report its release point with no change. A claimed press skips the plain-press takeover (it must not commit a tap in progress on the chrome) and calls `set_focus( focus, None, qh )`, mirroring what a pointer press outside any widget does: without it a focused text field would keep the text-input protocol, the on-screen keyboard and the key stream after the user had moved on to the page. `pointer_pos` is still assigned after the takeover, since `release_primary` uses it as the release point of the finger being committed.
Runtime fullscreen. `App::start_fullscreen` only covers the first commit; there was no way to enter or leave fullscreen afterwards. `App::fullscreen() -> Option<bool>` declares the desired state and is polled after every batch of `update`s, next to `requested_exit`. `AppData::sync_fullscreen` compares it with `applied_fullscreen`, the last value sent, and calls `xdg_toplevel.set_fullscreen( None )` or `unset_fullscreen` only on a change; `None`, the default, leaves the window state alone. `applied_fullscreen` is seeded with `start_fullscreen()`, so an app that starts fullscreen and reports `Some( true )` does not send a redundant request, and one that reports `Some( false )` is not pushed out of a fullscreen it asked for a moment earlier. It is a no-op for layer-shell and session-lock surfaces, which have no toplevel.
The `configure` handler now also zeroes `titlebar_height` while `WindowConfigure::is_fullscreen()` holds, as it already did for server-side decorations. The built-in titlebar was otherwise painted over a fullscreen surface, which is what the `start_fullscreen` doc comment described as happening "unless the app opts out" without there being a way to opt out. The height follows the compositor's reported state rather than the app's request, so it stays correct when the compositor refuses the request or leaves fullscreen on its own.
Both hooks are documented on the trait and listed in the changelog under Unreleased.
This commit is contained in:
2026-09-20 08:22:46 +02:00
parent eaf88754f3
commit 2252a63f16
6 changed files with 56 additions and 3 deletions
+17
View File
@@ -215,6 +215,8 @@ pub struct AppData<A: App>
pub exit_requested: bool,
/// First-configure latch for `App::start_fullscreen`.
pub pending_fullscreen: bool,
/// Last value of `App::fullscreen` sent to the compositor.
pub applied_fullscreen: Option<bool>,
/// First-configure latch: clears `max_size` after the compositor
/// has honoured the pinned initial size.
pub pending_size_hint_unpin: bool,
@@ -283,6 +285,21 @@ pub struct AppData<A: App>
impl<A: App> AppData<A>
{
/// Forward a change of [`App::fullscreen`] to the main toplevel.
pub( crate ) fn sync_fullscreen( &mut self )
{
let Some( wanted ) = self.app.fullscreen() else { return };
if self.applied_fullscreen == Some( wanted ) { return; }
let super::SurfaceKind::Window( ref window ) = self.main.surface else { return };
if wanted
{
window.set_fullscreen( None );
} else {
window.unset_fullscreen();
}
self.applied_fullscreen = Some( wanted );
}
/// Find which [`SurfaceFocus`] owns the given `WlSurface`, or `None` if it
/// does not correspond to any tracked surface. Safe even when the main
/// surface is still `Pending` (returns `None`).