aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/handlers/compositor.rs7
-rw-r--r--src/handlers/xdg_shell.rs8
-rw-r--r--src/layout/closing_window.rs79
-rw-r--r--src/layout/mod.rs32
-rw-r--r--src/layout/monitor.rs22
-rw-r--r--src/layout/workspace.rs57
-rw-r--r--src/utils/transaction.rs6
7 files changed, 172 insertions, 39 deletions
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
index e833494c..9614883d 100644
--- a/src/handlers/compositor.rs
+++ b/src/handlers/compositor.rs
@@ -20,6 +20,7 @@ use smithay::{delegate_compositor, delegate_shm};
use super::xdg_shell::add_mapped_toplevel_pre_commit_hook;
use crate::niri::{ClientState, State};
use crate::utils::send_scale_transform;
+use crate::utils::transaction::Transaction;
use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
impl CompositorHandler for State {
@@ -193,11 +194,13 @@ impl CompositorHandler for State {
});
// Must start the close animation before window.on_commit().
+ let transaction = Transaction::new();
if !is_mapped {
+ let blocker = transaction.blocker();
self.backend.with_primary_renderer(|renderer| {
self.niri
.layout
- .start_close_animation_for_window(renderer, &window);
+ .start_close_animation_for_window(renderer, &window, blocker);
});
}
@@ -216,7 +219,7 @@ impl CompositorHandler for State {
id: u64::from(id.get()),
});
- self.niri.layout.remove_window(&window);
+ self.niri.layout.remove_window(&window, transaction);
self.add_default_dmabuf_pre_commit_hook(surface);
if was_active {
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index ec0b99e3..b77f566e 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -40,6 +40,7 @@ use crate::input::resize_grab::ResizeGrab;
use crate::input::DOUBLE_CLICK_TIME;
use crate::layout::workspace::ColumnWidth;
use crate::niri::{PopupGrabState, State};
+use crate::utils::transaction::Transaction;
use crate::utils::{get_monotonic_time, send_scale_transform, ResizeEdge};
use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped, WindowRef};
@@ -485,16 +486,19 @@ impl XdgShellHandler for State {
self.backend.with_primary_renderer(|renderer| {
self.niri.layout.store_unmap_snapshot(renderer, &window);
});
+
+ let transaction = Transaction::new();
+ let blocker = transaction.blocker();
self.backend.with_primary_renderer(|renderer| {
self.niri
.layout
- .start_close_animation_for_window(renderer, &window);
+ .start_close_animation_for_window(renderer, &window, blocker);
});
let active_window = self.niri.layout.active_window().map(|(m, _)| &m.window);
let was_active = active_window == Some(&window);
- self.niri.layout.remove_window(&window);
+ self.niri.layout.remove_window(&window, transaction);
self.add_default_dmabuf_pre_commit_hook(surface.wl_surface());
if was_active {
diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs
index cbaf29be..8bbf0258 100644
--- a/src/layout/closing_window.rs
+++ b/src/layout/closing_window.rs
@@ -12,6 +12,7 @@ use smithay::backend::renderer::element::{Kind, RenderElement};
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture, Uniform};
use smithay::backend::renderer::Texture;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};
+use smithay::wayland::compositor::{Blocker, BlockerState};
use crate::animation::Animation;
use crate::niri_render_elements;
@@ -21,6 +22,7 @@ use crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders};
use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};
use crate::render_helpers::{render_to_encompassing_texture, RenderTarget};
+use crate::utils::transaction::TransactionBlocker;
#[derive(Debug)]
pub struct ClosingWindow {
@@ -46,7 +48,7 @@ pub struct ClosingWindow {
blocked_out_buffer_offset: Point<f64, Logical>,
/// The closing animation.
- anim: Animation,
+ anim_state: AnimationState,
/// Random seed for the shader.
random_seed: f32,
@@ -59,6 +61,29 @@ niri_render_elements! {
}
}
+#[derive(Debug)]
+enum AnimationState {
+ Waiting {
+ /// Blocker for a transaction before starting the animation.
+ blocker: TransactionBlocker,
+ anim: Animation,
+ },
+ Animating(Animation),
+}
+
+impl AnimationState {
+ pub fn new(blocker: TransactionBlocker, anim: Animation) -> Self {
+ if blocker.state() == BlockerState::Pending {
+ Self::Waiting { blocker, anim }
+ } else {
+ // This actually doesn't normally happen because the window is removed only after the
+ // closing animation is created. Though, it does happen with disable-transactions debug
+ // flag.
+ Self::Animating(anim)
+ }
+ }
+}
+
impl ClosingWindow {
pub fn new<E: RenderElement<GlesRenderer>>(
renderer: &mut GlesRenderer,
@@ -66,6 +91,7 @@ impl ClosingWindow {
scale: Scale<f64>,
geo_size: Size<f64, Logical>,
pos: Point<f64, Logical>,
+ blocker: TransactionBlocker,
anim: Animation,
) -> anyhow::Result<Self> {
let _span = tracy_client::span!("ClosingWindow::new");
@@ -107,17 +133,29 @@ impl ClosingWindow {
pos,
buffer_offset,
blocked_out_buffer_offset,
- anim,
+ anim_state: AnimationState::new(blocker, anim),
random_seed: fastrand::f32(),
})
}
pub fn advance_animations(&mut self, current_time: Duration) {
- self.anim.set_current_time(current_time);
+ match &mut self.anim_state {
+ AnimationState::Waiting { blocker, anim } => {
+ if blocker.state() != BlockerState::Pending {
+ let mut anim = anim.restarted(0., 1., 0.);
+ anim.set_current_time(current_time);
+ self.anim_state = AnimationState::Animating(anim);
+ }
+ }
+ AnimationState::Animating(anim) => anim.set_current_time(current_time),
+ }
}
pub fn are_animations_ongoing(&self) -> bool {
- !self.anim.is_done()
+ match &self.anim_state {
+ AnimationState::Waiting { .. } => true,
+ AnimationState::Animating(anim) => !anim.is_done(),
+ }
}
pub fn render(
@@ -127,15 +165,42 @@ impl ClosingWindow {
scale: Scale<f64>,
target: RenderTarget,
) -> ClosingWindowRenderElement {
- let progress = self.anim.value();
- let clamped_progress = self.anim.clamped_value().clamp(0., 1.);
-
let (buffer, offset) = if target.should_block_out(self.block_out_from) {
(&self.blocked_out_buffer, self.blocked_out_buffer_offset)
} else {
(&self.buffer, self.buffer_offset)
};
+ let anim = match &self.anim_state {
+ AnimationState::Waiting { .. } => {
+ let elem = TextureRenderElement::from_texture_buffer(
+ buffer.clone(),
+ Point::from((0., 0.)),
+ 1.,
+ None,
+ None,
+ Kind::Unspecified,
+ );
+
+ let elem = PrimaryGpuTextureRenderElement(elem);
+ let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), 1.);
+
+ let mut location = self.pos + offset;
+ location.x -= view_rect.loc.x;
+ let elem = RelocateRenderElement::from_element(
+ elem,
+ location.to_physical_precise_round(scale),
+ Relocate::Relative,
+ );
+
+ return elem.into();
+ }
+ AnimationState::Animating(anim) => anim,
+ };
+
+ let progress = anim.value();
+ let clamped_progress = anim.clamped_value().clamp(0., 1.);
+
if Shaders::get(renderer).program(ProgramType::Close).is_some() {
let area_loc = Vec2::new(view_rect.loc.x as f32, view_rect.loc.y as f32);
let area_size = Vec2::new(view_rect.size.w as f32, view_rect.size.h as f32);
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 9376112f..2b684ddf 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -52,7 +52,7 @@ use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::texture::TextureBuffer;
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
-use crate::utils::transaction::Transaction;
+use crate::utils::transaction::{Transaction, TransactionBlocker};
use crate::utils::{output_size, round_logical_in_physical_max1, ResizeEdge};
use crate::window::ResolvedWindowRules;
@@ -752,15 +752,13 @@ impl<W: LayoutElement> Layout<W> {
);
}
- pub fn remove_window(&mut self, window: &W::Id) -> Option<W> {
- let mut rv = None;
-
+ pub fn remove_window(&mut self, window: &W::Id, transaction: Transaction) -> Option<W> {
match &mut self.monitor_set {
MonitorSet::Normal { monitors, .. } => {
for mon in monitors {
for (idx, ws) in mon.workspaces.iter_mut().enumerate() {
if ws.has_window(window) {
- rv = Some(ws.remove_window(window));
+ let win = ws.remove_window(window, transaction);
// Clean up empty workspaces that are not active and not last.
if !ws.has_windows()
@@ -776,7 +774,7 @@ impl<W: LayoutElement> Layout<W> {
}
}
- break;
+ return Some(win);
}
}
}
@@ -784,20 +782,20 @@ impl<W: LayoutElement> Layout<W> {
MonitorSet::NoOutputs { workspaces, .. } => {
for (idx, ws) in workspaces.iter_mut().enumerate() {
if ws.has_window(window) {
- rv = Some(ws.remove_window(window));
+ let win = ws.remove_window(window, transaction);
// Clean up empty workspaces.
if !ws.has_windows() && workspaces[idx].name.is_none() {
workspaces.remove(idx);
}
- break;
+ return Some(win);
}
}
}
}
- rv
+ None
}
pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {
@@ -1970,7 +1968,12 @@ impl<W: LayoutElement> Layout<W> {
let width = column.width;
let is_full_width = column.is_full_width;
let window = ws
- .remove_tile_by_idx(ws.active_column_idx, column.active_tile_idx, None)
+ .remove_tile_by_idx(
+ ws.active_column_idx,
+ column.active_tile_idx,
+ Transaction::new(),
+ None,
+ )
.into_window();
let workspace_idx = monitors[new_idx].active_workspace_idx;
@@ -2022,7 +2025,7 @@ impl<W: LayoutElement> Layout<W> {
let Some(width) = width else { return };
- let window = self.remove_window(window).unwrap();
+ let window = self.remove_window(window, Transaction::new()).unwrap();
if let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set {
let new_idx = monitors
@@ -2425,6 +2428,7 @@ impl<W: LayoutElement> Layout<W> {
&mut self,
renderer: &mut GlesRenderer,
window: &W::Id,
+ blocker: TransactionBlocker,
) {
let _span = tracy_client::span!("Layout::start_close_animation_for_window");
@@ -2433,7 +2437,7 @@ impl<W: LayoutElement> Layout<W> {
for mon in monitors {
for ws in &mut mon.workspaces {
if ws.has_window(window) {
- ws.start_close_animation_for_window(renderer, window);
+ ws.start_close_animation_for_window(renderer, window, blocker);
return;
}
}
@@ -2442,7 +2446,7 @@ impl<W: LayoutElement> Layout<W> {
MonitorSet::NoOutputs { workspaces, .. } => {
for ws in workspaces {
if ws.has_window(window) {
- ws.start_close_animation_for_window(renderer, window);
+ ws.start_close_animation_for_window(renderer, window, blocker);
return;
}
}
@@ -3143,7 +3147,7 @@ mod tests {
layout.add_window_to_named_workspace(&ws_name, win, None, false);
}
Op::CloseWindow(id) => {
- layout.remove_window(&id);
+ layout.remove_window(&id, Transaction::new());
}
Op::FullscreenWindow(id) => {
layout.toggle_fullscreen(&id);
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index 423b2ef9..07223521 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -19,6 +19,7 @@ use crate::input::swipe_tracker::SwipeTracker;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::RenderTarget;
use crate::rubber_band::RubberBand;
+use crate::utils::transaction::Transaction;
use crate::utils::{output_size, to_physical_precise_round, ResizeEdge};
/// Amount of touchpad movement to scroll the height of one workspace.
@@ -442,7 +443,12 @@ impl<W: LayoutElement> Monitor<W> {
let width = column.width;
let is_full_width = column.is_full_width;
let window = workspace
- .remove_tile_by_idx(workspace.active_column_idx, column.active_tile_idx, None)
+ .remove_tile_by_idx(
+ workspace.active_column_idx,
+ column.active_tile_idx,
+ Transaction::new(),
+ None,
+ )
.into_window();
self.add_window(new_idx, window, true, width, is_full_width);
@@ -465,7 +471,12 @@ impl<W: LayoutElement> Monitor<W> {
let width = column.width;
let is_full_width = column.is_full_width;
let window = workspace
- .remove_tile_by_idx(workspace.active_column_idx, column.active_tile_idx, None)
+ .remove_tile_by_idx(
+ workspace.active_column_idx,
+ column.active_tile_idx,
+ Transaction::new(),
+ None,
+ )
.into_window();
self.add_window(new_idx, window, true, width, is_full_width);
@@ -488,7 +499,12 @@ impl<W: LayoutElement> Monitor<W> {
let width = column.width;
let is_full_width = column.is_full_width;
let window = workspace
- .remove_tile_by_idx(workspace.active_column_idx, column.active_tile_idx, None)
+ .remove_tile_by_idx(
+ workspace.active_column_idx,
+ column.active_tile_idx,
+ Transaction::new(),
+ None,
+ )
.into_window();
self.add_window(new_idx, window, true, width, is_full_width);
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 7548dc10..272a153b 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -22,7 +22,7 @@ use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::RenderTarget;
use crate::utils::id::IdCounter;
-use crate::utils::transaction::Transaction;
+use crate::utils::transaction::{Transaction, TransactionBlocker};
use crate::utils::{output_size, send_scale_transform, ResizeEdge};
use crate::window::ResolvedWindowRules;
@@ -1098,6 +1098,7 @@ impl<W: LayoutElement> Workspace<W> {
&mut self,
column_idx: usize,
window_idx: usize,
+ transaction: Transaction,
anim_config: Option<niri_config::Animation>,
) -> Tile<W> {
let offset = self.column_x(column_idx + 1) - self.column_x(column_idx);
@@ -1139,7 +1140,7 @@ impl<W: LayoutElement> Workspace<W> {
offset
} else {
column.active_tile_idx = min(column.active_tile_idx, column.tiles.len() - 1);
- column.update_tile_sizes(true);
+ column.update_tile_sizes_with_transaction(true, transaction);
self.data[column_idx].update(column);
prev_width - column.width()
@@ -1294,7 +1295,7 @@ impl<W: LayoutElement> Workspace<W> {
column
}
- pub fn remove_window(&mut self, window: &W::Id) -> W {
+ pub fn remove_window(&mut self, window: &W::Id, transaction: Transaction) -> W {
let column_idx = self
.columns
.iter()
@@ -1303,7 +1304,7 @@ impl<W: LayoutElement> Workspace<W> {
let column = &self.columns[column_idx];
let window_idx = column.position(window).unwrap();
- self.remove_tile_by_idx(column_idx, window_idx, None)
+ self.remove_tile_by_idx(column_idx, window_idx, transaction, None)
.into_window()
}
@@ -1490,6 +1491,7 @@ impl<W: LayoutElement> Workspace<W> {
&mut self,
renderer: &mut GlesRenderer,
window: &W::Id,
+ blocker: TransactionBlocker,
) {
let output_scale = Scale::from(self.scale.fractional_scale());
@@ -1542,7 +1544,21 @@ impl<W: LayoutElement> Workspace<W> {
let anim = Animation::new(0., 1., 0., self.options.animations.window_close.anim);
- let res = ClosingWindow::new(renderer, snapshot, output_scale, tile_size, tile_pos, anim);
+ let blocker = if self.options.disable_transactions {
+ TransactionBlocker::completed()
+ } else {
+ blocker
+ };
+
+ let res = ClosingWindow::new(
+ renderer,
+ snapshot,
+ output_scale,
+ tile_size,
+ tile_pos,
+ blocker,
+ anim,
+ );
match res {
Ok(closing) => {
self.closing_windows.push(closing);
@@ -1781,6 +1797,7 @@ impl<W: LayoutElement> Workspace<W> {
let tile = self.remove_tile_by_idx(
source_col_idx,
0,
+ Transaction::new(),
Some(self.options.animations.window_movement.0),
);
self.enter_output_for_window(tile.window());
@@ -1813,7 +1830,12 @@ impl<W: LayoutElement> Workspace<W> {
let mut offset = Point::from((source_column.render_offset().x, 0.));
- let tile = self.remove_tile_by_idx(source_col_idx, source_column.active_tile_idx, None);
+ let tile = self.remove_tile_by_idx(
+ source_col_idx,
+ source_column.active_tile_idx,
+ Transaction::new(),
+ None,
+ );
self.add_tile_at(
self.active_column_idx,
@@ -1861,6 +1883,7 @@ impl<W: LayoutElement> Workspace<W> {
let tile = self.remove_tile_by_idx(
source_col_idx,
0,
+ Transaction::new(),
Some(self.options.animations.window_movement.0),
);
self.enter_output_for_window(tile.window());
@@ -1888,7 +1911,12 @@ impl<W: LayoutElement> Workspace<W> {
let width = source_column.width;
let is_full_width = source_column.is_full_width;
- let tile = self.remove_tile_by_idx(source_col_idx, source_column.active_tile_idx, None);
+ let tile = self.remove_tile_by_idx(
+ source_col_idx,
+ source_column.active_tile_idx,
+ Transaction::new(),
+ None,
+ );
self.add_tile(
tile,
@@ -1921,7 +1949,7 @@ impl<W: LayoutElement> Workspace<W> {
let mut offset = Point::from((offset, 0.));
let prev_off = self.columns[source_column_idx].tile_offset(0);
- let tile = self.remove_tile_by_idx(source_column_idx, 0, None);
+ let tile = self.remove_tile_by_idx(source_column_idx, 0, Transaction::new(), None);
self.enter_output_for_window(tile.window());
let prev_next_x = self.column_x(self.active_column_idx + 1);
@@ -1970,8 +1998,12 @@ impl<W: LayoutElement> Workspace<W> {
let width = source_column.width;
let is_full_width = source_column.is_full_width;
- let tile =
- self.remove_tile_by_idx(self.active_column_idx, source_column.active_tile_idx, None);
+ let tile = self.remove_tile_by_idx(
+ self.active_column_idx,
+ source_column.active_tile_idx,
+ Transaction::new(),
+ None,
+ );
self.add_tile(
tile,
@@ -3031,6 +3063,10 @@ impl<W: LayoutElement> Column<W> {
}
fn update_tile_sizes(&mut self, animate: bool) {
+ self.update_tile_sizes_with_transaction(animate, Transaction::new());
+ }
+
+ fn update_tile_sizes_with_transaction(&mut self, animate: bool, transaction: Transaction) {
if self.is_fullscreen {
self.tiles[0].request_fullscreen(self.view_size);
return;
@@ -3195,7 +3231,6 @@ impl<W: LayoutElement> Column<W> {
assert_eq!(auto_tiles_left, 0);
}
- let transaction = Transaction::new();
for (tile, h) in zip(&mut self.tiles, heights) {
let WindowHeight::Fixed(height) = h else {
unreachable!()
diff --git a/src/utils/transaction.rs b/src/utils/transaction.rs
index 03dd6a2e..f4921947 100644
--- a/src/utils/transaction.rs
+++ b/src/utils/transaction.rs
@@ -148,6 +148,12 @@ impl Drop for Transaction {
}
}
+impl TransactionBlocker {
+ pub fn completed() -> Self {
+ Self(Weak::new())
+ }
+}
+
impl Blocker for TransactionBlocker {
fn state(&self) -> BlockerState {
if self.0.upgrade().map_or(true, |x| x.is_completed()) {