Skip to content

Commit

Permalink
layouts: add support for window gaps
Browse files Browse the repository at this point in the history
  • Loading branch information
Antikyth committed Nov 21, 2023
1 parent 280469b commit ae678c4
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/display_server/wayland/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use smithay::{
};

use super::grabs::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab};
use crate::state::MapState;
use crate::{layout::LayoutSettings, state::MapState};

type Point<N = i32, Space = LogicalSpace> = smithay::utils::Point<N, Space>;

Expand Down Expand Up @@ -319,7 +319,7 @@ impl WaylandState {
space: Space::default(),
loop_signal: event_loop.get_signal(),

aquariwm_state: crate::state::AquariWm::new(),
aquariwm_state: crate::state::AquariWm::new(LayoutSettings::default()),

// A whole bunch of Smithay-related state.
compositor_state: CompositorState::new::<Self>(&display_handle),
Expand Down
78 changes: 78 additions & 0 deletions src/display_server/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use x11rb_async::{
ConnectionExt,
CreateNotifyEvent as CreateNotify,
DestroyNotifyEvent as DestroyNotify,
EnterNotifyEvent as EnterNotify,
EventMask,
InputFocus,
KeyPressEvent as KeyPress,
MapRequestEvent as MapRequest,
UnmapNotifyEvent as UnmapNotify,
},
Expand All @@ -29,6 +32,7 @@ use x11rb_async::{
use crate::{
display_server::{AsyncDisplayServer, DisplayServer},
layout,
layout::LayoutSettings,
state,
};

Expand Down Expand Up @@ -124,12 +128,46 @@ impl DisplayServer for X11 {
},
}

const ENTER: u8 = 0x0d;
let exit_window_grab = async {
wm.conn
.grab_key(
false,
root,
x11::ModMask::M4 | x11::ModMask::SHIFT,
b'I',
x11::GrabMode::ASYNC,
x11::GrabMode::ASYNC,
)
.await?
.ignore_error();

<Result<_, Error>>::Ok(())
};
let spawn_terminal_grab = async {
wm.conn
.grab_key(
false,
root,
x11::ModMask::M4 | x11::ModMask::SHIFT,
ENTER,
x11::GrabMode::ASYNC,
x11::GrabMode::ASYNC,
)
.await?
.ignore_error();

Ok(())
};
try_join!(exit_window_grab, spawn_terminal_grab)?;

let mut state = state::AquariWm::with_tiling_layout_and_windows::<layout::managers::Stack<x11::Window>>(
0,
0,
width as u32,
height as u32,
wm.query_windows().await?,
LayoutSettings::default(),
);

if testing {
Expand Down Expand Up @@ -204,6 +242,46 @@ impl DisplayServer for X11 {
wm.circulate_window(&state, request.window, request.place).await?;
},

// Focus a window when the cursor enters it.
// TODO: move floating windows above (avoid flickering bug).
// TODO: implement focus behavior setting
Event::EnterNotify(EnterNotify { event, .. }) => {
const CURRENT_TIME: u32 = 0;

wm.conn
.set_input_focus(InputFocus::PARENT, event, CURRENT_TIME)
.await?
.ignore_error();
},

Event::KeyPress(KeyPress {
event, state, detail, ..
}) => {
event!(
Level::INFO,
"Key pressed, {event}, {state:?}, {detail}",
event = event,
state = state,
detail = detail,
);

if state == x11::KeyButMask::MOD4 | x11::KeyButMask::SHIFT {
match detail {
ENTER => {
if let Err(error) = crate::launch_terminal() {
event!(Level::WARN, "Failed to launch terminal: {error}");
}
},

b'I' => {
wm.conn.destroy_window(event).await?.ignore_error();
},

_ => (),
}
}
},

_ => (),
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use std::{collections::VecDeque, fmt::Debug};

use derive_extras::builder;

/// Contains `impl` blocks for types defined in [layout].
///
/// This is a separate module to keep the [layout] module file more readable.
Expand All @@ -16,6 +18,23 @@ mod implementations;
/// [layout managers]: TilingLayoutManager
pub mod managers;

// This is a false positive: `derive_extras::Default` is not the same as `Default`.
#[allow(unused_qualifications)]
/// Controls settings used when [applying] a [tiling layout].
///
/// [applying]: GroupNode::apply_changes
/// [tiling layout]: TilingLayout
#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_extras::Default, builder)]
#[new]
pub struct LayoutSettings {
/// The gap between [nodes] in the [tiling layout].
///
/// [nodes]: Node
/// [tiling layout]: TilingLayout
#[default = 10]
pub window_gap: u32,
}

/// Whether a window is [`Tiled`] or [`Floating`].
///
/// [`Tiled`]: Mode::Tiled
Expand Down
41 changes: 28 additions & 13 deletions src/layout/implementations/node_changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,12 +525,14 @@ impl<Window> GroupNode<Window> {
pub(crate) fn apply_changes<Error>(
&mut self,
reconfigure_window: &mut impl FnMut(&Window, i32, i32, u32, u32) -> Result<(), Error>,
settings: &LayoutSettings,
) -> Result<(), Error> {
// FIXME: need to also determine whether changes have been made to the layout settings.
// If no changes have been made to this group, apply all the child groups' changes and return.
if !self.changes_made() {
for node in self {
match node {
Node::Group(group) => group.apply_changes(reconfigure_window)?,
Node::Group(group) => group.apply_changes(reconfigure_window, settings)?,

Node::Window(WindowNode {
window,
Expand Down Expand Up @@ -622,7 +624,7 @@ impl<Window> GroupNode<Window> {
node.set_secondary_dimension(group_secondary, new_axis);

match node {
Node::Group(group) => group.apply_changes(reconfigure_window),
Node::Group(group) => group.apply_changes(reconfigure_window, settings),

Node::Window(WindowNode {
window,
Expand All @@ -638,29 +640,38 @@ impl<Window> GroupNode<Window> {
}
};

let new_nodes_len = (self.children.len() + self.additions.len()) as u32;
let current_nodes_len = self.children.len();
let new_nodes_len = (current_nodes_len + self.additions.len()) as u32;
let total_gap = if new_nodes_len == 0 {
0
} else {
(new_nodes_len - 1) * settings.window_gap
};
// The size of new additions.
let new_primary = if new_nodes_len == 0 {
group_primary
} else {
group_primary / new_nodes_len
(group_primary - total_gap) / new_nodes_len
};
let mut new_total_node_primary = 0;
// The new total size for the existing nodes to be resized to fit within.
//
// `u64` is used because we will be multiplying two 'u32' values, and `u64::MAX` is
// `u32::MAX * u32::MAX`.
let rescaling_primary = (group_primary - (new_primary * (additions.len() as u32))) as u64;
let rescaling_primary = (group_primary - (new_primary * (additions.len() as u32)) - total_gap) as u64;

let mut additions = additions.into_iter();
let mut next_addition = additions.next();

// Resize all the nodes appropriately.
for (index, node) in self.children.iter_mut().enumerate() {
let gap = (settings.window_gap as i32) * (index as i32);
let coord = (new_total_node_primary as i32) + gap;

// If `node` is an addition, resize it with the new size.
if let Some(addition) = next_addition {
if index == addition {
configure_node(node, new_total_node_primary as i32, new_primary)?;
configure_node(node, coord, new_primary)?;

next_addition = additions.next();

Expand All @@ -681,7 +692,7 @@ impl<Window> GroupNode<Window> {
// large - monitors don't tend to be millions of pixels in width or height.
let rescaled_primary: u32 = ((old_primary * rescaling_primary) / old_total_node_primary).shrink();

configure_node(node, new_total_node_primary as i32, rescaled_primary)?;
configure_node(node, coord, rescaled_primary)?;

new_total_node_primary += rescaled_primary;
}
Expand Down Expand Up @@ -722,7 +733,9 @@ mod tests {
assert_eq!(group.orientation(), NEW_ORIENTATION);

// Apply the change in orientation.
group.apply_changes(&mut resize_window).unwrap();
group
.apply_changes(&mut resize_window, &LayoutSettings::default())
.unwrap();

assert_eq!(group.orientation, NEW_ORIENTATION);
assert_eq!(group.new_orientation, None);
Expand Down Expand Up @@ -760,6 +773,8 @@ mod tests {
const GROUP_WIDTH: u32 = 3000;
const GROUP_HEIGHT: u32 = 1000;

let settings = LayoutSettings::new().window_gap(0);

// The width of each node after three nodes have been added.
const THREE_NODES_WIDTH: u32 = GROUP_WIDTH / 3;
// The width of each node after two nodes have been added.
Expand Down Expand Up @@ -787,7 +802,7 @@ mod tests {
);

// Resize the added window.
group.apply_changes(&mut resize_window).unwrap();
group.apply_changes(&mut resize_window, &settings).unwrap();

assert!(
matches!(
Expand All @@ -805,7 +820,7 @@ mod tests {
group.push_windows_back([2, 3]);

// Resize the existing window and two added windows.
group.apply_changes(&mut resize_window).unwrap();
group.apply_changes(&mut resize_window, &settings).unwrap();

for node in &group {
assert!(
Expand All @@ -826,7 +841,7 @@ mod tests {
group.remove(0);

// Resize the two remaining windows.
group.apply_changes(&mut resize_window).unwrap();
group.apply_changes(&mut resize_window, &settings).unwrap();

for node in &group {
assert!(
Expand All @@ -849,13 +864,13 @@ mod tests {
clone.push_window_front(1);
clone.remove(0);
// Apply the changes (of which there should be none).
clone.apply_changes(&mut resize_window).unwrap();
clone.apply_changes(&mut resize_window, &settings).unwrap();

assert_eq!(group, clone);

group.set_orientation(Orientation::TopToBottom);
// Apply the orientation change.
group.apply_changes(&mut resize_window).unwrap();
group.apply_changes(&mut resize_window, &settings).unwrap();

for node in &group {
assert!(
Expand Down
Loading

0 comments on commit ae678c4

Please sign in to comment.