feat: tile managed windows with layout tree
This commit is contained in:
1 parent
03f6b9b8e1
commit
58d91363d6
7 files changed
+116
-3
No files matched your search
Generated
+1
@@ -368,6 +368,7 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
|||||||
name = "ferese"
|
name = "ferese"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ferese-layout",
|
||||||
"libc",
|
"libc",
|
||||||
"smithay",
|
"smithay",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ license.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ferese-layout = { path = "../crates/ferese-layout" }
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
smithay.workspace = true
|
smithay.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ impl XdgActivationHandler for Ferese {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.focused_window = self.window_ids.get(&window).copied();
|
||||||
self.space.raise_element(&window, true);
|
self.space.raise_element(&window, true);
|
||||||
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
keyboard.set_focus(self, Some(surface), SERIAL_COUNTER.next_serial());
|
keyboard.set_focus(self, Some(surface), SERIAL_COUNTER.next_serial());
|
||||||
|
|||||||
@@ -34,8 +34,24 @@ impl XdgShellHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||||
self.space
|
self.add_tiled_window(Window::new_wayland_window(surface));
|
||||||
.map_element(Window::new_wayland_window(surface), (0, 0), true);
|
}
|
||||||
|
|
||||||
|
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
||||||
|
let Some(window) = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == surface.wl_surface())
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.remove_tiled_window(&window);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ impl Ferese {
|
|||||||
|
|
||||||
if let Some((window, _)) = self.space.element_under(pointer.current_location()) {
|
if let Some((window, _)) = self.space.element_under(pointer.current_location()) {
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
|
|
||||||
|
self.focused_window = self.window_ids.get(&window).copied();
|
||||||
self.space.raise_element(&window, true);
|
self.space.raise_element(&window, true);
|
||||||
let surface = window
|
let surface = window
|
||||||
.toplevel()
|
.toplevel()
|
||||||
@@ -140,6 +142,7 @@ impl Ferese {
|
|||||||
.clone();
|
.clone();
|
||||||
keyboard.set_focus(self, Some(surface), serial);
|
keyboard.set_focus(self, Some(surface), serial);
|
||||||
} else {
|
} else {
|
||||||
|
self.focused_window = None;
|
||||||
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
|
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+90
-1
@@ -1,4 +1,6 @@
|
|||||||
use std::{error::Error, ffi::OsString, sync::Arc, time::Instant};
|
use std::{collections::HashMap, error::Error, ffi::OsString, sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use ferese_layout::{Axis, LayoutTree, Rect, WindowId};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::{PopupManager, Space, Window, WindowSurfaceType},
|
desktop::{PopupManager, Space, Window, WindowSurfaceType},
|
||||||
@@ -35,6 +37,10 @@ pub struct Ferese {
|
|||||||
pub display_handle: DisplayHandle,
|
pub display_handle: DisplayHandle,
|
||||||
pub loop_signal: LoopSignal,
|
pub loop_signal: LoopSignal,
|
||||||
pub space: Space<Window>,
|
pub space: Space<Window>,
|
||||||
|
pub layout: LayoutTree,
|
||||||
|
pub window_ids: HashMap<Window, WindowId>,
|
||||||
|
pub focused_window: Option<WindowId>,
|
||||||
|
next_window_id: u64,
|
||||||
pub popups: PopupManager,
|
pub popups: PopupManager,
|
||||||
pub seat: Seat<Self>,
|
pub seat: Seat<Self>,
|
||||||
pub compositor_state: CompositorState,
|
pub compositor_state: CompositorState,
|
||||||
@@ -85,6 +91,10 @@ impl Ferese {
|
|||||||
display_handle,
|
display_handle,
|
||||||
loop_signal: event_loop.get_signal(),
|
loop_signal: event_loop.get_signal(),
|
||||||
space: Space::default(),
|
space: Space::default(),
|
||||||
|
layout: LayoutTree::default(),
|
||||||
|
window_ids: HashMap::new(),
|
||||||
|
focused_window: None,
|
||||||
|
next_window_id: 1,
|
||||||
popups: PopupManager::default(),
|
popups: PopupManager::default(),
|
||||||
seat,
|
seat,
|
||||||
compositor_state,
|
compositor_state,
|
||||||
@@ -145,6 +155,85 @@ impl Ferese {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_tiled_window(&mut self, window: Window) {
|
||||||
|
let id = WindowId(self.next_window_id);
|
||||||
|
self.next_window_id += 1;
|
||||||
|
|
||||||
|
if let Err(error) = self
|
||||||
|
.layout
|
||||||
|
.insert(id, self.focused_window, Axis::Horizontal, 0.5)
|
||||||
|
{
|
||||||
|
tracing::error!(%error, ?id, "failed to insert window into layout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.window_ids.insert(window.clone(), id);
|
||||||
|
self.focused_window = Some(id);
|
||||||
|
self.space.map_element(window, (0, 0), true);
|
||||||
|
self.relayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_tiled_window(&mut self, window: &Window) {
|
||||||
|
let Some(id) = self.window_ids.remove(window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.space.unmap_elem(window);
|
||||||
|
if let Err(error) = self.layout.remove(id) {
|
||||||
|
tracing::error!(%error, ?id, "failed to remove window from layout");
|
||||||
|
}
|
||||||
|
if self.focused_window == Some(id) {
|
||||||
|
self.focused_window = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.rev()
|
||||||
|
.find_map(|window| self.window_ids.get(window).copied());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.relayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relayout(&mut self) {
|
||||||
|
let Some(output) = self.space.outputs().next() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(output_geometry) = self.space.output_geometry(output) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let bounds = Rect::new(
|
||||||
|
output_geometry.loc.x as f64,
|
||||||
|
output_geometry.loc.y as f64,
|
||||||
|
output_geometry.size.w as f64,
|
||||||
|
output_geometry.size.h as f64,
|
||||||
|
);
|
||||||
|
let geometry = match self.layout.geometry(bounds) {
|
||||||
|
Ok(geometry) => geometry,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%error, "failed to compute tiled geometry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let placements = self
|
||||||
|
.window_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(window, id)| geometry.get(id).map(|rect| (window.clone(), *rect)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (window, rect) in placements {
|
||||||
|
let location = (rect.x.round() as i32, rect.y.round() as i32);
|
||||||
|
let size = (
|
||||||
|
(rect.width.round() as i32).max(1),
|
||||||
|
(rect.height.round() as i32).max(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.space.map_element(window.clone(), location, false);
|
||||||
|
if let Some(toplevel) = window.toplevel() {
|
||||||
|
toplevel.with_pending_state(|state| state.size = Some(size.into()));
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
|||||||
output_scale = scale;
|
output_scale = scale;
|
||||||
state.update_fractional_scale(scale);
|
state.update_fractional_scale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.relayout();
|
||||||
}
|
}
|
||||||
WinitEvent::Input(event) => state.process_input_event(event),
|
WinitEvent::Input(event) => state.process_input_event(event),
|
||||||
WinitEvent::Redraw => {
|
WinitEvent::Redraw => {
|
||||||
|
|||||||
Reference in new issue
Block a user