From 6f57d8d41338aedc6cc98b0f2638431772ae2298 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Tue, 11 Jul 2023 21:07:51 -0500 Subject: [PATCH] Add dwindle layout --- api/lua/output.lua | 2 +- src/api/msg.rs | 3 +- src/handlers.rs | 33 +++--- src/layout.rs | 262 ++++++++++++++++++++++++++++----------------- src/output.rs | 4 +- src/state.rs | 20 ++-- src/tag.rs | 9 +- src/xdg/request.rs | 10 +- 8 files changed, 209 insertions(+), 134 deletions(-) diff --git a/api/lua/output.lua b/api/lua/output.lua index eacf64b..1e4fbe5 100644 --- a/api/lua/output.lua +++ b/api/lua/output.lua @@ -112,7 +112,7 @@ function output.get_by_res(width, height) return outputs end ----Get the currently focused output. This is currently the one with the cursor on it. +---Get the currently focused output. This is currently implemented as the one with the cursor on it. ---@return Output|nil output The output, or nil if none are focused. function output.get_focused() SendMsg({ diff --git a/src/api/msg.rs b/src/api/msg.rs index c2f11ec..69cb516 100644 --- a/src/api/msg.rs +++ b/src/api/msg.rs @@ -47,12 +47,10 @@ pub enum Msg { }, // Tag management - // FIXME: tag_id should not be a string ToggleTag { output_name: String, tag_name: String, }, - // FIXME: tag_id should not be a string SwitchToTag { output_name: String, tag_name: String, @@ -91,6 +89,7 @@ pub enum Msg { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RequestId(pub u32); +#[allow(clippy::enum_variant_names)] #[derive(Debug, serde::Serialize, serde::Deserialize)] /// Messages that require a server response, usually to provide some data. pub enum Request { diff --git a/src/handlers.rs b/src/handlers.rs index c010080..11145bd 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -47,7 +47,6 @@ use smithay::{ use crate::{ backend::Backend, - layout::{Layout, LayoutVec}, state::{ClientState, State, WithState}, window::window_state::WindowResizeState, }; @@ -230,10 +229,7 @@ impl XdgShellHandler for State { self.space.outputs().next(), ) { (Some(output), _) | (None, Some(output)) => output.with_state(|state| { - let output_tags = state - .focused_tags() - .map(|tag| tag.clone()) - .collect::>(); + let output_tags = state.focused_tags().cloned().collect::>(); if !output_tags.is_empty() { output_tags } else if let Some(first_tag) = state.tags.first() { @@ -265,13 +261,15 @@ impl XdgShellHandler for State { self.loop_handle.insert_idle(move |data| { if let Some(focused_output) = &data.state.focus_state.focused_output { focused_output.with_state(|state| { - data.state - .windows - .to_master_stack( + let first_tag = state.focused_tags().next(); + if let Some(first_tag) = first_tag { + first_tag.layout().layout( + data.state.windows.clone(), + state.focused_tags().cloned().collect(), + &data.state.space, focused_output, - state.focused_tags().map(|tag| tag.clone()).collect(), - ) - .layout(&data.state.space, focused_output); + ); + } }); } }); @@ -282,12 +280,15 @@ impl XdgShellHandler for State { self.windows.retain(|window| window.toplevel() != &surface); if let Some(focused_output) = self.focus_state.focused_output.as_ref() { focused_output.with_state(|state| { - self.windows - .to_master_stack( + let first_tag = state.focused_tags().next(); + if let Some(first_tag) = first_tag { + first_tag.layout().layout( + self.windows.clone(), + state.focused_tags().cloned().collect(), + &self.space, focused_output, - state.focused_tags().map(|tag| tag.clone()).collect(), - ) - .layout(&self.space, focused_output); + ); + } }); } diff --git a/src/layout.rs b/src/layout.rs index 75904eb..9b1bfa4 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -5,7 +5,7 @@ // SPDX-License-Identifier: MPL-2.0 use smithay::{ - desktop::{space::SpaceElement, Space, Window}, + desktop::{Space, Window}, output::Output, utils::{Logical, Size}, }; @@ -24,127 +24,187 @@ pub enum Direction { Bottom, } -pub trait Layout { - fn layout(&self, space: &Space, output: &Output); +// TODO: couple this with the layouts +#[derive(Debug, Clone, Copy)] +pub enum Layout { + MasterStack, + Dwindle, } -pub struct MasterStack { - inner: Vec, - output: Output, -} +impl Layout { + pub fn layout( + &self, + windows: Vec, + tags: Vec, + space: &Space, + output: &Output, + ) { + let windows = filter_windows(&windows, tags); + match self { + Layout::MasterStack => { + let master = windows.first(); + let stack = windows.iter().skip(1); -impl MasterStack { - pub fn master(&self) -> Option<&Window> { - self.inner.first() - } + let Some(master) = master else { return }; - pub fn stack(&self) -> impl Iterator { - self.inner.iter().skip(1) - } + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; - fn layout_stack(&self, space: &Space, output: &Output) { - let stack_count = self.stack().count(); + let output_loc = output.current_location(); - let Some(output_geo) = space.output_geometry(output) else { - tracing::error!("could not get output geometry"); - return; - }; + let stack_count = stack.clone().count(); - let output_loc = output.current_location(); + if stack_count == 0 { + // one window + master.toplevel().with_pending_state(|state| { + state.size = Some(output_geo.size); + }); - let height = output_geo.size.h / stack_count as i32; + master.with_state(|state| { + state.resize_state = WindowResizeState::WaitingForAck( + master.toplevel().send_configure(), + (output_loc.x, output_loc.y).into(), + ); + }); + } else { + let new_master_size: Size = + (output_geo.size.w / 2, output_geo.size.h).into(); + master.toplevel().with_pending_state(|state| { + state.size = Some(new_master_size); + }); + master.with_state(|state| { + state.resize_state = WindowResizeState::WaitingForAck( + master.toplevel().send_configure(), + (output_loc.x, output_loc.y).into(), + ); + }); - for (i, win) in self.stack().enumerate() { - win.toplevel().with_pending_state(|state| { - state.size = Some((output_geo.size.w / 2, height).into()); - }); + let stack_count = stack_count; - win.with_state(|state| { - state.resize_state = WindowResizeState::WaitingForAck( - win.toplevel().send_configure(), - ( - output_geo.size.w / 2 + output_loc.x, - i as i32 * height + output_loc.y, - ) - .into(), - ); - }); - } - } -} + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; -impl Layout for MasterStack { - fn layout(&self, space: &Space, output: &Output) { - let Some(master) = self.master() else { - return; - }; + let output_loc = output.current_location(); - let Some(output_geo) = space.output_geometry(output) else { - tracing::error!("could not get output geometry"); - return; - }; + let height = output_geo.size.h / stack_count as i32; - let output_loc = output.current_location(); + for (i, win) in stack.enumerate() { + win.toplevel().with_pending_state(|state| { + state.size = Some((output_geo.size.w / 2, height).into()); + }); - if self.stack().count() == 0 { - // one window - master.toplevel().with_pending_state(|state| { - state.size = Some(output_geo.size); - }); + win.with_state(|state| { + state.resize_state = WindowResizeState::WaitingForAck( + win.toplevel().send_configure(), + ( + output_geo.size.w / 2 + output_loc.x, + i as i32 * height + output_loc.y, + ) + .into(), + ); + }); + } + } + } + Layout::Dwindle => { + let mut iter = windows.windows(2).peekable(); + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; - master.with_state(|state| { - state.resize_state = WindowResizeState::WaitingForAck( - master.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - } else { - let new_master_size: Size = - (output_geo.size.w / 2, output_geo.size.h).into(); - master.toplevel().with_pending_state(|state| { - state.size = Some(new_master_size); - }); - master.with_state(|state| { - state.resize_state = WindowResizeState::WaitingForAck( - master.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); + let output_loc = output.current_location(); - self.layout_stack(space, output); - } - } -} + if iter.peek().is_none() { + if let Some(window) = windows.first() { + window.toplevel().with_pending_state(|state| { + state.size = Some(output_geo.size); + }); -pub struct Dwindle { - inner: Vec, - output: Output, -} + window.with_state(|state| { + state.resize_state = WindowResizeState::WaitingForAck( + window.toplevel().send_configure(), + (output_loc.x, output_loc.y).into(), + ); + }); + } + } else { + let mut div_factor_w = 1; + let mut div_factor_h = 1; + let mut x_factor_1: f32; + let mut y_factor_1: f32; + let mut x_factor_2: f32 = 0.0; + let mut y_factor_2: f32 = 0.0; -impl Layout for Dwindle { - fn layout(&self, space: &Space, output: &Output) { - todo!() - } -} + for (i, wins) in iter.enumerate() { + let win1 = &wins[0]; + let win2 = &wins[1]; -pub trait LayoutVec { - /// Interpret this vec as a master-stack layout. - fn to_master_stack(&self, output: &Output, tags: Vec) -> MasterStack; - fn to_dwindle(&self, output: &Output, tags: Vec) -> Dwindle; -} + if i % 2 == 0 { + div_factor_w *= 2; + } else { + div_factor_h *= 2; + } -impl LayoutVec for Vec { - fn to_master_stack(&self, output: &Output, tags: Vec) -> MasterStack { - MasterStack { - inner: filter_windows(self, tags), - output: output.clone(), // TODO: get rid of? - } - } + win1.toplevel().with_pending_state(|state| { + let new_size = ( + output_geo.size.w / div_factor_w, + output_geo.size.h / div_factor_h, + ) + .into(); + state.size = Some(new_size); + }); + win2.toplevel().with_pending_state(|state| { + let new_size = ( + output_geo.size.w / div_factor_w, + output_geo.size.h / div_factor_h, + ) + .into(); + state.size = Some(new_size); + }); - fn to_dwindle(&self, output: &Output, tags: Vec) -> Dwindle { - Dwindle { - inner: filter_windows(self, tags), - output: output.clone(), + x_factor_1 = x_factor_2; + y_factor_1 = y_factor_2; + + if i % 2 == 0 { + x_factor_2 += (1.0 - x_factor_2) / 2.0; + } else { + y_factor_2 += (1.0 - y_factor_2) / 2.0; + } + + win1.with_state(|state| { + let new_loc = ( + (output_geo.size.w as f32 * x_factor_1 + output_loc.x as f32) + as i32, + (output_geo.size.h as f32 * y_factor_1 + output_loc.y as f32) + as i32, + ) + .into(); + state.resize_state = WindowResizeState::WaitingForAck( + win1.toplevel().send_configure(), + new_loc, + ); + }); + win2.with_state(|state| { + let new_loc = ( + (output_geo.size.w as f32 * x_factor_2 + output_loc.x as f32) + as i32, + (output_geo.size.h as f32 * y_factor_2 + output_loc.y as f32) + as i32, + ) + .into(); + state.resize_state = WindowResizeState::WaitingForAck( + win2.toplevel().send_configure(), + new_loc, + ); + }); + } + } + } } } } diff --git a/src/output.rs b/src/output.rs index 6d2e8bc..dcedfc7 100644 --- a/src/output.rs +++ b/src/output.rs @@ -38,7 +38,7 @@ impl WithState for Output { } impl OutputState { - pub fn focused_tags(&mut self) -> impl Iterator { - self.tags.iter_mut().filter(|tag| tag.active()) + pub fn focused_tags(&self) -> impl Iterator { + self.tags.iter().filter(|tag| tag.active()) } } diff --git a/src/state.rs b/src/state.rs index b1130b1..0627399 100644 --- a/src/state.rs +++ b/src/state.rs @@ -21,7 +21,6 @@ use crate::{ }, focus::FocusState, grab::resize_grab::ResizeSurfaceState, - layout::{Layout, LayoutVec}, tag::Tag, window::{window_state::WindowResizeState, WindowProperties}, }; @@ -187,7 +186,7 @@ impl State { .with_state(|op_state| { let tag = op_state.tags.iter().find(|tag| tag.name() == tag_id); if let Some(tag) = tag { - if state.tags.contains(&tag) { + if state.tags.contains(tag) { state.tags.retain(|tg| tg != tag); } else { state.tags.push(tag.clone()); @@ -638,15 +637,18 @@ impl State { pub fn re_layout(&mut self, output: &Output) { let windows = self.windows.iter().filter(|win| { - win.with_state(|state| state.tags.iter().any(|tag| self.output_for_tag(tag).is_some_and(|op| &op == output))) - }).cloned().collect::>(); + win.with_state(|state| state.tags.iter().any(|tag| self.output_for_tag(tag).is_some_and(|op| &op == output))) + }).cloned().collect::>(); let (render, do_not_render) = output.with_state(|state| { - self.windows - .to_master_stack( + let first_tag = state.focused_tags().next(); + if let Some(first_tag) = first_tag { + first_tag.layout().layout( + self.windows.clone(), + state.focused_tags().cloned().collect(), + &self.space, output, - state.focused_tags().map(|tag| tag.clone()).collect(), - ) - .layout(&self.space, output); + ); + } windows.iter().cloned().partition::, _>(|win| { win.with_state(|win_state| { diff --git a/src/tag.rs b/src/tag.rs index c418f0e..7769fcb 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -15,6 +15,7 @@ use smithay::output::Output; use crate::{ backend::Backend, + layout::Layout, state::{State, WithState}, }; @@ -37,7 +38,8 @@ struct TagInner { name: String, /// Whether this tag is active or not. active: bool, - // TODO: layout + /// What layout this tag has. + layout: Layout, } impl PartialEq for TagInner { @@ -67,6 +69,10 @@ impl Tag { pub fn set_active(&mut self, active: bool) { self.0.borrow_mut().active = active; } + + pub fn layout(&self) -> Layout { + self.0.borrow().layout + } } impl Tag { @@ -75,6 +81,7 @@ impl Tag { id: TagId::next(), name, active: false, + layout: Layout::Dwindle, // TODO: get from config }))) } } diff --git a/src/xdg/request.rs b/src/xdg/request.rs index ee2d61c..a7502f6 100644 --- a/src/xdg/request.rs +++ b/src/xdg/request.rs @@ -14,7 +14,7 @@ use smithay::{ use crate::{ backend::Backend, grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab}, - state::State, + state::{State, WithState}, }; pub fn move_request( @@ -94,6 +94,9 @@ pub fn resize_request( if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial) { let window = state.window_for_surface(wl_surface).unwrap(); + if window.with_state(|state| state.floating.is_tiled()) { + return; + } let initial_window_loc = state.space.element_location(&window).unwrap(); let initial_window_size = window.geometry().size; @@ -124,13 +127,16 @@ pub fn resize_request_force( edges: xdg_toplevel::ResizeEdge, button_used: u32, ) { - println!("resize_request_force started with edges {:?}", edges); let wl_surface = surface.wl_surface(); let pointer = seat.get_pointer().unwrap(); let window = state.window_for_surface(wl_surface).unwrap(); + if window.with_state(|state| state.floating.is_tiled()) { + return; + } + let initial_window_loc = state.space.element_location(&window).unwrap(); let initial_window_size = window.geometry().size;