From 769b7d69966eb44f004e72564b52bf7a05f94901 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 20 Dec 2023 20:29:44 +0000 Subject: [PATCH] shell: Refactor move_request, consider sticky windows --- src/shell/element/stack.rs | 104 +++++----------- src/shell/element/window.rs | 1 + src/shell/grabs/menu/default.rs | 2 +- src/shell/grabs/moving.rs | 89 ++++++++------ src/shell/mod.rs | 171 ++++++++++++++++++++++---- src/shell/workspace.rs | 61 +-------- src/wayland/handlers/xdg_shell/mod.rs | 1 + src/xwayland.rs | 9 +- 8 files changed, 247 insertions(+), 191 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 8fdb43ba..a978ad34 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1,10 +1,7 @@ -use super::{CosmicMapped, CosmicSurface, CosmicWindow}; +use super::CosmicSurface; use crate::{ shell::{ - focus::FocusDirection, - grabs::{MoveGrab, ReleaseMode}, - layout::tiling::NodeDesc, - Direction, Shell, Trigger, + focus::FocusDirection, grabs::ReleaseMode, layout::tiling::NodeDesc, Direction, Shell, }, state::State, utils::iced::{IcedElement, Program}, @@ -38,8 +35,8 @@ use smithay::{ pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, - GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, - GrabStartData as PointerGrabStartData, MotionEvent, PointerTarget, RelativeMotionEvent, + GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, + PointerTarget, RelativeMotionEvent, }, Seat, }, @@ -659,6 +656,7 @@ impl Program for CosmicStackInternal { &seat, serial, ReleaseMode::NoMouseButtons, + false, ); }); } @@ -713,20 +711,20 @@ impl Program for CosmicStackInternal { return; }; - let mut cursor = seat - .get_pointer() - .unwrap() - .current_location() - .to_i32_round(); - cursor.y -= TAB_HEIGHT; - Shell::menu_request( - state, - &surface, - &seat, - serial, - cursor - position.as_logical(), - true, - ); + let mut cursor = seat + .get_pointer() + .unwrap() + .current_location() + .to_i32_round(); + cursor.y -= TAB_HEIGHT; + Shell::menu_request( + state, + &surface, + &seat, + serial, + cursor - position.as_logical(), + true, + ); } }); } @@ -1172,60 +1170,18 @@ impl PointerTarget for CosmicStack { .0 .with_program(|p| p.windows.lock().unwrap().get(dragged_out).cloned()) { - if let Some(stack_mapped) = - data.common.shell.element_for_surface(&surface) - { - if let Some(workspace) = data.common.shell.space_for(stack_mapped) { - // TODO: Unify this somehow with Shell::move_request/Workspace::move_request - let button = 0x110; // BTN_LEFT - let pos = event.location.as_global(); - let start_data = PointerGrabStartData { - focus: None, - button, - location: pos.as_logical(), - }; - let mapped = CosmicMapped::from(CosmicWindow::new( - surface, - self.0.loop_handle(), - data.common.theme.clone(), - )); - let elem_geo = - workspace.element_geometry(stack_mapped).unwrap(); - let indicator_thickness = - data.common.theme.cosmic().active_hint as u8; - let was_tiled = workspace.is_tiled(stack_mapped); - - self.remove_idx(dragged_out); - mapped.configure(); - - let grab = MoveGrab::new( - start_data, - mapped, - seat, - pos, - pos.to_i32_round() - Point::from((elem_geo.size.w / 2, 24)), - indicator_thickness, - was_tiled, + let seat = seat.clone(); + if let Some(surface) = surface.wl_surface() { + let _ = data.common.event_loop_handle.insert_idle(move |state| { + Shell::move_request( + state, + &surface, + &seat, + None, ReleaseMode::NoMouseButtons, - data.common.event_loop_handle.clone(), - ); - if grab.is_tiling_grab() { - data.common.shell.set_overview_mode( - Some(Trigger::Pointer(button)), - data.common.event_loop_handle.clone(), - ); - } - - let seat = seat.clone(); - data.common.event_loop_handle.insert_idle(move |state| { - seat.get_pointer().unwrap().set_grab( - state, - grab, - event.serial, - smithay::input::pointer::Focus::Clear, - ); - }); - } + true, + ) + }); } } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 1ed7d9a2..2174cf03 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -262,6 +262,7 @@ impl Program for CosmicWindowInternal { &seat, serial, ReleaseMode::NoMouseButtons, + false, ); }); } diff --git a/src/shell/grabs/menu/default.rs b/src/shell/grabs/menu/default.rs index bc7d0c56..fd50af44 100644 --- a/src/shell/grabs/menu/default.rs +++ b/src/shell/grabs/menu/default.rs @@ -233,7 +233,7 @@ pub fn window_items( let _ = handle.insert_idle(move |state| { if let Some(surface) = move_clone.wl_surface() { let seat = state.common.last_active_seat().clone(); - Shell::move_request(state, &surface, &seat, None, ReleaseMode::Click); + Shell::move_request(state, &surface, &seat, None, ReleaseMode::Click, false); } }); })), diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 1d75caa9..4f13cee7 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -12,7 +12,7 @@ use crate::{ CosmicMappedRenderElement, }, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, - CosmicMapped, CosmicSurface, + CosmicMapped, CosmicSurface, ManagedLayer, }, utils::prelude::*, }; @@ -61,7 +61,7 @@ pub struct MoveGrabState { window_offset: Point, indicator_thickness: u8, start: Instant, - tiling: bool, + previous: ManagedLayer, stacking_indicator: Option<(StackHover, Point)>, } @@ -82,7 +82,7 @@ impl MoveGrabState { #[cfg(feature = "debug")] puffin::profile_function!(); - let scale = if self.tiling { + let scale = if self.previous == ManagedLayer::Tiling { 0.6 + ((1.0 - (Instant::now().duration_since(self.start).as_millis() as f64 / RESCALE_ANIMATION_DURATION) @@ -225,7 +225,7 @@ pub struct MoveGrab { seat: Seat, cursor_output: Output, window_outputs: HashSet, - tiling: bool, + previous: ManagedLayer, release: ReleaseMode, // SAFETY: This is only used on drop which will always be on the main thread evlh: NotSend>, @@ -290,7 +290,7 @@ impl PointerGrab for MoveGrab { } } - if self.tiling { + if self.previous == ManagedLayer::Tiling { let indicator_location = state .common .shell @@ -458,7 +458,7 @@ impl MoveGrab { initial_cursor_location: Point, initial_window_location: Point, indicator_thickness: u8, - was_tiled: bool, + previous_layer: ManagedLayer, release: ReleaseMode, evlh: LoopHandle<'static, State>, ) -> MoveGrab { @@ -475,7 +475,7 @@ impl MoveGrab { indicator_thickness, start: Instant::now(), stacking_indicator: None, - tiling: was_tiled, + previous: previous_layer, }; *seat @@ -495,14 +495,14 @@ impl MoveGrab { seat: seat.clone(), window_outputs: outputs, cursor_output: output, - tiling: was_tiled, + previous: previous_layer, release, evlh: NotSend(evlh), } } pub fn is_tiling_grab(&self) -> bool { - self.tiling + self.previous == ManagedLayer::Tiling } } @@ -512,7 +512,7 @@ impl Drop for MoveGrab { let output = self.seat.active_output(); let seat = self.seat.clone(); let window_outputs = self.window_outputs.drain().collect::>(); - let tiling = self.tiling; + let previous = self.previous; let window = self.window.clone(); let _ = self.evlh.0.insert_idle(move |state| { @@ -533,39 +533,58 @@ impl Drop for MoveGrab { 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 previous != ManagedLayer::Sticky { + state + .common + .shell + .toplevel_info_state + .toplevel_enter_workspace(&window, &workspace_handle); + } } - 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, - ); + match previous { + ManagedLayer::Tiling => { + let (window, location) = state + .common + .shell + .active_space_mut(&output) + .tiling_layer + .drop_window(grab_state.window); + Some((window, location.to_global(&output))) + } + ManagedLayer::Floating => { + 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)) + Some((window.clone(), window_location)) + } + ManagedLayer::Sticky => { + grab_state.window.set_geometry(Rectangle::from_loc_and_size( + window_location, + grab_state.window.geometry().size.as_global(), + )); + let set = state.common.shell.workspaces.sets.get_mut(&output).unwrap(); + set.sticky_layer.map_internal( + grab_state.window, + Some(window_location.to_local(&output)), + None, + ); + + Some((window.clone(), window_location)) + } } } else { None diff --git a/src/shell/mod.rs b/src/shell/mod.rs index d77d0fd8..134a2764 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -26,7 +26,7 @@ use smithay::{ }, wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle}, }, - utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, + utils::{Logical, Point, Rectangle, Serial, Size, SERIAL_COUNTER}, wayland::{ compositor::with_states, seat::WaylandFocus, @@ -73,10 +73,12 @@ use self::{ swap_indicator::{swap_indicator, SwapIndicator}, CosmicWindow, }, - focus::target::KeyboardFocusTarget, - grabs::{tab_items, window_items, Item, MenuGrab, ReleaseMode, ResizeEdge, ResizeGrab}, + focus::target::{KeyboardFocusTarget, PointerFocusTarget}, + grabs::{ + tab_items, window_items, Item, MenuGrab, MoveGrab, ReleaseMode, ResizeEdge, ResizeGrab, + }, layout::{ - floating::ResizeState, + floating::{FloatingLayout, ResizeState}, tiling::{NodeDesc, ResizeForkGrab, TilingLayout}, }, }; @@ -2098,52 +2100,179 @@ impl Shell { seat: &Seat, serial: impl Into>, release: ReleaseMode, + move_out_of_stack: bool, ) { let serial = serial.into(); - if let Some(start_data) = check_grab_preconditions(&seat, surface, serial, release) { - if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { let output = seat.active_output(); - let (window, _) = mapped + + if let Some(mut start_data) = check_grab_preconditions(&seat, surface, serial, release) { + if let Some(mut old_mapped) = + state.common.shell.element_for_wl_surface(surface).cloned() + { + let seats = state.common.seats().cloned().collect::>(); + for workspace in state.common.shell.workspaces.spaces_mut() { + for seat in seats.iter() { + let mut stack = workspace.focus_stack.get_mut(seat); + stack.remove(&old_mapped); + } + } + + let (window, _) = old_mapped .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) .unwrap(); + + let mapped = if move_out_of_stack { + let new_mapped: CosmicMapped = CosmicWindow::new( + window.clone(), + state.common.event_loop_handle.clone(), + state.common.theme.clone(), + ) + .into(); + start_data.focus = Some((new_mapped.clone().into(), Point::from((0, 0)))); + new_mapped + } else { + old_mapped.clone() + }; + let button = start_data.button; let active_hint = state.common.theme.cosmic().active_hint as u8; - if let Some(grab) = workspace.move_request( - &window, - &seat, - &output, - start_data, - active_hint as u8, - release, - state.common.event_loop_handle.clone(), - ) { - let handle = workspace.handle; + let pointer = seat.get_pointer().unwrap(); + let pos = pointer.current_location().as_global(); + + let (initial_window_location, layer, workspace_handle) = + if let Some(workspace) = state.common.shell.space_for_mut(&old_mapped) { + if workspace + .fullscreen + .as_ref() + .is_some_and(|f| f.surface == window) + { + let _ = workspace.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace + } + + let mut initial_window_location = workspace + .element_geometry(&old_mapped) + .unwrap() + .loc + .to_global(&output); + + if mapped.maximized_state.lock().unwrap().is_some() { + // If surface is maximized then unmaximize it + let new_size = workspace.unmaximize_request(&mapped); + let ratio = pos.to_local(&output).x / output.geometry().size.w as f64; + + initial_window_location = new_size + .map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into()) + .unwrap_or_else(|| pos) + .to_i32_round(); + } + + let layer = if mapped == old_mapped { + let was_floating = workspace.floating_layer.unmap(&mapped); + let was_tiled = workspace.tiling_layer.unmap_as_placeholder(&mapped); + assert!(was_floating != was_tiled.is_some()); + was_tiled.is_some() + } else { + workspace + .tiling_layer + .mapped() + .any(|(_, m, _)| m == &old_mapped) + } + .then_some(ManagedLayer::Tiling) + .unwrap_or(ManagedLayer::Floating); + + (initial_window_location, layer, workspace.handle) + } else if let Some(sticky_layer) = state + .common + .shell + .workspaces + .sets + .get_mut(&output) + .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) + .map(|set| &mut set.sticky_layer) + { + let mut initial_window_location = sticky_layer + .element_geometry(&old_mapped) + .unwrap() + .loc + .to_global(&output); + + if let Some(state) = mapped.maximized_state.lock().unwrap().take() { + // If surface is maximized then unmaximize it + mapped.set_maximized(false); + let new_size = state.original_geometry.size.as_logical(); + sticky_layer.map_internal( + mapped.clone(), + Some(state.original_geometry.loc), + Some(new_size), + ); + + let ratio = pos.to_local(&output).x / output.geometry().size.w as f64; + initial_window_location = + Point::::from((pos.x - (new_size.w as f64 * ratio), pos.y)) + .to_i32_round(); + } + + if mapped == old_mapped { + sticky_layer.unmap(&mapped); + } + + ( + initial_window_location, + ManagedLayer::Sticky, + state.common.shell.active_space(&output).handle, + ) + } else { + return; + }; + state .common .shell .toplevel_info_state - .toplevel_leave_workspace(&window, &handle); + .toplevel_leave_workspace(&window, &workspace_handle); state .common .shell .toplevel_info_state .toplevel_leave_output(&window, &output); + + if move_out_of_stack { + old_mapped.stack_ref_mut().unwrap().remove_window(&window); + state + .common + .shell + .workspaces + .space_for_handle_mut(&workspace_handle) + .unwrap() + .refresh(&state.common.shell.xdg_activation_state); + } + + let grab = MoveGrab::new( + start_data, + mapped, + seat, + pos, + initial_window_location, + active_hint as u8, + layer, + release, + state.common.event_loop_handle.clone(), + ); + if grab.is_tiling_grab() { state.common.shell.set_overview_mode( Some(Trigger::Pointer(button)), state.common.event_loop_handle.clone(), ); } + seat.get_pointer().unwrap().set_grab( state, grab, serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), Focus::Clear, ); - } - } } } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 9137e4af..4ebdfdbe 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -4,7 +4,6 @@ use crate::{ BackdropShader, GlMultiError, GlMultiFrame, GlMultiRenderer, }, shell::{ - grabs::MoveGrab, layout::{floating::FloatingLayout, tiling::TilingLayout}, OverviewMode, ANIMATION_DURATION, }, @@ -21,7 +20,6 @@ use crate::{ xwayland::XWaylandState, }; -use calloop::LoopHandle; use cosmic::theme::CosmicTheme; use id_tree::Tree; use indexmap::IndexSet; @@ -37,7 +35,7 @@ use smithay::{ ImportAll, ImportMem, Renderer, }, desktop::{layer_map_for_output, space::SpaceElement}, - input::{pointer::GrabStartData as PointerGrabStartData, Seat}, + input::Seat, output::Output, reexports::{ wayland_server::{protocol::wl_surface::WlSurface, Client, Resource}, @@ -151,6 +149,7 @@ pub struct ManagedState { pub enum ManagedLayer { Tiling, Floating, + Sticky, } #[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] @@ -678,62 +677,6 @@ impl Workspace { } } - pub fn move_request( - &mut self, - window: &CosmicSurface, - seat: &Seat, - output: &Output, - 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(); - - if self - .fullscreen - .as_ref() - .is_some_and(|f| &f.surface == window) - { - let _ = self.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace - } - - let mapped = self.element_for_surface(&window)?.clone(); - let mut initial_window_location = self - .element_geometry(&mapped) - .unwrap() - .loc - .to_global(&self.output); - - if mapped.maximized_state.lock().unwrap().is_some() { - // If surface is maximized then unmaximize it - let new_size = self.unmaximize_request(window); - let ratio = pos.to_local(&self.output).x / output.geometry().size.w as f64; - - initial_window_location = new_size - .map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into()) - .unwrap_or_else(|| pos) - .to_i32_round(); - } - - let was_floating = self.floating_layer.unmap(&mapped); - let was_tiled = self.tiling_layer.unmap_as_placeholder(&mapped); - assert!(was_floating != was_tiled.is_some()); - - Some(MoveGrab::new( - start_data, - mapped, - seat, - pos, - initial_window_location, - indicator_thickness, - was_tiled.is_some(), - release, - evlh, - )) - } - pub fn toggle_tiling(&mut self, seat: &Seat) { if self.tiling_enabled { for window in self diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 1fd76ed3..d67f8a36 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -147,6 +147,7 @@ impl XdgShellHandler for State { &seat, serial, ReleaseMode::NoMouseButtons, + false, ) } diff --git a/src/xwayland.rs b/src/xwayland.rs index 98c15562..5dcc4c5d 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -412,7 +412,14 @@ impl XwmHandler for State { fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { if let Some(wl_surface) = window.wl_surface() { let seat = self.common.last_active_seat().clone(); - Shell::move_request(self, &wl_surface, &seat, None, ReleaseMode::NoMouseButtons) + Shell::move_request( + self, + &wl_surface, + &seat, + None, + ReleaseMode::NoMouseButtons, + false, + ) } }