Restructure layout-ing

This commit is contained in:
Ottatop 2023-08-11 10:08:38 -05:00
parent 77695c666e
commit 275d968e77
11 changed files with 440 additions and 247 deletions

View file

@ -228,7 +228,8 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
None,
);
layer_map_for_output(&output).arrange();
state.re_layout(&output);
state.update_windows(&output);
// state.re_layout(&output);
}
WinitEvent::Focus(_) => {}
WinitEvent::Input(input_evt) => {
@ -299,6 +300,7 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
state
.tags
.iter()
.filter(|tag| tag.active())
.find(|tag| tag.fullscreen_window().is_some())
.cloned()
})

View file

@ -20,7 +20,7 @@ use crate::{
backend::Backend,
state::{State, WithState},
window::{
window_state::{Float, WindowResizeState},
window_state::{LocationRequestState, Status},
WindowElement,
},
};
@ -35,21 +35,22 @@ pub struct MoveSurfaceGrab<S: SeatHandler> {
impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
fn motion(
&mut self,
data: &mut State<B>,
state: &mut State<B>,
handle: &mut PointerInnerHandle<'_, State<B>>,
_focus: Option<(<State<B> as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &MotionEvent,
) {
handle.motion(data, None, event);
handle.motion(state, None, event);
if !self.window.alive() {
handle.unset_grab(data, event.serial, event.time);
handle.unset_grab(state, event.serial, event.time);
return;
}
data.space.raise_element(&self.window, false);
state.space.raise_element(&self.window, false);
if let WindowElement::X11(surface) = &self.window {
data.xwm
state
.xwm
.as_mut()
.expect("no xwm")
.raise_window(surface)
@ -59,17 +60,17 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
// tracing::info!("window geo is: {:?}", self.window.geometry());
// tracing::info!("loc is: {:?}", data.space.element_location(&self.window));
let tiled = self.window.with_state(|state| state.floating.is_tiled());
let tiled = self.window.with_state(|state| state.status.is_tiled());
if tiled {
// INFO: this is being used instead of space.element_under(event.location) because that
// | uses the bounding box, which is different from the actual geometry
let window_under = data
let window_under = state
.space
.elements()
.rev()
.find(|&win| {
if let Some(loc) = data.space.element_location(win) {
if let Some(loc) = state.space.element_location(win) {
let size = win.geometry().size;
let rect = Rectangle { size, loc };
rect.contains(event.location.to_i32_round())
@ -84,28 +85,36 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
return;
}
let is_floating = window_under.with_state(|state| state.floating.is_floating());
let is_floating = window_under.with_state(|state| state.status.is_floating());
if is_floating {
return;
}
let has_pending_resize = window_under
.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle));
let has_pending_resize = window_under.with_state(|state| {
!matches!(state.loc_request_state, LocationRequestState::Idle)
});
if has_pending_resize {
return;
}
data.swap_window_positions(&self.window, &window_under);
state.swap_window_positions(&self.window, &window_under);
}
} else {
let delta = event.location - self.start_data.location;
let new_loc = (self.initial_window_loc.to_f64() + delta).to_i32_round();
data.space.map_element(self.window.clone(), new_loc, true);
state.space.map_element(self.window.clone(), new_loc, true);
let size = state
.space
.element_geometry(&self.window)
.expect("window wasn't mapped")
.size;
self.window.with_state(|state| {
if state.floating.is_floating() {
state.floating = Float::Floating(new_loc);
if state.status.is_floating() {
state.status = Status::Floating(Rectangle::from_loc_and_size(new_loc, size));
}
});
if let WindowElement::X11(surface) = &self.window {

View file

@ -18,7 +18,7 @@ use smithay::{
use crate::{
backend::Backend,
state::{State, WithState},
window::{window_state::Float, WindowElement},
window::{window_state::Status, WindowElement},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -325,10 +325,15 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
if new_loc.x.is_some() || new_loc.y.is_some() {
state.space.map_element(window.clone(), window_loc, false);
let size = state
.space
.element_geometry(&window)
.expect("called element_geometry on unmapped window")
.size;
window.with_state(|state| {
if state.floating.is_floating() {
state.floating = Float::Floating(window_loc);
if state.status.is_floating() {
state.status = Status::Floating(Rectangle::from_loc_and_size(window_loc, size));
}
});
@ -360,7 +365,7 @@ pub fn resize_request_client<B: Backend>(
return;
};
if window.with_state(|state| state.floating.is_tiled()) {
if window.with_state(|state| state.status.is_tiled()) {
return;
}
@ -407,7 +412,7 @@ pub fn resize_request_server<B: Backend>(
return;
};
if window.with_state(|state| state.floating.is_tiled()) {
if window.with_state(|state| state.status.is_tiled()) {
return;
}

View file

@ -50,7 +50,7 @@ use crate::{
backend::Backend,
focus::FocusTarget,
state::{CalloopData, ClientState, State, WithState},
window::{window_state::WindowResizeState, WindowElement},
window::{window_state::LocationRequestState, WindowElement},
};
impl<B: Backend> BufferHandler for State<B> {
@ -120,8 +120,8 @@ impl<B: Backend> CompositorHandler for State<B> {
if let Some(window) = self.window_for_surface(surface) {
window.with_state(|state| {
if let WindowResizeState::Acknowledged(new_pos) = state.resize_state {
state.resize_state = WindowResizeState::Idle;
if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state {
state.loc_request_state = LocationRequestState::Idle;
if window.is_x11() {
tracing::warn!("did something with X11 window here");
}
@ -443,7 +443,8 @@ impl<B: Backend> WlrLayerShellHandler for State<B> {
// TODO: instead of deferring by 1 cycle, actually check if the surface has committed
// | before re-layouting
self.loop_handle.insert_idle(move |data| {
data.state.re_layout(&output);
data.state.update_windows(&output);
// data.state.re_layout(&output);
});
}
@ -466,7 +467,8 @@ impl<B: Backend> WlrLayerShellHandler for State<B> {
// | before re-layouting
if let Some(output) = output {
self.loop_handle.insert_idle(move |data| {
data.state.re_layout(&output);
data.state.update_windows(&output);
// data.state.re_layout(&output);
});
}
}

View file

@ -30,7 +30,7 @@ use crate::{
backend::Backend,
focus::FocusTarget,
state::{State, WithState},
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
window::{window_state::LocationRequestState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
};
impl<B: Backend> XdgShellHandler for State<B> {
@ -95,17 +95,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
self.windows.push(window.clone());
// self.space.map_element(window.clone(), (0, 0), true);
if let Some(focused_output) = self.focus_state.focused_output.clone() {
focused_output.with_state(|state| {
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(),
&mut self.space,
&focused_output,
);
}
});
self.update_windows(&focused_output);
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
tracing::debug!(
"blocker {}",
@ -277,12 +267,13 @@ impl<B: Backend> XdgShellHandler for State<B> {
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
if let Some(window) = self.window_for_surface(&surface) {
window.with_state(|state| {
if let WindowResizeState::Requested(serial, new_loc) = state.resize_state {
if let LocationRequestState::Requested(serial, new_loc) = state.loc_request_state {
match &configure {
Configure::Toplevel(configure) => {
if configure.serial >= serial {
// tracing::debug!("acked configure, new loc is {:?}", new_loc);
state.resize_state = WindowResizeState::Acknowledged(new_loc);
state.loc_request_state =
LocationRequestState::Acknowledged(new_loc);
if let Some(focused_output) =
self.focus_state.focused_output.clone()
{

View file

@ -28,7 +28,7 @@ use crate::{
backend::Backend,
focus::FocusTarget,
state::{CalloopData, WithState},
window::{window_state::Float, WindowBlocker, WindowElement, BLOCKER_COUNTER},
window::{window_state::Status, WindowBlocker, WindowElement, BLOCKER_COUNTER},
};
impl<B: Backend> XwmHandler for CalloopData<B> {
@ -128,7 +128,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
if should_float(surface) {
window.with_state(|state| {
state.floating = Float::Floating(loc);
state.status = Status::Floating(bbox);
});
}
@ -276,9 +276,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
self.state
.windows
.retain(|elem| win.wl_surface() != elem.wl_surface());
if win.with_state(|state| state.floating.is_tiled()) {
if win.with_state(|state| state.status.is_tiled()) {
if let Some(output) = win.output(&self.state) {
self.state.re_layout(&output);
self.state.update_windows(&output);
// self.state.re_layout(&output);
}
}
}

View file

@ -78,6 +78,7 @@ impl<B: Backend> State<B> {
[output]
.into_iter()
.flat_map(|op| op.with_state(|state| state.tags.clone()))
.filter(|tag| tag.active())
.find(|tag| tag.fullscreen_window().is_some())
.and_then(|tag| tag.fullscreen_window())
.map(|window| (FocusTarget::from(window), output_geo.loc))

View file

@ -4,16 +4,133 @@ use itertools::{Either, Itertools};
use smithay::{
desktop::{layer_map_for_output, Space},
output::Output,
utils::{Logical, Rectangle, Size},
utils::{Logical, Point, Rectangle, Size},
};
use crate::{
backend::Backend,
state::{State, WithState},
tag::Tag,
window::WindowElement,
window::{
window_state::{LocationRequestState, Status},
WindowElement,
},
};
// -------------------------------------------
impl<B: Backend> State<B> {
/// Compute the positions and sizes of tiled windows on
/// `output` according to the provided [`Layout`].
///
/// This will call `request_size_change` on tiled windows.
fn tile_windows(&self, output: &Output, windows: Vec<WindowElement>, layout: Layout) {
let Some(rect) = self.space.output_geometry(output).map(|op_geo| {
let map = layer_map_for_output(output);
if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
op_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(op_geo.loc + zone.loc, zone.size)
}
}) else {
// TODO: maybe default to something like 800x800 like in anvil so people still see
// | windows open
tracing::error!("Failed to get output geometry");
return;
};
match layout {
Layout::MasterStack => master_stack(windows, rect),
Layout::Dwindle => dwindle(windows, rect),
Layout::Spiral => spiral(windows, rect),
layout @ (Layout::CornerTopLeft
| Layout::CornerTopRight
| Layout::CornerBottomLeft
| Layout::CornerBottomRight) => corner(&layout, windows, rect),
}
}
pub fn update_windows(&mut self, output: &Output) {
let Some(layout) = output.with_state(|state| {
state.focused_tags().next().cloned().map(|tag| tag.layout())
}) else { return };
let (windows_on_foc_tags, windows_not_on_foc_tags): (Vec<_>, _) =
output.with_state(|state| {
let focused_tags = state.focused_tags().collect::<Vec<_>>();
self.windows.iter().cloned().partition(|win| {
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
})
});
let tiled_windows = windows_on_foc_tags
.iter()
.filter(|win| win.with_state(|state| state.status.is_tiled()))
.cloned()
.collect::<Vec<_>>();
self.tile_windows(output, tiled_windows, layout);
let output_geo = self.space.output_geometry(output).expect("no output geo");
for window in windows_on_foc_tags.iter() {
match window.with_state(|state| state.status) {
Status::Fullscreen(_) => {
window.change_geometry(output_geo);
}
Status::Maximized(_) => {
let map = layer_map_for_output(output);
let geo = if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
output_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(output_geo.loc + zone.loc, zone.size)
};
window.change_geometry(geo);
}
_ => (),
}
}
for window in windows_on_foc_tags.iter() {
window.with_state(|state| {
if let LocationRequestState::Sent(loc) = state.loc_request_state {
match &window {
WindowElement::Wayland(window) => {
let serial = window.toplevel().send_configure();
state.loc_request_state = LocationRequestState::Requested(serial, loc);
}
WindowElement::X11(_) => {
// already configured, just need to map
// maybe wait for all wayland windows to commit before mapping
self.space.map_element(window.clone(), loc, false);
state.loc_request_state = LocationRequestState::Idle;
}
}
}
});
}
self.loop_handle.insert_idle(|data| {
crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| {
for win in windows_not_on_foc_tags {
dt.state.space.unmap_elem(&win);
}
})
});
}
}
// -------------------------------------------
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Layout {
MasterStack,
@ -57,22 +174,18 @@ impl Layout {
tracing::debug!("Laying out with rect {rect:?}");
match self {
Layout::MasterStack => master_stack(windows, space, rect),
Layout::Dwindle => dwindle(windows, space, rect),
Layout::Spiral => spiral(windows, space, rect),
Layout::MasterStack => master_stack(windows, rect),
Layout::Dwindle => dwindle(windows, rect),
Layout::Spiral => spiral(windows, rect),
layout @ (Layout::CornerTopLeft
| Layout::CornerTopRight
| Layout::CornerBottomLeft
| Layout::CornerBottomRight) => corner(layout, windows, space, rect),
| Layout::CornerBottomRight) => corner(layout, windows, rect),
}
}
}
fn master_stack(
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
fn master_stack(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
let size = rect.size;
let loc = rect.loc;
@ -85,11 +198,11 @@ fn master_stack(
if stack_count == 0 {
// one window
master.request_size_change(space, loc, size);
master.change_geometry(Rectangle::from_loc_and_size(loc, size));
} else {
let loc = (loc.x, loc.y).into();
let loc: Point<i32, Logical> = (loc.x, loc.y).into();
let new_master_size: Size<i32, Logical> = (size.w / 2, size.h).into();
master.request_size_change(space, loc, new_master_size);
master.change_geometry(Rectangle::from_loc_and_size(loc, new_master_size));
let stack_count = stack_count;
@ -105,20 +218,15 @@ fn master_stack(
.collect::<Vec<_>>();
for (i, win) in stack.enumerate() {
win.request_size_change(
space,
(size.w / 2 + loc.x, y_s[i] + loc.y).into(),
(size.w / 2, i32::max(heights[i], 40)).into(),
);
win.change_geometry(Rectangle::from_loc_and_size(
Point::from((size.w / 2 + loc.x, y_s[i] + loc.y)),
Size::from((size.w / 2, i32::max(heights[i], 40))),
));
}
}
}
fn dwindle(
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
fn dwindle(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
let size = rect.size;
let loc = rect.loc;
@ -126,7 +234,7 @@ fn dwindle(
if iter.peek().is_none() {
if let Some(window) = windows.first() {
window.request_size_change(space, loc, size);
window.change_geometry(Rectangle::from_loc_and_size(loc, size));
}
} else {
let mut win1_size = size;
@ -146,41 +254,35 @@ fn dwindle(
Slice::Right => {
let width_partition = win1_size.w / 2;
win1.request_size_change(
space,
win1.change_geometry(Rectangle::from_loc_and_size(
win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
);
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
Slice::Below => {
let height_partition = win1_size.h / 2;
win1.request_size_change(
space,
win1.change_geometry(Rectangle::from_loc_and_size(
win1_loc,
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
);
Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
));
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
}
}
}
}
fn spiral(
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
fn spiral(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
let size = rect.size;
let loc = rect.loc;
@ -188,7 +290,7 @@ fn spiral(
if iter.peek().is_none() {
if let Some(window) = windows.first() {
window.request_size_change(space, loc, size);
window.change_geometry(Rectangle::from_loc_and_size(loc, size));
}
} else {
let mut win1_loc = loc;
@ -217,80 +319,73 @@ fn spiral(
Slice::Above => {
let height_partition = win1_size.h / 2;
win1.request_size_change(
space,
(win1_loc.x, win1_loc.y + height_partition).into(),
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
);
win1.change_geometry(Rectangle::from_loc_and_size(
Point::from((win1_loc.x, win1_loc.y + height_partition)),
Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
));
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
Slice::Below => {
let height_partition = win1_size.h / 2;
win1.request_size_change(
space,
win1.change_geometry(Rectangle::from_loc_and_size(
win1_loc,
(win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(),
);
Size::from((win1_size.w, win1_size.h - i32::max(height_partition, 40))),
));
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
Slice::Left => {
let width_partition = win1_size.w / 2;
win1.request_size_change(
space,
(win1_loc.x + width_partition, win1_loc.y).into(),
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
);
win1.change_geometry(Rectangle::from_loc_and_size(
Point::from((win1_loc.x + width_partition, win1_loc.y)),
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
Slice::Right => {
let width_partition = win1_size.w / 2;
win1.request_size_change(
space,
win1.change_geometry(Rectangle::from_loc_and_size(
win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
);
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size);
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
}
}
}
}
fn corner(
layout: &Layout,
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
fn corner(layout: &Layout, windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
let size = rect.size;
let loc = rect.loc;
match windows.len() {
0 => (),
1 => {
windows[0].request_size_change(space, loc, size);
windows[0].change_geometry(rect);
}
2 => {
windows[0].request_size_change(space, loc, (size.w / 2, size.h).into());
windows[0].change_geometry(Rectangle::from_loc_and_size(
loc,
Size::from((size.w / 2, size.h)),
));
windows[1].request_size_change(
space,
(loc.x + size.w / 2, loc.y).into(),
(size.w / 2, size.h).into(),
);
windows[1].change_geometry(Rectangle::from_loc_and_size(
Point::from((loc.x + size.w / 2, loc.y)),
Size::from((size.w / 2, size.h)),
));
}
_ => {
let mut windows = windows.into_iter();
@ -306,9 +401,8 @@ fn corner(
let div_factor = 2;
corner.request_size_change(
space,
match layout {
corner.change_geometry(Rectangle::from_loc_and_size(
Point::from(match layout {
Layout::CornerTopLeft => (loc.x, loc.y),
Layout::CornerTopRight => (loc.x + size.w - size.w / div_factor, loc.y),
Layout::CornerBottomLeft => (loc.x, loc.y + size.h - size.h / div_factor),
@ -317,10 +411,9 @@ fn corner(
loc.y + size.h - size.h / div_factor,
),
_ => unreachable!(),
}
.into(),
(size.w / div_factor, size.h / div_factor).into(),
);
}),
Size::from((size.w / div_factor, size.h / div_factor)),
));
let vert_stack_count = vert_stack.len();
@ -336,19 +429,17 @@ fn corner(
.collect::<Vec<_>>();
for (i, win) in vert_stack.iter().enumerate() {
win.request_size_change(
space,
(
win.change_geometry(Rectangle::from_loc_and_size(
Point::from((
match layout {
Layout::CornerTopLeft | Layout::CornerBottomLeft => size.w / 2 + loc.x,
Layout::CornerTopRight | Layout::CornerBottomRight => loc.x,
_ => unreachable!(),
},
y_s[i] + loc.y,
)
.into(),
(size.w / 2, i32::max(heights[i], 40)).into(),
);
)),
Size::from((size.w / 2, i32::max(heights[i], 40))),
));
}
let horiz_stack_count = horiz_stack.len();
@ -365,18 +456,16 @@ fn corner(
.collect::<Vec<_>>();
for (i, win) in horiz_stack.iter().enumerate() {
win.request_size_change(
space,
match layout {
win.change_geometry(Rectangle::from_loc_and_size(
Point::from(match layout {
Layout::CornerTopLeft => (x_s[i] + loc.x, loc.y + size.h / 2),
Layout::CornerTopRight => (x_s[i] + loc.x + size.w / 2, loc.y + size.h / 2),
Layout::CornerBottomLeft => (x_s[i] + loc.x, loc.y),
Layout::CornerBottomRight => (x_s[i] + loc.x + size.w / 2, loc.y),
_ => unreachable!(),
}
.into(),
(i32::max(widths[i], 1), size.h / 2).into(),
);
}),
Size::from((i32::max(widths[i], 1), size.h / 2)),
));
}
}
}
@ -385,7 +474,7 @@ fn corner(
fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> {
windows
.iter()
.filter(|window| window.with_state(|state| state.floating.is_tiled()))
.filter(|window| window.with_state(|state| state.status.is_tiled()))
.filter(|window| {
window.with_state(|state| {
for tag in state.tags.iter() {
@ -421,6 +510,7 @@ impl<B: Backend> State<B> {
.focused_output
.clone()
.expect("no focused output");
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
}

View file

@ -21,7 +21,7 @@ use crate::{
grab::resize_grab::ResizeSurfaceState,
tag::Tag,
window::{
window_state::{Float, WindowResizeState},
window_state::{LocationRequestState, Status},
WindowElement,
},
};
@ -187,7 +187,8 @@ impl<B: Backend> State<B> {
state.tags = vec![tag.clone()];
});
let Some(output) = tag.output(self) else { return };
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::ToggleTagOnWindow { window_id, tag_id } => {
let Some(window) = window_id.window(self) else { return };
@ -202,14 +203,16 @@ impl<B: Backend> State<B> {
});
let Some(output) = tag.output(self) else { return };
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::ToggleTag { tag_id } => {
tracing::debug!("ToggleTag");
if let Some(tag) = tag_id.tag(self) {
tag.set_active(!tag.active());
if let Some(output) = tag.output(self) {
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
}
}
@ -222,7 +225,8 @@ impl<B: Backend> State<B> {
}
tag.set_active(true);
});
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::AddTags {
output_name,
@ -252,7 +256,8 @@ impl<B: Backend> State<B> {
let Some(tag) = tag_id.tag(self) else { return };
tag.set_layout(layout);
let Some(output) = tag.output(self) else { return };
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::ConnectForAllOutputs { callback_id } => {
@ -288,7 +293,8 @@ impl<B: Backend> State<B> {
output.change_current_state(None, None, None, Some(loc));
self.space.map_output(&output, loc);
tracing::debug!("mapping output {} to {loc:?}", output.name());
self.re_layout(&output);
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::Quit => {
@ -358,7 +364,7 @@ impl<B: Backend> State<B> {
});
let floating = window
.as_ref()
.map(|win| win.with_state(|state| state.floating.is_floating()));
.map(|win| win.with_state(|state| state.status.is_floating()));
let focused = window.as_ref().and_then(|win| {
self.focus_state
.current_focus() // TODO: actual focus
@ -645,14 +651,6 @@ impl<B: Backend> State<B> {
}
pub fn re_layout(&mut self, output: &Output) {
for win in self.windows.iter() {
if win.is_wayland() {
win.with_state(|state| {
tracing::debug!("{:?}", state.resize_state);
})
}
}
let windows = self
.windows
.iter()
@ -705,16 +703,20 @@ impl<B: Backend> State<B> {
}
}
for (win, loc) in clone.into_iter().filter_map(|win| {
match win.with_state(|state| state.floating.clone()) {
Float::Tiled(_) => None,
Float::Floating(loc) => Some((win, loc)),
}
}) {
for (win, rect) in
clone
.into_iter()
.filter_map(|win| match win.with_state(|state| state.status) {
Status::Floating(loc) => Some((win, loc)),
_ => None,
})
{
if let WindowElement::X11(surface) = &win {
surface.set_mapped(true).expect("failed to map x11 win");
if !surface.is_override_redirect() {
surface.set_mapped(true).expect("failed to map x11 win");
}
}
dt.state.space.map_element(win, loc, false);
dt.state.space.map_element(win, rect.loc, false);
}
});
});
@ -731,7 +733,8 @@ pub fn schedule_on_commit<F, B: Backend>(
F: FnOnce(&mut CalloopData<B>) + 'static,
{
for window in windows.iter().filter(|win| win.alive()) {
if window.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle)) {
if window.with_state(|state| !matches!(state.loc_request_state, LocationRequestState::Idle))
{
data.state.loop_handle.insert_idle(|data| {
schedule_on_commit(data, windows, on_commit);
});

View file

@ -40,7 +40,7 @@ use crate::{
state::{State, WithState},
};
use self::window_state::{Float, WindowElementState, WindowResizeState};
use self::window_state::{LocationRequestState, Status, WindowElementState};
pub mod window_state;
@ -180,6 +180,29 @@ impl WindowElement {
}
}
/// Send a geometry change without mapping windows or sending
/// configures to Wayland windows.
///
/// Xwayland windows will still receive a configure.
// TODO: ^ does that make things flicker?
pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) {
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.size = Some(new_geo.size);
});
}
WindowElement::X11(surface) => {
surface
.configure(new_geo)
.expect("failed to configure x11 win");
}
}
self.with_state(|state| {
state.loc_request_state = LocationRequestState::Sent(new_geo.loc);
});
}
/// Request a size and loc change.
pub fn request_size_change(
&self,
@ -193,8 +216,8 @@ impl WindowElement {
state.size = Some(new_size);
});
self.with_state(|state| {
state.resize_state =
WindowResizeState::Requested(window.toplevel().send_configure(), new_loc)
state.loc_request_state =
LocationRequestState::Requested(window.toplevel().send_configure(), new_loc)
});
}
WindowElement::X11(surface) => {
@ -245,6 +268,107 @@ impl WindowElement {
pub fn is_x11(&self) -> bool {
matches!(self, Self::X11(..))
}
/// Set this window to floating.
///
/// This will change the size of the window only.
/// Call `State.update_windows` to perform mapping.
pub fn set_floating(&self) {
let status = self.with_state(|state| state.status);
match status {
Status::Floating(_) => (),
Status::Tiled(rect) | Status::Fullscreen(rect) | Status::Maximized(rect) => {
if let Some(rect) = rect {
self.change_geometry(rect);
self.with_state(|state| state.status = Status::Floating(rect));
} else {
// TODO: is this the same as from the space? prolly not
let geo = self.geometry();
self.with_state(|state| state.status = Status::Floating(geo));
}
}
}
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.unset(xdg_toplevel::State::TiledTop);
state.states.unset(xdg_toplevel::State::TiledBottom);
state.states.unset(xdg_toplevel::State::TiledLeft);
state.states.unset(xdg_toplevel::State::TiledRight);
});
}
}
/// Call compute_tiled_windows after this
pub fn set_tiled(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Tiled(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
pub fn set_fullscreen(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Fullscreen(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.set(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
pub fn set_maximized(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Maximized(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::Maximized);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
}
impl IsAlive for WindowElement {
@ -480,54 +604,10 @@ impl<B: Backend> State<B> {
/// Toggle a window's floating status.
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) {
let mut resize: Option<_> = None;
window.with_state(|window_state| {
match window_state.floating {
Float::Tiled(prev_loc_and_size) => {
if let Some((prev_loc, prev_size)) = prev_loc_and_size {
resize = Some((prev_loc, prev_size));
}
window_state.floating =
Float::Floating(resize.map(|(point, _)| point).unwrap_or_else(|| {
state
.space
.element_location(window)
.unwrap_or((0, 0).into())
}));
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| {
tl_state.states.unset(xdg_toplevel::State::TiledTop);
tl_state.states.unset(xdg_toplevel::State::TiledBottom);
tl_state.states.unset(xdg_toplevel::State::TiledLeft);
tl_state.states.unset(xdg_toplevel::State::TiledRight);
});
} // TODO: tiled states for x11
}
Float::Floating(current_loc) => {
window_state.floating = Float::Tiled(Some((
// We get the location this way because window.geometry().loc
// doesn't seem to be the actual location
// TODO: maybe store the location in state
current_loc,
window.geometry().size,
)));
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| {
tl_state.states.set(xdg_toplevel::State::TiledTop);
tl_state.states.set(xdg_toplevel::State::TiledBottom);
tl_state.states.set(xdg_toplevel::State::TiledLeft);
tl_state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
}
});
if let Some((prev_loc, prev_size)) = resize {
window.request_size_change(&mut state.space, prev_loc, prev_size);
if window.with_state(|state| state.status.is_floating()) {
window.set_tiled();
} else {
window.set_floating();
}
// TODO: don't use the focused output, use the one the window is on
@ -536,8 +616,9 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
.focused_output
.clone()
.expect("no focused output");
state.re_layout(&output);
// state.re_layout(&output);
state.update_windows(&output);
let render = output.with_state(|op_state| {
state
.windows
@ -545,7 +626,7 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
.cloned()
.filter(|win| {
win.with_state(|win_state| {
if win_state.floating.is_floating() {
if win_state.status.is_floating() {
return true;
}
for tag in win_state.tags.iter() {

View file

@ -2,13 +2,12 @@
use std::{
cell::RefCell,
fmt,
sync::atomic::{AtomicU32, Ordering},
};
use smithay::{
desktop::Window,
utils::{Logical, Point, Serial, Size},
utils::{Logical, Point, Rectangle, Serial},
};
use crate::{
@ -67,10 +66,10 @@ impl WithState for Window {
pub struct WindowElementState {
/// The id of this window.
pub id: WindowId,
/// Whether the window is floating or tiled.
pub floating: Float,
/// Whether the window is floating, tiled, fullscreen, or maximized.
pub status: Status,
/// The window's resize state. See [WindowResizeState] for more.
pub resize_state: WindowResizeState,
pub loc_request_state: LocationRequestState,
/// What tags the window is currently on.
pub tags: Vec<Tag>,
}
@ -99,11 +98,12 @@ pub struct WindowElementState {
/// [`resize_state`]: WindowState#structfield.resize_state
/// [`XdgShellHandler.ack_configure()`]: smithay::wayland::shell::xdg::XdgShellHandler#method.ack_configure
/// [`CompositorHandler.commit()`]: smithay::wayland::compositor::CompositorHandler#tymethod.commit
#[derive(Default, Clone)]
pub enum WindowResizeState {
#[derive(Debug, Default, Clone)]
pub enum LocationRequestState {
/// The window doesn't need to be moved.
#[default]
Idle,
Sent(Point<i32, Logical>),
/// The window has received a configure request with a new size. The desired location and the
/// configure request's serial should be provided here.
Requested(Serial, Point<i32, Logical>),
@ -114,24 +114,16 @@ pub enum WindowResizeState {
Acknowledged(Point<i32, Logical>),
}
impl fmt::Debug for WindowResizeState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Idle => write!(f, "Idle"),
Self::Requested(_arg0, _arg1) => write!(f, "Requested"),
Self::Acknowledged(_arg0) => write!(f, "Acknowledged"),
}
}
/// Whether the window is floating, tiled, fullscreen, or maximized.
#[derive(Debug, Clone, Copy)]
pub enum Status {
Floating(Rectangle<i32, Logical>),
Tiled(Option<Rectangle<i32, Logical>>),
Fullscreen(Option<Rectangle<i32, Logical>>),
Maximized(Option<Rectangle<i32, Logical>>),
}
#[derive(Debug, Clone)]
pub enum Float {
/// The previous location and size of the window when it was floating, if any.
Tiled(Option<(Point<i32, Logical>, Size<i32, Logical>)>),
Floating(Point<i32, Logical>),
}
impl Float {
impl Status {
/// Returns `true` if the float is [`Tiled`].
///
/// [`Tiled`]: Float::Tiled
@ -147,6 +139,22 @@ impl Float {
pub fn is_floating(&self) -> bool {
matches!(self, Self::Floating(_))
}
/// Returns `true` if the status is [`Fullscreen`].
///
/// [`Fullscreen`]: Status::Fullscreen
#[must_use]
pub fn is_fullscreen(&self) -> bool {
matches!(self, Self::Fullscreen(..))
}
/// Returns `true` if the status is [`Maximized`].
///
/// [`Maximized`]: Status::Maximized
#[must_use]
pub fn is_maximized(&self) -> bool {
matches!(self, Self::Maximized(..))
}
}
impl WindowElementState {
@ -161,8 +169,8 @@ impl Default for WindowElementState {
Self {
// INFO: I think this will assign the id on use of the state, not on window spawn.
id: WindowId::next(),
floating: Float::Tiled(None),
resize_state: WindowResizeState::Idle,
status: Status::Tiled(None),
loc_request_state: LocationRequestState::Idle,
tags: vec![],
}
}