ltk: opt-in raw touch stream for apps that consume input themselves
CI / build + test (push) Has been cancelled
CI / cargo audit (push) Has been cancelled

Add App::claims_raw_touch(), an opt-in hook that routes the primary finger through the raw on_touch_down/move/up callbacks instead of the built-in single-slot gesture machine. Until now the primary finger was consumed entirely by the widget gesture machine (taps, swipes, presses) and never reached the app, which made it impossible to drive a self-contained input consumer like an embedded WebView or a game canvas from a touchscreen: the widget tree saw the taps, the embedded content never did. With the hook active every finger surfaces verbatim through the raw stream, widget gestures never arm, and per-finger positions are cached in SurfaceState::touch_slots for all slots so wl_touch.up (which carries no coordinates) can still report a release point.
The touch module doc and the on_touch_down docs are updated to describe the new contract; the default remains unchanged for every existing app.
Pin the transitive dependency ignore to 0.4.23: rust-i18n pulls it via globwalk, 0.4.24+ requires a rustc newer than Debian's 1.85, and its manifest does not declare that MSRV, so a fresh resolution breaks the build on stable.
This commit is contained in:
2026-07-21 09:56:05 +02:00
parent 8762ab9ce0
commit 3d7f3c53de
3 changed files with 55 additions and 1 deletions
+40 -1
View File
@@ -26,6 +26,13 @@
//! per touch id, so an auxiliary slot that landed on an overlay
//! keeps reporting through that overlay's `App` callback even if
//! the finger drifts over the main surface.
//!
//! Apps that return `true` from [`App::claims_raw_touch`] opt out of
//! the primary-slot gesture machine entirely: every finger, primary
//! included, surfaces through the raw `on_touch_*` stream, and
//! widget presses, taps and swipes never fire. Positions are cached
//! in `SurfaceState::touch_slots` for all fingers in that mode so
//! `up` can report a release point.
use smithay_client_toolkit::seat::touch::TouchHandler;
use smithay_client_toolkit::reexports::client::
@@ -58,6 +65,16 @@ impl<A: App> TouchHandler for AppData<A>
let pos = self.surface( focus ).to_physical( position.0, position.1 );
self.pointer_pos = pos;
// Raw-touch apps take every finger verbatim; the gesture
// machine never arms. Slots are tracked so `up` (which carries
// no position in wl_touch) can report the release point.
if self.app.claims_raw_touch()
{
self.surface_mut( focus ).touch_slots.insert( id, pos );
self.app.on_touch_down( id as i64, pos.x, pos.y );
return;
}
// Compositor re-grab after the origin surface died mid-drag: this
// slot is already our primary and the drag state was migrated here,
// so treat the redundant `down` as a continuation, not a restart.
@@ -178,6 +195,16 @@ impl<A: App> TouchHandler for AppData<A>
)
{
let focus = self.touch_focus.remove( &id ).unwrap_or( SurfaceFocus::Main );
if self.app.claims_raw_touch()
{
let pos =
{
let ss = self.surface_mut( focus );
ss.touch_slots.remove( &id ).unwrap_or( self.pointer_pos )
};
self.app.on_touch_up( id as i64, pos.x, pos.y );
return;
}
// Auxiliary release: not the primary slot → recover the last
// recorded position for the up callback (Wayland's `wl_touch.up`
// does not carry one) and bail before reaching the gesture
@@ -227,6 +254,17 @@ impl<A: App> TouchHandler for AppData<A>
let focus = *self.touch_focus.get( &id ).unwrap_or( &SurfaceFocus::Main );
let pp = self.surface( focus ).to_physical( position.0, position.1 );
if self.app.claims_raw_touch()
{
self.pointer_pos = pp;
{
let ss = self.surface_mut( focus );
ss.touch_slots.insert( id, pp );
}
self.app.on_touch_move( id as i64, pp.x, pp.y );
return;
}
let is_primary =
{
let ss = self.surface( focus );
@@ -296,7 +334,8 @@ impl<A: App> TouchHandler for AppData<A>
impl<A: App> AppData<A>
{
/// Drop every in-flight touch gesture across all surfaces, notifying
/// the app of any auxiliary slots that were still down. Shared by the
/// the app of any slot still down that it was tracking raw (auxiliary
/// fingers, or every finger under `claims_raw_touch`). Shared by the
/// `wl_touch.cancel` handler and the touch-capability add / remove path
/// (suspend / resume on devices that power the touchscreen down): a
/// yanked capability never delivers the pending `up` / `cancel`, so