shell: Refactor move_request, consider sticky windows

This commit is contained in:
Victoria Brekenfeld 2023-12-20 20:29:44 +00:00 committed by Victoria Brekenfeld
parent 775e0ccbdc
commit 769b7d6996
8 changed files with 247 additions and 191 deletions

View file

@ -1,10 +1,7 @@
use super::{CosmicMapped, CosmicSurface, CosmicWindow}; use super::CosmicSurface;
use crate::{ use crate::{
shell::{ shell::{
focus::FocusDirection, focus::FocusDirection, grabs::ReleaseMode, layout::tiling::NodeDesc, Direction, Shell,
grabs::{MoveGrab, ReleaseMode},
layout::tiling::NodeDesc,
Direction, Shell, Trigger,
}, },
state::State, state::State,
utils::iced::{IcedElement, Program}, utils::iced::{IcedElement, Program},
@ -38,8 +35,8 @@ use smithay::{
pointer::{ pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
GrabStartData as PointerGrabStartData, MotionEvent, PointerTarget, RelativeMotionEvent, PointerTarget, RelativeMotionEvent,
}, },
Seat, Seat,
}, },
@ -659,6 +656,7 @@ impl Program for CosmicStackInternal {
&seat, &seat,
serial, serial,
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
false,
); );
}); });
} }
@ -713,20 +711,20 @@ impl Program for CosmicStackInternal {
return; return;
}; };
let mut cursor = seat let mut cursor = seat
.get_pointer() .get_pointer()
.unwrap() .unwrap()
.current_location() .current_location()
.to_i32_round(); .to_i32_round();
cursor.y -= TAB_HEIGHT; cursor.y -= TAB_HEIGHT;
Shell::menu_request( Shell::menu_request(
state, state,
&surface, &surface,
&seat, &seat,
serial, serial,
cursor - position.as_logical(), cursor - position.as_logical(),
true, true,
); );
} }
}); });
} }
@ -1172,60 +1170,18 @@ impl PointerTarget<State> for CosmicStack {
.0 .0
.with_program(|p| p.windows.lock().unwrap().get(dragged_out).cloned()) .with_program(|p| p.windows.lock().unwrap().get(dragged_out).cloned())
{ {
if let Some(stack_mapped) = let seat = seat.clone();
data.common.shell.element_for_surface(&surface) if let Some(surface) = surface.wl_surface() {
{ let _ = data.common.event_loop_handle.insert_idle(move |state| {
if let Some(workspace) = data.common.shell.space_for(stack_mapped) { Shell::move_request(
// TODO: Unify this somehow with Shell::move_request/Workspace::move_request state,
let button = 0x110; // BTN_LEFT &surface,
let pos = event.location.as_global(); &seat,
let start_data = PointerGrabStartData { None,
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,
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
data.common.event_loop_handle.clone(), true,
); )
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,
);
});
}
} }
} }
} }

View file

@ -262,6 +262,7 @@ impl Program for CosmicWindowInternal {
&seat, &seat,
serial, serial,
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
false,
); );
}); });
} }

View file

@ -233,7 +233,7 @@ pub fn window_items(
let _ = handle.insert_idle(move |state| { let _ = handle.insert_idle(move |state| {
if let Some(surface) = move_clone.wl_surface() { if let Some(surface) = move_clone.wl_surface() {
let seat = state.common.last_active_seat().clone(); 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);
} }
}); });
})), })),

View file

@ -12,7 +12,7 @@ use crate::{
CosmicMappedRenderElement, CosmicMappedRenderElement,
}, },
focus::target::{KeyboardFocusTarget, PointerFocusTarget}, focus::target::{KeyboardFocusTarget, PointerFocusTarget},
CosmicMapped, CosmicSurface, CosmicMapped, CosmicSurface, ManagedLayer,
}, },
utils::prelude::*, utils::prelude::*,
}; };
@ -61,7 +61,7 @@ pub struct MoveGrabState {
window_offset: Point<i32, Logical>, window_offset: Point<i32, Logical>,
indicator_thickness: u8, indicator_thickness: u8,
start: Instant, start: Instant,
tiling: bool, previous: ManagedLayer,
stacking_indicator: Option<(StackHover, Point<i32, Logical>)>, stacking_indicator: Option<(StackHover, Point<i32, Logical>)>,
} }
@ -82,7 +82,7 @@ impl MoveGrabState {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
puffin::profile_function!(); puffin::profile_function!();
let scale = if self.tiling { let scale = if self.previous == ManagedLayer::Tiling {
0.6 + ((1.0 0.6 + ((1.0
- (Instant::now().duration_since(self.start).as_millis() as f64 - (Instant::now().duration_since(self.start).as_millis() as f64
/ RESCALE_ANIMATION_DURATION) / RESCALE_ANIMATION_DURATION)
@ -225,7 +225,7 @@ pub struct MoveGrab {
seat: Seat<State>, seat: Seat<State>,
cursor_output: Output, cursor_output: Output,
window_outputs: HashSet<Output>, window_outputs: HashSet<Output>,
tiling: bool, previous: ManagedLayer,
release: ReleaseMode, release: ReleaseMode,
// SAFETY: This is only used on drop which will always be on the main thread // SAFETY: This is only used on drop which will always be on the main thread
evlh: NotSend<LoopHandle<'static, State>>, evlh: NotSend<LoopHandle<'static, State>>,
@ -290,7 +290,7 @@ impl PointerGrab<State> for MoveGrab {
} }
} }
if self.tiling { if self.previous == ManagedLayer::Tiling {
let indicator_location = state let indicator_location = state
.common .common
.shell .shell
@ -458,7 +458,7 @@ impl MoveGrab {
initial_cursor_location: Point<f64, Global>, initial_cursor_location: Point<f64, Global>,
initial_window_location: Point<i32, Global>, initial_window_location: Point<i32, Global>,
indicator_thickness: u8, indicator_thickness: u8,
was_tiled: bool, previous_layer: ManagedLayer,
release: ReleaseMode, release: ReleaseMode,
evlh: LoopHandle<'static, State>, evlh: LoopHandle<'static, State>,
) -> MoveGrab { ) -> MoveGrab {
@ -475,7 +475,7 @@ impl MoveGrab {
indicator_thickness, indicator_thickness,
start: Instant::now(), start: Instant::now(),
stacking_indicator: None, stacking_indicator: None,
tiling: was_tiled, previous: previous_layer,
}; };
*seat *seat
@ -495,14 +495,14 @@ impl MoveGrab {
seat: seat.clone(), seat: seat.clone(),
window_outputs: outputs, window_outputs: outputs,
cursor_output: output, cursor_output: output,
tiling: was_tiled, previous: previous_layer,
release, release,
evlh: NotSend(evlh), evlh: NotSend(evlh),
} }
} }
pub fn is_tiling_grab(&self) -> bool { 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 output = self.seat.active_output();
let seat = self.seat.clone(); let seat = self.seat.clone();
let window_outputs = self.window_outputs.drain().collect::<HashSet<_>>(); let window_outputs = self.window_outputs.drain().collect::<HashSet<_>>();
let tiling = self.tiling; let previous = self.previous;
let window = self.window.clone(); let window = self.window.clone();
let _ = self.evlh.0.insert_idle(move |state| { let _ = self.evlh.0.insert_idle(move |state| {
@ -533,39 +533,58 @@ impl Drop for MoveGrab {
grab_state.window.output_leave(old_output); grab_state.window.output_leave(old_output);
} }
for (window, _) in grab_state.window.windows() { for (window, _) in grab_state.window.windows() {
state
.common
.shell
.toplevel_info_state
.toplevel_enter_workspace(&window, &workspace_handle);
state state
.common .common
.shell .shell
.toplevel_info_state .toplevel_info_state
.toplevel_enter_output(&window, &output); .toplevel_enter_output(&window, &output);
if previous != ManagedLayer::Sticky {
state
.common
.shell
.toplevel_info_state
.toplevel_enter_workspace(&window, &workspace_handle);
}
} }
if tiling { match previous {
let (window, location) = state ManagedLayer::Tiling => {
.common let (window, location) = state
.shell .common
.active_space_mut(&output) .shell
.tiling_layer .active_space_mut(&output)
.drop_window(grab_state.window); .tiling_layer
Some((window, location.to_global(&output))) .drop_window(grab_state.window);
} else { Some((window, location.to_global(&output)))
grab_state.window.set_geometry(Rectangle::from_loc_and_size( }
window_location, ManagedLayer::Floating => {
grab_state.window.geometry().size.as_global(), grab_state.window.set_geometry(Rectangle::from_loc_and_size(
)); window_location,
let workspace = state.common.shell.active_space_mut(&output); grab_state.window.geometry().size.as_global(),
workspace.floating_layer.map_internal( ));
grab_state.window, let workspace = state.common.shell.active_space_mut(&output);
Some(window_location.to_local(&workspace.output)), workspace.floating_layer.map_internal(
None, 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 { } else {
None None

View file

@ -26,7 +26,7 @@ use smithay::{
}, },
wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle}, wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle},
}, },
utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, utils::{Logical, Point, Rectangle, Serial, Size, SERIAL_COUNTER},
wayland::{ wayland::{
compositor::with_states, compositor::with_states,
seat::WaylandFocus, seat::WaylandFocus,
@ -73,10 +73,12 @@ use self::{
swap_indicator::{swap_indicator, SwapIndicator}, swap_indicator::{swap_indicator, SwapIndicator},
CosmicWindow, CosmicWindow,
}, },
focus::target::KeyboardFocusTarget, focus::target::{KeyboardFocusTarget, PointerFocusTarget},
grabs::{tab_items, window_items, Item, MenuGrab, ReleaseMode, ResizeEdge, ResizeGrab}, grabs::{
tab_items, window_items, Item, MenuGrab, MoveGrab, ReleaseMode, ResizeEdge, ResizeGrab,
},
layout::{ layout::{
floating::ResizeState, floating::{FloatingLayout, ResizeState},
tiling::{NodeDesc, ResizeForkGrab, TilingLayout}, tiling::{NodeDesc, ResizeForkGrab, TilingLayout},
}, },
}; };
@ -2098,52 +2100,179 @@ impl Shell {
seat: &Seat<State>, seat: &Seat<State>,
serial: impl Into<Option<Serial>>, serial: impl Into<Option<Serial>>,
release: ReleaseMode, release: ReleaseMode,
move_out_of_stack: bool,
) { ) {
let serial = serial.into(); 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 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::<Vec<_>>();
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() .windows()
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) .find(|(w, _)| w.wl_surface().as_ref() == Some(surface))
.unwrap(); .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 button = start_data.button;
let active_hint = state.common.theme.cosmic().active_hint as u8; let active_hint = state.common.theme.cosmic().active_hint as u8;
if let Some(grab) = workspace.move_request( let pointer = seat.get_pointer().unwrap();
&window, let pos = pointer.current_location().as_global();
&seat,
&output, let (initial_window_location, layer, workspace_handle) =
start_data, if let Some(workspace) = state.common.shell.space_for_mut(&old_mapped) {
active_hint as u8, if workspace
release, .fullscreen
state.common.event_loop_handle.clone(), .as_ref()
) { .is_some_and(|f| f.surface == window)
let handle = workspace.handle; {
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::<f64, _>::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 state
.common .common
.shell .shell
.toplevel_info_state .toplevel_info_state
.toplevel_leave_workspace(&window, &handle); .toplevel_leave_workspace(&window, &workspace_handle);
state state
.common .common
.shell .shell
.toplevel_info_state .toplevel_info_state
.toplevel_leave_output(&window, &output); .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() { if grab.is_tiling_grab() {
state.common.shell.set_overview_mode( state.common.shell.set_overview_mode(
Some(Trigger::Pointer(button)), Some(Trigger::Pointer(button)),
state.common.event_loop_handle.clone(), state.common.event_loop_handle.clone(),
); );
} }
seat.get_pointer().unwrap().set_grab( seat.get_pointer().unwrap().set_grab(
state, state,
grab, grab,
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
Focus::Clear, Focus::Clear,
); );
}
}
} }
} }
} }

View file

@ -4,7 +4,6 @@ use crate::{
BackdropShader, GlMultiError, GlMultiFrame, GlMultiRenderer, BackdropShader, GlMultiError, GlMultiFrame, GlMultiRenderer,
}, },
shell::{ shell::{
grabs::MoveGrab,
layout::{floating::FloatingLayout, tiling::TilingLayout}, layout::{floating::FloatingLayout, tiling::TilingLayout},
OverviewMode, ANIMATION_DURATION, OverviewMode, ANIMATION_DURATION,
}, },
@ -21,7 +20,6 @@ use crate::{
xwayland::XWaylandState, xwayland::XWaylandState,
}; };
use calloop::LoopHandle;
use cosmic::theme::CosmicTheme; use cosmic::theme::CosmicTheme;
use id_tree::Tree; use id_tree::Tree;
use indexmap::IndexSet; use indexmap::IndexSet;
@ -37,7 +35,7 @@ use smithay::{
ImportAll, ImportMem, Renderer, ImportAll, ImportMem, Renderer,
}, },
desktop::{layer_map_for_output, space::SpaceElement}, desktop::{layer_map_for_output, space::SpaceElement},
input::{pointer::GrabStartData as PointerGrabStartData, Seat}, input::Seat,
output::Output, output::Output,
reexports::{ reexports::{
wayland_server::{protocol::wl_surface::WlSurface, Client, Resource}, wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
@ -151,6 +149,7 @@ pub struct ManagedState {
pub enum ManagedLayer { pub enum ManagedLayer {
Tiling, Tiling,
Floating, Floating,
Sticky,
} }
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] #[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
@ -678,62 +677,6 @@ impl Workspace {
} }
} }
pub fn move_request(
&mut self,
window: &CosmicSurface,
seat: &Seat<State>,
output: &Output,
start_data: PointerGrabStartData<State>,
indicator_thickness: u8,
release: ReleaseMode,
evlh: LoopHandle<'static, State>,
) -> Option<MoveGrab> {
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<State>) { pub fn toggle_tiling(&mut self, seat: &Seat<State>) {
if self.tiling_enabled { if self.tiling_enabled {
for window in self for window in self

View file

@ -147,6 +147,7 @@ impl XdgShellHandler for State {
&seat, &seat,
serial, serial,
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
false,
) )
} }

View file

@ -412,7 +412,14 @@ impl XwmHandler for State {
fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) {
if let Some(wl_surface) = window.wl_surface() { if let Some(wl_surface) = window.wl_surface() {
let seat = self.common.last_active_seat().clone(); 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,
)
} }
} }