mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-24 02:01:18 +07:00
Offscreen semitransparent tiles
Now that offscreen does damage tracking, we can reasonably do this. Note this only affects full-tile opacity, not window opacity.
This commit is contained in:
@@ -54,6 +54,7 @@ impl OpenAnimation {
|
|||||||
geo_size: Size<f64, Logical>,
|
geo_size: Size<f64, Logical>,
|
||||||
location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
|
alpha: f32,
|
||||||
) -> anyhow::Result<OpeningWindowRenderElement> {
|
) -> anyhow::Result<OpeningWindowRenderElement> {
|
||||||
let progress = self.anim.value();
|
let progress = self.anim.value();
|
||||||
let clamped_progress = self.anim.clamped_value().clamp(0., 1.);
|
let clamped_progress = self.anim.clamped_value().clamp(0., 1.);
|
||||||
@@ -102,7 +103,7 @@ impl OpenAnimation {
|
|||||||
area.size,
|
area.size,
|
||||||
None,
|
None,
|
||||||
scale.x as f32,
|
scale.x as f32,
|
||||||
1.,
|
alpha,
|
||||||
vec![
|
vec![
|
||||||
mat3_uniform("niri_input_to_geo", input_to_geo),
|
mat3_uniform("niri_input_to_geo", input_to_geo),
|
||||||
Uniform::new("niri_geo_size", geo_size.to_array()),
|
Uniform::new("niri_geo_size", geo_size.to_array()),
|
||||||
@@ -118,7 +119,7 @@ impl OpenAnimation {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let elem = elem.with_alpha(clamped_progress as f32);
|
let elem = elem.with_alpha(clamped_progress as f32 * alpha);
|
||||||
|
|
||||||
let center = geo_size.to_point().downscale(2.);
|
let center = geo_size.to_point().downscale(2.);
|
||||||
let elem = RescaleRenderElement::from_element(
|
let elem = RescaleRenderElement::from_element(
|
||||||
|
|||||||
+54
-23
@@ -18,6 +18,7 @@ use crate::niri_render_elements;
|
|||||||
use crate::render_helpers::border::BorderRenderElement;
|
use crate::render_helpers::border::BorderRenderElement;
|
||||||
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
|
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
|
||||||
use crate::render_helpers::damage::ExtraDamage;
|
use crate::render_helpers::damage::ExtraDamage;
|
||||||
|
use crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenRenderElement};
|
||||||
use crate::render_helpers::renderer::NiriRenderer;
|
use crate::render_helpers::renderer::NiriRenderer;
|
||||||
use crate::render_helpers::resize::ResizeRenderElement;
|
use crate::render_helpers::resize::ResizeRenderElement;
|
||||||
use crate::render_helpers::shadow::ShadowRenderElement;
|
use crate::render_helpers::shadow::ShadowRenderElement;
|
||||||
@@ -86,7 +87,7 @@ pub struct Tile<W: LayoutElement> {
|
|||||||
move_y_animation: Option<MoveAnimation>,
|
move_y_animation: Option<MoveAnimation>,
|
||||||
|
|
||||||
/// The animation of the tile's opacity.
|
/// The animation of the tile's opacity.
|
||||||
pub(super) alpha_animation: Option<Animation>,
|
pub(super) alpha_animation: Option<AlphaAnimation>,
|
||||||
|
|
||||||
/// Offset during the initial interactive move rubberband.
|
/// Offset during the initial interactive move rubberband.
|
||||||
pub(super) interactive_move_offset: Point<f64, Logical>,
|
pub(super) interactive_move_offset: Point<f64, Logical>,
|
||||||
@@ -122,6 +123,7 @@ niri_render_elements! {
|
|||||||
Border = BorderRenderElement,
|
Border = BorderRenderElement,
|
||||||
Shadow = ShadowRenderElement,
|
Shadow = ShadowRenderElement,
|
||||||
ClippedSurface = ClippedSurfaceRenderElement<R>,
|
ClippedSurface = ClippedSurfaceRenderElement<R>,
|
||||||
|
Offscreen = OffscreenRenderElement,
|
||||||
ExtraDamage = ExtraDamage,
|
ExtraDamage = ExtraDamage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +145,12 @@ struct MoveAnimation {
|
|||||||
from: f64,
|
from: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct AlphaAnimation {
|
||||||
|
pub(super) anim: Animation,
|
||||||
|
offscreen: OffscreenBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
impl<W: LayoutElement> Tile<W> {
|
impl<W: LayoutElement> Tile<W> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
window: W,
|
window: W,
|
||||||
@@ -311,7 +319,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(alpha) = &mut self.alpha_animation {
|
if let Some(alpha) = &mut self.alpha_animation {
|
||||||
if alpha.is_done() {
|
if alpha.anim.is_done() {
|
||||||
self.alpha_animation = None;
|
self.alpha_animation = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +339,6 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
|
|
||||||
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
||||||
let rules = self.window.rules();
|
let rules = self.window.rules();
|
||||||
let alpha = self.tile_alpha();
|
|
||||||
|
|
||||||
let draw_border_with_background = rules
|
let draw_border_with_background = rules
|
||||||
.draw_border_with_background
|
.draw_border_with_background
|
||||||
@@ -356,7 +363,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
),
|
),
|
||||||
radius,
|
radius,
|
||||||
self.scale,
|
self.scale,
|
||||||
alpha,
|
1.,
|
||||||
);
|
);
|
||||||
|
|
||||||
let radius = if self.is_fullscreen {
|
let radius = if self.is_fullscreen {
|
||||||
@@ -371,7 +378,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
is_active,
|
is_active,
|
||||||
radius,
|
radius,
|
||||||
self.scale,
|
self.scale,
|
||||||
alpha,
|
1.,
|
||||||
);
|
);
|
||||||
|
|
||||||
let draw_focus_ring_with_background = if self.effective_border_width().is_some() {
|
let draw_focus_ring_with_background = if self.effective_border_width().is_some() {
|
||||||
@@ -387,7 +394,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
view_rect,
|
view_rect,
|
||||||
radius,
|
radius,
|
||||||
self.scale,
|
self.scale,
|
||||||
alpha,
|
1.,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,14 +482,22 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
pub fn animate_alpha(&mut self, from: f64, to: f64, config: niri_config::Animation) {
|
pub fn animate_alpha(&mut self, from: f64, to: f64, config: niri_config::Animation) {
|
||||||
let from = from.clamp(0., 1.);
|
let from = from.clamp(0., 1.);
|
||||||
let to = to.clamp(0., 1.);
|
let to = to.clamp(0., 1.);
|
||||||
let current = self.alpha_animation.take().map(|anim| anim.clamped_value());
|
|
||||||
let current = current.unwrap_or(from);
|
let (current, offscreen) = if let Some(alpha) = self.alpha_animation.take() {
|
||||||
self.alpha_animation = Some(Animation::new(self.clock.clone(), current, to, 0., config));
|
(alpha.anim.clamped_value(), alpha.offscreen)
|
||||||
|
} else {
|
||||||
|
(from, OffscreenBuffer::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
self.alpha_animation = Some(AlphaAnimation {
|
||||||
|
anim: Animation::new(self.clock.clone(), current, to, 0., config),
|
||||||
|
offscreen,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_alpha_animates_to_1(&mut self) {
|
pub fn ensure_alpha_animates_to_1(&mut self) {
|
||||||
if let Some(anim) = &self.alpha_animation {
|
if let Some(alpha) = &self.alpha_animation {
|
||||||
if anim.to() != 1. {
|
if alpha.anim.to() != 1. {
|
||||||
// Cancel animation instead of starting a new one because the user likely wants to
|
// Cancel animation instead of starting a new one because the user likely wants to
|
||||||
// see the tile right away.
|
// see the tile right away.
|
||||||
self.alpha_animation = None;
|
self.alpha_animation = None;
|
||||||
@@ -490,13 +505,6 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opacity that applies to both window and decorations.
|
|
||||||
fn tile_alpha(&self) -> f32 {
|
|
||||||
self.alpha_animation
|
|
||||||
.as_ref()
|
|
||||||
.map_or(1., |anim| anim.clamped_value()) as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window(&self) -> &W {
|
pub fn window(&self) -> &W {
|
||||||
&self.window
|
&self.window
|
||||||
}
|
}
|
||||||
@@ -802,9 +810,6 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
|
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
|
||||||
};
|
};
|
||||||
|
|
||||||
let tile_alpha = self.tile_alpha();
|
|
||||||
let win_alpha = win_alpha * tile_alpha;
|
|
||||||
|
|
||||||
// This is here rather than in render_offset() because render_offset() is currently assumed
|
// This is here rather than in render_offset() because render_offset() is currently assumed
|
||||||
// by the code to be temporary. So, for example, interactive move will try to "grab" the
|
// by the code to be temporary. So, for example, interactive move will try to "grab" the
|
||||||
// tile at its current render offset and reset the render offset to zero by cancelling the
|
// tile at its current render offset and reset the render offset to zero by cancelling the
|
||||||
@@ -998,7 +1003,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
SolidColorRenderElement::from_buffer(
|
SolidColorRenderElement::from_buffer(
|
||||||
&self.fullscreen_backdrop,
|
&self.fullscreen_backdrop,
|
||||||
location,
|
location,
|
||||||
tile_alpha,
|
1.,
|
||||||
Kind::Unspecified,
|
Kind::Unspecified,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@@ -1028,7 +1033,13 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
) -> impl Iterator<Item = TileRenderElement<R>> + 'a {
|
) -> impl Iterator<Item = TileRenderElement<R>> + 'a {
|
||||||
let _span = tracy_client::span!("Tile::render");
|
let _span = tracy_client::span!("Tile::render");
|
||||||
|
|
||||||
|
let tile_alpha = self
|
||||||
|
.alpha_animation
|
||||||
|
.as_ref()
|
||||||
|
.map_or(1., |alpha| alpha.anim.clamped_value()) as f32;
|
||||||
|
|
||||||
let mut open_anim_elem = None;
|
let mut open_anim_elem = None;
|
||||||
|
let mut alpha_anim_elem = None;
|
||||||
let mut window_elems = None;
|
let mut window_elems = None;
|
||||||
|
|
||||||
if let Some(open) = &self.open_animation {
|
if let Some(open) = &self.open_animation {
|
||||||
@@ -1042,6 +1053,7 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
self.animated_tile_size(),
|
self.animated_tile_size(),
|
||||||
location,
|
location,
|
||||||
scale,
|
scale,
|
||||||
|
tile_alpha,
|
||||||
) {
|
) {
|
||||||
Ok(elem) => {
|
Ok(elem) => {
|
||||||
self.window()
|
self.window()
|
||||||
@@ -1052,15 +1064,34 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
warn!("error rendering window opening animation: {err:?}");
|
warn!("error rendering window opening animation: {err:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(alpha) = &self.alpha_animation {
|
||||||
|
let renderer = renderer.as_gles_renderer();
|
||||||
|
let elements =
|
||||||
|
self.render_inner(renderer, Point::from((0., 0.)), scale, focus_ring, target);
|
||||||
|
let elements = elements.collect::<Vec<TileRenderElement<_>>>();
|
||||||
|
match alpha.offscreen.render(renderer, scale, &elements) {
|
||||||
|
Ok((elem, _sync)) => {
|
||||||
|
let offset = elem.offset();
|
||||||
|
let elem = elem.with_alpha(tile_alpha).with_offset(location + offset);
|
||||||
|
|
||||||
|
self.window()
|
||||||
|
.set_offscreen_element_id(Some(elem.id().clone()));
|
||||||
|
alpha_anim_elem = Some(elem.into());
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("error rendering tile to offscreen for alpha animation: {err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if open_anim_elem.is_none() {
|
if open_anim_elem.is_none() && alpha_anim_elem.is_none() {
|
||||||
self.window().set_offscreen_element_id(None);
|
self.window().set_offscreen_element_id(None);
|
||||||
window_elems = Some(self.render_inner(renderer, location, scale, focus_ring, target));
|
window_elems = Some(self.render_inner(renderer, location, scale, focus_ring, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
open_anim_elem
|
open_anim_elem
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.chain(alpha_anim_elem)
|
||||||
.chain(window_elems.into_iter().flatten())
|
.chain(window_elems.into_iter().flatten())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1752,7 +1752,8 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
assert_abs_diff_eq!(tile_pos.x, rounded_pos.x, epsilon = 1e-5);
|
assert_abs_diff_eq!(tile_pos.x, rounded_pos.x, epsilon = 1e-5);
|
||||||
assert_abs_diff_eq!(tile_pos.y, rounded_pos.y, epsilon = 1e-5);
|
assert_abs_diff_eq!(tile_pos.y, rounded_pos.y, epsilon = 1e-5);
|
||||||
|
|
||||||
if let Some(anim) = &tile.alpha_animation {
|
if let Some(alpha) = &tile.alpha_animation {
|
||||||
|
let anim = &alpha.anim;
|
||||||
if visible {
|
if visible {
|
||||||
assert_eq!(anim.to(), 1., "visible tiles can animate alpha only to 1");
|
assert_eq!(anim.to(), 1., "visible tiles can animate alpha only to 1");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,11 @@ impl OffscreenRenderElement {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_offset(mut self, offset: Point<f64, Logical>) -> Self {
|
||||||
|
self.offset = offset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logical_size(&self) -> Size<f64, Logical> {
|
pub fn logical_size(&self) -> Size<f64, Logical> {
|
||||||
self.src_size
|
self.src_size
|
||||||
.to_f64()
|
.to_f64()
|
||||||
|
|||||||
Reference in New Issue
Block a user