aboutsummaryrefslogtreecommitdiff
path: root/src/layout/closing_window.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-04-09 22:37:10 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-04-09 23:42:01 +0400
commitdd011f1012e10b1e3a1dbe100cb603a457bba12a (patch)
tree2fb853be8bbe1ee5afdf3dea4974768874a5e793 /src/layout/closing_window.rs
parent301a2c06613c76d2c16a85ab21ad132e5618454b (diff)
downloadniri-dd011f1012e10b1e3a1dbe100cb603a457bba12a.tar.gz
niri-dd011f1012e10b1e3a1dbe100cb603a457bba12a.tar.bz2
niri-dd011f1012e10b1e3a1dbe100cb603a457bba12a.zip
Implement window closing animations
Diffstat (limited to 'src/layout/closing_window.rs')
-rw-r--r--src/layout/closing_window.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs
new file mode 100644
index 00000000..ec565ebf
--- /dev/null
+++ b/src/layout/closing_window.rs
@@ -0,0 +1,177 @@
+use std::time::Duration;
+
+use anyhow::Context as _;
+use niri_config::BlockOutFrom;
+use smithay::backend::allocator::Fourcc;
+use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
+use smithay::backend::renderer::element::utils::{
+ Relocate, RelocateRenderElement, RescaleRenderElement,
+};
+use smithay::backend::renderer::element::{Kind, RenderElement};
+use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
+use smithay::utils::{Logical, Point, Scale, Transform};
+
+use crate::animation::Animation;
+use crate::niri_render_elements;
+use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
+use crate::render_helpers::{render_to_texture, RenderSnapshot, RenderTarget};
+
+#[derive(Debug)]
+pub struct ClosingWindow {
+ /// Contents of the window.
+ buffer: TextureBuffer<GlesTexture>,
+
+ /// Blocked-out contents of the window.
+ blocked_out_buffer: TextureBuffer<GlesTexture>,
+
+ /// Where the window should be blocked out from.
+ block_out_from: Option<BlockOutFrom>,
+
+ /// Center of the window geometry.
+ center: Point<i32, Logical>,
+
+ /// Position in the workspace.
+ pos: Point<i32, Logical>,
+
+ /// How much the buffer should be offset.
+ buffer_offset: Point<i32, Logical>,
+
+ /// How much the blocked-out buffer should be offset.
+ blocked_out_buffer_offset: Point<i32, Logical>,
+
+ /// The closing animation.
+ anim: Animation,
+
+ /// Alpha the animation should start from.
+ starting_alpha: f32,
+
+ /// Scale the animation should start from.
+ starting_scale: f64,
+}
+
+niri_render_elements! {
+ ClosingWindowRenderElement => {
+ Texture = RelocateRenderElement<RescaleRenderElement<PrimaryGpuTextureRenderElement>>,
+ }
+}
+
+impl ClosingWindow {
+ #[allow(clippy::too_many_arguments)]
+ pub fn new<E: RenderElement<GlesRenderer>>(
+ renderer: &mut GlesRenderer,
+ snapshot: RenderSnapshot<E>,
+ scale: i32,
+ center: Point<i32, Logical>,
+ pos: Point<i32, Logical>,
+ anim: Animation,
+ starting_alpha: f32,
+ starting_scale: f64,
+ ) -> anyhow::Result<Self> {
+ let _span = tracy_client::span!("ClosingWindow::new");
+
+ let mut render_to_buffer = |elements: Vec<E>| -> anyhow::Result<_> {
+ let geo = elements
+ .iter()
+ .map(|ele| ele.geometry(Scale::from(f64::from(scale))))
+ .reduce(|a, b| a.merge(b))
+ .unwrap_or_default();
+
+ let elements = elements.iter().rev().map(|ele| {
+ RelocateRenderElement::from_element(
+ ele,
+ (-geo.loc.x, -geo.loc.y),
+ Relocate::Relative,
+ )
+ });
+ let offset = geo.loc.to_logical(scale);
+
+ let (texture, _sync_point) = render_to_texture(
+ renderer,
+ geo.size,
+ Scale::from(scale as f64),
+ Transform::Normal,
+ Fourcc::Abgr8888,
+ elements,
+ )
+ .context("error rendering to texture")?;
+
+ let buffer =
+ TextureBuffer::from_texture(renderer, texture, scale, Transform::Normal, None);
+ Ok((buffer, offset))
+ };
+
+ let (buffer, buffer_offset) =
+ render_to_buffer(snapshot.contents).context("error rendering contents")?;
+ let (blocked_out_buffer, blocked_out_buffer_offset) =
+ render_to_buffer(snapshot.blocked_out_contents)
+ .context("error rendering blocked-out contents")?;
+
+ Ok(Self {
+ buffer,
+ blocked_out_buffer,
+ block_out_from: snapshot.block_out_from,
+ center,
+ pos,
+ buffer_offset,
+ blocked_out_buffer_offset,
+ anim,
+ starting_alpha,
+ starting_scale,
+ })
+ }
+
+ pub fn advance_animations(&mut self, current_time: Duration) {
+ self.anim.set_current_time(current_time);
+ }
+
+ pub fn are_animations_ongoing(&self) -> bool {
+ !self.anim.is_done()
+ }
+
+ pub fn render(
+ &self,
+ view_pos: i32,
+ scale: Scale<f64>,
+ target: RenderTarget,
+ ) -> ClosingWindowRenderElement {
+ let val = self.anim.value();
+
+ let block_out = match self.block_out_from {
+ None => false,
+ Some(BlockOutFrom::Screencast) => target == RenderTarget::Screencast,
+ Some(BlockOutFrom::ScreenCapture) => target != RenderTarget::Output,
+ };
+ let (buffer, offset) = if block_out {
+ (&self.blocked_out_buffer, self.blocked_out_buffer_offset)
+ } else {
+ (&self.buffer, self.buffer_offset)
+ };
+
+ let elem = TextureRenderElement::from_texture_buffer(
+ Point::from((0., 0.)),
+ buffer,
+ Some(val.clamp(0., 1.) as f32 * self.starting_alpha),
+ None,
+ None,
+ Kind::Unspecified,
+ );
+
+ let elem = PrimaryGpuTextureRenderElement(elem);
+
+ let elem = RescaleRenderElement::from_element(
+ elem,
+ (self.center - offset).to_physical_precise_round(scale),
+ ((val / 5. + 0.8) * self.starting_scale).max(0.),
+ );
+
+ let mut location = self.pos + offset;
+ location.x -= view_pos;
+ let elem = RelocateRenderElement::from_element(
+ elem,
+ location.to_physical_precise_round(scale),
+ Relocate::Relative,
+ );
+
+ elem.into()
+ }
+}