diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index c0727b3b..d7d5daa6 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -34,7 +34,7 @@ pub use self::stack::CosmicStack; pub mod window; pub use self::window::CosmicWindow; -use super::focus::FocusDirection; +use super::{focus::FocusDirection, layout::floating::ResizeState}; space_elements! { #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -53,6 +53,7 @@ pub struct CosmicMapped { pub(super) tiling_node_id: Arc>>, //floating pub(super) last_geometry: Arc>>>, + pub(super) resize_state: Arc>>, } impl PartialEq for CosmicMapped { @@ -165,6 +166,40 @@ impl CosmicMapped { } } + pub fn set_resizing(&self, resizing: bool) { + for window in match &self.element { + CosmicMappedInternal::Stack(s) => { + Box::new(s.windows()) as Box> + } + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + _ => unreachable!(), + } { + match window.toplevel() { + Kind::Xdg(xdg) => xdg.with_pending_state(|state| { + if resizing { + state.states.set(XdgState::Resizing); + } else { + state.states.unset(XdgState::Resizing); + } + }), + // Kind::X11? + }; + } + } + + pub fn is_resizing(&self) -> bool { + let window = match &self.element { + CosmicMappedInternal::Stack(s) => s.active(), + CosmicMappedInternal::Window(w) => w.window.clone(), + _ => unreachable!(), + }; + + match window.toplevel() { + Kind::Xdg(xdg) => xdg.current_state().states.contains(XdgState::Resizing), + // Kind::X11? + } + } + pub fn set_tiled(&self, tiled: bool) { for toplevel in match &self.element { // we use the tiled state of stack windows anyway to get rid of decorations @@ -590,6 +625,7 @@ impl From for CosmicMapped { element: CosmicMappedInternal::Window(w), tiling_node_id: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), + resize_state: Arc::new(Mutex::new(None)), } } } @@ -600,6 +636,7 @@ impl From for CosmicMapped { element: CosmicMappedInternal::Stack(s), tiling_node_id: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), + resize_state: Arc::new(Mutex::new(None)), } } } diff --git a/src/shell/layout/floating/grabs.rs b/src/shell/layout/floating/grabs.rs index 800150aa..8ff20f92 100644 --- a/src/shell/layout/floating/grabs.rs +++ b/src/shell/layout/floating/grabs.rs @@ -1,32 +1,32 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{shell::focus::target::PointerFocusTarget, utils::prelude::*}; +use crate::{ + shell::{element::CosmicMapped, focus::target::PointerFocusTarget}, + utils::prelude::*, +}; use smithay::{ - desktop::{Kind, Window}, + desktop::space::SpaceElement, input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, - utils::{IsAlive, Logical, Point, Serial, Size}, - wayland::{ - compositor::with_states, - shell::xdg::{SurfaceCachedState, ToplevelConfigure, XdgToplevelSurfaceRoleAttributes}, - }, + utils::{IsAlive, Logical, Point, Size}, }; -use std::{cell::RefCell, convert::TryFrom, sync::Mutex}; +use std::convert::TryFrom; bitflags::bitflags! { - struct ResizeEdge: u32 { - const NONE = 0; - const TOP = 1; - const BOTTOM = 2; - const LEFT = 4; - const TOP_LEFT = 5; - const BOTTOM_LEFT = 6; - const RIGHT = 8; - const TOP_RIGHT = 9; - const BOTTOM_RIGHT = 10; + pub struct ResizeEdge: u32 { + const TOP = 0b0001; + const BOTTOM = 0b0010; + const LEFT = 0b0100; + const RIGHT = 0b1000; + + const TOP_LEFT = Self::TOP.bits | Self::LEFT.bits; + const BOTTOM_LEFT = Self::BOTTOM.bits | Self::LEFT.bits; + + const TOP_RIGHT = Self::TOP.bits | Self::RIGHT.bits; + const BOTTOM_RIGHT = Self::BOTTOM.bits | Self::RIGHT.bits; } } @@ -46,7 +46,7 @@ impl From for xdg_toplevel::ResizeEdge { /// Information about the resize operation. #[derive(Debug, Clone, Copy, Eq, PartialEq)] -struct ResizeData { +pub struct ResizeData { /// The edges the surface is being resized with. edges: ResizeEdge, /// The initial window location. @@ -57,26 +57,16 @@ struct ResizeData { /// State of the resize operation. #[derive(Debug, Clone, Copy, Eq, PartialEq)] -enum ResizeState { - /// The surface is not being resized. - NotResizing, +pub enum ResizeState { /// The surface is currently being resized. Resizing(ResizeData), - /// The resize has finished, and the surface needs to ack the final configure. - WaitingForFinalAck(ResizeData, Serial), /// The resize has finished, and the surface needs to commit its final state. WaitingForCommit(ResizeData), } -impl Default for ResizeState { - fn default() -> Self { - ResizeState::NotResizing - } -} - pub struct ResizeSurfaceGrab { start_data: PointerGrabStartData, - window: Window, + window: CosmicMapped, edges: ResizeEdge, initial_window_size: Size, last_window_size: Size, @@ -123,10 +113,7 @@ impl PointerGrab for ResizeSurfaceGrab { new_window_height = (self.initial_window_size.h as f64 + dy) as i32; } - let (min_size, max_size) = with_states(self.window.toplevel().wl_surface(), |states| { - let data = states.cached_state.current::(); - (data.min_size, data.max_size) - }); + let (min_size, max_size) = (self.window.min_size(), self.window.max_size()); let min_width = min_size.w.max(1); let min_height = min_size.h.max(1); @@ -146,15 +133,9 @@ impl PointerGrab for ResizeSurfaceGrab { self.last_window_size = (new_window_width, new_window_height).into(); - match &self.window.toplevel() { - Kind::Xdg(xdg) => { - xdg.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Resizing); - state.size = Some(self.last_window_size); - }); - xdg.send_configure(); - } - } + self.window.set_resizing(true); + self.window.set_size(self.last_window_size); + self.window.configure(); } fn button( @@ -173,23 +154,13 @@ impl PointerGrab for ResizeSurfaceGrab { return; } - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &self.window.toplevel() { - xdg.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Resizing); - state.size = Some(self.last_window_size); - }); - xdg.send_configure(); - } + self.window.set_resizing(false); + self.window.set_size(self.last_window_size); + self.window.configure(); - let mut resize_state = self - .window - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - if let ResizeState::Resizing(resize_data) = *resize_state { - *resize_state = ResizeState::WaitingForFinalAck(resize_data, event.serial); + let mut resize_state = self.window.resize_state.lock().unwrap(); + if let Some(ResizeState::Resizing(resize_data)) = *resize_state { + *resize_state = Some(ResizeState::WaitingForCommit(resize_data)); } else { panic!("invalid resize state: {:?}", resize_state); } @@ -213,7 +184,7 @@ impl PointerGrab for ResizeSurfaceGrab { impl ResizeSurfaceGrab { pub fn new( start_data: PointerGrabStartData, - window: Window, + mapped: CosmicMapped, edges: xdg_toplevel::ResizeEdge, initial_window_location: Point, initial_window_size: Size, @@ -224,92 +195,27 @@ impl ResizeSurfaceGrab { initial_window_size, }); - window - .user_data() - .insert_if_missing(|| RefCell::new(ResizeState::default())); - *window - .user_data() - .get::>() - .unwrap() - .borrow_mut() = resize_state; + *mapped.resize_state.lock().unwrap() = Some(resize_state); ResizeSurfaceGrab { start_data, - window, + window: mapped, edges: edges.into(), initial_window_size, last_window_size: initial_window_size, } } - pub fn ack_configure(window: &Window, configure: ToplevelConfigure) { - let surface = window.toplevel().wl_surface(); - - let waiting_for_serial = - if let Some(data) = window.user_data().get::>() { - if let ResizeState::WaitingForFinalAck(_, serial) = *data.borrow() { - Some(serial) - } else { - None - } - } else { - None - }; - - if let Some(serial) = waiting_for_serial { - // When the resize grab is released the surface - // resize state will be set to WaitingForFinalAck - // and the client will receive a configure request - // without the resize state to inform the client - // resizing has finished. Here we will wait for - // the client to acknowledge the end of the - // resizing. To check if the surface was resizing - // before sending the configure we need to use - // the current state as the received acknowledge - // will no longer have the resize state set - let is_resizing = with_states(&surface, |states| { - states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap() - .current - .states - .contains(xdg_toplevel::State::Resizing) - }); - - if configure.serial >= serial && is_resizing { - let mut resize_state = window - .user_data() - .get::>() - .unwrap() - .borrow_mut(); - if let ResizeState::WaitingForFinalAck(resize_data, _) = *resize_state { - *resize_state = ResizeState::WaitingForCommit(resize_data); - } else { - unreachable!() - } - } - } - } - - pub fn apply_resize_state( - window: &Window, - mut location: Point, - size: Size, - ) -> Option> { - let mut new_location = None; - - if let Some(resize_state) = window.user_data().get::>() { - let mut resize_state = resize_state.borrow_mut(); + pub fn apply_resize_to_location(window: CosmicMapped, space: &mut Workspace) { + if let Some(mut location) = space.floating_layer.space.element_location(&window) { + let mut new_location = None; + let mut resize_state = window.resize_state.lock().unwrap(); // If the window is being resized by top or left, its location must be adjusted // accordingly. match *resize_state { - ResizeState::Resizing(resize_data) - | ResizeState::WaitingForFinalAck(resize_data, _) - | ResizeState::WaitingForCommit(resize_data) => { + Some(ResizeState::Resizing(resize_data)) + | Some(ResizeState::WaitingForCommit(resize_data)) => { let ResizeData { edges, initial_window_location, @@ -317,6 +223,7 @@ impl ResizeSurfaceGrab { } = resize_data; if edges.intersects(ResizeEdge::TOP_LEFT) { + let size = window.geometry().size; if edges.intersects(ResizeEdge::LEFT) { location.x = initial_window_location.x + (initial_window_size.w - size.w); @@ -329,15 +236,30 @@ impl ResizeSurfaceGrab { new_location = Some(location); } } - ResizeState::NotResizing => (), - } + _ => {} + }; // Finish resizing. - if let ResizeState::WaitingForCommit(_) = *resize_state { - *resize_state = ResizeState::NotResizing; + if let Some(ResizeState::WaitingForCommit(_)) = *resize_state { + if !window.is_resizing() { + *resize_state = None; + } + } + std::mem::drop(resize_state); + + if let Some(new_location) = new_location { + for (window, offset) in window.windows() { + update_reactive_popups( + &window, + new_location + offset, + space.floating_layer.space.outputs(), + ); + } + space + .floating_layer + .space + .map_element(window, new_location, false); } } - - new_location } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 399f8cc9..ad05c3ff 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -3,10 +3,11 @@ use smithay::{ backend::renderer::{ImportAll, Renderer}, desktop::{layer_map_for_output, space::SpaceElement, Space, Window}, - input::Seat, + input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, + reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, render_elements, - utils::{Logical, Point, Rectangle}, + utils::{Logical, Point, Rectangle, Serial}, }; use std::collections::HashMap; @@ -185,33 +186,31 @@ impl FloatingLayout { } } - /* pub fn resize_request( - window: &CosmicWindow, + &mut self, + mapped: &CosmicMapped, seat: &Seat, serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, - ) { - // it is so stupid, that we have to do this here. TODO: Refactor grabs - let workspace = state - .common - .shell - .space_for_window_mut(window.toplevel().wl_surface()) - .unwrap(); - let space = &mut workspace.space; - + ) -> Option { if let Some(pointer) = seat.get_pointer() { - let location = space.window_location(&window).unwrap(); - let size = window.geometry().size; + let location = self.space.element_location(&mapped).unwrap(); + let size = mapped.geometry().size; - let grab = - grabs::ResizeSurfaceGrab::new(start_data, window.clone(), edges, location, size); + Some(grabs::ResizeSurfaceGrab::new( + start_data, + mapped.clone(), + edges, + location, + size, + )) - pointer.set_grab(state, grab, serial, Focus::Clear); + //pointer.set_grab(state, grab, serial, Focus::Clear); + } else { + None } } - */ pub fn mapped(&self) -> impl Iterator { self.space.elements() diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 2f8c4bcd..6790d7df 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -33,7 +33,10 @@ use std::{collections::HashMap, time::Duration}; use super::{ element::CosmicMapped, focus::{FocusStack, FocusStackMut}, - layout::{floating::FloatingRenderElement, tiling::TilingRenderElement}, + layout::{ + floating::{FloatingRenderElement, ResizeSurfaceGrab}, + tiling::TilingRenderElement, + }, }; #[derive(Debug)] @@ -253,32 +256,27 @@ impl Workspace { self.fullscreen.get(output).filter(|w| w.alive()) } - /* pub fn resize_request( - state: &mut State, - surface: &WlSurface, + &mut self, + mapped: &CosmicMapped, seat: &Seat, serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, - ) { - let workspace = state.common.shell.space_for_window_mut(surface).unwrap(); - let window = workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .unwrap() - .clone(); - - if workspace.fullscreen.values().any(|w| w == &window) { - return; + ) -> Option { + if mapped.is_fullscreen() || mapped.is_maximized() { + return None; } - if workspace.floating_layer.windows.contains(&window) { - FloatingLayout::resize_request(state, &window, seat, serial, start_data.clone(), edges) - } else if workspace.tiling_layer.windows.contains(&window) { - TilingLayout::resize_request(state, &window, seat, serial, start_data, edges) + if self.floating_layer.mapped().any(|m| m == mapped) { + self.floating_layer + .resize_request(mapped, seat, serial, start_data.clone(), edges) + } else if self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) { + //self.tiling_layer.resize_request(mapped, seat, serial, start_data, edges) + None + } else { + None } } - */ pub fn toggle_tiling(&mut self, seat: &Seat) { if self.tiling_enabled { diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index c9935665..a0199033 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -147,43 +147,18 @@ impl CompositorHandler for State { self.xdg_popup_ensure_initial_configure(&popup); } - /* // at last handle some special cases, like grabs and changing layer surfaces // If we would re-position the window inside the grab we would get a weird jittery animation. // We only want to resize once the client has acknoledged & commited the new size, // so we need to carefully track the state through different handlers. - if let Some((space, window)) = - self.common - .shell - .space_for_window_mut(surface) - .and_then(|workspace| { - workspace - .space - .window_for_surface(surface, WindowSurfaceType::TOPLEVEL) - .cloned() - .map(|window| (&mut workspace.space, window)) - }) - { - let new_location = - crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_state( - &window, - space.window_location(&window).unwrap(), - window.geometry().size, + if let Some(element) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(workspace) = self.common.shell.space_for_mut(&element) { + crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location( + element, workspace, ); - if let Some(location) = new_location { - space.map_window( - &window, - location, - crate::shell::layout::floating::FLOATING_INDEX, - true, - ); - for window in space.windows() { - update_reactive_popups(space, window); - } } } - */ // We need to know every potential output for importing to the right gpu and scheduling a render, // so call this only after every potential surface map operation has been done. diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 083ae411..f320b129 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -169,8 +169,21 @@ impl XdgShellHandler for State { ) { let seat = Seat::from_resource(&seat).unwrap(); if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { - if let Some(mapped) = self.common.shell.element_for_surface(surface.wl_surface()) { - // Shell::resize_request(self, mapped, &seat, serial, start_data, edges); + if let Some(mapped) = self + .common + .shell + .element_for_surface(surface.wl_surface()) + .cloned() + { + if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { + if let Some(grab) = + workspace.resize_request(&mapped, &seat, serial, start_data, edges) + { + seat.get_pointer() + .unwrap() + .set_grab(self, grab, serial, Focus::Clear); + } + } } } }