diff --git a/src/input/mod.rs b/src/input/mod.rs index 5d987a24..269cf568 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1378,6 +1378,16 @@ impl State { } } InputEvent::TouchUp { event, .. } => { + if let OverviewMode::Started(Trigger::Touch(slot), _) = + self.common.shell.overview_mode().0 + { + if slot == event.slot() { + self.common + .shell + .set_overview_mode(None, self.common.event_loop_handle.clone()); + } + } + if let Some(seat) = self.common.seat_with_device(&event.device()) { let serial = SERIAL_COUNTER.next_serial(); let touch = seat.get_touch().unwrap(); diff --git a/src/shell/grabs/mod.rs b/src/shell/grabs/mod.rs index c78f8b7a..1535e8b8 100644 --- a/src/shell/grabs/mod.rs +++ b/src/shell/grabs/mod.rs @@ -1,9 +1,13 @@ use smithay::{ - 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, }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{Logical, Point}, @@ -17,6 +21,35 @@ use super::{ layout::{floating::ResizeSurfaceGrab, tiling::ResizeForkGrab}, }; +#[derive(Debug, Clone)] +pub enum GrabStartData { + Touch(TouchGrabStartData), + Pointer(PointerGrabStartData), +} + +impl GrabStartData { + pub fn focus(&self) -> Option<&(PointerFocusTarget, Point)> { + match self { + Self::Touch(touch) => touch.focus.as_ref(), + Self::Pointer(pointer) => pointer.focus.as_ref(), + } + } + + pub fn set_focus(&mut self, focus: Option<(PointerFocusTarget, Point)>) { + match self { + Self::Touch(touch) => touch.focus = focus, + Self::Pointer(pointer) => pointer.focus = focus, + } + } + + pub fn location(&self) -> Point { + match self { + Self::Touch(touch) => touch.location, + Self::Pointer(pointer) => pointer.location, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ReleaseMode { Click, diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index d8343b79..a4893fe3 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -37,14 +37,15 @@ use smithay::{ GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, }, + touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle}, Seat, }, output::Output, - utils::{IsAlive, Logical, Point, Rectangle, Scale, SERIAL_COUNTER}, + utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial, SERIAL_COUNTER}, }; use std::{cell::RefCell, collections::HashSet, sync::atomic::Ordering, time::Instant}; -use super::ReleaseMode; +use super::{GrabStartData, ReleaseMode}; pub type SeatMoveGrabState = RefCell>; @@ -58,6 +59,7 @@ pub struct MoveGrabState { previous: ManagedLayer, snapping_zone: Option, stacking_indicator: Option<(StackHover, Point)>, + location: Point, } impl MoveGrabState { @@ -90,10 +92,8 @@ impl MoveGrabState { 0.4 }; - let cursor_at = seat.get_pointer().unwrap().current_location(); - let mut window_geo = self.window.geometry(); - window_geo.loc += cursor_at.to_i32_round() + self.window_offset; + window_geo.loc += self.location.to_i32_round() + self.window_offset; if !output .geometry() .as_logical() @@ -106,7 +106,7 @@ impl MoveGrabState { let output_scale: Scale = output.current_scale().fractional_scale().into(); let scaling_offset = self.window_offset - self.window_offset.to_f64().upscale(scale).to_i32_round(); - let render_location = cursor_at.to_i32_round() - output.geometry().loc.as_logical() + let render_location = self.location.to_i32_round() - output.geometry().loc.as_logical() + self.window_offset - scaling_offset; @@ -319,7 +319,7 @@ impl SnappingZone { pub struct MoveGrab { window: CosmicMapped, - start_data: PointerGrabStartData, + start_data: GrabStartData, seat: Seat, cursor_output: Output, window_outputs: HashSet, @@ -329,28 +329,19 @@ pub struct MoveGrab { evlh: NotSend>, } -impl PointerGrab for MoveGrab { - fn motion( - &mut self, - state: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - _focus: Option<(PointerFocusTarget, Point)>, - event: &MotionEvent, - ) { - let Some(current_output) = state - .common - .shell - .outputs() - .find(|output| { - output - .geometry() - .as_logical() - .overlaps_or_touches(Rectangle::from_loc_and_size( - handle.current_location().to_i32_floor(), - (0, 0), - )) - }) - .cloned() +impl MoveGrab { + fn update_location(&mut self, state: &mut State, location: Point) { + let Some(current_output) = + state + .common + .shell + .outputs() + .find(|output| { + output.geometry().as_logical().overlaps_or_touches( + Rectangle::from_loc_and_size(location.to_i32_floor(), (0, 0)), + ) + }) + .cloned() else { return; }; @@ -371,8 +362,10 @@ impl PointerGrab for MoveGrab { .get::() .map(|s| s.borrow_mut()); if let Some(grab_state) = borrow.as_mut().and_then(|s| s.as_mut()) { + grab_state.location = location; + let mut window_geo = self.window.geometry(); - window_geo.loc += event.location.to_i32_round() + grab_state.window_offset; + window_geo.loc += location.to_i32_round() + grab_state.window_offset; for output in state.common.shell.outputs() { if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) { if self.window_outputs.insert(output.clone()) { @@ -432,8 +425,7 @@ impl PointerGrab for MoveGrab { .iter() .find(|&x| { x.contains( - handle - .current_location() + location .as_global() .to_local(¤t_output) .to_i32_floor(), @@ -444,6 +436,18 @@ impl PointerGrab for MoveGrab { } } drop(borrow); + } +} + +impl PointerGrab for MoveGrab { + fn motion( + &mut self, + state: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + _focus: Option<(PointerFocusTarget, Point)>, + event: &MotionEvent, + ) { + self.update_location(state, event.location); // While the grab is active, no client has pointer focus handle.motion(state, None, event); @@ -570,16 +574,75 @@ impl PointerGrab for MoveGrab { } fn start_data(&self) -> &PointerGrabStartData { - &self.start_data + match &self.start_data { + GrabStartData::Pointer(start_data) => start_data, + _ => unreachable!(), + } + } +} + +impl TouchGrab for MoveGrab { + fn down( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + _focus: Option<(PointerFocusTarget, Point)>, + event: &touch::DownEvent, + seq: Serial, + ) { + handle.down(data, None, event, seq) + } + + fn up( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + event: &touch::UpEvent, + seq: Serial, + ) { + if event.slot == >::start_data(self).slot { + handle.unset_grab(data); + } + + handle.up(data, event, seq); + } + + fn motion( + &mut self, + data: &mut State, + handle: &mut TouchInnerHandle<'_, State>, + _focus: Option<(PointerFocusTarget, Point)>, + event: &touch::MotionEvent, + seq: Serial, + ) { + if event.slot == >::start_data(self).slot { + self.update_location(data, event.location); + } + + handle.motion(data, None, event, seq); + } + + fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) { + handle.frame(data, seq) + } + + fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) { + handle.unset_grab(data); + } + + fn start_data(&self) -> &TouchGrabStartData { + match &self.start_data { + GrabStartData::Touch(start_data) => start_data, + _ => unreachable!(), + } } } impl MoveGrab { pub fn new( - start_data: PointerGrabStartData, + start_data: GrabStartData, window: CosmicMapped, seat: &Seat, - initial_cursor_location: Point, initial_window_location: Point, indicator_thickness: u8, previous_layer: ManagedLayer, @@ -594,13 +657,15 @@ impl MoveGrab { let grab_state = MoveGrabState { window: window.clone(), - window_offset: (initial_window_location - initial_cursor_location.to_i32_round()) - .as_logical(), + window_offset: (initial_window_location + - start_data.location().as_global().to_i32_round()) + .as_logical(), indicator_thickness, start: Instant::now(), stacking_indicator: None, snapping_zone: None, previous: previous_layer, + location: start_data.location(), }; *seat diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 76e5c48d..860cdd6a 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -3234,7 +3234,10 @@ impl TilingLayout { } _ => None, } - } else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) { + } else if matches!( + overview, + OverviewMode::Started(Trigger::Pointer(_) | Trigger::Touch(_), _) + ) { let non_exclusive_zone = layer_map_for_output(&self.output) .non_exclusive_zone() .as_local(); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index e1fed28e..3577b1a4 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -13,6 +13,7 @@ use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::{ }; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ + backend::input::TouchSlot, desktop::{ layer_map_for_output, space::SpaceElement, LayerSurface, PopupKind, PopupManager, WindowSurface, WindowSurfaceType, @@ -85,7 +86,8 @@ use self::{ FocusDirection, }, grabs::{ - tab_items, window_items, Item, MenuGrab, MoveGrab, ReleaseMode, ResizeEdge, ResizeGrab, + tab_items, window_items, GrabStartData, Item, MenuGrab, MoveGrab, ReleaseMode, ResizeEdge, + ResizeGrab, }, layout::{ floating::{FloatingLayout, ResizeState}, @@ -103,6 +105,7 @@ pub enum Trigger { KeyboardSwap(KeyPattern, NodeDesc), KeyboardMove(KeyModifiers), Pointer(u32), + Touch(TouchSlot), } #[derive(Debug, Clone)] @@ -1180,7 +1183,7 @@ impl Shell { if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode, - OverviewMode::Started(Trigger::Pointer(_), _) + OverviewMode::Started(Trigger::Pointer(_) | Trigger::Touch(_), _) ) { set.workspaces[set.active].tiling_layer.cleanup_drag(); } @@ -1229,7 +1232,7 @@ impl Shell { if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode, - OverviewMode::Started(Trigger::Pointer(_), _) + OverviewMode::Started(Trigger::Pointer(_) | Trigger::Touch(_), _) ) { set.workspaces[set.active].tiling_layer.cleanup_drag(); } @@ -2338,7 +2341,7 @@ impl Shell { target_stack: bool, ) { let serial = serial.into(); - if let Some(start_data) = + if let Some(GrabStartData::Pointer(start_data)) = check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons) { if let Some(mapped) = state.common.shell.element_for_surface(surface).cloned() { @@ -2485,20 +2488,22 @@ impl Shell { state.common.theme.clone(), ) .into(); - start_data.focus = new_mapped.focus_under((0., 0.).into()); + start_data.set_focus(new_mapped.focus_under((0., 0.).into())); new_mapped } else { old_mapped.clone() }; - let button = start_data.button; + let trigger = match &start_data { + GrabStartData::Pointer(start_data) => Trigger::Pointer(start_data.button), + GrabStartData::Touch(start_data) => Trigger::Touch(start_data.slot), + }; let active_hint = if state.common.config.cosmic_conf.active_hint { state.common.theme.cosmic().active_hint as u8 } else { 0 }; - let pointer = seat.get_pointer().unwrap(); - let pos = pointer.current_location().as_global(); + let pos = start_data.location().as_global(); let (initial_window_location, layer, workspace_handle) = if let Some(workspace) = state.common.shell.space_for_mut(&old_mapped) { @@ -2608,11 +2613,12 @@ impl Shell { .refresh(&state.common.shell.xdg_activation_state); } + let is_touch_grab = matches!(start_data, GrabStartData::Touch(_)); + let grab = MoveGrab::new( start_data, mapped, seat, - pos, initial_window_location, active_hint as u8, layer, @@ -2621,18 +2627,26 @@ impl Shell { ); if grab.is_tiling_grab() { - state.common.shell.set_overview_mode( - Some(Trigger::Pointer(button)), - state.common.event_loop_handle.clone(), - ); + state + .common + .shell + .set_overview_mode(Some(trigger), state.common.event_loop_handle.clone()); } - seat.get_pointer().unwrap().set_grab( - state, - grab, - serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), - Focus::Clear, - ); + if is_touch_grab { + seat.get_touch().unwrap().set_grab( + state, + grab, + serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), + ); + } else { + seat.get_pointer().unwrap().set_grab( + state, + grab, + serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), + Focus::Clear, + ); + } } } } @@ -2828,7 +2842,7 @@ impl Shell { return; } - if let Some(mut start_data) = + if let Some(GrabStartData::Pointer(mut start_data)) = check_grab_preconditions(&seat, &surface, None, ReleaseMode::Click) { let (floating_layer, geometry) = if let Some(set) = state @@ -3086,7 +3100,7 @@ impl Shell { edges: ResizeEdge, ) { let serial = serial.into(); - if let Some(start_data) = + if let Some(GrabStartData::Pointer(start_data)) = check_grab_preconditions(&seat, surface, serial, ReleaseMode::NoMouseButtons) { if let Some(mapped) = state.common.shell.element_for_surface(surface).cloned() { @@ -3384,37 +3398,37 @@ pub fn check_grab_preconditions( surface: &WlSurface, serial: Option, release: ReleaseMode, -) -> Option> { +) -> Option { use smithay::reexports::wayland_server::Resource; - // TODO: touch resize. let pointer = seat.get_pointer().unwrap(); + let touch = seat.get_touch().unwrap(); - let start_data = pointer - .grab_start_data() - .unwrap_or_else(|| PointerGrabStartData { - focus: pointer.current_focus().map(|f| (f, Point::from((0, 0)))), - button: 0x110, - location: pointer.current_location(), - }); + let start_data = + if serial.map_or(false, |serial| touch.has_grab(serial)) { + GrabStartData::Touch(touch.grab_start_data().unwrap()) + } else { + GrabStartData::Pointer(pointer.grab_start_data().unwrap_or_else(|| { + PointerGrabStartData { + focus: pointer.current_focus().map(|f| (f, Point::from((0, 0)))), + button: 0x110, + location: pointer.current_location(), + } + })) + }; if release == ReleaseMode::NoMouseButtons { - // Check that this surface has a click grab. + // Check that this surface has a click or touch down grab. if !match serial { - Some(serial) => pointer.has_grab(serial), - None => pointer.is_grabbed(), + Some(serial) => pointer.has_grab(serial) || touch.has_grab(serial), + None => pointer.is_grabbed() | touch.is_grabbed(), } { return None; } // 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.id()) + if start_data.focus().is_none() + || !start_data.focus().unwrap().0.same_client_as(&surface.id()) { return None; }