Refactor window ownership and immutable frame sampling

This commit is contained in:
Samuel Onoja committed 2026-10-01 18:03:01 +01:00
1 parent c0010ccbb6
commit 07bda5a21c
31 files changed
+1222 -623

No files matched your search

+97 -69
View File
@@ -24,6 +24,7 @@ impl Ferese {
{
return;
}
let outputs = self.floating_outputs();
let Some((remembered, work)) = app_id
.as_deref()
@@ -65,13 +66,16 @@ impl Ferese {
self.add_tiled_window(window);
return;
}
if let Some(parent) = parent {
self.add_transient_window(window, parent);
return;
}
if let Some(pointer) = self.seat.get_pointer() {
self.focus_output_at(pointer.current_location());
}
let remembered_work = app_id
.filter(|app| !crate::window_rules::is_native_dialog(Some(app)))
.and_then(|app| self.floating_memory.get(app))
@@ -86,12 +90,13 @@ impl Ferese {
if let Some(work) = remembered_work {
self.focus_output_at((work.x + work.width / 2.0, work.y + work.height / 2.0).into());
}
let Some(bounds) = self.output_bounds() else {
self.add_tiled_window(window);
return;
};
let id = WindowId(self.next_window_id);
self.next_window_id += 1;
let id = self.windows.allocate_id();
let size_rect = client_size(&window)
.map(|size| natural_floating_rect(bounds, size))
.unwrap_or_else(|| centered_floating_rect(bounds));
@@ -100,7 +105,6 @@ impl Ferese {
.focused_window
.and_then(|focused| self.presented_window_rect(focused))
.unwrap_or(bounds);
self.floating_placement_anchors.insert(id, anchor);
let fullscreen = self.workspaces.active().fullscreen;
let focus = true;
if let Err(error) = self
@@ -110,25 +114,30 @@ impl Ferese {
tracing::error!(%error, ?id, "failed to insert rule-placed floating window");
return;
}
self.windows.register(window.clone(), id);
self.windows.update(id, |w| w.placement_anchor = Some(anchor));
if rule.width.is_none() && rule.height.is_none() && client_size(&window).is_none() && remembered_work.is_none()
{
self.natural_floating_pending.insert(id);
self.windows.update(id, |w| w.natural_floating_pending = true);
}
self.window_ids.insert(window.clone(), id);
self.window_stack.insert(id);
if let Some(fullscreen) = fullscreen {
self.floating_above_fullscreen.insert(id, fullscreen);
}
if focus {
self.focused_window = Some(id);
}
self.space.map_element(window, (0, 0), focus);
// apply_initial_window_rules performs the first relayout/configure.
}
pub fn add_tiled_window(&mut self, window: Window) {
let id = WindowId(self.next_window_id);
self.next_window_id += 1;
let id = self.windows.allocate_id();
let focus_new_window = self.workspaces.active().fullscreen.is_none();
let axis = self
@@ -147,11 +156,12 @@ impl Ferese {
return;
}
self.window_ids.insert(window.clone(), id);
self.windows.register(window.clone(), id);
self.window_stack.insert(id);
if focus_new_window {
self.focused_window = Some(id);
}
self.space.map_element(window, (0, 0), focus_new_window);
self.relayout();
@@ -165,18 +175,19 @@ impl Ferese {
self.add_tiled_window(window);
return;
};
let Some(bounds) = self.floating_bounds_for_window(parent) else {
self.add_tiled_window(window);
return;
};
let parent_rect = self.presented_window_rect(parent).unwrap_or(bounds);
let visible_parent = crate::floating::intersection(parent_rect, bounds).unwrap_or(bounds);
let size_rect = client_size(&window)
.map(|s| natural_floating_rect(bounds, s))
.unwrap_or_else(|| centered_transient_rect(visible_parent));
let rect = self.place_floating(&window, None, bounds, (size_rect.width, size_rect.height), None);
let id = WindowId(self.next_window_id);
self.next_window_id += 1;
let id = self.windows.allocate_id();
let focus = workspace == self.workspaces.active_id()
&& (self.focused_window == Some(parent) || self.workspaces.active().fullscreen == Some(parent));
@@ -185,11 +196,12 @@ impl Ferese {
return;
}
self.window_ids.insert(window.clone(), id);
self.windows.register(window.clone(), id);
self.window_stack.insert(id);
if client_size(&window).is_none() {
self.natural_floating_pending.insert(id);
self.windows.update(id, |w| w.natural_floating_pending = true);
}
if self
.workspaces
.workspace(workspace)
@@ -197,9 +209,11 @@ impl Ferese {
{
self.floating_above_fullscreen.insert(id, parent);
}
if focus {
self.focused_window = Some(id);
}
self.space.map_element(window, (0, 0), focus);
self.relayout();
if focus {
@@ -214,10 +228,15 @@ impl Ferese {
title: Option<&str>,
transient: bool,
) {
let Some(id) = self.window_ids.get(window).copied() else {
let Some(id) = self.windows.ids().get(window).copied() else {
return;
};
if !self.window_rules_applied.insert(id) {
if !self
.windows
.update(id, |w| !std::mem::replace(&mut w.rules_applied, true))
.unwrap_or(false)
{
return;
}
@@ -226,11 +245,12 @@ impl Ferese {
}
pub(super) fn reapply_window_rules(&mut self, old_rules: &[WindowRule]) {
let windows = self.window_ids.keys().cloned().collect::<Vec<_>>();
let windows = self.windows.ids().keys().cloned().collect::<Vec<_>>();
for window in windows {
let Some(toplevel) = window.toplevel() else {
continue;
};
let (app_id, title, transient) = with_states(toplevel.wl_surface(), |states| {
let attributes = states.data_map.get::<XdgToplevelSurfaceData>().unwrap().lock().unwrap();
(
@@ -248,15 +268,18 @@ impl Ferese {
}
pub(super) fn apply_window_rule_result(&mut self, window: &Window, rule: crate::window_rules::WindowRuleResult) {
let Some(id) = self.window_ids.get(window).copied() else {
let Some(id) = self.windows.ids().get(window).copied() else {
return;
};
let Some(bounds) = self.floating_bounds_for_window(id) else {
return;
};
if rule == Default::default() {
return;
}
let axis = self
.workspaces
.active()
@@ -269,6 +292,7 @@ impl Ferese {
{
tracing::warn!(%error, ?id, workspace, "failed to apply window workspace rule");
}
let bounds = self.floating_bounds_for_window(id).unwrap_or(bounds);
let should_float = rule
@@ -287,7 +311,7 @@ impl Ferese {
.and_then(|entry| entry.restore(&self.floating_outputs()))
.is_none()
{
self.natural_floating_pending.insert(id);
self.windows.update(id, |w| w.natural_floating_pending = true);
}
}
rect.width = rule.width.unwrap_or(rect.width).min(bounds.width);
@@ -309,6 +333,7 @@ impl Ferese {
} else {
Ok(())
};
if let Err(error) = result {
tracing::warn!(%error, ?id, "failed to apply floating window rule");
}
@@ -331,16 +356,19 @@ impl Ferese {
}
pub fn make_window_transient(&mut self, window: &Window, parent: WindowId) {
let Some(id) = self.window_ids.get(window).copied() else {
let Some(id) = self.windows.ids().get(window).copied() else {
return;
};
if self.workspaces.workspace_for_window(id) != self.workspaces.workspace_for_window(parent) {
tracing::warn!(?id, ?parent, "ignored transient parent on another workspace");
return;
}
let Some(bounds) = self.floating_bounds_for_window(parent) else {
return;
};
let parent_rect = self.presented_window_rect(parent).unwrap_or(bounds);
let visible_parent = crate::floating::intersection(parent_rect, bounds).unwrap_or(bounds);
let size_rect = client_size(window)
@@ -369,6 +397,7 @@ impl Ferese {
{
self.floating_above_fullscreen.insert(id, parent);
}
if self.focused_window == Some(parent) {
if let Err(error) = self.workspaces.focus_window(id) {
tracing::warn!(%error, ?id, "failed to focus transient window");
@@ -385,35 +414,18 @@ impl Ferese {
pub fn remove_tiled_window(&mut self, window: &Window) {
// Uncommitted toplevels have not entered a workspace yet.
self.space.unmap_elem(window);
let Some(id) = self.window_ids.remove(window) else {
let Some(id) = self.windows.remove(window) else {
return;
};
self.focus_history.remove(id);
self.window_geometry.remove(&id);
self.resize_transactions.remove(&id);
self.resize_snapshots.remove(&id);
self.window_resize_fills.remove(&id);
self.maximized_windows.remove(&id);
self.maximized_column_widths.remove(&id);
self.render.remove_window(id);
self.window_stack.remove(id);
self.floating_above_fullscreen.remove(&id);
self.natural_floating_pending.remove(&id);
self.floating_window_memory.remove(&id);
self.floating_placement_anchors.remove(&id);
self.floating_resize_anchors.remove(&id);
self.window_borders.remove(&id);
self.window_dims.remove(&id);
self.window_dimming.remove(&id);
self.window_focus.remove(&id);
self.window_shadows.remove(&id);
self.closing_windows.remove(&id);
self.window_rules_applied.remove(&id);
self.scrolling_world_x.remove(&id);
self.viewport_coupled_widths.remove(&id);
self.pending_column_width_cycles.remove(&id);
if let Err(error) = self.workspaces.remove_window(id) {
tracing::error!(%error, ?id, "failed to remove window from layout");
}
if self.focused_window == Some(id) {
self.focused_window = self.workspaces.active().last_focused;
}
@@ -423,7 +435,8 @@ impl Ferese {
pub(crate) fn capture_resize_before_commit(&mut self, surface: &WlSurface) {
let Some((window, id)) = self
.window_ids
.windows
.ids()
.iter()
.find(|(window, _)| {
window
@@ -434,9 +447,11 @@ impl Ferese {
else {
return;
};
let Some(transaction) = self.resize_transactions.get(&id) else {
let Some(transaction) = self.windows.transaction(&id) else {
return;
};
let serial = with_states(surface, |states| {
states
.data_map
@@ -446,6 +461,7 @@ impl Ferese {
if !transaction.accepts(serial) {
return;
}
let Some(output) = self
.space
.outputs()
@@ -454,10 +470,11 @@ impl Ferese {
else {
return;
};
let source_geometry = transaction.source_geometry().unwrap_or_else(|| window.geometry());
let scale = output.current_scale().fractional_scale();
let source_size = source_geometry.size.to_physical_precise_round(scale);
if self.resize_snapshots.get(&id).is_some_and(|snapshot| {
if self.render.snapshot(&id).is_some_and(|snapshot| {
crate::presentation::snapshot_covers_source(
smithay::backend::renderer::Texture::size(&snapshot.texture),
source_size,
@@ -467,7 +484,8 @@ impl Ferese {
}) {
return;
}
let used: usize = self.resize_snapshots.values().map(|snapshot| snapshot.bytes()).sum();
let used: usize = self.render.snapshots().map(|snapshot| snapshot.bytes()).sum();
let remaining = crate::presentation::SNAPSHOT_BUDGET.saturating_sub(used);
let result = if let Some(backend) = &self.nested_backend {
// Commit dispatch does not run inside the Winit event callback;
@@ -490,7 +508,7 @@ impl Ferese {
match result {
Ok(Some(snapshot)) => {
tracing::debug!(?id, bytes = snapshot.bytes(), "captured native resize handoff");
self.resize_snapshots.insert(id, snapshot);
self.render.set_snapshot(id, snapshot);
}
Ok(None) => {}
Err(error) => {
@@ -500,7 +518,7 @@ impl Ferese {
}
pub fn record_client_commit(&mut self, window: &Window) {
let Some(&id) = self.window_ids.get(window) else {
let Some(&id) = self.windows.ids().get(window) else {
return;
};
// current_serial is promoted by Smithay on commit, unlike
@@ -514,28 +532,30 @@ impl Ferese {
})
});
if self
.resize_transactions
.get(&id)
.windows
.transaction(&id)
.is_some_and(|transaction| transaction.accepts(committed_serial))
{
self.resize_transactions.remove(&id);
self.windows.clear_transaction(&id);
}
let Some(size) = client_size(window) else {
return;
};
let fullscreen = self
.workspaces
.workspace_for_window(id)
.and_then(|workspace| self.workspaces.workspace(workspace))
.is_some_and(|workspace| workspace.fullscreen == Some(id));
let normal_geometry = !fullscreen
&& !self.maximized_windows.contains(&id)
&& self
.window_geometry
.get(&id)
.is_none_or(|geometry| !geometry.is_zooming());
let initial_floating =
self.natural_floating_pending.remove(&id) || self.floating_placement_anchors.contains_key(&id);
&& !self.windows.record(id).is_some_and(|w| w.maximized)
&& self.windows.geometry(&id).is_none_or(|geometry| !geometry.is_zooming());
let initial_floating = self
.windows
.update(id, |w| std::mem::take(&mut w.natural_floating_pending))
.unwrap_or(false)
|| self.windows.record(id).is_some_and(|w| w.placement_anchor.is_some());
if initial_floating
&& normal_geometry
&& matches!(self.workspaces.placement(id), Some(WindowPlacement::Floating { .. }))
@@ -548,12 +568,12 @@ impl Ferese {
(f64::from(size.width), f64::from(size.height)),
Some((Some(f64::from(size.width)), Some(f64::from(size.height)))),
);
self.floating_placement_anchors.remove(&id);
self.windows.update(id, |w| w.placement_anchor = None);
if self.workspaces.set_floating_rect(id, rect).is_ok() {
// This is the first mapped client buffer, not a user resize.
// Do not animate from the temporary placement box.
self.window_geometry.insert(id, WindowGeometry::new(rect, Some(size)));
self.resize_transactions.remove(&id);
self.windows.set_geometry(id, WindowGeometry::new(rect, Some(size)));
self.windows.clear_transaction(&id);
self.relayout();
}
}
@@ -561,7 +581,7 @@ impl Ferese {
// terminal cell grids, or a dialog changing its contents). Its committed
// window geometry is authoritative once the latest configure is committed.
// Never let an old buffer undo a newer resize or a fullscreen transition.
self.floating_placement_anchors.remove(&id);
self.windows.update(id, |w| w.placement_anchor = None);
let settled_configure = window.toplevel().is_some_and(|toplevel| {
with_states(toplevel.wl_surface(), |states| {
states.data_map.get::<XdgToplevelSurfaceData>().is_some_and(|data| {
@@ -581,22 +601,25 @@ impl Ferese {
&& ClientSize::from_rect(rect) != size
{
let mut rect = Rect::new(rect.x, rect.y, f64::from(size.width), f64::from(size.height));
if let Some((left, top, anchor)) = self.floating_resize_anchors.get(&id) {
if let Some((left, top, anchor)) = self.windows.record(id).and_then(|w| w.resize_anchor.as_ref()) {
rect = crate::floating::anchored_size(*anchor, (rect.width, rect.height), *left, *top);
} else if initial_floating && let Some(work) = self.floating_bounds_for_window(id) {
rect = crate::floating::clamp_to_work(rect, work);
}
if self.workspaces.set_floating_rect(id, rect).is_ok() {
self.window_geometry.insert(id, WindowGeometry::new(rect, Some(size)));
self.resize_transactions.remove(&id);
self.resize_snapshots.remove(&id);
self.windows.set_geometry(id, WindowGeometry::new(rect, Some(size)));
self.windows.clear_transaction(&id);
self.render.clear_snapshot(&id);
self.relayout();
}
}
if settled_configure && self.floating_resize_anchors.remove(&id).is_some() {
if settled_configure && self.windows.update(id, |w| w.resize_anchor.take()).flatten().is_some() {
self.remember_floating(window);
}
let Some(geometry) = self.window_geometry.get_mut(&id) else {
let Some(geometry) = self.windows.geometry_mut(&id) else {
return;
};
@@ -614,17 +637,21 @@ impl Ferese {
return;
}
self.closing_windows.entry(focused).or_default();
self.windows.update(focused, |record| {
record.closing.get_or_insert_default();
});
crate::backends::direct::render_all(self);
}
pub(crate) fn close_managed_window(&mut self, id: WindowId) -> bool {
if !self.window_ids.values().any(|window_id| *window_id == id) {
if !self.windows.ids().values().any(|window_id| *window_id == id) {
return false;
}
if self.animations_enabled {
self.closing_windows.entry(id).or_default();
self.windows.update(id, |record| {
record.closing.get_or_insert_default();
});
crate::backends::direct::render_all(self);
} else {
self.send_window_close(id);
@@ -635,7 +662,8 @@ impl Ferese {
pub(super) fn send_window_close(&self, id: WindowId) {
let Some(toplevel) = self
.window_ids
.windows
.ids()
.iter()
.find_map(|(window, window_id)| (*window_id == id).then(|| window.toplevel()).flatten())
else {