diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index a79ea64f..e42abb44 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1161,6 +1161,7 @@ impl PointerTarget for CosmicStack { indicator_thickness, was_tiled, ReleaseMode::NoMouseButtons, + data.common.event_loop_handle.clone(), ); if grab.is_tiling_grab() { data.common.shell.set_overview_mode( diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index e29ff43c..1d75caa9 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -17,6 +17,7 @@ use crate::{ utils::prelude::*, }; +use calloop::LoopHandle; use cosmic::theme::CosmicTheme; use smithay::{ backend::{ @@ -39,7 +40,7 @@ use smithay::{ }, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, + utils::{IsAlive, Logical, Point, Rectangle, Scale, SERIAL_COUNTER}, wayland::compositor::SurfaceData, }; use std::{ @@ -215,6 +216,9 @@ impl MoveGrabState { } } +struct NotSend(pub T); +unsafe impl Send for NotSend {} + pub struct MoveGrab { window: CosmicMapped, start_data: PointerGrabStartData, @@ -223,6 +227,8 @@ pub struct MoveGrab { window_outputs: HashSet, tiling: bool, release: ReleaseMode, + // SAFETY: This is only used on drop which will always be on the main thread + evlh: NotSend>, } impl PointerGrab for MoveGrab { @@ -318,7 +324,7 @@ impl PointerGrab for MoveGrab { // While the grab is active, no client has pointer focus handle.motion(state, None, event); if !self.window.alive() { - self.ungrab(state, handle, event.serial, event.time); + handle.unset_grab(state, event.serial, event.time, true); } } @@ -343,12 +349,12 @@ impl PointerGrab for MoveGrab { match self.release { ReleaseMode::NoMouseButtons => { if handle.current_pressed().is_empty() { - self.ungrab(state, handle, event.serial, event.time); + handle.unset_grab(state, event.serial, event.time, true); } } ReleaseMode::Click => { if event.state == ButtonState::Pressed { - self.ungrab(state, handle, event.serial, event.time); + handle.unset_grab(state, event.serial, event.time, true); } } } @@ -454,6 +460,7 @@ impl MoveGrab { indicator_thickness: u8, was_tiled: bool, release: ReleaseMode, + evlh: LoopHandle<'static, State>, ) -> MoveGrab { let output = seat.active_output(); let mut outputs = HashSet::new(); @@ -490,106 +497,109 @@ impl MoveGrab { cursor_output: output, tiling: was_tiled, release, + evlh: NotSend(evlh), } } pub fn is_tiling_grab(&self) -> bool { self.tiling } +} - fn ungrab( - &mut self, - state: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - serial: Serial, - time: u32, - ) { +impl Drop for MoveGrab { + fn drop(&mut self) { // No more buttons are pressed, release the grab. let output = self.seat.active_output(); + let seat = self.seat.clone(); + let window_outputs = self.window_outputs.drain().collect::>(); + let tiling = self.tiling; + let window = self.window.clone(); - let position: Option<(CosmicMapped, Point)> = if let Some(grab_state) = self - .seat - .user_data() - .get::() - .and_then(|s| s.borrow_mut().take()) - { - if grab_state.window.alive() { - let window_location = (handle.current_location().to_i32_round() - + grab_state.window_offset) - .as_global(); + let _ = self.evlh.0.insert_idle(move |state| { + let pointer = seat.get_pointer().unwrap(); - let workspace_handle = state.common.shell.active_space(&output).handle; - for old_output in self.window_outputs.iter().filter(|o| *o != &output) { - grab_state.window.output_leave(old_output); - } - for (window, _) in grab_state.window.windows() { - state - .common - .shell - .toplevel_info_state - .toplevel_enter_workspace(&window, &workspace_handle); - state - .common - .shell - .toplevel_info_state - .toplevel_enter_output(&window, &output); - } + let position: Option<(CosmicMapped, Point)> = if let Some(grab_state) = + seat.user_data() + .get::() + .and_then(|s| s.borrow_mut().take()) + { + if grab_state.window.alive() { + let window_location = (pointer.current_location().to_i32_round() + + grab_state.window_offset) + .as_global(); - if self.tiling { - let (window, location) = state - .common - .shell - .active_space_mut(&output) - .tiling_layer - .drop_window(grab_state.window); - Some((window, location.to_global(&output))) + let workspace_handle = state.common.shell.active_space(&output).handle; + for old_output in window_outputs.iter().filter(|o| *o != &output) { + grab_state.window.output_leave(old_output); + } + for (window, _) in grab_state.window.windows() { + state + .common + .shell + .toplevel_info_state + .toplevel_enter_workspace(&window, &workspace_handle); + state + .common + .shell + .toplevel_info_state + .toplevel_enter_output(&window, &output); + } + + if tiling { + let (window, location) = state + .common + .shell + .active_space_mut(&output) + .tiling_layer + .drop_window(grab_state.window); + Some((window, location.to_global(&output))) + } else { + grab_state.window.set_geometry(Rectangle::from_loc_and_size( + window_location, + grab_state.window.geometry().size.as_global(), + )); + let workspace = state.common.shell.active_space_mut(&output); + workspace.floating_layer.map_internal( + grab_state.window, + Some(window_location.to_local(&workspace.output)), + None, + ); + + Some((window.clone(), window_location)) + } } else { - grab_state.window.set_geometry(Rectangle::from_loc_and_size( - window_location, - grab_state.window.geometry().size.as_global(), - )); - let workspace = state.common.shell.active_space_mut(&output); - workspace.floating_layer.map_internal( - grab_state.window, - Some(window_location.to_local(&workspace.output)), - None, - ); - - Some((self.window.clone(), window_location)) + None } } else { None + }; + + { + let cursor_state = seat.user_data().get::().unwrap(); + cursor_state.set_shape(CursorShape::Default); } - } else { - None - }; - handle.unset_grab(state, serial, time, true); - - { - let cursor_state = self.seat.user_data().get::().unwrap(); - cursor_state.set_shape(CursorShape::Default); - } - - if let Some((mapped, position)) = position { - handle.motion( - state, - Some(( - PointerFocusTarget::from(mapped.clone()), - position.as_logical() - self.window.geometry().loc, - )), - &MotionEvent { - location: handle.current_location(), - serial, - time, - }, - ); - Common::set_focus( - state, - Some(&KeyboardFocusTarget::from(mapped)), - &self.seat, - Some(serial), - ) - } + if let Some((mapped, position)) = position { + let serial = SERIAL_COUNTER.next_serial(); + pointer.motion( + state, + Some(( + PointerFocusTarget::from(mapped.clone()), + position.as_logical() - window.geometry().loc, + )), + &MotionEvent { + location: pointer.current_location(), + serial, + time: 0, + }, + ); + Common::set_focus( + state, + Some(&KeyboardFocusTarget::from(mapped)), + &seat, + Some(serial), + ) + } + }); } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 0a35a4ff..33e51eb1 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2007,6 +2007,7 @@ impl Shell { start_data, active_hint as u8, release, + state.common.event_loop_handle.clone(), ) { let handle = workspace.handle; state diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 6e1932f3..b815d368 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -21,6 +21,7 @@ use crate::{ xwayland::XWaylandState, }; +use calloop::LoopHandle; use cosmic::theme::CosmicTheme; use id_tree::Tree; use indexmap::IndexSet; @@ -685,6 +686,7 @@ impl Workspace { start_data: PointerGrabStartData, indicator_thickness: u8, release: ReleaseMode, + evlh: LoopHandle<'static, State>, ) -> Option { let pointer = seat.get_pointer().unwrap(); let pos = pointer.current_location().as_global(); @@ -728,6 +730,7 @@ impl Workspace { indicator_thickness, was_tiled.is_some(), release, + evlh, )) }