shell/floating: Reintroduce resizing

This commit is contained in:
Victoria Brekenfeld 2022-10-25 14:43:50 +02:00
parent bb07ab4155
commit 7d068ab6bc
6 changed files with 154 additions and 210 deletions

View file

@ -34,7 +34,7 @@ pub use self::stack::CosmicStack;
pub mod window; pub mod window;
pub use self::window::CosmicWindow; pub use self::window::CosmicWindow;
use super::focus::FocusDirection; use super::{focus::FocusDirection, layout::floating::ResizeState};
space_elements! { space_elements! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -53,6 +53,7 @@ pub struct CosmicMapped {
pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>, pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>,
//floating //floating
pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>, pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
pub(super) resize_state: Arc<Mutex<Option<ResizeState>>>,
} }
impl PartialEq for CosmicMapped { 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<dyn Iterator<Item = Window>>
}
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) { pub fn set_tiled(&self, tiled: bool) {
for toplevel in match &self.element { for toplevel in match &self.element {
// we use the tiled state of stack windows anyway to get rid of decorations // we use the tiled state of stack windows anyway to get rid of decorations
@ -590,6 +625,7 @@ impl From<CosmicWindow> for CosmicMapped {
element: CosmicMappedInternal::Window(w), element: CosmicMappedInternal::Window(w),
tiling_node_id: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)),
last_geometry: 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<CosmicStack> for CosmicMapped {
element: CosmicMappedInternal::Stack(s), element: CosmicMappedInternal::Stack(s),
tiling_node_id: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)),
last_geometry: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)),
resize_state: Arc::new(Mutex::new(None)),
} }
} }
} }

View file

@ -1,32 +1,32 @@
// SPDX-License-Identifier: GPL-3.0-only // 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::{ use smithay::{
desktop::{Kind, Window}, desktop::space::SpaceElement,
input::pointer::{ input::pointer::{
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle, PointerInnerHandle,
}, },
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::{IsAlive, Logical, Point, Serial, Size}, utils::{IsAlive, Logical, Point, Size},
wayland::{
compositor::with_states,
shell::xdg::{SurfaceCachedState, ToplevelConfigure, XdgToplevelSurfaceRoleAttributes},
},
}; };
use std::{cell::RefCell, convert::TryFrom, sync::Mutex}; use std::convert::TryFrom;
bitflags::bitflags! { bitflags::bitflags! {
struct ResizeEdge: u32 { pub struct ResizeEdge: u32 {
const NONE = 0; const TOP = 0b0001;
const TOP = 1; const BOTTOM = 0b0010;
const BOTTOM = 2; const LEFT = 0b0100;
const LEFT = 4; const RIGHT = 0b1000;
const TOP_LEFT = 5;
const BOTTOM_LEFT = 6; const TOP_LEFT = Self::TOP.bits | Self::LEFT.bits;
const RIGHT = 8; const BOTTOM_LEFT = Self::BOTTOM.bits | Self::LEFT.bits;
const TOP_RIGHT = 9;
const BOTTOM_RIGHT = 10; const TOP_RIGHT = Self::TOP.bits | Self::RIGHT.bits;
const BOTTOM_RIGHT = Self::BOTTOM.bits | Self::RIGHT.bits;
} }
} }
@ -46,7 +46,7 @@ impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
/// Information about the resize operation. /// Information about the resize operation.
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct ResizeData { pub struct ResizeData {
/// The edges the surface is being resized with. /// The edges the surface is being resized with.
edges: ResizeEdge, edges: ResizeEdge,
/// The initial window location. /// The initial window location.
@ -57,26 +57,16 @@ struct ResizeData {
/// State of the resize operation. /// State of the resize operation.
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum ResizeState { pub enum ResizeState {
/// The surface is not being resized.
NotResizing,
/// The surface is currently being resized. /// The surface is currently being resized.
Resizing(ResizeData), 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. /// The resize has finished, and the surface needs to commit its final state.
WaitingForCommit(ResizeData), WaitingForCommit(ResizeData),
} }
impl Default for ResizeState {
fn default() -> Self {
ResizeState::NotResizing
}
}
pub struct ResizeSurfaceGrab { pub struct ResizeSurfaceGrab {
start_data: PointerGrabStartData<State>, start_data: PointerGrabStartData<State>,
window: Window, window: CosmicMapped,
edges: ResizeEdge, edges: ResizeEdge,
initial_window_size: Size<i32, Logical>, initial_window_size: Size<i32, Logical>,
last_window_size: Size<i32, Logical>, last_window_size: Size<i32, Logical>,
@ -123,10 +113,7 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
new_window_height = (self.initial_window_size.h as f64 + dy) as i32; 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 (min_size, max_size) = (self.window.min_size(), self.window.max_size());
let data = states.cached_state.current::<SurfaceCachedState>();
(data.min_size, data.max_size)
});
let min_width = min_size.w.max(1); let min_width = min_size.w.max(1);
let min_height = min_size.h.max(1); let min_height = min_size.h.max(1);
@ -146,15 +133,9 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
self.last_window_size = (new_window_width, new_window_height).into(); self.last_window_size = (new_window_width, new_window_height).into();
match &self.window.toplevel() { self.window.set_resizing(true);
Kind::Xdg(xdg) => { self.window.set_size(self.last_window_size);
xdg.with_pending_state(|state| { self.window.configure();
state.states.set(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
xdg.send_configure();
}
}
} }
fn button( fn button(
@ -173,23 +154,13 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
return; return;
} }
#[allow(irrefutable_let_patterns)] self.window.set_resizing(false);
if let Kind::Xdg(xdg) = &self.window.toplevel() { self.window.set_size(self.last_window_size);
xdg.with_pending_state(|state| { self.window.configure();
state.states.unset(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
xdg.send_configure();
}
let mut resize_state = self let mut resize_state = self.window.resize_state.lock().unwrap();
.window if let Some(ResizeState::Resizing(resize_data)) = *resize_state {
.user_data() *resize_state = Some(ResizeState::WaitingForCommit(resize_data));
.get::<RefCell<ResizeState>>()
.unwrap()
.borrow_mut();
if let ResizeState::Resizing(resize_data) = *resize_state {
*resize_state = ResizeState::WaitingForFinalAck(resize_data, event.serial);
} else { } else {
panic!("invalid resize state: {:?}", resize_state); panic!("invalid resize state: {:?}", resize_state);
} }
@ -213,7 +184,7 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
impl ResizeSurfaceGrab { impl ResizeSurfaceGrab {
pub fn new( pub fn new(
start_data: PointerGrabStartData<State>, start_data: PointerGrabStartData<State>,
window: Window, mapped: CosmicMapped,
edges: xdg_toplevel::ResizeEdge, edges: xdg_toplevel::ResizeEdge,
initial_window_location: Point<i32, Logical>, initial_window_location: Point<i32, Logical>,
initial_window_size: Size<i32, Logical>, initial_window_size: Size<i32, Logical>,
@ -224,92 +195,27 @@ impl ResizeSurfaceGrab {
initial_window_size, initial_window_size,
}); });
window *mapped.resize_state.lock().unwrap() = Some(resize_state);
.user_data()
.insert_if_missing(|| RefCell::new(ResizeState::default()));
*window
.user_data()
.get::<RefCell<ResizeState>>()
.unwrap()
.borrow_mut() = resize_state;
ResizeSurfaceGrab { ResizeSurfaceGrab {
start_data, start_data,
window, window: mapped,
edges: edges.into(), edges: edges.into(),
initial_window_size, initial_window_size,
last_window_size: initial_window_size, last_window_size: initial_window_size,
} }
} }
pub fn ack_configure(window: &Window, configure: ToplevelConfigure) { pub fn apply_resize_to_location(window: CosmicMapped, space: &mut Workspace) {
let surface = window.toplevel().wl_surface(); if let Some(mut location) = space.floating_layer.space.element_location(&window) {
let mut new_location = None;
let waiting_for_serial =
if let Some(data) = window.user_data().get::<RefCell<ResizeState>>() {
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::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.current
.states
.contains(xdg_toplevel::State::Resizing)
});
if configure.serial >= serial && is_resizing {
let mut resize_state = window
.user_data()
.get::<RefCell<ResizeState>>()
.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<i32, Logical>,
size: Size<i32, Logical>,
) -> Option<Point<i32, Logical>> {
let mut new_location = None;
if let Some(resize_state) = window.user_data().get::<RefCell<ResizeState>>() {
let mut resize_state = resize_state.borrow_mut();
let mut resize_state = window.resize_state.lock().unwrap();
// If the window is being resized by top or left, its location must be adjusted // If the window is being resized by top or left, its location must be adjusted
// accordingly. // accordingly.
match *resize_state { match *resize_state {
ResizeState::Resizing(resize_data) Some(ResizeState::Resizing(resize_data))
| ResizeState::WaitingForFinalAck(resize_data, _) | Some(ResizeState::WaitingForCommit(resize_data)) => {
| ResizeState::WaitingForCommit(resize_data) => {
let ResizeData { let ResizeData {
edges, edges,
initial_window_location, initial_window_location,
@ -317,6 +223,7 @@ impl ResizeSurfaceGrab {
} = resize_data; } = resize_data;
if edges.intersects(ResizeEdge::TOP_LEFT) { if edges.intersects(ResizeEdge::TOP_LEFT) {
let size = window.geometry().size;
if edges.intersects(ResizeEdge::LEFT) { if edges.intersects(ResizeEdge::LEFT) {
location.x = location.x =
initial_window_location.x + (initial_window_size.w - size.w); initial_window_location.x + (initial_window_size.w - size.w);
@ -329,15 +236,30 @@ impl ResizeSurfaceGrab {
new_location = Some(location); new_location = Some(location);
} }
} }
ResizeState::NotResizing => (), _ => {}
} };
// Finish resizing. // Finish resizing.
if let ResizeState::WaitingForCommit(_) = *resize_state { if let Some(ResizeState::WaitingForCommit(_)) = *resize_state {
*resize_state = ResizeState::NotResizing; 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
} }
} }

View file

@ -3,10 +3,11 @@
use smithay::{ use smithay::{
backend::renderer::{ImportAll, Renderer}, backend::renderer::{ImportAll, Renderer},
desktop::{layer_map_for_output, space::SpaceElement, Space, Window}, desktop::{layer_map_for_output, space::SpaceElement, Space, Window},
input::Seat, input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output, output::Output,
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
render_elements, render_elements,
utils::{Logical, Point, Rectangle}, utils::{Logical, Point, Rectangle, Serial},
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -185,33 +186,31 @@ impl FloatingLayout {
} }
} }
/*
pub fn resize_request( pub fn resize_request(
window: &CosmicWindow, &mut self,
mapped: &CosmicMapped,
seat: &Seat<State>, seat: &Seat<State>,
serial: Serial, serial: Serial,
start_data: PointerGrabStartData<State>, start_data: PointerGrabStartData<State>,
edges: ResizeEdge, edges: ResizeEdge,
) { ) -> Option<ResizeSurfaceGrab> {
// 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;
if let Some(pointer) = seat.get_pointer() { if let Some(pointer) = seat.get_pointer() {
let location = space.window_location(&window).unwrap(); let location = self.space.element_location(&mapped).unwrap();
let size = window.geometry().size; let size = mapped.geometry().size;
let grab = Some(grabs::ResizeSurfaceGrab::new(
grabs::ResizeSurfaceGrab::new(start_data, window.clone(), edges, location, size); 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<Item = &CosmicMapped> { pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
self.space.elements() self.space.elements()

View file

@ -33,7 +33,10 @@ use std::{collections::HashMap, time::Duration};
use super::{ use super::{
element::CosmicMapped, element::CosmicMapped,
focus::{FocusStack, FocusStackMut}, focus::{FocusStack, FocusStackMut},
layout::{floating::FloatingRenderElement, tiling::TilingRenderElement}, layout::{
floating::{FloatingRenderElement, ResizeSurfaceGrab},
tiling::TilingRenderElement,
},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -253,32 +256,27 @@ impl Workspace {
self.fullscreen.get(output).filter(|w| w.alive()) self.fullscreen.get(output).filter(|w| w.alive())
} }
/*
pub fn resize_request( pub fn resize_request(
state: &mut State, &mut self,
surface: &WlSurface, mapped: &CosmicMapped,
seat: &Seat<State>, seat: &Seat<State>,
serial: Serial, serial: Serial,
start_data: PointerGrabStartData<State>, start_data: PointerGrabStartData<State>,
edges: ResizeEdge, edges: ResizeEdge,
) { ) -> Option<ResizeSurfaceGrab> {
let workspace = state.common.shell.space_for_window_mut(surface).unwrap(); if mapped.is_fullscreen() || mapped.is_maximized() {
let window = workspace return None;
.space
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
.unwrap()
.clone();
if workspace.fullscreen.values().any(|w| w == &window) {
return;
} }
if workspace.floating_layer.windows.contains(&window) { if self.floating_layer.mapped().any(|m| m == mapped) {
FloatingLayout::resize_request(state, &window, seat, serial, start_data.clone(), edges) self.floating_layer
} else if workspace.tiling_layer.windows.contains(&window) { .resize_request(mapped, seat, serial, start_data.clone(), edges)
TilingLayout::resize_request(state, &window, seat, serial, start_data, 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<State>) { pub fn toggle_tiling(&mut self, seat: &Seat<State>) {
if self.tiling_enabled { if self.tiling_enabled {

View file

@ -147,43 +147,18 @@ impl CompositorHandler for State {
self.xdg_popup_ensure_initial_configure(&popup); self.xdg_popup_ensure_initial_configure(&popup);
} }
/*
// at last handle some special cases, like grabs and changing layer surfaces // 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. // 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, // 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. // so we need to carefully track the state through different handlers.
if let Some((space, window)) = if let Some(element) = self.common.shell.element_for_surface(surface).cloned() {
self.common if let Some(workspace) = self.common.shell.space_for_mut(&element) {
.shell crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location(
.space_for_window_mut(surface) element, workspace,
.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(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, // 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. // so call this only after every potential surface map operation has been done.

View file

@ -169,8 +169,21 @@ impl XdgShellHandler for State {
) { ) {
let seat = Seat::from_resource(&seat).unwrap(); let seat = Seat::from_resource(&seat).unwrap();
if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { 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()) { if let Some(mapped) = self
// Shell::resize_request(self, mapped, &seat, serial, start_data, edges); .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);
}
}
} }
} }
} }