diff --git a/anvil/src/focus.rs b/anvil/src/focus.rs index 354bd419284e..8df759658384 100644 --- a/anvil/src/focus.rs +++ b/anvil/src/focus.rs @@ -1,7 +1,5 @@ -use smithay::input::pointer::{ - GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, - GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, -}; +#[cfg(feature = "xwayland")] +use smithay::xwayland::X11Surface; pub use smithay::{ backend::input::KeyState, desktop::{LayerSurface, PopupKind}, @@ -14,36 +12,69 @@ pub use smithay::{ utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, }; +use smithay::{ + desktop::Window, + input::{ + pointer::{ + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, + GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, + }, + touch::TouchTarget, + }, +}; use crate::{ - shell::WindowElement, + shell::{WindowElement, SSD}, state::{AnvilState, Backend}, }; #[derive(Debug, Clone, PartialEq)] -pub enum FocusTarget { - Window(WindowElement), +pub enum KeyboardFocusTarget { + Window(Window), + #[cfg(feature = "xwayland")] + X11Surface(X11Surface), LayerSurface(LayerSurface), Popup(PopupKind), } -impl IsAlive for FocusTarget { +impl IsAlive for KeyboardFocusTarget { fn alive(&self) -> bool { match self { - FocusTarget::Window(w) => w.alive(), - FocusTarget::LayerSurface(l) => l.alive(), - FocusTarget::Popup(p) => p.alive(), + KeyboardFocusTarget::Window(w) => w.alive(), + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(s) => s.alive(), + KeyboardFocusTarget::LayerSurface(l) => l.alive(), + KeyboardFocusTarget::Popup(p) => p.alive(), } } } -impl From for WlSurface { - fn from(target: FocusTarget) -> Self { +#[derive(Debug, Clone, PartialEq)] +pub enum PointerFocusTarget { + WlSurface(WlSurface), + #[cfg(feature = "xwayland")] + X11Surface(X11Surface), + SSD(SSD), +} + +impl IsAlive for PointerFocusTarget { + fn alive(&self) -> bool { + match self { + PointerFocusTarget::WlSurface(w) => w.alive(), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => w.alive(), + PointerFocusTarget::SSD(x) => x.alive(), + } + } +} + +impl From for WlSurface { + fn from(target: PointerFocusTarget) -> Self { target.wl_surface().unwrap() } } -impl PointerTarget> for FocusTarget { +impl PointerTarget> for PointerFocusTarget { fn enter( &self, seat: &Seat>, @@ -51,9 +82,10 @@ impl PointerTarget> for FocusTarge event: &MotionEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::enter(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::enter(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::enter(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::enter(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::enter(w, seat, data, event), } } fn motion( @@ -63,9 +95,10 @@ impl PointerTarget> for FocusTarge event: &MotionEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::motion(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::motion(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::motion(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::motion(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::motion(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::motion(w, seat, data, event), } } fn relative_motion( @@ -75,9 +108,10 @@ impl PointerTarget> for FocusTarge event: &RelativeMotionEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::relative_motion(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::relative_motion(l.wl_surface(), seat, data, event), - FocusTarget::Popup(p) => PointerTarget::relative_motion(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::relative_motion(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::relative_motion(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::relative_motion(w, seat, data, event), } } fn button( @@ -87,9 +121,10 @@ impl PointerTarget> for FocusTarge event: &ButtonEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::button(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::button(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::button(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::button(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::button(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::button(w, seat, data, event), } } fn axis( @@ -99,16 +134,18 @@ impl PointerTarget> for FocusTarge frame: AxisFrame, ) { match self { - FocusTarget::Window(w) => PointerTarget::axis(w, seat, data, frame), - FocusTarget::LayerSurface(l) => PointerTarget::axis(l, seat, data, frame), - FocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame), + PointerFocusTarget::WlSurface(w) => PointerTarget::axis(w, seat, data, frame), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::axis(w, seat, data, frame), + PointerFocusTarget::SSD(w) => PointerTarget::axis(w, seat, data, frame), } } fn frame(&self, seat: &Seat>, data: &mut AnvilState) { match self { - FocusTarget::Window(w) => PointerTarget::frame(w, seat, data), - FocusTarget::LayerSurface(l) => PointerTarget::frame(l, seat, data), - FocusTarget::Popup(p) => PointerTarget::frame(p.wl_surface(), seat, data), + PointerFocusTarget::WlSurface(w) => PointerTarget::frame(w, seat, data), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::frame(w, seat, data), + PointerFocusTarget::SSD(w) => PointerTarget::frame(w, seat, data), } } fn leave( @@ -119,9 +156,10 @@ impl PointerTarget> for FocusTarge time: u32, ) { match self { - FocusTarget::Window(w) => PointerTarget::leave(w, seat, data, serial, time), - FocusTarget::LayerSurface(l) => PointerTarget::leave(l, seat, data, serial, time), - FocusTarget::Popup(p) => PointerTarget::leave(p.wl_surface(), seat, data, serial, time), + PointerFocusTarget::WlSurface(w) => PointerTarget::leave(w, seat, data, serial, time), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::leave(w, seat, data, serial, time), + PointerFocusTarget::SSD(w) => PointerTarget::leave(w, seat, data, serial, time), } } fn gesture_swipe_begin( @@ -131,9 +169,10 @@ impl PointerTarget> for FocusTarge event: &GestureSwipeBeginEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_swipe_begin(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_swipe_begin(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), } } fn gesture_swipe_update( @@ -143,9 +182,10 @@ impl PointerTarget> for FocusTarge event: &GestureSwipeUpdateEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_swipe_update(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_swipe_update(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), } } fn gesture_swipe_end( @@ -155,9 +195,10 @@ impl PointerTarget> for FocusTarge event: &GestureSwipeEndEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_swipe_end(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_swipe_end(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), } } fn gesture_pinch_begin( @@ -167,9 +208,10 @@ impl PointerTarget> for FocusTarge event: &GesturePinchBeginEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_pinch_begin(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_pinch_begin(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), } } fn gesture_pinch_update( @@ -179,9 +221,10 @@ impl PointerTarget> for FocusTarge event: &GesturePinchUpdateEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_pinch_update(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_pinch_update(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), } } fn gesture_pinch_end( @@ -191,9 +234,10 @@ impl PointerTarget> for FocusTarge event: &GesturePinchEndEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_pinch_end(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_pinch_end(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), } } fn gesture_hold_begin( @@ -203,9 +247,10 @@ impl PointerTarget> for FocusTarge event: &GestureHoldBeginEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_hold_begin(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_hold_begin(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), } } fn gesture_hold_end( @@ -215,14 +260,15 @@ impl PointerTarget> for FocusTarge event: &GestureHoldEndEvent, ) { match self { - FocusTarget::Window(w) => PointerTarget::gesture_hold_end(w, seat, data, event), - FocusTarget::LayerSurface(l) => PointerTarget::gesture_hold_end(l, seat, data, event), - FocusTarget::Popup(p) => PointerTarget::gesture_hold_end(p.wl_surface(), seat, data, event), + PointerFocusTarget::WlSurface(w) => PointerTarget::gesture_hold_end(w, seat, data, event), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => PointerTarget::gesture_hold_end(w, seat, data, event), + PointerFocusTarget::SSD(w) => PointerTarget::gesture_hold_end(w, seat, data, event), } } } -impl KeyboardTarget> for FocusTarget { +impl KeyboardTarget> for KeyboardFocusTarget { fn enter( &self, seat: &Seat>, @@ -231,9 +277,15 @@ impl KeyboardTarget> for FocusTarg serial: Serial, ) { match self { - FocusTarget::Window(w) => KeyboardTarget::enter(w, seat, data, keys, serial), - FocusTarget::LayerSurface(l) => KeyboardTarget::enter(l, seat, data, keys, serial), - FocusTarget::Popup(p) => KeyboardTarget::enter(p.wl_surface(), seat, data, keys, serial), + KeyboardFocusTarget::Window(w) => { + KeyboardTarget::enter(w.toplevel().wl_surface(), seat, data, keys, serial) + } + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(w) => KeyboardTarget::enter(w, seat, data, keys, serial), + KeyboardFocusTarget::LayerSurface(l) => { + KeyboardTarget::enter(l.wl_surface(), seat, data, keys, serial) + } + KeyboardFocusTarget::Popup(p) => KeyboardTarget::enter(p.wl_surface(), seat, data, keys, serial), } } fn leave( @@ -243,9 +295,13 @@ impl KeyboardTarget> for FocusTarg serial: Serial, ) { match self { - FocusTarget::Window(w) => KeyboardTarget::leave(w, seat, data, serial), - FocusTarget::LayerSurface(l) => KeyboardTarget::leave(l, seat, data, serial), - FocusTarget::Popup(p) => KeyboardTarget::leave(p.wl_surface(), seat, data, serial), + KeyboardFocusTarget::Window(w) => { + KeyboardTarget::leave(w.toplevel().wl_surface(), seat, data, serial) + } + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(w) => KeyboardTarget::leave(w, seat, data, serial), + KeyboardFocusTarget::LayerSurface(l) => KeyboardTarget::leave(l.wl_surface(), seat, data, serial), + KeyboardFocusTarget::Popup(p) => KeyboardTarget::leave(p.wl_surface(), seat, data, serial), } } fn key( @@ -258,9 +314,17 @@ impl KeyboardTarget> for FocusTarg time: u32, ) { match self { - FocusTarget::Window(w) => KeyboardTarget::key(w, seat, data, key, state, serial, time), - FocusTarget::LayerSurface(l) => KeyboardTarget::key(l, seat, data, key, state, serial, time), - FocusTarget::Popup(p) => { + KeyboardFocusTarget::Window(w) => { + KeyboardTarget::key(w.toplevel().wl_surface(), seat, data, key, state, serial, time) + } + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(w) => { + KeyboardTarget::key(w, seat, data, key, state, serial, time) + } + KeyboardFocusTarget::LayerSurface(l) => { + KeyboardTarget::key(l.wl_surface(), seat, data, key, state, serial, time) + } + KeyboardFocusTarget::Popup(p) => { KeyboardTarget::key(p.wl_surface(), seat, data, key, state, serial, time) } } @@ -273,46 +337,212 @@ impl KeyboardTarget> for FocusTarg serial: Serial, ) { match self { - FocusTarget::Window(w) => KeyboardTarget::modifiers(w, seat, data, modifiers, serial), - FocusTarget::LayerSurface(l) => KeyboardTarget::modifiers(l, seat, data, modifiers, serial), - FocusTarget::Popup(p) => KeyboardTarget::modifiers(p.wl_surface(), seat, data, modifiers, serial), + KeyboardFocusTarget::Window(w) => { + KeyboardTarget::modifiers(w.toplevel().wl_surface(), seat, data, modifiers, serial) + } + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(w) => KeyboardTarget::modifiers(w, seat, data, modifiers, serial), + KeyboardFocusTarget::LayerSurface(l) => { + KeyboardTarget::modifiers(l.wl_surface(), seat, data, modifiers, serial) + } + KeyboardFocusTarget::Popup(p) => { + KeyboardTarget::modifiers(p.wl_surface(), seat, data, modifiers, serial) + } + } + } +} + +impl TouchTarget> for PointerFocusTarget { + fn down( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &smithay::input::touch::DownEvent, + seq: Serial, + ) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::down(w, seat, data, event, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::down(w, seat, data, event, seq), + PointerFocusTarget::SSD(w) => TouchTarget::down(w, seat, data, event, seq), + } + } + + fn up( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &smithay::input::touch::UpEvent, + seq: Serial, + ) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::up(w, seat, data, event, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::up(w, seat, data, event, seq), + PointerFocusTarget::SSD(w) => TouchTarget::up(w, seat, data, event, seq), + } + } + + fn motion( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &smithay::input::touch::MotionEvent, + seq: Serial, + ) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::motion(w, seat, data, event, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::motion(w, seat, data, event, seq), + PointerFocusTarget::SSD(w) => TouchTarget::motion(w, seat, data, event, seq), + } + } + + fn frame(&self, seat: &Seat>, data: &mut AnvilState, seq: Serial) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::frame(w, seat, data, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::frame(w, seat, data, seq), + PointerFocusTarget::SSD(w) => TouchTarget::frame(w, seat, data, seq), + } + } + + fn cancel(&self, seat: &Seat>, data: &mut AnvilState, seq: Serial) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::cancel(w, seat, data, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::cancel(w, seat, data, seq), + PointerFocusTarget::SSD(w) => TouchTarget::cancel(w, seat, data, seq), + } + } + + fn shape( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &smithay::input::touch::ShapeEvent, + seq: Serial, + ) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::shape(w, seat, data, event, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::shape(w, seat, data, event, seq), + PointerFocusTarget::SSD(w) => TouchTarget::shape(w, seat, data, event, seq), + } + } + + fn orientation( + &self, + seat: &Seat>, + data: &mut AnvilState, + event: &smithay::input::touch::OrientationEvent, + seq: Serial, + ) { + match self { + PointerFocusTarget::WlSurface(w) => TouchTarget::orientation(w, seat, data, event, seq), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => TouchTarget::orientation(w, seat, data, event, seq), + PointerFocusTarget::SSD(w) => TouchTarget::orientation(w, seat, data, event, seq), } } } -impl WaylandFocus for FocusTarget { +impl WaylandFocus for PointerFocusTarget { fn wl_surface(&self) -> Option { match self { - FocusTarget::Window(w) => w.wl_surface(), - FocusTarget::LayerSurface(l) => Some(l.wl_surface().clone()), - FocusTarget::Popup(p) => Some(p.wl_surface().clone()), + PointerFocusTarget::WlSurface(w) => w.wl_surface(), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => w.wl_surface(), + PointerFocusTarget::SSD(_) => None, } } fn same_client_as(&self, object_id: &ObjectId) -> bool { match self { - FocusTarget::Window(WindowElement::Wayland(w)) => w.same_client_as(object_id), + PointerFocusTarget::WlSurface(w) => w.same_client_as(object_id), + #[cfg(feature = "xwayland")] + PointerFocusTarget::X11Surface(w) => w.same_client_as(object_id), + PointerFocusTarget::SSD(w) => w + .wl_surface() + .map(|surface| surface.same_client_as(object_id)) + .unwrap_or(false), + } + } +} + +impl WaylandFocus for KeyboardFocusTarget { + fn wl_surface(&self) -> Option { + match self { + KeyboardFocusTarget::Window(w) => w.wl_surface(), #[cfg(feature = "xwayland")] - FocusTarget::Window(WindowElement::X11(w)) => w.same_client_as(object_id), - FocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), - FocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), + KeyboardFocusTarget::X11Surface(s) => s.wl_surface(), + KeyboardFocusTarget::LayerSurface(l) => Some(l.wl_surface().clone()), + KeyboardFocusTarget::Popup(p) => Some(p.wl_surface().clone()), } } } -impl From for FocusTarget { +impl From for PointerFocusTarget { + fn from(value: WlSurface) -> Self { + PointerFocusTarget::WlSurface(value) + } +} + +impl From<&WlSurface> for PointerFocusTarget { + fn from(value: &WlSurface) -> Self { + PointerFocusTarget::from(value.clone()) + } +} + +impl From for PointerFocusTarget { + fn from(value: PopupKind) -> Self { + PointerFocusTarget::from(value.wl_surface()) + } +} + +#[cfg(feature = "xwayland")] +impl From for PointerFocusTarget { + fn from(value: X11Surface) -> Self { + PointerFocusTarget::X11Surface(value) + } +} + +#[cfg(feature = "xwayland")] +impl From<&X11Surface> for PointerFocusTarget { + fn from(value: &X11Surface) -> Self { + PointerFocusTarget::from(value.clone()) + } +} + +impl From for KeyboardFocusTarget { fn from(w: WindowElement) -> Self { - FocusTarget::Window(w) + match w { + WindowElement::Wayland(w) => KeyboardFocusTarget::Window(w), + #[cfg(feature = "xwayland")] + WindowElement::X11(s) => KeyboardFocusTarget::X11Surface(s), + } } } -impl From for FocusTarget { +impl From for KeyboardFocusTarget { fn from(l: LayerSurface) -> Self { - FocusTarget::LayerSurface(l) + KeyboardFocusTarget::LayerSurface(l) } } -impl From for FocusTarget { +impl From for KeyboardFocusTarget { fn from(p: PopupKind) -> Self { - FocusTarget::Popup(p) + KeyboardFocusTarget::Popup(p) + } +} + +impl From for PointerFocusTarget { + fn from(value: KeyboardFocusTarget) -> Self { + match value { + KeyboardFocusTarget::Window(w) => PointerFocusTarget::from(w.toplevel().wl_surface()), + #[cfg(feature = "xwayland")] + KeyboardFocusTarget::X11Surface(s) => PointerFocusTarget::from(s), + KeyboardFocusTarget::LayerSurface(surface) => PointerFocusTarget::from(surface.wl_surface()), + KeyboardFocusTarget::Popup(popup) => PointerFocusTarget::from(popup.wl_surface()), + } } } diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index a43b94323633..080615100726 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -1,7 +1,7 @@ use std::{convert::TryInto, process::Command, sync::atomic::Ordering}; use crate::{ - focus::FocusTarget, + focus::PointerFocusTarget, shell::{FullscreenSurface, WindowElement}, AnvilState, }; @@ -52,14 +52,17 @@ use smithay::{ input::{ Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, PointerMotionEvent, ProximityState, TabletToolButtonEvent, - TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, + TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, }, session::Session, }, - input::pointer::{ - GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, - GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - RelativeMotionEvent, + input::{ + pointer::{ + GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, + GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, + RelativeMotionEvent, + }, + touch::{DownEvent, UpEvent}, }, wayland::{ pointer_constraints::{with_pointer_constraint, PointerConstraint}, @@ -240,7 +243,7 @@ impl AnvilState { let state = wl_pointer::ButtonState::from(evt.state()); if wl_pointer::ButtonState::Pressed == state { - self.update_keyboard_focus(serial); + self.update_keyboard_focus(self.pointer.current_location(), serial); }; let pointer = self.pointer.clone(); pointer.button( @@ -255,8 +258,9 @@ impl AnvilState { pointer.frame(self); } - fn update_keyboard_focus(&mut self, serial: Serial) { + fn update_keyboard_focus(&mut self, location: Point, serial: Serial) { let keyboard = self.seat.get_keyboard().unwrap(); + let touch = self.seat.get_touch(); let input_method = self.seat.input_method(); // change the keyboard focus unless the pointer or keyboard is grabbed // We test for any matching surface type here but always use the root @@ -267,12 +271,11 @@ impl AnvilState { // subsurface menus (for example firefox-wayland). // see here for a discussion about that issue: // https://gitlab.freedesktop.org/wayland/wayland/-/issues/294 - if !self.pointer.is_grabbed() && (!keyboard.is_grabbed() || input_method.keyboard_grabbed()) { - let output = self - .space - .output_under(self.pointer.current_location()) - .next() - .cloned(); + if !self.pointer.is_grabbed() + && (!keyboard.is_grabbed() || input_method.keyboard_grabbed()) + && !touch.map(|touch| touch.is_grabbed()).unwrap_or(false) + { + let output = self.space.output_under(location).next().cloned(); if let Some(output) = output.as_ref() { let output_geo = self.space.output_geometry(output).unwrap(); if let Some(window) = output @@ -280,10 +283,9 @@ impl AnvilState { .get::() .and_then(|f| f.get()) { - if let Some((_, _)) = window.surface_under( - self.pointer.current_location() - output_geo.loc.to_f64(), - WindowSurfaceType::ALL, - ) { + if let Some((_, _)) = + window.surface_under(location - output_geo.loc.to_f64(), WindowSurfaceType::ALL) + { #[cfg(feature = "xwayland")] if let WindowElement::X11(surf) = &window { self.xwm.as_mut().unwrap().raise_window(surf).unwrap(); @@ -295,12 +297,12 @@ impl AnvilState { let layers = layer_map_for_output(output); if let Some(layer) = layers - .layer_under(WlrLayer::Overlay, self.pointer.current_location()) - .or_else(|| layers.layer_under(WlrLayer::Top, self.pointer.current_location())) + .layer_under(WlrLayer::Overlay, location) + .or_else(|| layers.layer_under(WlrLayer::Top, location)) { if layer.can_receive_keyboard_focus() { if let Some((_, _)) = layer.surface_under( - self.pointer.current_location() + location - output_geo.loc.to_f64() - layers.layer_geometry(layer).unwrap().loc.to_f64(), WindowSurfaceType::ALL, @@ -312,17 +314,13 @@ impl AnvilState { } } - if let Some((window, _)) = self - .space - .element_under(self.pointer.current_location()) - .map(|(w, p)| (w.clone(), p)) - { + if let Some((window, _)) = self.space.element_under(location).map(|(w, p)| (w.clone(), p)) { self.space.raise_element(&window, true); - keyboard.set_focus(self, Some(window.clone().into()), serial); #[cfg(feature = "xwayland")] if let WindowElement::X11(surf) = &window { self.xwm.as_mut().unwrap().raise_window(surf).unwrap(); } + keyboard.set_focus(self, Some(window.into()), serial); return; } @@ -330,12 +328,12 @@ impl AnvilState { let output_geo = self.space.output_geometry(output).unwrap(); let layers = layer_map_for_output(output); if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, self.pointer.current_location()) - .or_else(|| layers.layer_under(WlrLayer::Background, self.pointer.current_location())) + .layer_under(WlrLayer::Bottom, location) + .or_else(|| layers.layer_under(WlrLayer::Background, location)) { if layer.can_receive_keyboard_focus() { if let Some((_, _)) = layer.surface_under( - self.pointer.current_location() + location - output_geo.loc.to_f64() - layers.layer_geometry(layer).unwrap().loc.to_f64(), WindowSurfaceType::ALL, @@ -348,7 +346,10 @@ impl AnvilState { } } - pub fn surface_under(&self, pos: Point) -> Option<(FocusTarget, Point)> { + pub fn surface_under( + &self, + pos: Point, + ) -> Option<(PointerFocusTarget, Point)> { let output = self.space.outputs().find(|o| { let geometry = self.space.output_geometry(o).unwrap(); geometry.contains(pos.to_i32_round()) @@ -357,26 +358,57 @@ impl AnvilState { let layers = layer_map_for_output(output); let mut under = None; - if let Some(window) = output + if let Some((surface, loc)) = output .user_data() .get::() .and_then(|f| f.get()) + .and_then(|w| w.surface_under(pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL)) { - under = Some((window.into(), output_geo.loc)); - } else if let Some(layer) = layers + under = Some((surface, loc + output_geo.loc)); + } else if let Some(focus) = layers .layer_under(WlrLayer::Overlay, pos) .or_else(|| layers.layer_under(WlrLayer::Top, pos)) + .and_then(|layer| { + let layer_loc = layers.layer_geometry(layer).unwrap().loc; + layer + .surface_under( + pos - output_geo.loc.to_f64() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .map(|(surface, loc)| { + ( + PointerFocusTarget::from(surface), + loc + layer_loc + output_geo.loc, + ) + }) + }) { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - under = Some((layer.clone().into(), output_geo.loc + layer_loc)) - } else if let Some((window, location)) = self.space.element_under(pos) { - under = Some((window.clone().into(), location)); - } else if let Some(layer) = layers + under = Some(focus) + } else if let Some(focus) = self.space.element_under(pos).and_then(|(window, loc)| { + window + .surface_under(pos - loc.to_f64(), WindowSurfaceType::ALL) + .map(|(surface, surf_loc)| (surface, surf_loc + loc)) + }) { + under = Some(focus); + } else if let Some(focus) = layers .layer_under(WlrLayer::Bottom, pos) .or_else(|| layers.layer_under(WlrLayer::Background, pos)) + .and_then(|layer| { + let layer_loc = layers.layer_geometry(layer).unwrap().loc; + layer + .surface_under( + pos - output_geo.loc.to_f64() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .map(|(surface, loc)| { + ( + PointerFocusTarget::from(surface), + loc + layer_loc + output_geo.loc, + ) + }) + }) { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - under = Some((layer.clone().into(), output_geo.loc + layer_loc)); + under = Some(focus) }; under } @@ -731,12 +763,22 @@ impl AnvilState { InputEvent::GesturePinchEnd { event, .. } => self.on_gesture_pinch_end::(event), InputEvent::GestureHoldBegin { event, .. } => self.on_gesture_hold_begin::(event), InputEvent::GestureHoldEnd { event, .. } => self.on_gesture_hold_end::(event), + + InputEvent::TouchDown { event } => self.on_touch_down::(event), + InputEvent::TouchUp { event } => self.on_touch_up::(event), + InputEvent::TouchMotion { event } => self.on_touch_motion::(event), + InputEvent::TouchFrame { event } => self.on_touch_frame::(event), + InputEvent::TouchCancel { event } => self.on_touch_cancel::(event), + InputEvent::DeviceAdded { device } => { if device.has_capability(DeviceCapability::TabletTool) { self.seat .tablet_seat() .add_tablet::(dh, &TabletDescriptor::from(&device)); } + if device.has_capability(DeviceCapability::Touch) && self.seat.get_touch().is_none() { + self.seat.add_touch(); + } } InputEvent::DeviceRemoved { device } => { if device.has_capability(DeviceCapability::TabletTool) { @@ -1024,7 +1066,7 @@ impl AnvilState { tool.tip_down(serial, evt.time_msec()); // change the keyboard focus - self.update_keyboard_focus(serial); + self.update_keyboard_focus(self.pointer.current_location(), serial); } TabletToolTipState::Up => { tool.tip_up(evt.time_msec()); @@ -1148,6 +1190,102 @@ impl AnvilState { ); } + fn touch_location_transformed>( + &self, + evt: &E, + ) -> Option> { + let output = self + .space + .outputs() + .find(|output| output.name().starts_with("eDP")) + .or_else(|| self.space.outputs().next()); + + let Some(output) = output else { + return None; + }; + + let Some(output_geometry) = self.space.output_geometry(output) else { + return None; + }; + + let transform = output.current_transform(); + let size = transform.invert().transform_size(output_geometry.size); + Some( + transform.transform_point_in(evt.position_transformed(size), &size.to_f64()) + + output_geometry.loc.to_f64(), + ) + } + + fn on_touch_down(&mut self, evt: B::TouchDownEvent) { + let Some(handle) = self.seat.get_touch() else { + return; + }; + + let Some(touch_location) = self.touch_location_transformed(&evt) else { + return; + }; + + let serial = SCOUNTER.next_serial(); + self.update_keyboard_focus(touch_location, serial); + + let under = self.surface_under(touch_location); + handle.down( + self, + under, + &DownEvent { + slot: evt.slot(), + location: touch_location, + serial, + time: evt.time_msec(), + }, + ); + } + fn on_touch_up(&mut self, evt: B::TouchUpEvent) { + let Some(handle) = self.seat.get_touch() else { + return; + }; + let serial = SCOUNTER.next_serial(); + handle.up( + self, + &UpEvent { + slot: evt.slot(), + serial, + time: evt.time_msec(), + }, + ) + } + fn on_touch_motion(&mut self, evt: B::TouchMotionEvent) { + let Some(handle) = self.seat.get_touch() else { + return; + }; + let Some(touch_location) = self.touch_location_transformed(&evt) else { + return; + }; + + let under = self.surface_under(touch_location); + handle.motion( + self, + under, + &smithay::input::touch::MotionEvent { + slot: evt.slot(), + location: touch_location, + time: evt.time_msec(), + }, + ); + } + fn on_touch_frame(&mut self, _evt: B::TouchFrameEvent) { + let Some(handle) = self.seat.get_touch() else { + return; + }; + handle.frame(self); + } + fn on_touch_cancel(&mut self, _evt: B::TouchCancelEvent) { + let Some(handle) = self.seat.get_touch() else { + return; + }; + handle.cancel(self); + } + fn clamp_coords(&self, pos: Point) -> Point { if self.space.outputs().next().is_none() { return pos; diff --git a/anvil/src/shell/element.rs b/anvil/src/shell/element.rs index ba2b9c573fce..30c9131f958d 100644 --- a/anvil/src/shell/element.rs +++ b/anvil/src/shell/element.rs @@ -1,29 +1,24 @@ use std::time::Duration; use smithay::{ - backend::{ - input::KeyState, - renderer::{ - element::{ - solid::SolidColorRenderElement, surface::WaylandSurfaceRenderElement, AsRenderElements, - }, - ImportAll, ImportMem, Renderer, Texture, - }, + backend::renderer::{ + element::{solid::SolidColorRenderElement, surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, ImportMem, Renderer, Texture, }, desktop::{space::SpaceElement, utils::OutputPresentationFeedback, Window, WindowSurfaceType}, input::{ - keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, }, + touch::TouchTarget, Seat, }, output::Output, reexports::{ wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, - wayland_server::protocol::wl_surface::WlSurface, + wayland_server::protocol::wl_surface::{self, WlSurface}, }, render_elements, utils::{user_data::UserDataMap, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial}, @@ -39,7 +34,7 @@ use smithay::{ }; use super::ssd::HEADER_BAR_HEIGHT; -use crate::AnvilState; +use crate::{focus::PointerFocusTarget, AnvilState}; #[derive(Debug, Clone, PartialEq)] pub enum WindowElement { @@ -53,14 +48,27 @@ impl WindowElement { &self, location: Point, window_type: WindowSurfaceType, - ) -> Option<(WlSurface, Point)> { - match self { - WindowElement::Wayland(w) => w.surface_under(location, window_type), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => w - .wl_surface() - .and_then(|s| under_from_surface_tree(&s, location, (0, 0), window_type)), + ) -> Option<(PointerFocusTarget, Point)> { + let state = self.decoration_state(); + if state.is_ssd && location.y < HEADER_BAR_HEIGHT as f64 { + return Some((PointerFocusTarget::SSD(SSD(self.clone())), Point::default())); } + let offset = if state.is_ssd { + Point::from((0, HEADER_BAR_HEIGHT)) + } else { + Point::default() + }; + let (under, loc) = match self { + WindowElement::Wayland(w) => w + .surface_under(location - offset.to_f64(), window_type) + .map(|(surface, loc)| (PointerFocusTarget::WlSurface(surface), loc)), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => w.wl_surface().and_then(|s| { + under_from_surface_tree(&s, location - offset.to_f64(), (0, 0), window_type) + .map(|(_, loc)| (PointerFocusTarget::X11Surface(w.clone()), loc)) + }), + }?; + Some((under, loc + offset)) } pub fn with_surfaces(&self, processor: F) @@ -166,7 +174,7 @@ impl WindowElement { pub fn wl_surface(&self) -> Option { match self { - WindowElement::Wayland(w) => w.wl_surface(), + WindowElement::Wayland(w) => Some(w.toplevel().wl_surface().clone()), #[cfg(feature = "xwayland")] WindowElement::X11(w) => w.wl_surface(), } @@ -191,325 +199,186 @@ impl IsAlive for WindowElement { } } -impl PointerTarget> for WindowElement { - fn enter(&self, seat: &Seat>, data: &mut AnvilState, event: &MotionEvent) { - let mut state = self.decoration_state(); +#[derive(Debug, Clone, PartialEq)] +pub struct SSD(WindowElement); + +impl IsAlive for SSD { + fn alive(&self) -> bool { + self.0.alive() + } +} + +impl WaylandFocus for SSD { + fn wl_surface(&self) -> Option { + self.0.wl_surface() + } +} + +impl PointerTarget> for SSD { + fn enter(&self, _seat: &Seat>, _data: &mut AnvilState, event: &MotionEvent) { + let mut state = self.0.decoration_state(); if state.is_ssd { - if event.location.y < HEADER_BAR_HEIGHT as f64 { - state.header_bar.pointer_enter(event.location); - } else { - state.header_bar.pointer_leave(); - let mut event = event.clone(); - event.location.y -= HEADER_BAR_HEIGHT as f64; - match self { - WindowElement::Wayland(w) => PointerTarget::enter(w, seat, data, &event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::enter(w, seat, data, &event), - }; - state.ptr_entered_window = true; - } - } else { - state.ptr_entered_window = true; - match self { - WindowElement::Wayland(w) => PointerTarget::enter(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::enter(w, seat, data, event), - }; + state.header_bar.pointer_enter(event.location); } } - fn motion(&self, seat: &Seat>, data: &mut AnvilState, event: &MotionEvent) { - let mut state = self.decoration_state(); + fn motion( + &self, + _seat: &Seat>, + _data: &mut AnvilState, + event: &MotionEvent, + ) { + let mut state = self.0.decoration_state(); if state.is_ssd { - if event.location.y < HEADER_BAR_HEIGHT as f64 { - match self { - WindowElement::Wayland(w) => { - PointerTarget::leave(w, seat, data, event.serial, event.time) - } - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::leave(w, seat, data, event.serial, event.time), - }; - state.ptr_entered_window = false; - state.header_bar.pointer_enter(event.location); - } else { - state.header_bar.pointer_leave(); - let mut event = event.clone(); - event.location.y -= HEADER_BAR_HEIGHT as f64; - if state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::motion(w, seat, data, &event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::motion(w, seat, data, &event), - }; - } else { - state.ptr_entered_window = true; - match self { - WindowElement::Wayland(w) => PointerTarget::enter(w, seat, data, &event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::enter(w, seat, data, &event), - }; - } - } - } else { - match self { - WindowElement::Wayland(w) => PointerTarget::motion(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::motion(w, seat, data, event), - }; + state.header_bar.pointer_enter(event.location); } } fn relative_motion( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &RelativeMotionEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &RelativeMotionEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::relative_motion(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::relative_motion(w, seat, data, event), - } - } } fn button(&self, seat: &Seat>, data: &mut AnvilState, event: &ButtonEvent) { - let mut state = self.decoration_state(); + let mut state = self.0.decoration_state(); if state.is_ssd { - if state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::button(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::button(w, seat, data, event), - }; - } else { - state.header_bar.clicked(seat, data, self, event.serial); - } - } else { - match self { - WindowElement::Wayland(w) => PointerTarget::button(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::button(w, seat, data, event), - }; - } - } - fn axis(&self, seat: &Seat>, data: &mut AnvilState, frame: AxisFrame) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::axis(w, seat, data, frame), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::axis(w, seat, data, frame), - } - } - } - fn frame(&self, seat: &Seat>, data: &mut AnvilState) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::frame(w, seat, data), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::frame(w, seat, data), - } + state.header_bar.clicked(seat, data, &self.0, event.serial); } } + fn axis(&self, _seat: &Seat>, _data: &mut AnvilState, _frame: AxisFrame) {} + fn frame(&self, _seat: &Seat>, _data: &mut AnvilState) {} fn leave( &self, - seat: &Seat>, - data: &mut AnvilState, - serial: Serial, - time: u32, + _seat: &Seat>, + _data: &mut AnvilState, + _serial: Serial, + _time: u32, ) { - let mut state = self.decoration_state(); + let mut state = self.0.decoration_state(); if state.is_ssd { state.header_bar.pointer_leave(); - if state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::leave(w, seat, data, serial, time), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::leave(w, seat, data, serial, time), - }; - state.ptr_entered_window = false; - } - } else { - match self { - WindowElement::Wayland(w) => PointerTarget::leave(w, seat, data, serial, time), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::leave(w, seat, data, serial, time), - }; - state.ptr_entered_window = false; } } fn gesture_swipe_begin( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GestureSwipeBeginEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GestureSwipeBeginEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), - } - } } fn gesture_swipe_update( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GestureSwipeUpdateEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GestureSwipeUpdateEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), - } - } } fn gesture_swipe_end( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GestureSwipeEndEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GestureSwipeEndEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), - } - } } fn gesture_pinch_begin( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GesturePinchBeginEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GesturePinchBeginEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), - } - } } fn gesture_pinch_update( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GesturePinchUpdateEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GesturePinchUpdateEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), - } - } } fn gesture_pinch_end( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GesturePinchEndEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GesturePinchEndEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), - } - } } fn gesture_hold_begin( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GestureHoldBeginEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GestureHoldBeginEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), - } - } } fn gesture_hold_end( &self, - seat: &Seat>, - data: &mut AnvilState, - event: &GestureHoldEndEvent, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &GestureHoldEndEvent, ) { - let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { - match self { - WindowElement::Wayland(w) => PointerTarget::gesture_hold_end(w, seat, data, event), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => PointerTarget::gesture_hold_end(w, seat, data, event), - } - } } } -impl KeyboardTarget> for WindowElement { - fn enter( +impl TouchTarget> for SSD { + fn down( &self, seat: &Seat>, data: &mut AnvilState, - keys: Vec>, - serial: Serial, + event: &smithay::input::touch::DownEvent, + _seq: Serial, ) { - match self { - WindowElement::Wayland(w) => KeyboardTarget::enter(w, seat, data, keys, serial), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => KeyboardTarget::enter(w, seat, data, keys, serial), - } - } - fn leave(&self, seat: &Seat>, data: &mut AnvilState, serial: Serial) { - match self { - WindowElement::Wayland(w) => KeyboardTarget::leave(w, seat, data, serial), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => KeyboardTarget::leave(w, seat, data, serial), + let mut state = self.0.decoration_state(); + if state.is_ssd { + state.header_bar.pointer_enter(event.location); + state.header_bar.touch_down(seat, data, &self.0, event.serial); } } - fn key( + + fn up( &self, seat: &Seat>, data: &mut AnvilState, - key: KeysymHandle<'_>, - state: KeyState, - serial: Serial, - time: u32, + event: &smithay::input::touch::UpEvent, + _seq: Serial, ) { - match self { - WindowElement::Wayland(w) => KeyboardTarget::key(w, seat, data, key, state, serial, time), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => KeyboardTarget::key(w, seat, data, key, state, serial, time), + let mut state = self.0.decoration_state(); + if state.is_ssd { + state.header_bar.touch_up(seat, data, &self.0, event.serial); } } - fn modifiers( + + fn motion( &self, - seat: &Seat>, - data: &mut AnvilState, - modifiers: ModifiersState, - serial: Serial, + _seat: &Seat>, + _data: &mut AnvilState, + event: &smithay::input::touch::MotionEvent, + _seq: Serial, ) { - match self { - WindowElement::Wayland(w) => KeyboardTarget::modifiers(w, seat, data, modifiers, serial), - #[cfg(feature = "xwayland")] - WindowElement::X11(w) => KeyboardTarget::modifiers(w, seat, data, modifiers, serial), + let mut state = self.0.decoration_state(); + if state.is_ssd { + state.header_bar.pointer_enter(event.location); } } + + fn frame(&self, _seat: &Seat>, _data: &mut AnvilState, _seq: Serial) {} + + fn cancel(&self, _seat: &Seat>, _data: &mut AnvilState, _seq: Serial) {} + + fn shape( + &self, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &smithay::input::touch::ShapeEvent, + _seq: Serial, + ) { + } + + fn orientation( + &self, + _seat: &Seat>, + _data: &mut AnvilState, + _event: &smithay::input::touch::OrientationEvent, + _seq: Serial, + ) { + } } impl SpaceElement for WindowElement { diff --git a/anvil/src/shell/grabs.rs b/anvil/src/shell/grabs.rs index 02187bc1be5a..79034b873110 100644 --- a/anvil/src/shell/grabs.rs +++ b/anvil/src/shell/grabs.rs @@ -2,11 +2,14 @@ use std::cell::RefCell; use smithay::{ desktop::space::SpaceElement, - input::pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, - GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, - GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, - PointerInnerHandle, RelativeMotionEvent, + input::{ + pointer::{ + AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, + GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, + GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, + PointerInnerHandle, RelativeMotionEvent, + }, + touch::{GrabStartData as TouchGrabStartData, TouchGrab}, }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{IsAlive, Logical, Point, Serial, Size}, @@ -17,22 +20,22 @@ use smithay::{utils::Rectangle, xwayland::xwm::ResizeEdge as X11ResizeEdge}; use super::{SurfaceData, WindowElement}; use crate::{ - focus::FocusTarget, + focus::PointerFocusTarget, state::{AnvilState, Backend}, }; -pub struct MoveSurfaceGrab { +pub struct PointerMoveSurfaceGrab { pub start_data: PointerGrabStartData>, pub window: WindowElement, pub initial_window_location: Point, } -impl PointerGrab> for MoveSurfaceGrab { +impl PointerGrab> for PointerMoveSurfaceGrab { fn motion( &mut self, data: &mut AnvilState, handle: &mut PointerInnerHandle<'_, AnvilState>, - _focus: Option<(FocusTarget, Point)>, + _focus: Option<(PointerFocusTarget, Point)>, event: &MotionEvent, ) { // While the grab is active, no client has pointer focus @@ -49,7 +52,7 @@ impl PointerGrab> for MoveSurfaceG &mut self, data: &mut AnvilState, handle: &mut PointerInnerHandle<'_, AnvilState>, - focus: Option<(FocusTarget, Point)>, + focus: Option<(PointerFocusTarget, Point)>, event: &RelativeMotionEvent, ) { handle.relative_motion(data, focus, event); @@ -162,6 +165,85 @@ impl PointerGrab> for MoveSurfaceG } } +pub struct TouchMoveSurfaceGrab { + pub start_data: TouchGrabStartData>, + pub window: WindowElement, + pub initial_window_location: Point, +} + +impl TouchGrab> for TouchMoveSurfaceGrab { + fn down( + &mut self, + _data: &mut AnvilState, + _handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _focus: Option<( + as smithay::input::SeatHandler>::TouchFocus, + Point, + )>, + _event: &smithay::input::touch::DownEvent, + _seq: Serial, + ) { + } + + fn up( + &mut self, + data: &mut AnvilState, + handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + event: &smithay::input::touch::UpEvent, + seq: Serial, + ) { + if event.slot != self.start_data.slot { + return; + } + + handle.up(data, event, seq); + handle.unset_grab(data); + } + + fn motion( + &mut self, + data: &mut AnvilState, + _handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _focus: Option<( + as smithay::input::SeatHandler>::TouchFocus, + Point, + )>, + event: &smithay::input::touch::MotionEvent, + _seq: Serial, + ) { + if event.slot != self.start_data.slot { + return; + } + + let delta = event.location - self.start_data.location; + let new_location = self.initial_window_location.to_f64() + delta; + data.space + .map_element(self.window.clone(), new_location.to_i32_round(), true); + } + + fn frame( + &mut self, + _data: &mut AnvilState, + _handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _seq: Serial, + ) { + } + + fn cancel( + &mut self, + data: &mut AnvilState, + handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + seq: Serial, + ) { + handle.cancel(data, seq); + handle.unset_grab(data); + } + + fn start_data(&self) -> &smithay::input::touch::GrabStartData> { + &self.start_data + } +} + bitflags::bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ResizeEdge: u32 { @@ -232,7 +314,7 @@ pub enum ResizeState { WaitingForCommit(ResizeData), } -pub struct ResizeSurfaceGrab { +pub struct PointerResizeSurfaceGrab { pub start_data: PointerGrabStartData>, pub window: WindowElement, pub edges: ResizeEdge, @@ -241,12 +323,12 @@ pub struct ResizeSurfaceGrab { pub last_window_size: Size, } -impl PointerGrab> for ResizeSurfaceGrab { +impl PointerGrab> for PointerResizeSurfaceGrab { fn motion( &mut self, data: &mut AnvilState, handle: &mut PointerInnerHandle<'_, AnvilState>, - _focus: Option<(FocusTarget, Point)>, + _focus: Option<(PointerFocusTarget, Point)>, event: &MotionEvent, ) { // While the grab is active, no client has pointer focus @@ -331,7 +413,7 @@ impl PointerGrab> for ResizeSurfac &mut self, data: &mut AnvilState, handle: &mut PointerInnerHandle<'_, AnvilState>, - focus: Option<(FocusTarget, Point)>, + focus: Option<(PointerFocusTarget, Point)>, event: &RelativeMotionEvent, ) { handle.relative_motion(data, focus, event); @@ -524,3 +606,233 @@ impl PointerGrab> for ResizeSurfac &self.start_data } } + +pub struct TouchResizeSurfaceGrab { + pub start_data: TouchGrabStartData>, + pub window: WindowElement, + pub edges: ResizeEdge, + pub initial_window_location: Point, + pub initial_window_size: Size, + pub last_window_size: Size, +} + +impl TouchGrab> for TouchResizeSurfaceGrab { + fn down( + &mut self, + _data: &mut AnvilState, + _handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _focus: Option<( + as smithay::input::SeatHandler>::TouchFocus, + Point, + )>, + _event: &smithay::input::touch::DownEvent, + _seq: Serial, + ) { + } + + fn up( + &mut self, + data: &mut AnvilState, + handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + event: &smithay::input::touch::UpEvent, + _seq: Serial, + ) { + if event.slot != self.start_data.slot { + return; + } + handle.unset_grab(data); + + // If toplevel is dead, we can't resize it, so we return early. + if !self.window.alive() { + return; + } + + match &self.window { + WindowElement::Wayland(w) => { + let xdg = w.toplevel(); + xdg.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Resizing); + state.size = Some(self.last_window_size); + }); + xdg.send_pending_configure(); + if self.edges.intersects(ResizeEdge::TOP_LEFT) { + let geometry = self.window.geometry(); + let mut location = data.space.element_location(&self.window).unwrap(); + + if self.edges.intersects(ResizeEdge::LEFT) { + location.x = + self.initial_window_location.x + (self.initial_window_size.w - geometry.size.w); + } + if self.edges.intersects(ResizeEdge::TOP) { + location.y = + self.initial_window_location.y + (self.initial_window_size.h - geometry.size.h); + } + + data.space.map_element(self.window.clone(), location, true); + } + + with_states(&self.window.wl_surface().unwrap(), |states| { + let mut data = states + .data_map + .get::>() + .unwrap() + .borrow_mut(); + if let ResizeState::Resizing(resize_data) = data.resize_state { + data.resize_state = ResizeState::WaitingForFinalAck(resize_data, event.serial); + } else { + panic!("invalid resize state: {:?}", data.resize_state); + } + }); + } + #[cfg(feature = "xwayland")] + WindowElement::X11(x11) => { + let mut location = data.space.element_location(&self.window).unwrap(); + if self.edges.intersects(ResizeEdge::TOP_LEFT) { + let geometry = self.window.geometry(); + + if self.edges.intersects(ResizeEdge::LEFT) { + location.x = + self.initial_window_location.x + (self.initial_window_size.w - geometry.size.w); + } + if self.edges.intersects(ResizeEdge::TOP) { + location.y = + self.initial_window_location.y + (self.initial_window_size.h - geometry.size.h); + } + + data.space.map_element(self.window.clone(), location, true); + } + x11.configure(Rectangle::from_loc_and_size(location, self.last_window_size)) + .unwrap(); + + let Some(surface) = self.window.wl_surface() else { + // X11 Window got unmapped, abort + return; + }; + with_states(&surface, |states| { + let mut data = states + .data_map + .get::>() + .unwrap() + .borrow_mut(); + if let ResizeState::Resizing(resize_data) = data.resize_state { + data.resize_state = ResizeState::WaitingForCommit(resize_data); + } else { + panic!("invalid resize state: {:?}", data.resize_state); + } + }); + } + } + } + + fn motion( + &mut self, + data: &mut AnvilState, + handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _focus: Option<( + as smithay::input::SeatHandler>::TouchFocus, + Point, + )>, + event: &smithay::input::touch::MotionEvent, + _seq: Serial, + ) { + if event.slot != self.start_data.slot { + return; + } + + // It is impossible to get `min_size` and `max_size` of dead toplevel, so we return early. + if !self.window.alive() { + handle.unset_grab(data); + return; + } + + let (mut dx, mut dy) = (event.location - self.start_data.location).into(); + + let mut new_window_width = self.initial_window_size.w; + let mut new_window_height = self.initial_window_size.h; + + let left_right = ResizeEdge::LEFT | ResizeEdge::RIGHT; + let top_bottom = ResizeEdge::TOP | ResizeEdge::BOTTOM; + + if self.edges.intersects(left_right) { + if self.edges.intersects(ResizeEdge::LEFT) { + dx = -dx; + } + + new_window_width = (self.initial_window_size.w as f64 + dx) as i32; + } + + if self.edges.intersects(top_bottom) { + if self.edges.intersects(ResizeEdge::TOP) { + dy = -dy; + } + + new_window_height = (self.initial_window_size.h as f64 + dy) as i32; + } + + let (min_size, max_size) = if let Some(surface) = self.window.wl_surface() { + with_states(&surface, |states| { + let data = states.cached_state.current::(); + (data.min_size, data.max_size) + }) + } else { + ((0, 0).into(), (0, 0).into()) + }; + + let min_width = min_size.w.max(1); + let min_height = min_size.h.max(1); + let max_width = if max_size.w == 0 { + i32::max_value() + } else { + max_size.w + }; + let max_height = if max_size.h == 0 { + i32::max_value() + } else { + max_size.h + }; + + new_window_width = new_window_width.max(min_width).min(max_width); + new_window_height = new_window_height.max(min_height).min(max_height); + + self.last_window_size = (new_window_width, new_window_height).into(); + + match &self.window { + WindowElement::Wayland(w) => { + let xdg = w.toplevel(); + xdg.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Resizing); + state.size = Some(self.last_window_size); + }); + xdg.send_pending_configure(); + } + #[cfg(feature = "xwayland")] + WindowElement::X11(x11) => { + let location = data.space.element_location(&self.window).unwrap(); + x11.configure(Rectangle::from_loc_and_size(location, self.last_window_size)) + .unwrap(); + } + } + } + + fn frame( + &mut self, + _data: &mut AnvilState, + _handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + _seq: Serial, + ) { + } + + fn cancel( + &mut self, + data: &mut AnvilState, + handle: &mut smithay::input::touch::TouchInnerHandle<'_, AnvilState>, + seq: Serial, + ) { + handle.cancel(data, seq); + handle.unset_grab(data); + } + + fn start_data(&self) -> &smithay::input::touch::GrabStartData> { + &self.start_data + } +} diff --git a/anvil/src/shell/ssd.rs b/anvil/src/shell/ssd.rs index d3574ddd6553..bf03fc2833a7 100644 --- a/anvil/src/shell/ssd.rs +++ b/anvil/src/shell/ssd.rs @@ -19,7 +19,6 @@ use super::WindowElement; pub struct WindowState { pub is_ssd: bool, - pub ptr_entered_window: bool, pub header_bar: HeaderBar, } @@ -104,6 +103,71 @@ impl HeaderBar { }; } + pub fn touch_down( + &mut self, + seat: &Seat>, + state: &mut AnvilState, + window: &WindowElement, + serial: Serial, + ) { + match self.pointer_loc.as_ref() { + Some(loc) if loc.x >= (self.width - BUTTON_WIDTH) as f64 => {} + Some(loc) if loc.x >= (self.width - (BUTTON_WIDTH * 2)) as f64 => {} + Some(_) => { + match window { + WindowElement::Wayland(w) => { + let seat = seat.clone(); + let toplevel = w.toplevel().clone(); + state + .handle + .insert_idle(move |data| data.state.move_request_xdg(&toplevel, &seat, serial)); + } + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => { + let window = w.clone(); + state + .handle + .insert_idle(move |data| data.state.move_request_x11(&window)); + } + }; + } + _ => {} + }; + } + + pub fn touch_up( + &mut self, + _seat: &Seat>, + state: &mut AnvilState, + window: &WindowElement, + _serial: Serial, + ) { + match self.pointer_loc.as_ref() { + Some(loc) if loc.x >= (self.width - BUTTON_WIDTH) as f64 => { + match window { + WindowElement::Wayland(w) => w.toplevel().send_close(), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => { + let _ = w.close(); + } + }; + } + Some(loc) if loc.x >= (self.width - (BUTTON_WIDTH * 2)) as f64 => { + match window { + WindowElement::Wayland(w) => state.maximize_request(w.toplevel().clone()), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => { + let surface = w.clone(); + state + .handle + .insert_idle(move |data| data.state.maximize_request_x11(&surface)); + } + }; + } + _ => {} + }; + } + pub fn redraw(&mut self, width: u32) { if width == 0 { self.width = 0; @@ -206,7 +270,6 @@ impl WindowElement { self.user_data().insert_if_missing(|| { RefCell::new(WindowState { is_ssd: false, - ptr_entered_window: false, header_bar: HeaderBar { pointer_loc: None, width: 0, diff --git a/anvil/src/shell/x11.rs b/anvil/src/shell/x11.rs index fc7b2103235c..d24151645f7f 100644 --- a/anvil/src/shell/x11.rs +++ b/anvil/src/shell/x11.rs @@ -23,11 +23,11 @@ use smithay::{ }; use tracing::{error, trace}; -use crate::{focus::FocusTarget, state::Backend, AnvilState, CalloopData}; +use crate::{focus::KeyboardFocusTarget, state::Backend, AnvilState, CalloopData}; use super::{ - place_new_window, FullscreenSurface, MoveSurfaceGrab, ResizeData, ResizeState, ResizeSurfaceGrab, - SurfaceData, WindowElement, + place_new_window, FullscreenSurface, PointerMoveSurfaceGrab, PointerResizeSurfaceGrab, ResizeData, + ResizeState, SurfaceData, TouchMoveSurfaceGrab, WindowElement, }; #[derive(Debug, Default)] @@ -241,7 +241,7 @@ impl XwmHandler for CalloopData { }); }); - let grab = ResizeSurfaceGrab { + let grab = PointerResizeSurfaceGrab { start_data, window: element.clone(), edges: edges.into(), @@ -261,7 +261,7 @@ impl XwmHandler for CalloopData { fn allow_selection_access(&mut self, xwm: XwmId, _selection: SelectionTarget) -> bool { if let Some(keyboard) = self.state.seat.get_keyboard() { // check that an X11 window is focused - if let Some(FocusTarget::Window(WindowElement::X11(surface))) = keyboard.current_focus() { + if let Some(KeyboardFocusTarget::X11Surface(surface)) = keyboard.current_focus() { if surface.xwm_id().unwrap() == xwm { return true; } @@ -346,6 +346,47 @@ impl AnvilState { } pub fn move_request_x11(&mut self, window: &X11Surface) { + if let Some(touch) = self.seat.get_touch() { + if let Some(start_data) = touch.grab_start_data() { + let element = self + .space + .elements() + .find(|e| matches!(e, WindowElement::X11(w) if w == window)); + + if let Some(element) = element { + let mut initial_window_location = self.space.element_location(element).unwrap(); + + // If surface is maximized then unmaximize it + if window.is_maximized() { + window.set_maximized(false).unwrap(); + let pos = start_data.location; + initial_window_location = (pos.x as i32, pos.y as i32).into(); + if let Some(old_geo) = window + .user_data() + .get::() + .and_then(|data| data.restore()) + { + window + .configure(Rectangle::from_loc_and_size( + initial_window_location, + old_geo.size, + )) + .unwrap(); + } + } + + let grab = TouchMoveSurfaceGrab { + start_data, + window: element.clone(), + initial_window_location, + }; + + touch.set_grab(self, grab, SERIAL_COUNTER.next_serial()); + return; + } + } + } + // luckily anvil only supports one seat anyway... let Some(start_data) = self.pointer.grab_start_data() else { return; @@ -380,7 +421,7 @@ impl AnvilState { } } - let grab = MoveSurfaceGrab { + let grab = PointerMoveSurfaceGrab { start_data, window: element.clone(), initial_window_location, diff --git a/anvil/src/shell/xdg.rs b/anvil/src/shell/xdg.rs index 82458194127f..23cbff3207b4 100644 --- a/anvil/src/shell/xdg.rs +++ b/anvil/src/shell/xdg.rs @@ -8,8 +8,7 @@ use smithay::{ input::{pointer::Focus, Seat}, output::Output, reexports::{ - wayland_protocols::xdg::decoration as xdg_decoration, - wayland_protocols::xdg::shell::server::xdg_toplevel, + wayland_protocols::xdg::{decoration as xdg_decoration, shell::server::xdg_toplevel}, wayland_server::{ protocol::{wl_output, wl_seat, wl_surface::WlSurface}, Resource, @@ -28,13 +27,14 @@ use smithay::{ use tracing::{trace, warn}; use crate::{ - focus::FocusTarget, + focus::KeyboardFocusTarget, + shell::{TouchMoveSurfaceGrab, TouchResizeSurfaceGrab}, state::{AnvilState, Backend}, }; use super::{ - fullscreen_output_geometry, place_new_window, FullscreenSurface, MoveSurfaceGrab, ResizeData, - ResizeState, ResizeSurfaceGrab, SurfaceData, WindowElement, + fullscreen_output_geometry, place_new_window, FullscreenSurface, PointerMoveSurfaceGrab, + PointerResizeSurfaceGrab, ResizeData, ResizeState, SurfaceData, WindowElement, }; impl XdgShellHandler for AnvilState { @@ -84,8 +84,64 @@ impl XdgShellHandler for AnvilState { serial: Serial, edges: xdg_toplevel::ResizeEdge, ) { + tracing::info!("resize_request"); let seat: Seat> = Seat::from_resource(&seat).unwrap(); - // TODO: touch resize. + + if let Some(touch) = seat.get_touch() { + if touch.has_grab(serial) { + let start_data = touch.grab_start_data().unwrap(); + tracing::info!(?start_data); + + // If the client disconnects after requesting a move + // we can just ignore the request + let Some(window) = self.window_for_surface(surface.wl_surface()) else { + tracing::info!("no window"); + return; + }; + + // If the focus was for a different surface, ignore the request. + if start_data.focus.is_none() + || !start_data + .focus + .as_ref() + .unwrap() + .0 + .same_client_as(&surface.wl_surface().id()) + { + tracing::info!("different surface"); + return; + } + let geometry = window.geometry(); + let loc = self.space.element_location(&window).unwrap(); + let (initial_window_location, initial_window_size) = (loc, geometry.size); + + with_states(surface.wl_surface(), move |states| { + states + .data_map + .get::>() + .unwrap() + .borrow_mut() + .resize_state = ResizeState::Resizing(ResizeData { + edges: edges.into(), + initial_window_location, + initial_window_size, + }); + }); + + let grab = TouchResizeSurfaceGrab { + start_data, + window, + edges: edges.into(), + initial_window_location, + initial_window_size, + last_window_size: initial_window_size, + }; + + touch.set_grab(self, grab, serial); + return; + } + } + let pointer = seat.get_pointer().unwrap(); // Check that this surface has a click grab. @@ -126,7 +182,7 @@ impl XdgShellHandler for AnvilState { }); }); - let grab = ResizeSurfaceGrab { + let grab = PointerResizeSurfaceGrab { start_data, window, edges: edges.into(), @@ -331,7 +387,7 @@ impl XdgShellHandler for AnvilState { .elements() .find(|w| w.wl_surface().map(|s| s == root).unwrap_or(false)) .cloned() - .map(FocusTarget::Window) + .map(KeyboardFocusTarget::from) .or_else(|| { self.space .outputs() @@ -339,7 +395,7 @@ impl XdgShellHandler for AnvilState { let map = layer_map_for_output(o); map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL).cloned() }) - .map(FocusTarget::LayerSurface) + .map(KeyboardFocusTarget::LayerSurface) }) }) { let ret = self.popups.grab_popup(root, kind, &seat, serial); @@ -373,7 +429,65 @@ impl XdgShellHandler for AnvilState { impl AnvilState { pub fn move_request_xdg(&mut self, surface: &ToplevelSurface, seat: &Seat, serial: Serial) { - // TODO: touch move. + if let Some(touch) = seat.get_touch() { + if touch.has_grab(serial) { + let start_data = touch.grab_start_data().unwrap(); + + // If the client disconnects after requesting a move + // we can just ignore the request + let Some(window) = self.window_for_surface(surface.wl_surface()) else { + return; + }; + + // If the focus was for a different surface, ignore the request. + if start_data.focus.is_none() + || !start_data + .focus + .as_ref() + .unwrap() + .0 + .same_client_as(&surface.wl_surface().id()) + { + return; + } + + let mut initial_window_location = self.space.element_location(&window).unwrap(); + + // If surface is maximized then unmaximize it + let current_state = surface.current_state(); + if current_state.states.contains(xdg_toplevel::State::Maximized) { + surface.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Maximized); + state.size = None; + }); + + surface.send_configure(); + + // NOTE: In real compositor mouse location should be mapped to a new window size + // For example, you could: + // 1) transform mouse pointer position from compositor space to window space (location relative) + // 2) divide the x coordinate by width of the window to get the percentage + // - 0.0 would be on the far left of the window + // - 0.5 would be in middle of the window + // - 1.0 would be on the far right of the window + // 3) multiply the percentage by new window width + // 4) by doing that, drag will look a lot more natural + // + // but for anvil needs setting location to pointer location is fine + initial_window_location = start_data.location.to_i32_round(); + } + + let grab = TouchMoveSurfaceGrab { + start_data, + window, + initial_window_location, + }; + + touch.set_grab(self, grab, serial); + return; + } + } + let pointer = seat.get_pointer().unwrap(); // Check that this surface has a click grab. @@ -428,7 +542,7 @@ impl AnvilState { initial_window_location = (pos.x as i32, pos.y as i32).into(); } - let grab = MoveSurfaceGrab { + let grab = PointerMoveSurfaceGrab { start_data, window, initial_window_location, diff --git a/anvil/src/state.rs b/anvil/src/state.rs index 905311874413..fe626a6f1f7c 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -89,7 +89,10 @@ use smithay::{ #[cfg(feature = "xwayland")] use crate::cursor::Cursor; -use crate::{focus::FocusTarget, shell::WindowElement}; +use crate::{ + focus::{KeyboardFocusTarget, PointerFocusTarget}, + shell::WindowElement, +}; #[cfg(feature = "xwayland")] use smithay::{ delegate_xwayland_keyboard_grab, @@ -246,14 +249,15 @@ impl ShmHandler for AnvilState { delegate_shm!(@ AnvilState); impl SeatHandler for AnvilState { - type KeyboardFocus = FocusTarget; - type PointerFocus = FocusTarget; + type KeyboardFocus = KeyboardFocusTarget; + type PointerFocus = PointerFocusTarget; + type TouchFocus = PointerFocusTarget; fn seat_state(&mut self) -> &mut SeatState> { &mut self.seat_state } - fn focus_changed(&mut self, seat: &Seat, target: Option<&FocusTarget>) { + fn focus_changed(&mut self, seat: &Seat, target: Option<&KeyboardFocusTarget>) { let dh = &self.display_handle; let wl_surface = target.and_then(WaylandFocus::wl_surface); @@ -498,12 +502,16 @@ delegate_security_context!(@ AnvilState XWaylandKeyboardGrabHandler for AnvilState { - fn keyboard_focus_for_xsurface(&self, surface: &WlSurface) -> Option { + fn keyboard_focus_for_xsurface(&self, surface: &WlSurface) -> Option { let elem = self .space .elements() .find(|elem| elem.wl_surface().as_ref() == Some(surface))?; - Some(FocusTarget::Window(elem.clone())) + if let WindowElement::X11(_) = elem { + Some(KeyboardFocusTarget::from(elem.clone())) + } else { + None + } } } #[cfg(feature = "xwayland")] diff --git a/examples/minimal.rs b/examples/minimal.rs index 73382ed73972..0aff829c5116 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -110,6 +110,7 @@ impl ShmHandler for App { impl SeatHandler for App { type KeyboardFocus = WlSurface; type PointerFocus = WlSurface; + type TouchFocus = WlSurface; fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state diff --git a/examples/seat.rs b/examples/seat.rs index 00706d3142cd..8b2afe24e02a 100644 --- a/examples/seat.rs +++ b/examples/seat.rs @@ -16,6 +16,7 @@ struct App { impl SeatHandler for App { type KeyboardFocus = WlSurface; type PointerFocus = WlSurface; + type TouchFocus = WlSurface; fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state diff --git a/smallvil/src/handlers/mod.rs b/smallvil/src/handlers/mod.rs index fa24a2ead38a..959564b55821 100644 --- a/smallvil/src/handlers/mod.rs +++ b/smallvil/src/handlers/mod.rs @@ -20,6 +20,7 @@ use smithay::{delegate_data_device, delegate_output, delegate_seat}; impl SeatHandler for Smallvil { type KeyboardFocus = WlSurface; type PointerFocus = WlSurface; + type TouchFocus = WlSurface; fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state diff --git a/src/desktop/wayland/layer.rs b/src/desktop/wayland/layer.rs index b4d2a2f2134a..85034e203e6f 100644 --- a/src/desktop/wayland/layer.rs +++ b/src/desktop/wayland/layer.rs @@ -1,17 +1,7 @@ use crate::{ - backend::input::KeyState, desktop::{utils::*, PopupManager}, - input::{ - keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, - GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, - GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, - }, - Seat, SeatHandler, - }, output::{Output, WeakOutput}, - utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial}, + utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle}, wayland::{ compositor::{with_states, with_surface_tree_downward, SurfaceData, TraversalAction}, dmabuf::DmabufFeedback, @@ -25,16 +15,12 @@ use crate::{ use indexmap::IndexSet; use tracing::{debug_span, trace}; use wayland_protocols::wp::presentation_time::server::wp_presentation_feedback; -use wayland_server::{ - backend::ObjectId, - protocol::wl_surface::{self, WlSurface}, - Resource, -}; +use wayland_server::protocol::wl_surface::{self, WlSurface}; use std::{ cell::{RefCell, RefMut}, hash::{Hash, Hasher}, - sync::{Arc, Mutex}, + sync::Arc, time::Duration, }; @@ -491,7 +477,6 @@ pub(crate) struct LayerSurfaceInner { pub(crate) id: usize, surface: WlrLayerSurface, namespace: String, - focused_surface: Mutex>, userdata: UserDataMap, } @@ -514,7 +499,6 @@ impl LayerSurface { id: next_layer_id(), surface, namespace, - focused_surface: Mutex::new(None), userdata: UserDataMap::new(), })) } @@ -700,130 +684,8 @@ impl LayerSurface { } } -impl PointerTarget for LayerSurface { - fn enter(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { - if let Some((surface, loc)) = self.surface_under(event.location, WindowSurfaceType::ALL) { - let mut new_event = event.clone(); - new_event.location -= loc.to_f64(); - if let Some(old_surface) = self.0.focused_surface.lock().unwrap().replace(surface.clone()) { - if old_surface != surface { - PointerTarget::::leave(&old_surface, seat, data, event.serial, event.time); - PointerTarget::::enter(&surface, seat, data, &new_event); - } else { - PointerTarget::::motion(&surface, seat, data, &new_event) - } - } else { - PointerTarget::::enter(&surface, seat, data, &new_event) - } - } - } - fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { - PointerTarget::::enter(self, seat, data, event) - } - fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::relative_motion(surface, seat, data, event) - } - } - fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::button(surface, seat, data, event) - } - } - fn axis(&self, seat: &Seat, data: &mut D, frame: AxisFrame) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::axis(surface, seat, data, frame) - } - } - fn frame(&self, seat: &Seat, data: &mut D) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::frame(surface, seat, data) - } - } - fn leave(&self, seat: &Seat, data: &mut D, serial: Serial, time: u32) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().take() { - PointerTarget::::leave(&surface, seat, data, serial, time) - } - } - - fn gesture_swipe_begin(&self, seat: &Seat, data: &mut D, event: &GestureSwipeBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_begin(surface, seat, data, event) - } - } - - fn gesture_swipe_update(&self, seat: &Seat, data: &mut D, event: &GestureSwipeUpdateEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_update(surface, seat, data, event) - } - } - - fn gesture_swipe_end(&self, seat: &Seat, data: &mut D, event: &GestureSwipeEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_end(surface, seat, data, event) - } - } - - fn gesture_pinch_begin(&self, seat: &Seat, data: &mut D, event: &GesturePinchBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_begin(surface, seat, data, event) - } - } - - fn gesture_pinch_update(&self, seat: &Seat, data: &mut D, event: &GesturePinchUpdateEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_update(surface, seat, data, event) - } - } - - fn gesture_pinch_end(&self, seat: &Seat, data: &mut D, event: &GesturePinchEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_end(surface, seat, data, event) - } - } - - fn gesture_hold_begin(&self, seat: &Seat, data: &mut D, event: &GestureHoldBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_hold_begin(surface, seat, data, event) - } - } - - fn gesture_hold_end(&self, seat: &Seat, data: &mut D, event: &GestureHoldEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_hold_end(surface, seat, data, event) - } - } -} - -impl KeyboardTarget for LayerSurface { - fn enter(&self, seat: &Seat, data: &mut D, keys: Vec>, serial: Serial) { - KeyboardTarget::::enter(self.0.surface.wl_surface(), seat, data, keys, serial) - } - fn leave(&self, seat: &Seat, data: &mut D, serial: Serial) { - KeyboardTarget::::leave(self.0.surface.wl_surface(), seat, data, serial) - } - fn key( - &self, - seat: &Seat, - data: &mut D, - key: KeysymHandle<'_>, - state: KeyState, - serial: Serial, - time: u32, - ) { - KeyboardTarget::::key(self.0.surface.wl_surface(), seat, data, key, state, serial, time) - } - fn modifiers(&self, seat: &Seat, data: &mut D, modifiers: ModifiersState, serial: Serial) { - KeyboardTarget::::modifiers(self.0.surface.wl_surface(), seat, data, modifiers, serial) - } -} - impl WaylandFocus for LayerSurface { fn wl_surface(&self) -> Option { Some(self.0.surface.wl_surface().clone()) } - - fn same_client_as(&self, object_id: &ObjectId) -> bool { - self.0.surface.wl_surface().id().same_client_as(object_id) - } } diff --git a/src/desktop/wayland/window.rs b/src/desktop/wayland/window.rs index 3ac283928ddb..7d7f234b4d0d 100644 --- a/src/desktop/wayland/window.rs +++ b/src/desktop/wayland/window.rs @@ -1,17 +1,7 @@ use crate::{ - backend::input::KeyState, desktop::{space::RenderZindex, utils::*, PopupManager}, - input::{ - keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, - pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, - GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, - GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, - }, - Seat, SeatHandler, - }, output::Output, - utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial}, + utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle}, wayland::{ compositor::{with_states, SurfaceData}, dmabuf::DmabufFeedback, @@ -40,7 +30,6 @@ pub(crate) struct WindowInner { toplevel: ToplevelSurface, bbox: Mutex>, pub(crate) z_index: AtomicU8, - focused_surface: Mutex>, user_data: UserDataMap, } @@ -100,7 +89,6 @@ impl Window { toplevel, bbox: Mutex::new(Rectangle::from_loc_and_size((0, 0), (0, 0))), z_index: AtomicU8::new(RenderZindex::Shell as u8), - focused_surface: Mutex::new(None), user_data: UserDataMap::new(), })) } @@ -287,124 +275,6 @@ impl Window { } } -impl PointerTarget for Window { - fn enter(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { - if let Some((surface, loc)) = self.surface_under(event.location, WindowSurfaceType::ALL) { - let mut new_event = event.clone(); - new_event.location -= loc.to_f64(); - if let Some(old_surface) = self.0.focused_surface.lock().unwrap().replace(surface.clone()) { - if old_surface != surface { - PointerTarget::::leave(&old_surface, seat, data, event.serial, event.time); - PointerTarget::::enter(&surface, seat, data, &new_event); - } else { - PointerTarget::::motion(&surface, seat, data, &new_event); - } - } else { - PointerTarget::::enter(&surface, seat, data, &new_event) - } - } - } - fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent) { - PointerTarget::::enter(self, seat, data, event) - } - fn relative_motion(&self, seat: &Seat, data: &mut D, event: &RelativeMotionEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::relative_motion(surface, seat, data, event) - } - } - fn button(&self, seat: &Seat, data: &mut D, event: &ButtonEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::button(surface, seat, data, event) - } - } - fn axis(&self, seat: &Seat, data: &mut D, frame: AxisFrame) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::axis(surface, seat, data, frame) - } - } - fn frame(&self, seat: &Seat, data: &mut D) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::frame(surface, seat, data) - } - } - fn leave(&self, seat: &Seat, data: &mut D, serial: Serial, time: u32) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().take() { - PointerTarget::::leave(&surface, seat, data, serial, time) - } - } - - fn gesture_swipe_begin(&self, seat: &Seat, data: &mut D, event: &GestureSwipeBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_begin(surface, seat, data, event) - } - } - - fn gesture_swipe_update(&self, seat: &Seat, data: &mut D, event: &GestureSwipeUpdateEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_update(surface, seat, data, event) - } - } - - fn gesture_swipe_end(&self, seat: &Seat, data: &mut D, event: &GestureSwipeEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_swipe_end(surface, seat, data, event) - } - } - - fn gesture_pinch_begin(&self, seat: &Seat, data: &mut D, event: &GesturePinchBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_begin(surface, seat, data, event) - } - } - - fn gesture_pinch_update(&self, seat: &Seat, data: &mut D, event: &GesturePinchUpdateEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_update(surface, seat, data, event) - } - } - - fn gesture_pinch_end(&self, seat: &Seat, data: &mut D, event: &GesturePinchEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_pinch_end(surface, seat, data, event) - } - } - - fn gesture_hold_begin(&self, seat: &Seat, data: &mut D, event: &GestureHoldBeginEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_hold_begin(surface, seat, data, event) - } - } - - fn gesture_hold_end(&self, seat: &Seat, data: &mut D, event: &GestureHoldEndEvent) { - if let Some(surface) = self.0.focused_surface.lock().unwrap().as_ref() { - PointerTarget::::gesture_hold_end(surface, seat, data, event) - } - } -} - -impl KeyboardTarget for Window { - fn enter(&self, seat: &Seat, data: &mut D, keys: Vec>, serial: Serial) { - KeyboardTarget::::enter(self.0.toplevel.wl_surface(), seat, data, keys, serial) - } - fn leave(&self, seat: &Seat, data: &mut D, serial: Serial) { - KeyboardTarget::::leave(self.0.toplevel.wl_surface(), seat, data, serial) - } - fn key( - &self, - seat: &Seat, - data: &mut D, - key: KeysymHandle<'_>, - state: KeyState, - serial: Serial, - time: u32, - ) { - KeyboardTarget::::key(self.0.toplevel.wl_surface(), seat, data, key, state, serial, time) - } - fn modifiers(&self, seat: &Seat, data: &mut D, modifiers: ModifiersState, serial: Serial) { - KeyboardTarget::::modifiers(self.0.toplevel.wl_surface(), seat, data, modifiers, serial) - } -} - impl WaylandFocus for Window { fn wl_surface(&self) -> Option { Some(self.0.toplevel.wl_surface().clone()) diff --git a/src/input/mod.rs b/src/input/mod.rs index b5e18f97893f..fff4b1a08954 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -24,6 +24,7 @@ //! # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, //! # GestureHoldBeginEvent, GestureHoldEndEvent}, //! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, +//! # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, //! # }; //! # use smithay::utils::{IsAlive, Serial}; //! @@ -75,11 +76,21 @@ //! # ) {} //! # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} //! # } +//! # impl TouchTarget for Target { +//! # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} +//! # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} +//! # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} +//! # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} +//! # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} +//! # } //! //! // implement the required traits //! impl SeatHandler for State { //! type KeyboardFocus = Target; //! type PointerFocus = Target; +//! type TouchFocus = Target; //! //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state @@ -117,12 +128,17 @@ use std::{ use tracing::{info_span, instrument}; -use self::keyboard::{Error as KeyboardError, KeyboardHandle, KeyboardTarget, LedState}; use self::pointer::{CursorImageStatus, PointerHandle, PointerTarget}; +use self::touch::TouchTarget; +use self::{ + keyboard::{Error as KeyboardError, KeyboardHandle, KeyboardTarget, LedState}, + touch::TouchHandle, +}; use crate::utils::user_data::UserDataMap; pub mod keyboard; pub mod pointer; +pub mod touch; /// Handler trait for Seats pub trait SeatHandler: Sized { @@ -130,6 +146,8 @@ pub trait SeatHandler: Sized { type KeyboardFocus: KeyboardTarget + 'static; /// Type used to represent the target currently holding the pointer focus type PointerFocus: PointerTarget + 'static; + /// Type used to represent the target currently holding the touch focus + type TouchFocus: TouchTarget + 'static; /// [SeatState] getter fn seat_state(&mut self) -> &mut SeatState; @@ -190,9 +208,8 @@ impl Hash for Seat { pub(crate) struct Inner { pub(crate) pointer: Option>, pub(crate) keyboard: Option>, + pub(crate) touch: Option>, - #[cfg(feature = "wayland_frontend")] - pub(crate) touch: Option, #[cfg(feature = "wayland_frontend")] pub(crate) global: Option, #[cfg(feature = "wayland_frontend")] @@ -273,9 +290,8 @@ impl SeatState { inner: Mutex::new(Inner { pointer: None, keyboard: None, - - #[cfg(feature = "wayland_frontend")] touch: None, + #[cfg(feature = "wayland_frontend")] global: None, #[cfg(feature = "wayland_frontend")] @@ -316,6 +332,7 @@ impl Seat { /// # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, /// # GestureHoldBeginEvent, GestureHoldEndEvent}, /// # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + /// # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, /// # }; /// # use smithay::utils::{IsAlive, Serial}; /// # @@ -355,10 +372,20 @@ impl Seat { /// # ) {} /// # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} /// # } + /// # impl TouchTarget for Target { + /// # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} + /// # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} + /// # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} + /// # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} + /// # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} + /// # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} + /// # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} + /// # } /// # struct State; /// # impl SeatHandler for State { /// # type KeyboardFocus = Target; /// # type PointerFocus = Target; + /// # type TouchFocus = Target; /// # /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&Target>) { unimplemented!() } @@ -426,6 +453,7 @@ impl Seat { /// # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, /// # GestureHoldBeginEvent, GestureHoldEndEvent}, /// # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + /// # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, /// # }; /// # use smithay::utils::{IsAlive, Serial}; /// # @@ -465,11 +493,21 @@ impl Seat { /// # ) {} /// # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} /// # } + /// # impl TouchTarget for Target { + /// # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} + /// # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} + /// # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} + /// # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} + /// # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} + /// # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} + /// # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} + /// # } /// # /// # struct State; /// # impl SeatHandler for State { /// # type KeyboardFocus = Target; /// # type PointerFocus = Target; + /// # type TouchFocus = Target; /// # /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&Target>) { unimplemented!() } @@ -528,6 +566,65 @@ impl Seat { } } + /// Adds the touch capability to this seat + /// + /// You are provided a [`TouchHandle`], which allows you to send input events + /// to this pointer. This handle can be cloned. + /// + /// Calling this method on a seat that already has a touch capability + /// will overwrite it, and will be seen by the clients as if the + /// touchscreen was unplugged and a new one was plugged in. + /// + /// # Examples + /// + /// ```no_run + /// # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; + /// # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; + /// # + /// # struct State; + /// # impl SeatHandler for State { + /// # type KeyboardFocus = WlSurface; + /// # type PointerFocus = WlSurface; + /// # type TouchFocus = WlSurface; + /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } + /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } + /// # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } + /// # } + /// # let mut seat: Seat = unimplemented!(); + /// let touch_handle = seat.add_touch(); + /// ``` + pub fn add_touch(&mut self) -> TouchHandle { + let mut inner = self.arc.inner.lock().unwrap(); + let touch = TouchHandle::new(); + if inner.touch.is_some() { + // If there's already a tocuh device, remove it notify the clients about the change. + inner.touch = None; + #[cfg(feature = "wayland_frontend")] + inner.send_all_caps(); + } + inner.touch = Some(touch.clone()); + #[cfg(feature = "wayland_frontend")] + inner.send_all_caps(); + touch + } + + /// Access the touch device of this seat, if any. + pub fn get_touch(&self) -> Option> { + self.arc.inner.lock().unwrap().touch.clone() + } + + /// Remove the touch capability from this seat + /// + /// Clients will be appropriately notified. + pub fn remove_touch(&mut self) { + let mut inner = self.arc.inner.lock().unwrap(); + if inner.touch.is_some() { + inner.touch = None; + #[cfg(feature = "wayland_frontend")] + inner.send_all_caps(); + } + } + /// Gets this seat's name pub fn name(&self) -> &str { &self.arc.name diff --git a/src/input/touch/grab.rs b/src/input/touch/grab.rs new file mode 100644 index 000000000000..a73c5e4c53b9 --- /dev/null +++ b/src/input/touch/grab.rs @@ -0,0 +1,256 @@ +use std::fmt; + +use crate::{ + backend::input::TouchSlot, + input::SeatHandler, + utils::{Logical, Point, Serial}, +}; + +use super::{DownEvent, MotionEvent, TouchInnerHandle, UpEvent}; + +/// A trait to implement a touch grab +/// +/// In some context, it is necessary to temporarily change the behavior of the touch handler. This is +/// typically known as a touch grab. A typical example would be, during a drag'n'drop operation, +/// the underlying surfaces will no longer receive classic pointer event, but rather special events. +/// +/// This trait is the interface to intercept regular touch events and change them as needed, its +/// interface mimics the [`TouchHandle`](super::TouchHandle) interface. +/// +/// Any interactions with [`TouchHandle`](super::TouchHandle) +/// should be done using [`TouchInnerHandle`], as handle is borrowed/locked before grab methods are called, +/// so calling methods on [`TouchHandle`](super::TouchHandle) would result in a deadlock. +/// +/// If your logic decides that the grab should end, both [`TouchInnerHandle`] +/// and [`TouchHandle`](super::TouchHandle) have +/// a method to change it. +/// +/// When your grab ends (either as you requested it or if it was forcefully cancelled by the server), +/// the struct implementing this trait will be dropped. As such you should put clean-up logic in the destructor, +/// rather than trying to guess when the grab will end. +pub trait TouchGrab: Send { + /// A new touch point appeared + /// + /// This method allows you attach additional behavior to a down event, possibly altering it. + /// You generally will want to invoke [`TouchInnerHandle::down`] as part of your processing. If you + /// don't, the rest of the compositor will behave as if the down event never occurred. + /// + /// Some grabs (such as drag'n'drop, shell resize and motion) drop down events while they are active, + /// the default touch grab will keep the focus on the surface that started the grab. + fn down( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + seq: Serial, + ); + /// A touch point disappeared + /// + /// This method allows you attach additional behavior to a up event, possibly altering it. + /// You generally will want to invoke [`TouchInnerHandle::up`] as part of your processing. + /// If you don't, the rest of the compositor will behave as if the up event never occurred. + /// + /// Some grabs (such as drag'n'drop, shell resize and motion) drop up events while they are active, + /// but will end when the touch point that initiated the grab disappeared. + fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial); + + /// A touch point has changed coordinates. + /// + /// This method allows you attach additional behavior to a motion event, possibly altering it. + /// You generally will want to invoke [`TouchInnerHandle::motion`] as part of your processing. + /// If you don't, the rest of the compositor will behave as if the motion event never occurred. + /// + /// **Note** that this is **not** intended to update the focus of the touch point, the focus + /// is only set on a down event. The focus provided to this function can be used to find DnD + /// targets during touch motion. + fn motion( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + seq: Serial, + ); + + /// Marks the end of a set of events that logically belong together. + /// + /// This method allows you attach additional behavior to a frame event, possibly altering it. + /// You generally will want to invoke [`TouchInnerHandle::frame`] as part of your processing. + /// If you don't, the rest of the compositor will behave as if the frame event never occurred. + /// + /// This will to be called after one or more calls to down/motion events. + fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial); + + /// A touch session has been cancelled. + /// + /// This method allows you attach additional behavior to a cancel event, possibly altering it. + /// You generally will want to invoke [`TouchInnerHandle::cancel`] as part of your processing. + /// If you don't, the rest of the compositor will behave as if the cancel event never occurred. + /// + /// Usually called in case the compositor decides the touch stream is a global gesture. + fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial); + + /// The data about the event that started the grab. + fn start_data(&self) -> &GrabStartData; +} + +/// Data about the event that started the grab. +pub struct GrabStartData { + /// The focused surface and its location, if any, at the start of the grab. + /// + /// The location coordinates are in the global compositor space. + pub focus: Option<(::TouchFocus, Point)>, + /// The touch point that initiated the grab. + pub slot: TouchSlot, + /// The location of the down event that initiated the grab, in the global compositor space. + pub location: Point, +} + +impl fmt::Debug for GrabStartData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GrabStartData") + .field("focus", &self.focus.as_ref().map(|_| "...")) + .field("slot", &self.slot) + .field("location", &self.location) + .finish() + } +} + +impl Clone for GrabStartData { + fn clone(&self) -> Self { + GrabStartData { + focus: self.focus.clone(), + slot: self.slot, + location: self.location, + } + } +} + +pub(super) enum GrabStatus { + None, + Active(Serial, Box>), + Borrowed, +} + +// TouchGrab is a trait, so we have to impl Debug manually +impl fmt::Debug for GrabStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GrabStatus::None => f.debug_tuple("GrabStatus::None").finish(), + GrabStatus::Active(serial, _) => f.debug_tuple("GrabStatus::Active").field(&serial).finish(), + GrabStatus::Borrowed => f.debug_tuple("GrabStatus::Borrowed").finish(), + } + } +} + +// The default grab, the behavior when no particular grab is in progress +pub(super) struct DefaultGrab; + +impl TouchGrab for DefaultGrab { + fn down( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + seq: Serial, + ) { + handle.down(data, focus.clone(), event, seq); + handle.set_grab( + data, + event.serial, + ClickGrab { + start_data: GrabStartData { + focus, + slot: event.slot, + location: event.location, + }, + touch_points: 1, + }, + ); + } + + fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) { + handle.up(data, event, seq) + } + + fn motion( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + seq: Serial, + ) { + handle.motion(data, focus, event, seq) + } + + fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { + handle.frame(data, seq) + } + + fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { + handle.cancel(data, seq) + } + + fn start_data(&self) -> &GrabStartData { + unreachable!() + } +} + +// A click grab, basic grab started when an user clicks a surface +// to maintain it focused until the user releases the click. +// +// In case the user maintains several simultaneous clicks, release +// the grab once all are released. +struct ClickGrab { + start_data: GrabStartData, + touch_points: usize, +} + +impl TouchGrab for ClickGrab { + fn down( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + _focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + seq: Serial, + ) { + handle.down(data, self.start_data.focus.clone(), event, seq); + self.touch_points += 1; + } + + fn up(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, event: &UpEvent, seq: Serial) { + handle.up(data, event, seq); + self.touch_points = self.touch_points.saturating_sub(1); + if self.touch_points == 0 { + handle.unset_grab(data); + } + } + + fn motion( + &mut self, + data: &mut D, + handle: &mut TouchInnerHandle<'_, D>, + _focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + seq: Serial, + ) { + handle.motion(data, self.start_data.focus.clone(), event, seq) + } + + fn frame(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { + handle.frame(data, seq) + } + + fn cancel(&mut self, data: &mut D, handle: &mut TouchInnerHandle<'_, D>, seq: Serial) { + handle.cancel(data, seq); + handle.unset_grab(data); + } + + fn start_data(&self) -> &GrabStartData { + &self.start_data + } +} diff --git a/src/input/touch/mod.rs b/src/input/touch/mod.rs new file mode 100644 index 000000000000..f57d89d6203b --- /dev/null +++ b/src/input/touch/mod.rs @@ -0,0 +1,582 @@ +//! Touch-related types for smithay's input abstraction + +use std::collections::HashMap; +use std::fmt; +use std::sync::{Arc, Mutex}; + +use tracing::{info_span, instrument}; + +use crate::backend::input::TouchSlot; +use crate::utils::{IsAlive, Logical, Point, Serial, SerialCounter}; + +use self::grab::{DefaultGrab, GrabStatus}; +pub use grab::{GrabStartData, TouchGrab}; + +use super::{Seat, SeatHandler}; + +mod grab; + +/// An handle to a touch handler +/// +/// It can be cloned and all clones manipulate the same internal state. +/// +/// This handle gives you access to an interface to send touch events to your +/// clients. +/// +/// When sending events using this handle, they will be intercepted by a touch +/// grab if any is active. See the [`TouchGrab`] trait for details. +pub struct TouchHandle { + pub(crate) inner: Arc>>, + #[cfg(feature = "wayland_frontend")] + pub(crate) known_instances: + Arc)>>>, + pub(crate) span: tracing::Span, +} + +#[cfg(not(feature = "wayland_frontend"))] +impl fmt::Debug for TouchHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchHandle").field("inner", &self.inner).finish() + } +} + +#[cfg(feature = "wayland_frontend")] +impl fmt::Debug for TouchHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchHandle") + .field("inner", &self.inner) + .field("known_instances", &self.known_instances) + .finish() + } +} + +impl Clone for TouchHandle { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + #[cfg(feature = "wayland_frontend")] + known_instances: self.known_instances.clone(), + span: self.span.clone(), + } + } +} + +impl std::hash::Hash for TouchHandle { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.inner).hash(state) + } +} + +impl std::cmp::PartialEq for TouchHandle { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + +impl std::cmp::Eq for TouchHandle {} + +pub(crate) struct TouchInternal { + focus: HashMap>, + seq_counter: SerialCounter, + grab: GrabStatus, +} + +struct TouchSlotState { + focus: Option<(::TouchFocus, Point)>, + pending: Serial, + current: Option, +} + +impl fmt::Debug for TouchSlotState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchSlotState") + .field("focus", &self.focus) + .field("pending", &self.pending) + .field("current", &self.current) + .finish() + } +} + +// image_callback does not implement debug, so we have to impl Debug manually +impl fmt::Debug for TouchInternal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchInternal") + .field("focus", &self.focus) + .field("grab", &self.grab) + .finish() + } +} + +/// Pointer motion event +#[derive(Debug, Clone)] +pub struct DownEvent { + /// Slot of this event + pub slot: TouchSlot, + /// Location of the touch in compositor space + pub location: Point, + /// Serial of the event + pub serial: Serial, + /// Timestamp of the event, with millisecond granularity + pub time: u32, +} + +/// Pointer motion event +#[derive(Debug, Clone)] +pub struct UpEvent { + /// Slot of this event + pub slot: TouchSlot, + /// Serial of the event + pub serial: Serial, + /// Timestamp of the event, with millisecond granularity + pub time: u32, +} + +/// Pointer motion event +#[derive(Debug, Clone)] +pub struct MotionEvent { + /// Slot of this event + pub slot: TouchSlot, + /// Location of the touch in compositor space + pub location: Point, + /// Timestamp of the event, with millisecond granularity + pub time: u32, +} + +/// Pointer motion event +#[derive(Debug, Clone, Copy)] +pub struct ShapeEvent { + /// Slot of this event + pub slot: TouchSlot, + /// Length of the major axis in surface-local coordinates + pub major: f64, + /// Length of the minor axis in surface-local coordinates + pub minor: f64, +} + +/// Pointer motion event +#[derive(Debug, Clone, Copy)] +pub struct OrientationEvent { + /// Slot of this event + pub slot: TouchSlot, + /// Angle between major axis and positive surface y-axis in degrees + pub orientation: f64, +} + +/// Trait representing object that can receive touch interactions +pub trait TouchTarget: IsAlive + PartialEq + Clone + fmt::Debug + Send +where + D: SeatHandler, +{ + /// A new touch point has appeared on the target. + /// + /// This touch point is assigned a unique ID. Future events from this touch point reference this ID. + /// The ID ceases to be valid after a touch up event and may be reused in the future. + fn down(&self, seat: &Seat, data: &mut D, event: &DownEvent, seq: Serial); + + /// The touch point has disappeared. + /// + /// No further events will be sent for this touch point and the touch point's ID + /// is released and may be reused in a future touch down event. + fn up(&self, seat: &Seat, data: &mut D, event: &UpEvent, seq: Serial); + + /// A touch point has changed coordinates. + fn motion(&self, seat: &Seat, data: &mut D, event: &MotionEvent, seq: Serial); + + /// Indicates the end of a set of events that logically belong together. + fn frame(&self, seat: &Seat, data: &mut D, seq: Serial); + + /// Touch session cancelled. + /// + /// Touch cancellation applies to all touch points currently active on this target. + /// The client is responsible for finalizing the touch points, future touch points on + /// this target may reuse the touch point ID. + fn cancel(&self, seat: &Seat, data: &mut D, seq: Serial); + + /// Sent when a touch point has changed its shape. + /// + /// A touch point shape is approximated by an ellipse through the major and minor axis length. + /// The major axis length describes the longer diameter of the ellipse, while the minor axis + /// length describes the shorter diameter. Major and minor are orthogonal and both are specified + /// in surface-local coordinates. The center of the ellipse is always at the touch point location + /// as reported by [`TouchTarget::down`] or [`TouchTarget::motion`]. + fn shape(&self, seat: &Seat, data: &mut D, event: &ShapeEvent, seq: Serial); + + /// Sent when a touch point has changed its orientation. + /// + /// The orientation describes the clockwise angle of a touch point's major axis to the positive surface + /// y-axis and is normalized to the -180 to +180 degree range. The granularity of orientation depends + /// on the touch device, some devices only support binary rotation values between 0 and 90 degrees. + fn orientation(&self, seat: &Seat, data: &mut D, event: &OrientationEvent, seq: Serial); +} + +impl TouchHandle { + pub(crate) fn new() -> TouchHandle { + TouchHandle { + inner: Arc::new(Mutex::new(TouchInternal::new())), + #[cfg(feature = "wayland_frontend")] + known_instances: Arc::new(Mutex::new(Vec::new())), + span: info_span!("input_touch"), + } + } + + /// Change the current grab on this touch to the provided grab + /// + /// Overwrites any current grab. + #[instrument(level = "debug", parent = &self.span, skip(self, data, grab))] + pub fn set_grab + 'static>(&self, data: &mut D, grab: G, serial: Serial) { + let seat = self.get_seat(data); + self.inner.lock().unwrap().set_grab(data, &seat, serial, grab); + } + + /// Remove any current grab on this touch, resetting it to the default behavior + #[instrument(level = "debug", parent = &self.span, skip(self, data))] + pub fn unset_grab(&self, data: &mut D) { + let seat = self.get_seat(data); + self.inner.lock().unwrap().unset_grab(data, &seat); + } + + /// Check if this touch is currently grabbed with this serial + pub fn has_grab(&self, serial: Serial) -> bool { + let guard = self.inner.lock().unwrap(); + match guard.grab { + GrabStatus::Active(s, _) => s == serial, + _ => false, + } + } + + /// Check if this touch is currently being grabbed + pub fn is_grabbed(&self) -> bool { + let guard = self.inner.lock().unwrap(); + !matches!(guard.grab, GrabStatus::None) + } + + /// Returns the start data for the grab, if any. + pub fn grab_start_data(&self) -> Option> { + let guard = self.inner.lock().unwrap(); + match &guard.grab { + GrabStatus::Active(_, g) => Some(g.start_data().clone()), + _ => None, + } + } + + /// Notify that a new touch point appeared + /// + /// You provide the location of the touch, in the form of: + /// + /// - The coordinates of the touch in the global compositor space + /// - The surface on top of which the touch point is, and the coordinates of its + /// origin in the global compositor space (or `None` of the touch is not + /// on top of a client surface). + pub fn down( + &self, + data: &mut D, + focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + ) { + let mut inner = self.inner.lock().unwrap(); + let seat = self.get_seat(data); + let seq = inner.seq_counter.next_serial(); + inner.with_grab(&seat, |handle, grab| { + grab.down(data, handle, focus, event, seq); + }); + } + + /// Notify that a touch point disappeared + pub fn up(&self, data: &mut D, event: &UpEvent) { + let mut inner = self.inner.lock().unwrap(); + let seat = self.get_seat(data); + let seq = inner.seq_counter.next_serial(); + inner.with_grab(&seat, |handle, grab| { + grab.up(data, handle, event, seq); + }); + } + + /// Notify that a touch point has changed coordinates. + /// + /// You provide the location of the touch, in the form of: + /// + /// - The coordinates of the touch in the global compositor space + /// - The surface on top of which the touch point is, and the coordinates of its + /// origin in the global compositor space (or `None` of the touch is not + /// on top of a client surface). + /// + /// **Note** that this will **not** update the focus of the touch point, the focus + /// is only set on [`TouchHandle::down`]. The focus provided to this function + /// can be used to find DnD targets during touch motion. + pub fn motion( + &self, + data: &mut D, + focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + ) { + let mut inner = self.inner.lock().unwrap(); + let seat = self.get_seat(data); + let seq = inner.seq_counter.next_serial(); + inner.with_grab(&seat, |handle, grab| { + grab.motion(data, handle, focus, event, seq); + }); + } + + /// Notify about the end of a set of events that logically belong together. + /// + /// This needs to be called after one or move calls to [`TouchHandle::down`] or [`TouchHandle::motion`] + pub fn frame(&self, data: &mut D) { + let mut inner = self.inner.lock().unwrap(); + let seat = self.get_seat(data); + let seq = inner.seq_counter.next_serial(); + inner.with_grab(&seat, |handle, grab| { + grab.frame(data, handle, seq); + }); + } + + /// Notify that the touch session has been cancelled. + /// + /// Use in case you decide the touch stream is a global gesture. + /// This will remove all current focus targets, and no further events will be sent + /// until a new touch point appears. + pub fn cancel(&self, data: &mut D) { + let mut inner = self.inner.lock().unwrap(); + let seat = self.get_seat(data); + let seq = inner.seq_counter.next_serial(); + inner.with_grab(&seat, |handle, grab| { + grab.cancel(data, handle, seq); + }); + } + + fn get_seat(&self, data: &mut D) -> Seat { + let seat_state = data.seat_state(); + seat_state + .seats + .iter() + .find(|seat| seat.get_touch().map(|h| &h == self).unwrap_or(false)) + .cloned() + .unwrap() + } +} + +/// This inner handle is accessed from inside a pointer grab logic, and directly +/// sends event to the client +pub struct TouchInnerHandle<'a, D: SeatHandler> { + inner: &'a mut TouchInternal, + seat: &'a Seat, +} + +impl<'a, D: SeatHandler> fmt::Debug for TouchInnerHandle<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchInnerHandle") + .field("inner", &self.inner) + .field("seat", &self.seat.arc.name) + .finish() + } +} + +impl<'a, D: SeatHandler + 'static> TouchInnerHandle<'a, D> { + /// Change the current grab on this pointer to the provided grab + /// + /// Overwrites any current grab. + pub fn set_grab + 'static>(&mut self, data: &mut D, serial: Serial, grab: G) { + self.inner.set_grab(data, self.seat, serial, grab); + } + + /// Remove any current grab on this pointer, resetting it to the default behavior + /// + /// This will also restore the focus of the underlying pointer if restore_focus + /// is [`true`] + pub fn unset_grab(&mut self, data: &mut D) { + self.inner.unset_grab(data, self.seat); + } + + /// Notify that a new touch point appeared + /// + /// You provide the location of the touch, in the form of: + /// + /// - The coordinates of the touch in the global compositor space + /// - The surface on top of which the touch point is, and the coordinates of its + /// origin in the global compositor space (or `None` of the touch is not + /// on top of a client surface). + pub fn down( + &mut self, + data: &mut D, + focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + seq: Serial, + ) { + self.inner.down(data, self.seat, focus, event, seq) + } + + /// Notify that a touch point disappeared + pub fn up(&mut self, data: &mut D, event: &UpEvent, seq: Serial) { + self.inner.up(data, self.seat, event, seq) + } + + /// Notify that a touch point has changed coordinates. + /// + /// You provide the location of the touch, in the form of: + /// + /// - The coordinates of the touch in the global compositor space + /// - The surface on top of which the touch point is, and the coordinates of its + /// origin in the global compositor space (or `None` of the touch is not + /// on top of a client surface). + /// + /// **Note** that this will **not** update the focus of the touch point, the focus + /// is only set on [`TouchHandle::down`]. The focus provided to this function + /// can be used to find DnD targets during touch motion. + pub fn motion( + &mut self, + data: &mut D, + focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + seq: Serial, + ) { + self.inner.motion(data, self.seat, focus, event, seq) + } + + /// Notify about the end of a set of events that logically belong together. + /// + /// This needs to be called after one or move calls to [`TouchHandle::down`] or [`TouchHandle::motion`] + pub fn frame(&mut self, data: &mut D, seq: Serial) { + self.inner.frame(data, self.seat, seq) + } + + /// Notify that the touch session has been cancelled. + /// + /// Use in case you decide the touch stream is a global gesture. + /// This will remove all current focus targets, and no further events will be sent + /// until a new touch point appears. + pub fn cancel(&mut self, data: &mut D, seq: Serial) { + self.inner.cancel(data, self.seat, seq) + } +} + +impl TouchInternal { + fn new() -> Self { + Self { + focus: Default::default(), + seq_counter: SerialCounter::new(), + grab: GrabStatus::None, + } + } + + fn set_grab + 'static>( + &mut self, + _data: &mut D, + _seat: &Seat, + serial: Serial, + grab: G, + ) { + self.grab = GrabStatus::Active(serial, Box::new(grab)); + } + + fn unset_grab(&mut self, _data: &mut D, _seat: &Seat) { + self.grab = GrabStatus::None; + } + + fn down( + &mut self, + data: &mut D, + seat: &Seat, + focus: Option<(::TouchFocus, Point)>, + event: &DownEvent, + seq: Serial, + ) { + self.focus + .entry(event.slot) + .and_modify(|state| { + state.pending = seq; + state.focus = focus.clone(); + }) + .or_insert_with(|| TouchSlotState { + focus, + pending: seq, + current: None, + }); + let state = self.focus.get(&event.slot).unwrap(); + if let Some((focus, loc)) = state.focus.as_ref() { + let mut new_event = event.clone(); + new_event.location -= loc.to_f64(); + focus.down(seat, data, &new_event, seq); + } + } + + fn up(&mut self, data: &mut D, seat: &Seat, event: &UpEvent, seq: Serial) { + let Some(state) = self.focus.get_mut(&event.slot) else { + return; + }; + state.pending = seq; + if let Some((focus, _)) = state.focus.take() { + focus.up(seat, data, event, seq); + } + } + + fn motion( + &mut self, + data: &mut D, + seat: &Seat, + _focus: Option<(::TouchFocus, Point)>, + event: &MotionEvent, + seq: Serial, + ) { + let Some(state) = self.focus.get_mut(&event.slot) else { + return; + }; + state.pending = seq; + if let Some((focus, loc)) = state.focus.as_ref() { + let mut new_event = event.clone(); + new_event.location -= loc.to_f64(); + focus.motion(seat, data, &new_event, seq); + } + } + + fn frame(&mut self, data: &mut D, seat: &Seat, seq: Serial) { + for state in self.focus.values_mut() { + if state.current.map(|c| c >= state.pending).unwrap_or(false) { + continue; + } + state.current = Some(seq); + if let Some((focus, _)) = state.focus.as_ref() { + focus.frame(seat, data, seq); + } + } + } + + fn cancel(&mut self, data: &mut D, seat: &Seat, seq: Serial) { + for state in self.focus.values_mut() { + if state.current.map(|c| c >= state.pending).unwrap_or(false) { + continue; + } + state.current = Some(seq); + if let Some((focus, _)) = state.focus.take() { + focus.cancel(seat, data, seq); + } + } + } + + fn with_grab(&mut self, seat: &Seat, f: F) + where + F: FnOnce(&mut TouchInnerHandle<'_, D>, &mut dyn TouchGrab), + { + let mut grab = std::mem::replace(&mut self.grab, GrabStatus::Borrowed); + match grab { + GrabStatus::Borrowed => panic!("Accessed a touch grab from within a touch grab access."), + GrabStatus::Active(_, ref mut handler) => { + // If this grab is associated with a surface that is no longer alive, discard it + if let Some((ref focus, _)) = handler.start_data().focus { + if !focus.alive() { + self.grab = GrabStatus::None; + f(&mut TouchInnerHandle { inner: self, seat }, &mut DefaultGrab); + return; + } + } + f(&mut TouchInnerHandle { inner: self, seat }, &mut **handler); + } + GrabStatus::None => { + f(&mut TouchInnerHandle { inner: self, seat }, &mut DefaultGrab); + } + } + + if let GrabStatus::Borrowed = self.grab { + // the grab has not been ended nor replaced, put it back in place + self.grab = grab; + } + } +} diff --git a/src/utils/serial.rs b/src/utils/serial.rs index a56429cea794..5fd478317acf 100644 --- a/src/utils/serial.rs +++ b/src/utils/serial.rs @@ -3,9 +3,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; /// A global [`SerialCounter`] for use in your compositor. /// /// Is is also used internally by some parts of Smithay. -pub static SERIAL_COUNTER: SerialCounter = SerialCounter { - serial: AtomicU32::new(1), -}; +pub static SERIAL_COUNTER: SerialCounter = SerialCounter::new(); /// A serial type, whose comparison takes into account the wrapping-around behavior of the /// underlying counter. @@ -69,6 +67,13 @@ pub struct SerialCounter { } impl SerialCounter { + /// Create a new counter starting at `1` + pub const fn new() -> Self { + Self { + serial: AtomicU32::new(1), + } + } + /// Retrieve the next serial from the counter pub fn next_serial(&self) -> Serial { let _ = self diff --git a/src/wayland/cursor_shape.rs b/src/wayland/cursor_shape.rs index 5e1450edad98..d2b582831447 100644 --- a/src/wayland/cursor_shape.rs +++ b/src/wayland/cursor_shape.rs @@ -19,6 +19,7 @@ //! # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, //! # GestureHoldBeginEvent, GestureHoldEndEvent}, //! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, +//! # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, //! # Seat, SeatHandler, SeatState, //! # }; //! # use smithay::utils::{IsAlive, Serial}; @@ -67,6 +68,15 @@ //! # ) {} //! # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} //! # } +//! # impl TouchTarget for Target { +//! # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} +//! # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} +//! # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} +//! # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} +//! # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} +//! # } //! # struct State { //! # seat_state: SeatState, //! # }; @@ -74,6 +84,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = Target; //! # type PointerFocus = Target; +//! # type TouchFocus = Target; //! # //! # fn seat_state(&mut self) -> &mut SeatState { //! # &mut self.seat_state diff --git a/src/wayland/idle_notify/mod.rs b/src/wayland/idle_notify/mod.rs index b979b5d18fd3..f64e5fd09c86 100644 --- a/src/wayland/idle_notify/mod.rs +++ b/src/wayland/idle_notify/mod.rs @@ -23,6 +23,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = WlSurface; //! # type PointerFocus = WlSurface; +//! # type TouchFocus = WlSurface; //! # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } //! # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } //! # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } diff --git a/src/wayland/input_method/mod.rs b/src/wayland/input_method/mod.rs index 6060ba1787ae..f414da75b46f 100644 --- a/src/wayland/input_method/mod.rs +++ b/src/wayland/input_method/mod.rs @@ -39,6 +39,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; +//! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } diff --git a/src/wayland/keyboard_shortcuts_inhibit/mod.rs b/src/wayland/keyboard_shortcuts_inhibit/mod.rs index 8ba9977fdaa5..2c7fcada9b52 100644 --- a/src/wayland/keyboard_shortcuts_inhibit/mod.rs +++ b/src/wayland/keyboard_shortcuts_inhibit/mod.rs @@ -183,6 +183,7 @@ pub trait KeyboardShortcutsInhibitorSeat { /// # impl SeatHandler for State { /// # type KeyboardFocus = WlSurface; /// # type PointerFocus = WlSurface; + /// # type TouchFocus = WlSurface; /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } /// # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } diff --git a/src/wayland/pointer_gestures.rs b/src/wayland/pointer_gestures.rs index dee4960e20a6..ec9402fc84cb 100644 --- a/src/wayland/pointer_gestures.rs +++ b/src/wayland/pointer_gestures.rs @@ -28,6 +28,7 @@ //! # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, //! # GestureHoldBeginEvent, GestureHoldEndEvent}, //! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, +//! # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, //! # Seat, SeatHandler, SeatState, //! # }; //! # use smithay::utils::{IsAlive, Serial}; @@ -68,6 +69,15 @@ //! # ) {} //! # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} //! # } +//! # impl TouchTarget for Target { +//! # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} +//! # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} +//! # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} +//! # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} +//! # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} +//! # } //! # struct State { //! # seat_state: SeatState, //! # }; @@ -75,6 +85,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = Target; //! # type PointerFocus = Target; +//! # type TouchFocus = Target; //! # //! # fn seat_state(&mut self) -> &mut SeatState { //! # &mut self.seat_state diff --git a/src/wayland/relative_pointer.rs b/src/wayland/relative_pointer.rs index b4e42f4d3fdc..7e67980da246 100644 --- a/src/wayland/relative_pointer.rs +++ b/src/wayland/relative_pointer.rs @@ -16,6 +16,7 @@ //! # GesturePinchBeginEvent, GesturePinchUpdateEvent, GesturePinchEndEvent, //! # GestureHoldBeginEvent, GestureHoldEndEvent}, //! # keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, +//! # touch::{DownEvent, UpEvent, MotionEvent as TouchMotionEvent, ShapeEvent, OrientationEvent, TouchTarget}, //! # Seat, SeatHandler, SeatState, //! # }; //! # use smithay::utils::{IsAlive, Serial}; @@ -56,6 +57,15 @@ //! # ) {} //! # fn modifiers(&self, seat: &Seat, data: &mut State, modifiers: ModifiersState, serial: Serial) {} //! # } +//! # impl TouchTarget for Target { +//! # fn down(&self, seat: &Seat, data: &mut State, event: &DownEvent, seq: Serial) {} +//! # fn up(&self, seat: &Seat, data: &mut State, event: &UpEvent, seq: Serial) {} +//! # fn motion(&self, seat: &Seat, data: &mut State, event: &TouchMotionEvent, seq: Serial) {} +//! # fn frame(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn cancel(&self, seat: &Seat, data: &mut State, seq: Serial) {} +//! # fn shape(&self, seat: &Seat, data: &mut State, event: &ShapeEvent, seq: Serial) {} +//! # fn orientation(&self, seat: &Seat, data: &mut State, event: &OrientationEvent, seq: Serial) {} +//! # } //! # struct State { //! # seat_state: SeatState, //! # }; @@ -63,6 +73,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = Target; //! # type PointerFocus = Target; +//! # type TouchFocus = Target; //! # //! # fn seat_state(&mut self) -> &mut SeatState { //! # &mut self.seat_state diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index a6a029213474..b5b87a780163 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -30,6 +30,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; +//! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } @@ -68,7 +69,7 @@ use crate::input::{Inner, Seat, SeatHandler, SeatRc, SeatState}; pub use self::{ keyboard::KeyboardUserData, pointer::{PointerUserData, CURSOR_IMAGE_ROLE}, - touch::{TouchHandle, TouchUserData}, + touch::TouchUserData, }; use wayland_server::{ @@ -200,60 +201,60 @@ impl Seat { self.arc.inner.lock().unwrap().global.as_ref().cloned() } - /// Adds the touch capability to this seat - /// - /// You are provided a [`TouchHandle`], which allows you to send input events - /// to this pointer. This handle can be cloned. - /// - /// Calling this method on a seat that already has a touch capability - /// will overwrite it, and will be seen by the clients as if the - /// touchscreen was unplugged and a new one was plugged in. - /// - /// # Examples - /// - /// ```no_run - /// # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; - /// # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; - /// # - /// # struct State; - /// # impl SeatHandler for State { - /// # type KeyboardFocus = WlSurface; - /// # type PointerFocus = WlSurface; - /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } - /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } - /// # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } - /// # } - /// # let mut seat: Seat = unimplemented!(); - /// let touch_handle = seat.add_touch(); - /// ``` - pub fn add_touch(&mut self) -> TouchHandle { - let mut inner = self.arc.inner.lock().unwrap(); - let touch = TouchHandle::new(); - if inner.touch.is_some() { - // If there's already a tocuh device, remove it notify the clients about the change. - inner.touch = None; - inner.send_all_caps(); - } - inner.touch = Some(touch.clone()); - inner.send_all_caps(); - touch - } - - /// Access the touch device of this seat, if any. - pub fn get_touch(&self) -> Option { - self.arc.inner.lock().unwrap().touch.clone() - } - - /// Remove the touch capability from this seat - /// - /// Clients will be appropriately notified. - pub fn remove_touch(&mut self) { - let mut inner = self.arc.inner.lock().unwrap(); - if inner.touch.is_some() { - inner.touch = None; - inner.send_all_caps(); - } - } + // /// Adds the touch capability to this seat + // /// + // /// You are provided a [`TouchHandle`], which allows you to send input events + // /// to this pointer. This handle can be cloned. + // /// + // /// Calling this method on a seat that already has a touch capability + // /// will overwrite it, and will be seen by the clients as if the + // /// touchscreen was unplugged and a new one was plugged in. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// # use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; + // /// # use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; + // /// # + // /// # struct State; + // /// # impl SeatHandler for State { + // /// # type KeyboardFocus = WlSurface; + // /// # type PointerFocus = WlSurface; + // /// # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } + // /// # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } + // /// # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } + // /// # } + // /// # let mut seat: Seat = unimplemented!(); + // /// let touch_handle = seat.add_touch(); + // /// ``` + // pub fn add_touch(&mut self) -> TouchHandle { + // let mut inner = self.arc.inner.lock().unwrap(); + // let touch = TouchHandle::new(); + // if inner.touch.is_some() { + // // If there's already a tocuh device, remove it notify the clients about the change. + // inner.touch = None; + // inner.send_all_caps(); + // } + // inner.touch = Some(touch.clone()); + // inner.send_all_caps(); + // touch + // } + + // /// Access the touch device of this seat, if any. + // pub fn get_touch(&self) -> Option { + // self.arc.inner.lock().unwrap().touch.clone() + // } + + // /// Remove the touch capability from this seat + // /// + // /// Clients will be appropriately notified. + // pub fn remove_touch(&mut self) { + // let mut inner = self.arc.inner.lock().unwrap(); + // if inner.touch.is_some() { + // inner.touch = None; + // inner.send_all_caps(); + // } + // } } /// User data for seat @@ -285,7 +286,7 @@ macro_rules! delegate_seat { $crate::reexports::wayland_server::protocol::wl_keyboard::WlKeyboard: $crate::wayland::seat::KeyboardUserData<$ty> ] => $crate::input::SeatState<$ty>); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?$ty: [ - $crate::reexports::wayland_server::protocol::wl_touch::WlTouch: $crate::wayland::seat::TouchUserData + $crate::reexports::wayland_server::protocol::wl_touch::WlTouch: $crate::wayland::seat::TouchUserData<$ty> ] => $crate::input::SeatState<$ty>); }; } @@ -295,7 +296,7 @@ where D: Dispatch>, D: Dispatch>, D: Dispatch>, - D: Dispatch, + D: Dispatch>, D: SeatHandler, ::KeyboardFocus: WaylandFocus, D: 'static, @@ -382,7 +383,7 @@ where D: Dispatch>, D: Dispatch>, D: Dispatch>, - D: Dispatch, + D: Dispatch>, D: SeatHandler, D: 'static, { diff --git a/src/wayland/seat/touch.rs b/src/wayland/seat/touch.rs index 8b67f7369703..cd3f6054a04c 100644 --- a/src/wayland/seat/touch.rs +++ b/src/wayland/seat/touch.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; +use std::fmt; use wayland_server::{ backend::ClientId, @@ -10,186 +7,116 @@ use wayland_server::{ }; use super::{SeatHandler, SeatState}; -use crate::backend::input::TouchSlot; -use crate::utils::Serial; -use crate::utils::{Logical, Point}; -use crate::wayland::seat::wl_surface::WlSurface; +use crate::input::touch::TouchTarget; +use crate::input::{ + touch::{MotionEvent, OrientationEvent, ShapeEvent}, + Seat, +}; +use crate::{input::touch::DownEvent, wayland::seat::wl_surface::WlSurface}; +use crate::{input::touch::TouchHandle, utils::Serial}; -/// An handle to a touch handler. -/// -/// It can be cloned and all clones manipulate the same internal state. -/// -/// This handle gives you access to an interface to send touch events to your -/// clients. -#[derive(Debug, Clone)] -pub struct TouchHandle { - inner: Arc>, +impl TouchHandle { + pub(crate) fn new_touch(&self, touch: WlTouch) { + let mut guard = self.known_instances.lock().unwrap(); + guard.push((touch, None)); + } } -impl TouchHandle { - pub(crate) fn new() -> Self { - Self { - inner: Default::default(), +fn for_each_focused_touch( + seat: &Seat, + surface: &WlSurface, + seq: Serial, + mut f: impl FnMut(WlTouch), +) { + if let Some(touch) = seat.get_touch() { + let mut inner = touch.known_instances.lock().unwrap(); + for (ptr, last_seq) in &mut *inner { + if ptr.id().same_client_as(&surface.id()) && last_seq.map(|last| last < seq).unwrap_or(true) { + *last_seq = Some(seq); + f(ptr.clone()); + } } } +} - /// Register a new touch handle to this handler - /// - /// This should be done first, before anything else is done with this touch handle. - pub(crate) fn new_touch(&self, touch: WlTouch) { - self.inner.lock().unwrap().known_handles.push(touch); +#[cfg(feature = "wayland_frontend")] +impl TouchTarget for WlSurface +where + D: SeatHandler + 'static, +{ + fn down(&self, seat: &Seat, _data: &mut D, event: &DownEvent, seq: Serial) { + let serial = event.serial; + let slot = event.slot; + for_each_focused_touch(seat, self, seq, |touch| { + touch.down( + serial.into(), + event.time, + self, + slot.into(), + event.location.x, + event.location.y, + ); + }) } - /// Notify clients about new touch points. - /// - /// The `position` parameter is in surface-local coordinates. - pub fn down( - &self, - serial: Serial, - time: u32, - surface: &WlSurface, - position: Point, - slot: TouchSlot, - ) { - self.inner - .lock() - .unwrap() - .down(serial, time, surface, position, slot); + fn up(&self, seat: &Seat, _data: &mut D, event: &crate::input::touch::UpEvent, seq: Serial) { + let serial = event.serial; + let slot = event.slot; + for_each_focused_touch(seat, self, seq, |touch| { + touch.up(serial.into(), event.time, slot.into()); + }) } - /// Notify clients about touch point removal. - pub fn up(&self, serial: Serial, time: u32, slot: TouchSlot) { - self.inner.lock().unwrap().up(serial, time, slot); + fn motion(&self, seat: &Seat, _data: &mut D, event: &MotionEvent, seq: Serial) { + let slot = event.slot; + for_each_focused_touch(seat, self, seq, |touch| { + touch.motion(event.time, slot.into(), event.location.x, event.location.y); + }) } - /// Notify clients about touch motion. - pub fn motion(&self, time: u32, slot: TouchSlot, location: Point) { - self.inner.lock().unwrap().motion(time, slot, location); + fn frame(&self, seat: &Seat, _data: &mut D, seq: Serial) { + for_each_focused_touch(seat, self, seq, |touch| { + touch.frame(); + }) } - /// Notify clients about touch shape changes. - pub fn shape(&self, slot: TouchSlot, major: f64, minor: f64) { - self.inner.lock().unwrap().shape(slot, major, minor); + fn cancel(&self, seat: &Seat, _data: &mut D, seq: Serial) { + for_each_focused_touch(seat, self, seq, |touch| { + touch.cancel(); + }) } - /// Notify clients about touch shape orientation. - pub fn orientation(&self, slot: TouchSlot, orientation: f64) { - self.inner.lock().unwrap().orientation(slot, orientation); + fn shape(&self, seat: &Seat, _data: &mut D, event: &ShapeEvent, seq: Serial) { + let slot = event.slot; + for_each_focused_touch(seat, self, seq, |touch| { + touch.shape(slot.into(), event.major, event.minor); + }) } - /// Notify clients about touch cancellation. - /// - /// This should be sent by the compositor when the touch stream is recognized as - /// a global gesture. Cancellation applies to all currently active touch slots. - pub fn cancel(&self) { - self.inner.lock().unwrap().cancel(); + fn orientation(&self, seat: &Seat, _data: &mut D, event: &OrientationEvent, seq: Serial) { + let slot = event.slot; + for_each_focused_touch(seat, self, seq, |touch| { + touch.orientation(slot.into(), event.orientation); + }) } } -/// Touch-slot focused Wayland client state. -#[derive(Default, Debug)] -struct TouchFocus { - surface_offset: Point, - handles: Vec, +/// User data for touch +pub struct TouchUserData { + pub(crate) handle: Option>, } -#[derive(Default, Debug)] -struct TouchInternal { - known_handles: Vec, - focus: HashMap, -} - -impl TouchInternal { - fn down( - &mut self, - serial: Serial, - time: u32, - surface: &WlSurface, - position: Point, - slot: TouchSlot, - ) { - // Update focused client state. - let focus = self.focus.entry(slot).or_default(); - focus.handles.clear(); - - // Select all WlTouch instances associated to the active WlSurface. - for handle in &self.known_handles { - if handle.id().same_client_as(&surface.id()) { - focus.handles.push(handle.clone()); - } - } - - self.with_focused_handles(slot, |handle| { - handle.down(serial.into(), time, surface, slot.into(), position.x, position.y) - }); - } - - fn up(&mut self, serial: Serial, time: u32, slot: TouchSlot) { - self.with_focused_handles(slot, |handle| handle.up(serial.into(), time, slot.into())); - - // Clear this slot's associated WlTouch handles. - if let Some(focus) = self.focus.get_mut(&slot) { - focus.handles.clear(); - } - } - - fn motion(&self, time: u32, slot: TouchSlot, location: Point) { - let focus = match self.focus.get(&slot) { - Some(slot) => slot, - None => return, - }; - - let (x, y) = (location - focus.surface_offset).into(); - self.with_focused_handles(slot, |handle| handle.motion(time, slot.into(), x, y)); - } - - fn shape(&self, slot: TouchSlot, major: f64, minor: f64) { - self.with_focused_handles(slot, |handle| { - if handle.version() >= 6 { - handle.shape(slot.into(), major, minor); - } - }); - } - - fn orientation(&self, slot: TouchSlot, orientation: f64) { - self.with_focused_handles(slot, |handle| { - if handle.version() >= 6 { - handle.orientation(slot.into(), orientation); - } - }); +impl fmt::Debug for TouchUserData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TouchUserData") + .field("handle", &self.handle) + .finish() } - - fn cancel(&mut self) { - for handle in &self.known_handles { - handle.cancel(); - } - - self.focus.clear(); - } - - #[inline] - fn with_focused_handles(&self, slot: TouchSlot, mut f: F) - where - F: FnMut(&WlTouch), - { - if let Some(focus) = self.focus.get(&slot) { - for handle in &focus.handles { - f(handle); - handle.frame(); - } - } - } -} - -/// User data for keyboard -#[derive(Debug)] -pub struct TouchUserData { - pub(crate) handle: Option, } -impl Dispatch for SeatState +impl Dispatch, D> for SeatState where - D: Dispatch, + D: Dispatch>, D: SeatHandler, D: 'static, { @@ -198,20 +125,19 @@ where _client: &wayland_server::Client, _resource: &WlTouch, _request: wl_touch::Request, - _data: &TouchUserData, + _data: &TouchUserData, _dhandle: &DisplayHandle, _data_init: &mut wayland_server::DataInit<'_, D>, ) { } - fn destroyed(_state: &mut D, _client_id: ClientId, touch: &WlTouch, data: &TouchUserData) { + fn destroyed(_state: &mut D, _client_id: ClientId, touch: &WlTouch, data: &TouchUserData) { if let Some(ref handle) = data.handle { handle - .inner + .known_instances .lock() .unwrap() - .known_handles - .retain(|k| k.id() != touch.id()) + .retain(|(p, _)| p.id() != touch.id()); } } } diff --git a/src/wayland/selection/data_device/device.rs b/src/wayland/selection/data_device/device.rs index 4b32f830d91d..5a65527c118e 100644 --- a/src/wayland/selection/data_device/device.rs +++ b/src/wayland/selection/data_device/device.rs @@ -42,6 +42,7 @@ where D: DataDeviceHandler, D: SeatHandler, ::PointerFocus: WaylandFocus, + ::TouchFocus: WaylandFocus, ::KeyboardFocus: WaylandFocus, D: 'static, { @@ -83,13 +84,35 @@ where let start_data = pointer.grab_start_data().unwrap(); pointer.set_grab( handler, - dnd_grab::DnDGrab::new(dh, start_data, source, origin, seat, icon), + dnd_grab::DnDGrab::new_pointer(dh, start_data, source, origin, seat, icon), serial, Focus::Clear, ); return; } } + if let Some(touch) = seat.get_touch() { + if touch.has_grab(serial) { + if let Some(ref icon) = icon { + if compositor::give_role(icon, DND_ICON_ROLE).is_err() { + resource.post_error( + wl_data_device::Error::Role, + "Given surface already has an other role", + ); + return; + } + } + // The StartDrag is in response to a touch implicit grab, all is good + handler.started(source.clone(), icon.clone(), seat.clone()); + let start_data = touch.grab_start_data().unwrap(); + touch.set_grab( + handler, + dnd_grab::DnDGrab::new_touch(dh, start_data, source, origin, seat, icon), + serial, + ); + return; + } + } debug!(serial = ?serial, client = ?client, "denying drag from client without implicit grab"); } wl_data_device::Request::SetSelection { source, .. } => { diff --git a/src/wayland/selection/data_device/dnd_grab.rs b/src/wayland/selection/data_device/dnd_grab.rs index b70cc64d500f..5fee3488f39f 100644 --- a/src/wayland/selection/data_device/dnd_grab.rs +++ b/src/wayland/selection/data_device/dnd_grab.rs @@ -23,9 +23,10 @@ use crate::{ GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, + touch::{GrabStartData as TouchGrabStartData, TouchGrab}, Seat, SeatHandler, }, - utils::{IsAlive, Logical, Point}, + utils::{IsAlive, Logical, Point, Serial, SERIAL_COUNTER}, wayland::{seat::WaylandFocus, selection::seat_data::SeatData}, }; @@ -33,7 +34,8 @@ use super::{with_source_metadata, ClientDndGrabHandler, DataDeviceHandler}; pub(crate) struct DnDGrab { dh: DisplayHandle, - start_data: PointerGrabStartData, + pointer_start_data: Option>, + touch_start_data: Option>, data_source: Option, current_focus: Option, pending_offers: Vec, @@ -44,7 +46,7 @@ pub(crate) struct DnDGrab { } impl DnDGrab { - pub(crate) fn new( + pub(crate) fn new_pointer( dh: &DisplayHandle, start_data: PointerGrabStartData, source: Option, @@ -54,7 +56,30 @@ impl DnDGrab { ) -> Self { Self { dh: dh.clone(), - start_data, + pointer_start_data: Some(start_data), + touch_start_data: None, + data_source: source, + current_focus: None, + pending_offers: Vec::with_capacity(1), + offer_data: None, + origin, + icon, + seat, + } + } + + pub(crate) fn new_touch( + dh: &DisplayHandle, + start_data: TouchGrabStartData, + source: Option, + origin: WlSurface, + seat: Seat, + icon: Option, + ) -> Self { + Self { + dh: dh.clone(), + pointer_start_data: None, + touch_start_data: Some(start_data), data_source: source, current_focus: None, pending_offers: Vec::with_capacity(1), @@ -66,23 +91,19 @@ impl DnDGrab { } } -impl PointerGrab for DnDGrab +impl DnDGrab where D: DataDeviceHandler, D: SeatHandler, - ::PointerFocus: WaylandFocus, D: 'static, { - fn motion( + fn update_focus( &mut self, - data: &mut D, - handle: &mut PointerInnerHandle<'_, D>, - focus: Option<(::PointerFocus, Point)>, - event: &MotionEvent, + focus: Option<(F, Point)>, + location: Point, + serial: Serial, + time: u32, ) { - // While the grab is active, no client has pointer focus - handle.motion(data, None, event); - let seat_data = self .seat .user_data() @@ -116,7 +137,7 @@ where Ok(c) => c, Err(_) => return, }; - let (x, y) = (event.location - surface_location.to_f64()).into(); + let (x, y) = (location - surface_location.to_f64()).into(); if self.current_focus.is_none() { // We entered a new surface, send the data offer if appropriate if let Some(ref source) = self.data_source { @@ -155,7 +176,7 @@ where offer.source_actions(meta.dnd_action); }) .unwrap(); - device.enter(event.serial.into(), &surface, x, y, Some(&offer)); + device.enter(serial.into(), &surface, x, y, Some(&offer)); self.pending_offers.push(offer); } self.offer_data = Some(offer_data); @@ -164,7 +185,7 @@ where if self.origin.id().same_client_as(&surface.id()) { for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) { - device.enter(event.serial.into(), &surface, x, y, None); + device.enter(serial.into(), &surface, x, y, None); } } } @@ -175,7 +196,7 @@ where if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) { for device in seat_data.known_data_devices() { if device.id().same_client_as(&surface.id()) { - device.motion(event.time, x, y); + device.motion(time, x, y); } } } @@ -183,6 +204,80 @@ where } } + fn drop(&mut self, data: &mut D) { + // the user dropped, proceed to the drop + let seat_data = self + .seat + .user_data() + .get::>>() + .unwrap() + .borrow_mut(); + let validated = if let Some(ref data) = self.offer_data { + let data = data.lock().unwrap(); + data.accepted && (!data.chosen_action.is_empty()) + } else { + false + }; + if let Some(ref surface) = self.current_focus { + if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) { + for device in seat_data.known_data_devices() { + if device.id().same_client_as(&surface.id()) && validated { + device.drop(); + } + } + } + } + if let Some(ref offer_data) = self.offer_data { + let mut data = offer_data.lock().unwrap(); + if validated { + data.dropped = true; + } else { + data.active = false; + } + } + if let Some(ref source) = self.data_source { + if source.version() >= 3 { + source.dnd_drop_performed(); + } + if !validated { + source.cancelled(); + } + } + + ClientDndGrabHandler::dropped(data, self.seat.clone()); + self.icon = None; + // in all cases abandon the drop + // no more buttons are pressed, release the grab + if let Some(ref surface) = self.current_focus { + for device in seat_data.known_data_devices() { + if device.id().same_client_as(&surface.id()) { + device.leave(); + } + } + } + } +} + +impl PointerGrab for DnDGrab +where + D: DataDeviceHandler, + D: SeatHandler, + ::PointerFocus: WaylandFocus, + D: 'static, +{ + fn motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &MotionEvent, + ) { + // While the grab is active, no client has pointer focus + handle.motion(data, None, event); + + self.update_focus(focus, event.location, event.serial, event.time); + } + fn relative_motion( &mut self, data: &mut D, @@ -196,55 +291,7 @@ where fn button(&mut self, data: &mut D, handle: &mut PointerInnerHandle<'_, D>, event: &ButtonEvent) { if handle.current_pressed().is_empty() { // the user dropped, proceed to the drop - let seat_data = self - .seat - .user_data() - .get::>>() - .unwrap() - .borrow_mut(); - let validated = if let Some(ref data) = self.offer_data { - let data = data.lock().unwrap(); - data.accepted && (!data.chosen_action.is_empty()) - } else { - false - }; - if let Some(ref surface) = self.current_focus { - if self.data_source.is_some() || self.origin.id().same_client_as(&surface.id()) { - for device in seat_data.known_data_devices() { - if device.id().same_client_as(&surface.id()) && validated { - device.drop(); - } - } - } - } - if let Some(ref offer_data) = self.offer_data { - let mut data = offer_data.lock().unwrap(); - if validated { - data.dropped = true; - } else { - data.active = false; - } - } - if let Some(ref source) = self.data_source { - if source.version() >= 3 { - source.dnd_drop_performed(); - } - if !validated { - source.cancelled(); - } - } - - ClientDndGrabHandler::dropped(data, self.seat.clone()); - self.icon = None; - // in all cases abandon the drop - // no more buttons are pressed, release the grab - if let Some(ref surface) = self.current_focus { - for device in seat_data.known_data_devices() { - if device.id().same_client_as(&surface.id()) { - device.leave(); - } - } - } + self.drop(data); handle.unset_grab(data, event.serial, event.time, true); } } @@ -331,7 +378,78 @@ where } fn start_data(&self) -> &PointerGrabStartData { - &self.start_data + self.pointer_start_data.as_ref().unwrap() + } +} + +impl TouchGrab for DnDGrab +where + D: DataDeviceHandler, + D: SeatHandler, + ::TouchFocus: WaylandFocus, + D: 'static, +{ + fn down( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _focus: Option<(::TouchFocus, Point)>, + _event: &crate::input::touch::DownEvent, + _seq: crate::utils::Serial, + ) { + // Ignore + } + + fn up( + &mut self, + data: &mut D, + handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + event: &crate::input::touch::UpEvent, + _seq: crate::utils::Serial, + ) { + if event.slot != self.start_data().slot { + return; + } + + self.drop(data); + handle.unset_grab(data); + } + + fn motion( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &crate::input::touch::MotionEvent, + _seq: crate::utils::Serial, + ) { + if event.slot != self.start_data().slot { + return; + } + + self.update_focus(focus, event.location, SERIAL_COUNTER.next_serial(), event.time); + } + + fn frame( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _seq: crate::utils::Serial, + ) { + } + + fn cancel( + &mut self, + data: &mut D, + handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _seq: crate::utils::Serial, + ) { + // TODO: should we cancel something here? + handle.unset_grab(data); + } + + fn start_data(&self) -> &TouchGrabStartData { + self.touch_start_data.as_ref().unwrap() } } diff --git a/src/wayland/selection/data_device/mod.rs b/src/wayland/selection/data_device/mod.rs index 2a0b7b1412cd..467c8d4fd312 100644 --- a/src/wayland/selection/data_device/mod.rs +++ b/src/wayland/selection/data_device/mod.rs @@ -49,6 +49,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = WlSurface; //! # type PointerFocus = WlSurface; +//! # type TouchFocus = WlSurface; //! # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } //! # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } //! # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } @@ -86,6 +87,7 @@ use wayland_server::{ use crate::{ input::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, + touch::GrabStartData as TouchGrabStartData, Seat, SeatHandler, }, utils::Serial, @@ -376,21 +378,29 @@ pub fn start_dnd( seat: &Seat, data: &mut D, serial: Serial, - start_data: PointerGrabStartData, + pointer_start_data: Option>, + touch_start_data: Option>, metadata: SourceMetadata, ) where D: SeatHandler + DataDeviceHandler + 'static, ::PointerFocus: WaylandFocus, + ::TouchFocus: WaylandFocus, { seat.user_data() .insert_if_missing(|| RefCell::new(SeatData::::new())); - if let Some(pointer) = seat.get_pointer() { + if let (Some(pointer_start_data), Some(pointer)) = (pointer_start_data, seat.get_pointer()) { pointer.set_grab( data, - server_dnd_grab::ServerDnDGrab::new(dh, start_data, metadata, seat.clone()), + server_dnd_grab::ServerDnDGrab::new_pointer(dh, pointer_start_data, metadata, seat.clone()), serial, Focus::Keep, ); + } else if let (Some(touch_start_data), Some(touch)) = (touch_start_data, seat.get_touch()) { + touch.set_grab( + data, + server_dnd_grab::ServerDnDGrab::new_touch(dh, touch_start_data, metadata, seat.clone()), + serial, + ); } } diff --git a/src/wayland/selection/data_device/server_dnd_grab.rs b/src/wayland/selection/data_device/server_dnd_grab.rs index 207d07ee62f3..9845f89e18ac 100644 --- a/src/wayland/selection/data_device/server_dnd_grab.rs +++ b/src/wayland/selection/data_device/server_dnd_grab.rs @@ -15,7 +15,7 @@ use wayland_server::{ DisplayHandle, Resource, }; -use crate::utils::{Logical, Point}; +use crate::utils::{Logical, Point, Serial, SERIAL_COUNTER}; use crate::wayland::seat::WaylandFocus; use crate::{ input::{ @@ -25,6 +25,7 @@ use crate::{ GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, + touch::{GrabStartData as TouchGrabStartData, TouchGrab}, Seat, SeatHandler, }, wayland::selection::seat_data::SeatData, @@ -34,7 +35,8 @@ use super::{DataDeviceHandler, DataDeviceUserData, ServerDndGrabHandler, SourceM pub(crate) struct ServerDnDGrab { dh: DisplayHandle, - start_data: PointerGrabStartData, + pointer_start_data: Option>, + touch_start_data: Option>, metadata: super::SourceMetadata, current_focus: Option, pending_offers: Vec, @@ -43,7 +45,7 @@ pub(crate) struct ServerDnDGrab { } impl ServerDnDGrab { - pub(crate) fn new( + pub(crate) fn new_pointer( dh: &DisplayHandle, start_data: PointerGrabStartData, metadata: super::SourceMetadata, @@ -51,7 +53,26 @@ impl ServerDnDGrab { ) -> Self { Self { dh: dh.clone(), - start_data, + pointer_start_data: Some(start_data), + touch_start_data: None, + metadata, + current_focus: None, + pending_offers: Vec::with_capacity(1), + offer_data: None, + seat, + } + } + + pub(crate) fn new_touch( + dh: &DisplayHandle, + start_data: TouchGrabStartData, + metadata: super::SourceMetadata, + seat: Seat, + ) -> Self { + Self { + dh: dh.clone(), + pointer_start_data: None, + touch_start_data: Some(start_data), metadata, current_focus: None, pending_offers: Vec::with_capacity(1), @@ -61,27 +82,19 @@ impl ServerDnDGrab { } } -impl PointerGrab for ServerDnDGrab +impl ServerDnDGrab where D: DataDeviceHandler, D: SeatHandler, - ::PointerFocus: WaylandFocus, D: 'static, { - fn motion( + fn update_focus( &mut self, - data: &mut D, - handle: &mut PointerInnerHandle<'_, D>, - focus: Option<(::PointerFocus, Point)>, - event: &MotionEvent, + focus: Option<(F, Point)>, + location: Point, + serial: Serial, + time: u32, ) { - let location = event.location; - let serial = event.serial; - let time = event.time; - - // While the grab is active, no client has pointer focus - handle.motion(data, None, event); - let seat_data = self .seat .user_data() @@ -167,6 +180,76 @@ where } } + fn drop(&mut self, data: &mut D) { + // the user dropped, proceed to the drop + let seat_data = self + .seat + .user_data() + .get::>>() + .unwrap() + .borrow_mut(); + let validated = if let Some(ref data) = self.offer_data { + let data = data.lock().unwrap(); + data.accepted && (!data.chosen_action.is_empty()) + } else { + false + }; + if let Some(ref surface) = self.current_focus { + for device in seat_data.known_data_devices() { + if device.id().same_client_as(&surface.id()) && validated { + device.drop(); + } + } + } + if let Some(ref offer_data) = self.offer_data { + let mut data = offer_data.lock().unwrap(); + if validated { + data.dropped = true; + } else { + data.active = false; + } + } + + ServerDndGrabHandler::dropped(data, self.seat.clone()); + if !validated { + data.cancelled(self.seat.clone()); + } + // in all cases abandon the drop + // no more buttons are pressed, release the grab + if let Some(ref surface) = self.current_focus { + for device in seat_data.known_data_devices() { + if device.id().same_client_as(&surface.id()) { + device.leave(); + } + } + } + } +} + +impl PointerGrab for ServerDnDGrab +where + D: DataDeviceHandler, + D: SeatHandler, + ::PointerFocus: WaylandFocus, + D: 'static, +{ + fn motion( + &mut self, + data: &mut D, + handle: &mut PointerInnerHandle<'_, D>, + focus: Option<(::PointerFocus, Point)>, + event: &MotionEvent, + ) { + let location = event.location; + let serial = event.serial; + let time = event.time; + + // While the grab is active, no client has pointer focus + handle.motion(data, None, event); + + self.update_focus(focus, location, serial, time); + } + fn relative_motion( &mut self, data: &mut D, @@ -183,47 +266,7 @@ where if handle.current_pressed().is_empty() { // the user dropped, proceed to the drop - let seat_data = self - .seat - .user_data() - .get::>>() - .unwrap() - .borrow_mut(); - let validated = if let Some(ref data) = self.offer_data { - let data = data.lock().unwrap(); - data.accepted && (!data.chosen_action.is_empty()) - } else { - false - }; - if let Some(ref surface) = self.current_focus { - for device in seat_data.known_data_devices() { - if device.id().same_client_as(&surface.id()) && validated { - device.drop(); - } - } - } - if let Some(ref offer_data) = self.offer_data { - let mut data = offer_data.lock().unwrap(); - if validated { - data.dropped = true; - } else { - data.active = false; - } - } - - ServerDndGrabHandler::dropped(data, self.seat.clone()); - if !validated { - data.cancelled(self.seat.clone()); - } - // in all cases abandon the drop - // no more buttons are pressed, release the grab - if let Some(ref surface) = self.current_focus { - for device in seat_data.known_data_devices() { - if device.id().same_client_as(&surface.id()) { - device.leave(); - } - } - } + self.drop(data); handle.unset_grab(data, serial, time, true); } } @@ -310,7 +353,82 @@ where } fn start_data(&self) -> &PointerGrabStartData { - &self.start_data + self.pointer_start_data.as_ref().unwrap() + } +} + +impl TouchGrab for ServerDnDGrab +where + D: DataDeviceHandler, + D: SeatHandler, + ::TouchFocus: WaylandFocus, + D: 'static, +{ + fn down( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _focus: Option<(::TouchFocus, Point)>, + _event: &crate::input::touch::DownEvent, + _seq: Serial, + ) { + // Ignore + } + + fn up( + &mut self, + data: &mut D, + handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + event: &crate::input::touch::UpEvent, + _seq: Serial, + ) { + if event.slot != self.start_data().slot { + return; + } + + // the user dropped, proceed to the drop + self.drop(data); + handle.unset_grab(data); + } + + fn motion( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + focus: Option<(::TouchFocus, Point)>, + event: &crate::input::touch::MotionEvent, + _seq: Serial, + ) { + if event.slot != self.start_data().slot { + return; + } + + let location = event.location; + let time = event.time; + + self.update_focus(focus, location, SERIAL_COUNTER.next_serial(), time); + } + + fn frame( + &mut self, + _data: &mut D, + _handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _seq: Serial, + ) { + } + + fn cancel( + &mut self, + data: &mut D, + handle: &mut crate::input::touch::TouchInnerHandle<'_, D>, + _seq: Serial, + ) { + // TODO: should we cancel something here? + handle.unset_grab(data); + } + + fn start_data(&self) -> &TouchGrabStartData { + self.touch_start_data.as_ref().unwrap() } } diff --git a/src/wayland/selection/primary_selection/mod.rs b/src/wayland/selection/primary_selection/mod.rs index 807b112f5bdd..6d13968a54ea 100644 --- a/src/wayland/selection/primary_selection/mod.rs +++ b/src/wayland/selection/primary_selection/mod.rs @@ -46,6 +46,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = WlSurface; //! # type PointerFocus = WlSurface; +//! # type TouchFocus = WlSurface; //! # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } //! # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } //! # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } diff --git a/src/wayland/selection/wlr_data_control/mod.rs b/src/wayland/selection/wlr_data_control/mod.rs index b6640ba623a2..a3d98199a6db 100644 --- a/src/wayland/selection/wlr_data_control/mod.rs +++ b/src/wayland/selection/wlr_data_control/mod.rs @@ -27,6 +27,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = WlSurface; //! # type PointerFocus = WlSurface; +//! # type TouchFocus = WlSurface; //! # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } //! # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } //! # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } diff --git a/src/wayland/shell/xdg/decoration.rs b/src/wayland/shell/xdg/decoration.rs index 9576991efaf4..215ffe4d2de5 100644 --- a/src/wayland/shell/xdg/decoration.rs +++ b/src/wayland/shell/xdg/decoration.rs @@ -68,6 +68,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = Target; //! type PointerFocus = Target; +//! type TouchFocus = Target; //! //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index 5f439b679eb0..474f30d2cbc7 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -81,6 +81,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = Target; //! type PointerFocus = Target; +//! type TouchFocus = Target; //! //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state diff --git a/src/wayland/tablet_manager/mod.rs b/src/wayland/tablet_manager/mod.rs index 617b2bcc5dcb..494aafd021fd 100644 --- a/src/wayland/tablet_manager/mod.rs +++ b/src/wayland/tablet_manager/mod.rs @@ -40,6 +40,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; +//! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } diff --git a/src/wayland/text_input/mod.rs b/src/wayland/text_input/mod.rs index 8da0fe328e88..10aa2a02e8da 100644 --- a/src/wayland/text_input/mod.rs +++ b/src/wayland/text_input/mod.rs @@ -28,6 +28,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; +//! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } diff --git a/src/wayland/virtual_keyboard/mod.rs b/src/wayland/virtual_keyboard/mod.rs index e02ea0533d87..a2e499fcb8e3 100644 --- a/src/wayland/virtual_keyboard/mod.rs +++ b/src/wayland/virtual_keyboard/mod.rs @@ -27,6 +27,7 @@ //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; +//! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } diff --git a/src/wayland/xwayland_keyboard_grab.rs b/src/wayland/xwayland_keyboard_grab.rs index 2356c66ed858..646dfdccdc8e 100644 --- a/src/wayland/xwayland_keyboard_grab.rs +++ b/src/wayland/xwayland_keyboard_grab.rs @@ -19,6 +19,7 @@ //! # impl SeatHandler for State { //! # type KeyboardFocus = WlSurface; //! # type PointerFocus = WlSurface; +//! # type TouchFocus = WlSurface; //! # fn seat_state(&mut self) -> &mut SeatState { unimplemented!() } //! # fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { unimplemented!() } //! # fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { unimplemented!() } diff --git a/src/xwayland/xwm/surface.rs b/src/xwayland/xwm/surface.rs index 8222d94d0182..0acf0d2af58a 100644 --- a/src/xwayland/xwm/surface.rs +++ b/src/xwayland/xwm/surface.rs @@ -7,6 +7,7 @@ use crate::{ GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent, }, + touch::TouchTarget, Seat, SeatHandler, }, utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle, Serial, Size}, @@ -996,3 +997,53 @@ impl PointerTarget for X11Surface { } } } + +impl TouchTarget for X11Surface { + fn down(&self, seat: &Seat, data: &mut D, event: &crate::input::touch::DownEvent, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::down(surface, seat, data, event, seq) + } + } + + fn up(&self, seat: &Seat, data: &mut D, event: &crate::input::touch::UpEvent, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::up(surface, seat, data, event, seq) + } + } + + fn motion(&self, seat: &Seat, data: &mut D, event: &crate::input::touch::MotionEvent, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::motion(surface, seat, data, event, seq) + } + } + + fn frame(&self, seat: &Seat, data: &mut D, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::frame(surface, seat, data, seq) + } + } + + fn cancel(&self, seat: &Seat, data: &mut D, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::cancel(surface, seat, data, seq) + } + } + + fn shape(&self, seat: &Seat, data: &mut D, event: &crate::input::touch::ShapeEvent, seq: Serial) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::shape(surface, seat, data, event, seq) + } + } + + fn orientation( + &self, + seat: &Seat, + data: &mut D, + event: &crate::input::touch::OrientationEvent, + seq: Serial, + ) { + if let Some(surface) = self.state.lock().unwrap().wl_surface.as_ref() { + TouchTarget::orientation(surface, seat, data, event, seq) + } + } +}