diff --git a/src/shell/grabs.rs b/src/shell/grabs.rs new file mode 100644 index 00000000..ac5f6877 --- /dev/null +++ b/src/shell/grabs.rs @@ -0,0 +1,107 @@ +use smithay::{ + input::pointer::{ + AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, + PointerInnerHandle, + }, + reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, + utils::{Logical, Point}, +}; + +use crate::state::State; + +use super::{ + focus::target::PointerFocusTarget, + layout::{floating::ResizeSurfaceGrab, tiling::ResizeForkGrab}, +}; + +bitflags::bitflags! { + 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; + } +} + +impl From for ResizeEdge { + #[inline] + fn from(x: xdg_toplevel::ResizeEdge) -> Self { + Self::from_bits(x.into()).unwrap() + } +} + +impl From for xdg_toplevel::ResizeEdge { + #[inline] + fn from(x: ResizeEdge) -> Self { + Self::try_from(x.bits()).unwrap() + } +} + +pub enum ResizeGrab { + Floating(ResizeSurfaceGrab), + Tiling(ResizeForkGrab), +} + +impl From for ResizeGrab { + fn from(grab: ResizeSurfaceGrab) -> Self { + ResizeGrab::Floating(grab) + } +} + +impl From for ResizeGrab { + fn from(grab: ResizeForkGrab) -> Self { + ResizeGrab::Tiling(grab) + } +} + +impl PointerGrab for ResizeGrab { + fn motion( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + focus: Option<(PointerFocusTarget, Point)>, + event: &MotionEvent, + ) { + match self { + ResizeGrab::Floating(grab) => grab.motion(data, handle, focus, event), + ResizeGrab::Tiling(grab) => grab.motion(data, handle, focus, event), + } + } + + fn button( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &ButtonEvent, + ) { + match self { + ResizeGrab::Floating(grab) => grab.button(data, handle, event), + ResizeGrab::Tiling(grab) => grab.button(data, handle, event), + } + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + details: AxisFrame, + ) { + match self { + ResizeGrab::Floating(grab) => grab.axis(data, handle, details), + ResizeGrab::Tiling(grab) => grab.axis(data, handle, details), + } + } + + fn start_data(&self) -> &PointerGrabStartData { + match self { + ResizeGrab::Floating(grab) => grab.start_data(), + ResizeGrab::Tiling(grab) => grab.start_data(), + } + } +} diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 8ff20f92..d228d0ee 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{element::CosmicMapped, focus::target::PointerFocusTarget}, + shell::{element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge}, utils::prelude::*, }; use smithay::{ @@ -10,39 +10,8 @@ use smithay::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, }, - reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{IsAlive, Logical, Point, Size}, }; -use std::convert::TryFrom; - -bitflags::bitflags! { - 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; - } -} - -impl From for ResizeEdge { - #[inline] - fn from(x: xdg_toplevel::ResizeEdge) -> Self { - Self::from_bits(x.into()).unwrap() - } -} - -impl From for xdg_toplevel::ResizeEdge { - #[inline] - fn from(x: ResizeEdge) -> Self { - Self::try_from(x.bits()).unwrap() - } -} /// Information about the resize operation. #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -185,12 +154,12 @@ impl ResizeSurfaceGrab { pub fn new( start_data: PointerGrabStartData, mapped: CosmicMapped, - edges: xdg_toplevel::ResizeEdge, + edges: ResizeEdge, initial_window_location: Point, initial_window_size: Size, ) -> ResizeSurfaceGrab { let resize_state = ResizeState::Resizing(ResizeData { - edges: edges.into(), + edges, initial_window_location, initial_window_size, }); @@ -200,7 +169,7 @@ impl ResizeSurfaceGrab { ResizeSurfaceGrab { start_data, window: mapped, - edges: edges.into(), + edges, initial_window_size, last_window_size: initial_window_size, } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 728ee8a4..0b34644d 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -5,7 +5,6 @@ use smithay::{ desktop::{layer_map_for_output, space::SpaceElement, Space, Window}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, render_elements, utils::{Logical, Point, Rectangle, Serial}, }; @@ -14,6 +13,7 @@ use std::collections::HashMap; use crate::{ shell::{ element::{CosmicMapped, CosmicMappedRenderElement}, + grabs::ResizeEdge, OutputNotMapped, }, state::State, diff --git a/src/shell/layout/tiling/grabs.rs b/src/shell/layout/tiling/grabs.rs index a12f5a8e..b23c4965 100644 --- a/src/shell/layout/tiling/grabs.rs +++ b/src/shell/layout/tiling/grabs.rs @@ -1,23 +1,52 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{shell::layout::Orientation, utils::prelude::*}; -use atomic_float::AtomicF64; +use crate::{ + shell::{focus::target::PointerFocusTarget, layout::Orientation}, + utils::prelude::*, +}; +use id_tree::NodeId; use smithay::{ input::pointer::{ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, }, - reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{Logical, Point, Size}, + output::{Output, WeakOutput}, + utils::{Logical, Point}, }; -use std::sync::{atomic::Ordering, Arc}; + +use super::Data; pub struct ResizeForkGrab { - pub start_data: PointerGrabStartData, - pub orientation: Orientation, - pub initial_size: Size, - pub initial_ratio: f64, - pub ratio: Arc, + start_data: PointerGrabStartData, + idx: usize, + initial_size_upleft: i32, + initial_size_downright: i32, + node: NodeId, + output: WeakOutput, +} + +impl ResizeForkGrab { + pub fn new( + start_data: PointerGrabStartData, + node: NodeId, + output: &Output, + data: &Data, + idx: usize, + ) -> ResizeForkGrab { + let sizes = match data { + Data::Group { ref sizes, .. } => sizes, + _ => panic!("Resizing without a group?!?"), + }; + + ResizeForkGrab { + start_data, + idx, + initial_size_upleft: sizes.iter().take(idx + 1).sum(), + initial_size_downright: sizes.iter().skip(idx + 1).sum(), + node, + output: output.downgrade(), + } + } } impl PointerGrab for ResizeForkGrab { @@ -25,21 +54,139 @@ impl PointerGrab for ResizeForkGrab { &mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>, - _focus: Option<(WlSurface, Point)>, + _focus: Option<(PointerFocusTarget, Point)>, event: &MotionEvent, ) { // While the grab is active, no client has pointer focus handle.motion(data, None, event); let delta = event.location - self.start_data.location; - let delta = match self.orientation { - Orientation::Vertical => delta.x / self.initial_size.w as f64, - Orientation::Horizontal => delta.y / self.initial_size.h as f64, - }; - self.ratio.store( - 0.9f64.min(0.1f64.max(self.initial_ratio + delta)), - Ordering::SeqCst, - ); + + if let Some(output) = self.output.upgrade() { + let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer; + if let Some(tree) = tiling_layer.trees.get_mut(&output) { + if tree.get(&self.node).is_ok() { + let orientation = tree.get(&self.node).unwrap().data().orientation(); + let delta = match orientation { + Orientation::Vertical => delta.x, + Orientation::Horizontal => delta.y, + } + .round() as i32; + + let upleft_node_id = + match tree.children_ids(&self.node).unwrap().skip(self.idx).next() { + Some(elem) => elem, + None => { + return; + } + }; + let downright_node_id = match tree + .children_ids(&self.node) + .unwrap() + .skip(self.idx + 1) + .next() + { + Some(elem) => elem, + None => { + return; + } + }; + + let next_mapped = |mut node| loop { + if let Some(node_id) = node { + match tree.get(&node_id).unwrap().data() { + Data::Group { orientation: o, .. } if o == &orientation => { + node = tree.children_ids(&node_id).unwrap().last().cloned(); + } + _ => { + break node_id; + } + } + } else { + unreachable!() + } + }; + let upleft_mapped_id = next_mapped(Some(upleft_node_id.clone())); + let downright_mapped_id = next_mapped(Some(downright_node_id.clone())); + + let new_upleft_size = self.initial_size_upleft + delta; + let new_downright_size = self.initial_size_downright - delta; + let new_upleft_mapped_size = match orientation { + Orientation::Horizontal => { + tree.get(&upleft_mapped_id) + .unwrap() + .data() + .geometry() + .size + .h + } + Orientation::Vertical => { + tree.get(&upleft_mapped_id) + .unwrap() + .data() + .geometry() + .size + .w + } + } + delta; + let new_downright_mapped_size = match orientation { + Orientation::Horizontal => { + tree.get(&downright_mapped_id) + .unwrap() + .data() + .geometry() + .size + .h + } + Orientation::Vertical => { + tree.get(&downright_mapped_id) + .unwrap() + .data() + .geometry() + .size + .w + } + } - delta; + + if new_upleft_mapped_size > 100 && new_downright_mapped_size > 100 { + // lets update + { + let node = tree.get_mut(&self.node).unwrap(); + let data = node.data_mut(); + match data { + Data::Group { sizes, .. } => { + sizes[self.idx] = new_upleft_size; + sizes[self.idx + 1] = new_downright_size; + } + _ => unreachable!(), + }; + } + for (mapped_id, mapped_size) in &[ + (upleft_mapped_id, new_upleft_mapped_size), + (downright_mapped_id, new_downright_mapped_size), + ] { + let parent = tree.get(mapped_id).unwrap().parent().cloned().unwrap(); + if parent != self.node { + let idx = tree + .children_ids(&parent) + .unwrap() + .position(|id| id == mapped_id) + .unwrap(); + let node = tree.get_mut(&parent).unwrap(); + let data = node.data_mut(); + match data { + Data::Group { sizes, .. } => { + sizes[idx] = *mapped_size; + } + _ => unreachable!(), + }; + } + } + return tiling_layer.refresh(); + } + } + } + } } fn button( diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 941cc003..d716944f 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -7,37 +7,27 @@ use crate::{ target::{KeyboardFocusTarget, WindowGroup}, FocusDirection, }, + grabs::ResizeEdge, layout::Orientation, OutputNotMapped, }, utils::prelude::*, - wayland::handlers::xdg_shell::popup::{self, get_popup_toplevel}, + wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use smithay::{ backend::renderer::{element::AsRenderElements, ImportAll, Renderer}, - desktop::{layer_map_for_output, space::SpaceElement, PopupKind, PopupManager, Window}, - input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData}, - Seat, - }, - output::{Output, WeakOutput}, + desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Window}, + input::{pointer::GrabStartData as PointerGrabStartData, Seat}, + output::Output, render_elements, utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, }; -use std::{ - borrow::Borrow, - cell::RefCell, - collections::{HashMap, VecDeque}, - hash::Hash, - sync::{atomic::AtomicBool, Arc}, -}; +use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc}; -/* mod grabs; pub use self::grabs::*; -*/ #[derive(Debug, Clone)] struct OutputData { @@ -908,68 +898,58 @@ impl TilingLayout { TilingLayout::update_space_positions(&mut self.trees, self.gaps); } - /* pub fn resize_request( - window: &CosmicWindow, - seat: &Seat, - serial: Serial, + &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; - let trees = &mut workspace.tiling_layer.trees; + ) -> Option { + let (output, mut node_id) = self.trees.iter().find_map(|(output, tree)| { + let root_id = tree.root_node_id()?; + tree.traverse_pre_order_ids(root_id) + .unwrap() + .find(|id| tree.get(id).unwrap().data().is_mapped(Some(mapped))) + .map(|id| (&output.output, id)) + })?; - if let Some(pointer) = seat.get_pointer() { - if let Some(info) = window.user_data().get::>() { - let output = info.borrow().output; - let tree = TilingLayout::active_tree(trees, output); - let mut node_id = info.borrow().node.clone(); - - while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { - if let &Data::Fork { - ref orientation, - ref ratio, - } = tree.get(&fork).unwrap().data() - { - // found a fork - // which child are we? - let first = tree.children_ids(&fork).unwrap().next() == Some(&child); - match (first, orientation, edges) { - (true, Orientation::Horizontal, ResizeEdge::Bottom) - | (false, Orientation::Horizontal, ResizeEdge::Top) - | (true, Orientation::Vertical, ResizeEdge::Right) - | (false, Orientation::Vertical, ResizeEdge::Left) => { - let output = space.outputs().nth(output).cloned(); - if let Some(output) = output { - let grab = ResizeForkGrab { - start_data, - orientation: *orientation, - initial_ratio: ratio.load(Ordering::SeqCst), - initial_size: layer_map_for_output(&output) - .non_exclusive_zone() - .size, - ratio: ratio.clone(), - }; - - pointer.set_grab(state, grab, serial, Focus::Clear); - } - return; - } - _ => {} // continue iterating - } - } - node_id = fork; - } + let tree = self.trees.get(output).unwrap(); + while let Some(group_id) = tree.get(&node_id).unwrap().parent() { + let orientation = tree.get(group_id).unwrap().data().orientation(); + if !((orientation == Orientation::Vertical + && (edges.contains(ResizeEdge::LEFT) || edges.contains(ResizeEdge::RIGHT))) + || (orientation == Orientation::Horizontal + && (edges.contains(ResizeEdge::TOP) || edges.contains(ResizeEdge::BOTTOM)))) + { + node_id = group_id.clone(); + continue; } + + let node_idx = tree + .children_ids(group_id) + .unwrap() + .position(|id| id == &node_id) + .unwrap(); + let idx = match edges { + x if x.intersects(ResizeEdge::TOP_LEFT) => node_idx - 1, + _ => node_idx, + }; + if idx > tree.get(&group_id).unwrap().data().len() { + return None; + } + + return Some(ResizeForkGrab::new( + start_data, + group_id.clone(), + output, + tree.get(&group_id).unwrap().data(), + idx, + )); } + + None } - */ fn last_active_window<'a>( tree: &mut Tree, diff --git a/src/shell/mod.rs b/src/shell/mod.rs index fa83f184..1be6cd36 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -34,6 +34,7 @@ use crate::{ mod element; pub mod focus; +pub mod grabs; pub mod layout; mod workspace; pub use self::element::CosmicMappedRenderElement; diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 0ecb3c7b..7120cdcc 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -36,10 +36,8 @@ use std::{collections::HashMap, time::Duration}; use super::{ element::CosmicMapped, focus::{FocusStack, FocusStackMut}, - layout::{ - floating::{FloatingRenderElement, ResizeSurfaceGrab}, - tiling::TilingRenderElement, - }, + grabs::ResizeGrab, + layout::{floating::FloatingRenderElement, tiling::TilingRenderElement}, }; #[derive(Debug)] @@ -266,16 +264,20 @@ impl Workspace { serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, - ) -> Option { + ) -> Option { if mapped.is_fullscreen() || mapped.is_maximized() { return None; } + + let edges = edges.into(); if self.floating_layer.mapped().any(|m| m == mapped) { self.floating_layer .resize_request(mapped, seat, serial, start_data.clone(), edges) + .map(Into::into) } else if self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) { - //self.tiling_layer.resize_request(mapped, seat, serial, start_data, edges) - None + self.tiling_layer + .resize_request(mapped, seat, serial, start_data, edges) + .map(Into::into) } else { None }