From 69c71179d4f41f58088bf20334081a6ba93612b6 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 30 Mar 2022 23:08:35 +0200 Subject: [PATCH] input: Implement focus switched on tiling layout --- src/config.rs | 11 +- src/input/mod.rs | 12 +- src/shell/layout/combined.rs | 42 +++- src/shell/layout/mod.rs | 18 ++ src/shell/layout/tiling/mod.rs | 389 ++++++++++++++++++++------------- src/shell/mod.rs | 16 ++ src/shell/workspace.rs | 14 ++ 7 files changed, 336 insertions(+), 166 deletions(-) diff --git a/src/config.rs b/src/config.rs index c8946584..3f5e6024 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only +use crate::shell::layout::FocusDirection; use serde::Deserialize; use smithay::wayland::seat::Keysym; pub use smithay::{ @@ -181,15 +182,7 @@ pub enum Action { Close, Workspace(u8), MoveToWorkspace(u8), - Focus(FocusAction), + Focus(FocusDirection), Orientation(crate::shell::layout::Orientation), Spawn(String), } - -#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)] -pub enum FocusAction { - Left, - Right, - Up, - Down, -} diff --git a/src/input/mod.rs b/src/input/mod.rs index 44bfe045..7ed8f6b8 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -302,9 +302,15 @@ impl Common { *num, ); } - Action::Focus(focus) => match focus { - _ => { /* TODO */ } - }, + Action::Focus(focus) => { + let current_output = active_output(seat, &self); + self.shell.move_focus( + seat, + ¤t_output, + *focus, + self.seats.iter(), + ); + } Action::Orientation(orientation) => { let output = active_output(seat, &self); self.shell.set_orientation( diff --git a/src/shell/layout/combined.rs b/src/shell/layout/combined.rs index c2b2a9ab..106b91ff 100644 --- a/src/shell/layout/combined.rs +++ b/src/shell/layout/combined.rs @@ -1,4 +1,4 @@ -use super::Layout; +use super::{FocusDirection, Layout, Orientation}; use smithay::{ desktop::{Space, Window}, reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, @@ -108,4 +108,44 @@ impl Layout for Combined { .resize_request(space, window, seat, serial, start_data, edges) } } + + fn move_focus<'a>( + &mut self, + direction: FocusDirection, + seat: &Seat, + space: &mut Space, + focus_stack: Box + 'a>, + ) -> Option { + let focus_stack = focus_stack.collect::>(); + match self.first.move_focus( + direction, + seat, + space, + Box::new(focus_stack.clone().into_iter()), + ) { + Some(window) => Some(window), + None => { + self.second + .move_focus(direction, seat, space, Box::new(focus_stack.into_iter())) + } + } + } + + fn update_orientation<'a>( + &mut self, + orientation: Orientation, + seat: &Seat, + space: &mut Space, + focus_stack: Box + 'a>, + ) { + let focus_stack = focus_stack.collect::>(); + self.first.update_orientation( + orientation, + seat, + space, + Box::new(focus_stack.clone().into_iter()), + ); + self.second + .update_orientation(orientation, seat, space, Box::new(focus_stack.into_iter())); + } } diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index 5e565e67..798b6a1a 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -23,6 +23,14 @@ pub enum Orientation { Vertical, } +#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] +pub enum FocusDirection { + Left, + Right, + Up, + Down, +} + pub trait Layout { fn map_window<'a>( &mut self, @@ -34,6 +42,16 @@ pub trait Layout { fn refresh(&mut self, space: &mut Space); fn unmap_window(&mut self, space: &mut Space, window: &Window); + fn move_focus<'a>( + &mut self, + direction: FocusDirection, + seat: &Seat, + space: &mut Space, + focus_stack: Box + 'a>, + ) -> Option { + let _ = (direction, seat, space, focus_stack); + None + } fn update_orientation<'a>( &mut self, orientation: Orientation, diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 91d20a31..e2f09dfe 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::shell::layout::{Layout, Orientation}; +use crate::shell::layout::{FocusDirection, Layout, Orientation}; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use smithay::{ desktop::{layer_map_for_output, Kind, Space, Window}, @@ -119,12 +119,12 @@ impl Layout for TilingLayout { Ok(window_id) } else { // we create a new fork - new_fork(tree, node_id, new_window) + TilingLayout::new_fork(tree, node_id, new_window) } } else { // nothing? then we add to the root if let Some(root_id) = tree.root_node_id().cloned() { - new_fork(tree, &root_id, new_window) + TilingLayout::new_fork(tree, &root_id, new_window) } else { tree.insert(new_window, InsertBehavior::AsRoot) } @@ -146,12 +146,76 @@ impl Layout for TilingLayout { self.refresh(space); } + fn move_focus<'a>( + &mut self, + direction: FocusDirection, + seat: &Seat, + space: &mut Space, + focus_stack: Box + 'a>, + ) -> Option { + let output = super::output_from_seat(seat, space); + output + .user_data() + .insert_if_missing(|| RefCell::new(OutputInfo::default())); + let mut output_info = output + .user_data() + .get::>() + .unwrap() + .borrow_mut(); + let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new); + + if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) { + let mut node_id = last_active; + while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { + if let &Data::Fork { + ref orientation, .. + } = tree.get(&fork).unwrap().data() + { + // found a fork + // which child are we? + let first = tree.children_ids(&fork).unwrap().next() == Some(&child); + let focus_subtree = match (first, orientation, direction) { + (true, Orientation::Horizontal, FocusDirection::Down) + | (true, Orientation::Vertical, FocusDirection::Right) => { + tree.children_ids(&fork).unwrap().skip(1).next() + } + (false, Orientation::Horizontal, FocusDirection::Up) + | (false, Orientation::Vertical, FocusDirection::Left) => { + tree.children_ids(&fork).unwrap().next() + } + _ => None, // continue iterating + }; + + if focus_subtree.is_some() { + let mut node_id = focus_subtree; + while node_id.is_some() + && !matches!( + tree.get(node_id.as_ref().unwrap()).unwrap().data(), + Data::Window(_) + ) + { + // TODO, if ndoe_id is a stack, we want to assign the active node instead of the first + node_id = tree.children_ids(node_id.as_ref().unwrap()).unwrap().next(); + } + if let Some(Data::Window(window)) = + node_id.and_then(|i| tree.get(&i).ok()).map(|n| n.data()) + { + return Some(window.clone()); + } + } + } + node_id = fork; + } + } + + None + } fn update_orientation<'a>( &mut self, new_orientation: Orientation, seat: &Seat, space: &mut Space, - mut focus_stack: Box + 'a>, + focus_stack: Box + 'a>, ) { { let output = super::output_from_seat(seat, space); @@ -165,25 +229,15 @@ impl Layout for TilingLayout { .borrow_mut(); let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); - let last_active = focus_stack - .find_map(|window| tree.root_node_id() - .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() - .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) - ) - ); - - if let Some(ref node_id) = last_active { - let mut node_id = node_id.clone(); - while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() { + if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) { + if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) { if let &mut Data::Fork { ref mut orientation, .. - } = tree.get_mut(&parent_id).unwrap().data_mut() + } = tree.get_mut(&fork).unwrap().data_mut() { *orientation = new_orientation; - break; } - node_id = parent_id; } } } @@ -191,8 +245,10 @@ impl Layout for TilingLayout { } fn refresh(&mut self, space: &mut Space) { - while let Some(dead_windows) = - Some(update_space_positions(self.idx, space, self.gaps)).filter(|v| !v.is_empty()) + while let Some(dead_windows) = Some(TilingLayout::update_space_positions( + self.idx, space, self.gaps, + )) + .filter(|v| !v.is_empty()) { for window in dead_windows { self.unmap_window(&window); @@ -226,15 +282,16 @@ impl Layout for TilingLayout { let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); let mut node_id = info.borrow().node.clone(); - while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() { + + while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) { if let &Data::Fork { ref orientation, ref ratio, - } = tree.get(&parent_id).unwrap().data() + } = tree.get(&fork).unwrap().data() { // found a fork // which child are we? - let first = tree.children_ids(&parent_id).unwrap().next() == Some(&node_id); + let first = tree.children_ids(&fork).unwrap().next() == Some(&child); match (first, orientation, edges) { (true, Orientation::Horizontal, ResizeEdge::Bottom) | (false, Orientation::Horizontal, ResizeEdge::Top) @@ -242,22 +299,19 @@ impl Layout for TilingLayout { | (false, Orientation::Vertical, ResizeEdge::Left) => { let grab = ResizeForkGrab { start_data, - node_id: parent_id, + node_id: fork, initial_ratio: *ratio, idx: self.idx, output: output.clone(), }; - slog_scope::debug!("Tiling resize grabs"); pointer.set_grab(grab, serial, 0); return; } - (x, y, z) => { - slog_scope::debug!("Nope: {:?}, {:?}, {:?}", x, y, z); - } + _ => {} // continue iterating } } - node_id = parent_id; + node_id = fork; } } } @@ -265,15 +319,42 @@ impl Layout for TilingLayout { } impl TilingLayout { + fn last_active<'a>( + tree: &mut Tree, + mut focus_stack: Box + 'a>, + ) -> Option { + let last_active = focus_stack + .find_map(|window| tree.root_node_id() + .and_then(|root| tree.traverse_pre_order_ids(root).unwrap() + .find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window)) + ) + ); + + last_active + } + + fn find_fork(tree: &mut Tree, mut node_id: NodeId) -> Option<(NodeId, NodeId)> { + while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() { + if let &Data::Fork { .. } = tree.get(&parent_id).unwrap().data() { + return Some((parent_id, node_id)); + } + node_id = parent_id; + } + None + } + fn unmap_window(&mut self, window: &Window) { if let Some(info) = window.user_data().get::>() { let output = &info.borrow().output; + output + .user_data() + .insert_if_missing(|| RefCell::new(OutputInfo::default())); let mut output_info = output .user_data() .get::>() .unwrap() .borrow_mut(); - let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); + let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new); let node_id = info.borrow().node.clone(); let parent_id = tree @@ -335,151 +416,153 @@ impl TilingLayout { None => {} // root _ => unreachable!(), } - // update_space_positions(space, self.gaps); } } -} -fn new_fork( - tree: &mut Tree, - old_id: &NodeId, - new: Node, -) -> Result { - let new_fork = Node::new(Data::fork()); - let old = tree.get(old_id)?; - let parent_id = old.parent().cloned(); - let pos = parent_id.as_ref().and_then(|parent_id| { - tree.children_ids(parent_id) - .unwrap() - .position(|id| id == old_id) - }); + fn new_fork( + tree: &mut Tree, + old_id: &NodeId, + new: Node, + ) -> Result { + let new_fork = Node::new(Data::fork()); + let old = tree.get(old_id)?; + let parent_id = old.parent().cloned(); + let pos = parent_id.as_ref().and_then(|parent_id| { + tree.children_ids(parent_id) + .unwrap() + .position(|id| id == old_id) + }); - let fork_id = tree - .insert( - new_fork, - if let Some(parent) = parent_id.as_ref() { - InsertBehavior::UnderNode(parent) - } else { - InsertBehavior::AsRoot - }, - ) - .unwrap(); + let fork_id = tree + .insert( + new_fork, + if let Some(parent) = parent_id.as_ref() { + InsertBehavior::UnderNode(parent) + } else { + InsertBehavior::AsRoot + }, + ) + .unwrap(); - tree.move_node(old_id, MoveBehavior::ToParent(&fork_id)) - .unwrap(); - // keep position - if let Some(old_pos) = pos { - tree.make_nth_sibling(&fork_id, old_pos).unwrap(); + tree.move_node(old_id, MoveBehavior::ToParent(&fork_id)) + .unwrap(); + // keep position + if let Some(old_pos) = pos { + tree.make_nth_sibling(&fork_id, old_pos).unwrap(); + } + tree.insert(new, InsertBehavior::UnderNode(&fork_id)) } - tree.insert(new, InsertBehavior::UnderNode(&fork_id)) -} -fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec { - let mut dead_windows = Vec::new(); - let (outer, inner) = gaps; - for output in space.outputs().cloned().collect::>().into_iter() { - if let Some(mut info) = output - .user_data() - .get::>() - .map(|x| x.borrow_mut()) - { - let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new); - if let Some(root) = tree.root_node_id() { - let mut stack = Vec::new(); + fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec { + let mut dead_windows = Vec::new(); + let (outer, inner) = gaps; + for output in space.outputs().cloned().collect::>().into_iter() { + if let Some(mut info) = output + .user_data() + .get::>() + .map(|x| x.borrow_mut()) + { + let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new); + if let Some(root) = tree.root_node_id() { + let mut stack = Vec::new(); - let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone()); - // TODO saturate? minimum? - if let Some(mut geo) = geo.as_mut() { - geo.loc.x += outer; - geo.loc.y += outer; - geo.size.w -= outer * 2; - geo.size.h -= outer * 2; - } + let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone()); + // TODO saturate? minimum? + if let Some(mut geo) = geo.as_mut() { + geo.loc.x += outer; + geo.loc.y += outer; + geo.size.w -= outer * 2; + geo.size.h -= outer * 2; + } - for node in tree.traverse_pre_order(root).unwrap() { - let geo = stack.pop().unwrap_or(geo); - match node.data() { - Data::Fork { orientation, ratio } => { - if let Some(geo) = geo { - match orientation { - Orientation::Horizontal => { - let top_size = ( - geo.size.w, - ((geo.size.h as f64) * ratio).ceil() as i32, - ); - stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x, geo.loc.y + top_size.1), - (geo.size.w, geo.size.h - top_size.1), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, top_size, - ))); + for node in tree.traverse_pre_order(root).unwrap() { + let geo = stack.pop().unwrap_or(geo); + match node.data() { + Data::Fork { orientation, ratio } => { + if let Some(geo) = geo { + match orientation { + Orientation::Horizontal => { + let top_size = ( + geo.size.w, + ((geo.size.h as f64) * ratio).ceil() as i32, + ); + stack.push(Some(Rectangle::from_loc_and_size( + (geo.loc.x, geo.loc.y + top_size.1), + (geo.size.w, geo.size.h - top_size.1), + ))); + stack.push(Some(Rectangle::from_loc_and_size( + geo.loc, top_size, + ))); + } + Orientation::Vertical => { + let left_size = ( + ((geo.size.w as f64) * ratio).ceil() as i32, + geo.size.h, + ); + stack.push(Some(Rectangle::from_loc_and_size( + (geo.loc.x + left_size.0, geo.loc.y), + (geo.size.w - left_size.0, geo.size.h), + ))); + stack.push(Some(Rectangle::from_loc_and_size( + geo.loc, left_size, + ))); + } } - Orientation::Vertical => { - let left_size = ( - ((geo.size.w as f64) * ratio).ceil() as i32, - geo.size.h, - ); - stack.push(Some(Rectangle::from_loc_and_size( - (geo.loc.x + left_size.0, geo.loc.y), - (geo.size.w - left_size.0, geo.size.h), - ))); - stack.push(Some(Rectangle::from_loc_and_size( - geo.loc, left_size, - ))); - } - } - } else { - stack.push(None); - stack.push(None); - } - } - Data::Stack { active, len } => { - for i in 0..*len { - if i == *active { - stack.push(geo); } else { stack.push(None); + stack.push(None); } } - } - Data::Window(window) => { - if window.toplevel().alive() { - if let Some(geo) = geo { - #[allow(irrefutable_let_patterns)] - if let Kind::Xdg(xdg) = &window.toplevel() { - let ret = xdg.with_pending_state(|state| { - state.size = Some( - (geo.size.w - inner * 2, geo.size.h - inner * 2) - .into(), - ); - state.states.set(XdgState::TiledLeft); - state.states.set(XdgState::TiledRight); - state.states.set(XdgState::TiledTop); - state.states.set(XdgState::TiledBottom); - }); - if ret.is_ok() { - xdg.send_configure(); - } + Data::Stack { active, len } => { + for i in 0..*len { + if i == *active { + stack.push(geo); + } else { + stack.push(None); } - let window_geo = window.geometry(); - space.map_window( - &window, - ( - geo.loc.x + inner - window_geo.loc.x, - geo.loc.y + inner - window_geo.loc.y, - ), - false, - ); } - } else { - dead_windows.push(window.clone()); + } + Data::Window(window) => { + if window.toplevel().alive() { + if let Some(geo) = geo { + #[allow(irrefutable_let_patterns)] + if let Kind::Xdg(xdg) = &window.toplevel() { + let ret = xdg.with_pending_state(|state| { + state.size = Some( + ( + geo.size.w - inner * 2, + geo.size.h - inner * 2, + ) + .into(), + ); + state.states.set(XdgState::TiledLeft); + state.states.set(XdgState::TiledRight); + state.states.set(XdgState::TiledTop); + state.states.set(XdgState::TiledBottom); + }); + if ret.is_ok() { + xdg.send_configure(); + } + } + let window_geo = window.geometry(); + space.map_window( + &window, + ( + geo.loc.x + inner - window_geo.loc.x, + geo.loc.y + inner - window_geo.loc.y, + ), + false, + ); + } + } else { + dead_windows.push(window.clone()); + } } } } } } } + dead_windows } - dead_windows } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index abdf1479..02381b19 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -409,6 +409,22 @@ impl Shell { .update_orientation(seat, orientation) } + pub fn move_focus<'a>( + &mut self, + seat: &Seat, + output: &Output, + focus: layout::FocusDirection, + seats: impl Iterator, + ) { + if let Some(surface) = self + .active_space_mut(output) + .move_focus(seat, focus) + .and_then(|window| window.toplevel().get_surface().cloned()) + { + self.set_focus(Some(&surface), seat, seats, None) + } + } + fn set_focus<'a>( &mut self, surface: Option<&WlSurface>, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 76022436..5c67d170 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -126,6 +126,20 @@ impl Workspace { .update_orientation(orientation, seat, &mut self.space, focus_stack.iter()) } + pub fn move_focus(&mut self, seat: &Seat, focus: layout::FocusDirection) -> Option { + seat.user_data() + .insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new()))); + let focus_stack = FocusStackMut(RefMut::map( + seat.user_data() + .get::() + .unwrap() + .borrow_mut(), + |map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()), + )); + self.layout + .move_focus(focus, seat, &mut self.space, focus_stack.iter()) + } + pub fn maximize_request(&mut self, window: &Window, output: &Output) { self.layout .maximize_request(&mut self.space, window, output)