2023-12-27 21:51:42 +04:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
use niri_config::{Color, CornerRadius, GradientInterpolation};
|
2024-04-13 11:07:23 +04:00
|
|
|
use smithay::backend::allocator::Fourcc;
|
2024-02-07 11:32:02 +04:00
|
|
|
use smithay::backend::renderer::element::{Element, Kind};
|
2024-04-13 14:16:07 +04:00
|
|
|
use smithay::backend::renderer::gles::GlesRenderer;
|
|
|
|
|
use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};
|
2023-12-27 21:51:42 +04:00
|
|
|
|
2024-02-21 21:27:44 +04:00
|
|
|
use super::focus_ring::{FocusRing, FocusRingRenderElement};
|
2024-05-15 19:38:29 +04:00
|
|
|
use super::opening_window::{OpenAnimation, OpeningWindowRenderElement};
|
2024-04-13 11:07:23 +04:00
|
|
|
use super::{
|
2024-04-13 14:16:07 +04:00
|
|
|
LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot, Options,
|
2024-04-13 11:07:23 +04:00
|
|
|
RESIZE_ANIMATION_THRESHOLD,
|
|
|
|
|
};
|
2024-11-23 11:27:27 +03:00
|
|
|
use crate::animation::{Animation, Clock};
|
2024-02-05 14:05:08 +04:00
|
|
|
use crate::niri_render_elements;
|
2024-05-01 19:06:08 +04:00
|
|
|
use crate::render_helpers::border::BorderRenderElement;
|
2024-05-02 08:14:21 +04:00
|
|
|
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
|
|
|
|
|
use crate::render_helpers::damage::ExtraDamage;
|
2024-02-06 11:24:50 +04:00
|
|
|
use crate::render_helpers::renderer::NiriRenderer;
|
2024-04-21 20:10:35 +04:00
|
|
|
use crate::render_helpers::resize::ResizeRenderElement;
|
2024-04-13 14:16:07 +04:00
|
|
|
use crate::render_helpers::snapshot::RenderSnapshot;
|
2024-06-17 09:16:28 +03:00
|
|
|
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
2024-05-01 19:00:11 +04:00
|
|
|
use crate::render_helpers::{render_to_encompassing_texture, RenderTarget};
|
2024-08-22 14:44:11 +03:00
|
|
|
use crate::utils::transaction::Transaction;
|
2023-12-27 21:51:42 +04:00
|
|
|
|
|
|
|
|
/// Toplevel window with decorations.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct Tile<W: LayoutElement> {
|
|
|
|
|
/// The toplevel window itself.
|
|
|
|
|
window: W,
|
|
|
|
|
|
|
|
|
|
/// The border around the window.
|
|
|
|
|
border: FocusRing,
|
|
|
|
|
|
2024-02-06 19:47:36 +04:00
|
|
|
/// The focus ring around the window.
|
|
|
|
|
///
|
|
|
|
|
/// It's supposed to be on the Workspace, but for the sake of a nicer open animation it's
|
|
|
|
|
/// currently here.
|
|
|
|
|
focus_ring: FocusRing,
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
/// Whether this tile is fullscreen.
|
|
|
|
|
///
|
|
|
|
|
/// This will update only when the `window` actually goes fullscreen, rather than right away,
|
|
|
|
|
/// to avoid black backdrop flicker before the window has had a chance to resize.
|
|
|
|
|
is_fullscreen: bool,
|
|
|
|
|
|
|
|
|
|
/// The black backdrop for fullscreen windows.
|
|
|
|
|
fullscreen_backdrop: SolidColorBuffer,
|
|
|
|
|
|
|
|
|
|
/// The size we were requested to fullscreen into.
|
2024-06-17 09:16:28 +03:00
|
|
|
fullscreen_size: Size<f64, Logical>,
|
2023-12-27 21:51:42 +04:00
|
|
|
|
2024-02-07 11:32:02 +04:00
|
|
|
/// The animation upon opening a window.
|
2024-05-15 19:38:29 +04:00
|
|
|
open_animation: Option<OpenAnimation>,
|
2024-02-07 11:32:02 +04:00
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
/// The animation of the window resizing.
|
|
|
|
|
resize_animation: Option<ResizeAnimation>,
|
|
|
|
|
|
2024-04-19 12:44:24 +04:00
|
|
|
/// The animation of a tile visually moving horizontally.
|
|
|
|
|
move_x_animation: Option<MoveAnimation>,
|
|
|
|
|
|
|
|
|
|
/// The animation of a tile visually moving vertically.
|
|
|
|
|
move_y_animation: Option<MoveAnimation>,
|
2024-04-13 11:07:23 +04:00
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
/// Offset during the initial interactive move rubberband.
|
|
|
|
|
pub(super) interactive_move_offset: Point<f64, Logical>,
|
|
|
|
|
|
2024-05-01 19:00:11 +04:00
|
|
|
/// Snapshot of the last render for use in the close animation.
|
2024-05-14 15:00:11 +04:00
|
|
|
unmap_snapshot: Option<TileRenderSnapshot>,
|
2024-05-01 19:00:11 +04:00
|
|
|
|
2024-05-02 08:14:21 +04:00
|
|
|
/// Extra damage for clipped surface corner radius changes.
|
|
|
|
|
rounded_corner_damage: RoundedCornerDamage,
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
/// Scale of the output the tile is on (and rounds its sizes to).
|
|
|
|
|
scale: f64,
|
|
|
|
|
|
2024-11-23 11:27:27 +03:00
|
|
|
/// Clock for driving animations.
|
|
|
|
|
pub(super) clock: Clock,
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
/// Configurable properties of the layout.
|
2024-10-10 09:24:20 +03:00
|
|
|
pub(super) options: Rc<Options>,
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 14:05:08 +04:00
|
|
|
niri_render_elements! {
|
2024-02-21 09:05:41 +04:00
|
|
|
TileRenderElement<R> => {
|
2024-02-05 14:05:08 +04:00
|
|
|
LayoutElement = LayoutElementRenderElement<R>,
|
2024-02-21 21:27:44 +04:00
|
|
|
FocusRing = FocusRingRenderElement,
|
2024-02-21 11:08:48 +04:00
|
|
|
SolidColor = SolidColorRenderElement,
|
2024-05-15 19:38:29 +04:00
|
|
|
Opening = OpeningWindowRenderElement,
|
2024-04-21 20:10:35 +04:00
|
|
|
Resize = ResizeRenderElement,
|
2024-05-01 19:06:08 +04:00
|
|
|
Border = BorderRenderElement,
|
|
|
|
|
ClippedSurface = ClippedSurfaceRenderElement<R>,
|
2024-05-02 08:14:21 +04:00
|
|
|
ExtraDamage = ExtraDamage,
|
2024-02-05 14:05:08 +04:00
|
|
|
}
|
2024-02-04 22:52:11 +04:00
|
|
|
}
|
|
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
pub type TileRenderSnapshot =
|
2024-05-01 19:00:11 +04:00
|
|
|
RenderSnapshot<TileRenderElement<GlesRenderer>, TileRenderElement<GlesRenderer>>;
|
2024-04-09 22:37:10 +04:00
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct ResizeAnimation {
|
|
|
|
|
anim: Animation,
|
2024-06-17 09:16:28 +03:00
|
|
|
size_from: Size<f64, Logical>,
|
2024-04-13 14:16:07 +04:00
|
|
|
snapshot: LayoutElementRenderSnapshot,
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct MoveAnimation {
|
|
|
|
|
anim: Animation,
|
2024-06-17 09:16:28 +03:00
|
|
|
from: f64,
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
impl<W: LayoutElement> Tile<W> {
|
2024-11-23 11:27:27 +03:00
|
|
|
pub fn new(window: W, scale: f64, clock: Clock, options: Rc<Options>) -> Self {
|
2024-04-24 21:49:07 +04:00
|
|
|
let rules = window.rules();
|
|
|
|
|
let border_config = rules.border.resolve_against(options.border);
|
2024-04-24 22:17:53 +04:00
|
|
|
let focus_ring_config = rules.focus_ring.resolve_against(options.focus_ring.into());
|
2024-04-24 21:49:07 +04:00
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
Self {
|
|
|
|
|
window,
|
2024-04-24 21:49:07 +04:00
|
|
|
border: FocusRing::new(border_config.into()),
|
2024-04-24 22:17:53 +04:00
|
|
|
focus_ring: FocusRing::new(focus_ring_config.into()),
|
2023-12-27 21:51:42 +04:00
|
|
|
is_fullscreen: false, // FIXME: up-to-date fullscreen right away, but we need size.
|
2024-06-17 09:16:28 +03:00
|
|
|
fullscreen_backdrop: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
|
2023-12-27 21:51:42 +04:00
|
|
|
fullscreen_size: Default::default(),
|
2024-02-07 11:32:02 +04:00
|
|
|
open_animation: None,
|
2024-04-13 11:07:23 +04:00
|
|
|
resize_animation: None,
|
2024-04-19 12:44:24 +04:00
|
|
|
move_x_animation: None,
|
|
|
|
|
move_y_animation: None,
|
2024-07-15 15:51:48 +02:00
|
|
|
interactive_move_offset: Point::from((0., 0.)),
|
2024-05-14 15:00:11 +04:00
|
|
|
unmap_snapshot: None,
|
2024-05-02 08:14:21 +04:00
|
|
|
rounded_corner_damage: Default::default(),
|
2024-06-17 09:16:28 +03:00
|
|
|
scale,
|
2024-11-23 11:27:27 +03:00
|
|
|
clock,
|
2023-12-27 21:51:42 +04:00
|
|
|
options,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn update_config(&mut self, scale: f64, options: Rc<Options>) {
|
|
|
|
|
self.scale = scale;
|
2024-05-04 11:11:01 +04:00
|
|
|
self.options = options;
|
|
|
|
|
|
2024-04-24 21:49:07 +04:00
|
|
|
let rules = self.window.rules();
|
|
|
|
|
|
|
|
|
|
let border_config = rules.border.resolve_against(self.options.border);
|
|
|
|
|
self.border.update_config(border_config.into());
|
|
|
|
|
|
2024-04-24 22:17:53 +04:00
|
|
|
let focus_ring_config = rules
|
|
|
|
|
.focus_ring
|
|
|
|
|
.resolve_against(self.options.focus_ring.into());
|
|
|
|
|
self.focus_ring.update_config(focus_ring_config.into());
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-03 21:21:58 +04:00
|
|
|
pub fn update_shaders(&mut self) {
|
|
|
|
|
self.border.update_shaders();
|
|
|
|
|
self.focus_ring.update_shaders();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-28 10:49:31 +04:00
|
|
|
pub fn update_window(&mut self) {
|
|
|
|
|
// FIXME: remove when we can get a fullscreen size right away.
|
2024-06-17 09:16:28 +03:00
|
|
|
if self.fullscreen_size != Size::from((0., 0.)) {
|
2023-12-28 10:49:31 +04:00
|
|
|
self.is_fullscreen = self.window.is_fullscreen();
|
|
|
|
|
}
|
2024-04-13 11:07:23 +04:00
|
|
|
|
|
|
|
|
if let Some(animate_from) = self.window.take_animation_snapshot() {
|
|
|
|
|
let size_from = if let Some(resize) = self.resize_animation.take() {
|
|
|
|
|
// Compute like in animated_window_size(), but using the snapshot geometry (since
|
|
|
|
|
// the current one is already overwritten).
|
|
|
|
|
let mut size = animate_from.size;
|
|
|
|
|
|
|
|
|
|
let val = resize.anim.value();
|
|
|
|
|
let size_from = resize.size_from;
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = size_from.w + (size.w - size_from.w) * val;
|
|
|
|
|
size.h = size_from.h + (size.h - size_from.h) * val;
|
2024-04-13 11:07:23 +04:00
|
|
|
|
|
|
|
|
size
|
|
|
|
|
} else {
|
|
|
|
|
animate_from.size
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
let change = self.window.size().to_f64().to_point() - size_from.to_point();
|
|
|
|
|
let change = f64::max(change.x.abs(), change.y.abs());
|
2024-04-13 11:07:23 +04:00
|
|
|
if change > RESIZE_ANIMATION_THRESHOLD {
|
2024-11-23 11:27:27 +03:00
|
|
|
let anim = Animation::new(
|
2024-11-24 09:41:43 +03:00
|
|
|
self.clock.clone(),
|
2024-11-23 11:27:27 +03:00
|
|
|
0.,
|
|
|
|
|
1.,
|
|
|
|
|
0.,
|
|
|
|
|
self.options.animations.window_resize.anim,
|
|
|
|
|
);
|
2024-04-13 11:07:23 +04:00
|
|
|
self.resize_animation = Some(ResizeAnimation {
|
|
|
|
|
anim,
|
|
|
|
|
size_from,
|
|
|
|
|
snapshot: animate_from,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
self.resize_animation = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
|
|
|
|
|
let rules = self.window.rules();
|
|
|
|
|
let border_config = rules.border.resolve_against(self.options.border);
|
|
|
|
|
self.border.update_config(border_config.into());
|
2024-04-24 22:17:53 +04:00
|
|
|
let focus_ring_config = rules
|
|
|
|
|
.focus_ring
|
|
|
|
|
.resolve_against(self.options.focus_ring.into());
|
|
|
|
|
self.focus_ring.update_config(focus_ring_config.into());
|
2024-05-02 08:14:21 +04:00
|
|
|
|
|
|
|
|
let window_size = self.window_size();
|
|
|
|
|
let radius = rules
|
|
|
|
|
.geometry_corner_radius
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
.fit_to(window_size.w as f32, window_size.h as f32);
|
|
|
|
|
self.rounded_corner_damage.set_corner_radius(radius);
|
|
|
|
|
self.rounded_corner_damage.set_size(window_size);
|
2023-12-28 10:49:31 +04:00
|
|
|
}
|
|
|
|
|
|
2024-11-24 09:41:43 +03:00
|
|
|
pub fn advance_animations(&mut self) {
|
2024-05-15 19:38:29 +04:00
|
|
|
if let Some(open) = &mut self.open_animation {
|
|
|
|
|
if open.is_done() {
|
2024-04-13 11:07:23 +04:00
|
|
|
self.open_animation = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(resize) = &mut self.resize_animation {
|
|
|
|
|
if resize.anim.is_done() {
|
|
|
|
|
self.resize_animation = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 12:44:24 +04:00
|
|
|
if let Some(move_) = &mut self.move_x_animation {
|
|
|
|
|
if move_.anim.is_done() {
|
|
|
|
|
self.move_x_animation = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if let Some(move_) = &mut self.move_y_animation {
|
2024-04-13 11:07:23 +04:00
|
|
|
if move_.anim.is_done() {
|
2024-04-19 12:44:24 +04:00
|
|
|
self.move_y_animation = None;
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
}
|
2024-05-04 11:10:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn are_animations_ongoing(&self) -> bool {
|
|
|
|
|
self.open_animation.is_some()
|
|
|
|
|
|| self.resize_animation.is_some()
|
|
|
|
|
|| self.move_x_animation.is_some()
|
|
|
|
|
|| self.move_y_animation.is_some()
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn update(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
2024-05-01 19:06:08 +04:00
|
|
|
let rules = self.window.rules();
|
|
|
|
|
|
|
|
|
|
let draw_border_with_background = rules
|
2024-03-19 18:22:25 +04:00
|
|
|
.draw_border_with_background
|
|
|
|
|
.unwrap_or_else(|| !self.window.has_ssd());
|
2024-06-17 09:16:28 +03:00
|
|
|
let border_width = self.effective_border_width().unwrap_or(0.);
|
2024-05-01 19:06:08 +04:00
|
|
|
let radius = if self.is_fullscreen {
|
|
|
|
|
CornerRadius::default()
|
|
|
|
|
} else {
|
|
|
|
|
rules
|
|
|
|
|
.geometry_corner_radius
|
|
|
|
|
.map_or(CornerRadius::default(), |radius| {
|
|
|
|
|
radius.expanded_by(border_width as f32)
|
|
|
|
|
})
|
|
|
|
|
};
|
2024-05-04 11:10:02 +04:00
|
|
|
self.border.update_render_elements(
|
2024-05-01 19:06:08 +04:00
|
|
|
self.animated_window_size(),
|
2024-05-04 11:10:02 +04:00
|
|
|
is_active,
|
2024-05-01 19:06:08 +04:00
|
|
|
!draw_border_with_background,
|
2024-05-04 11:10:02 +04:00
|
|
|
Rectangle::from_loc_and_size(
|
|
|
|
|
view_rect.loc - Point::from((border_width, border_width)),
|
|
|
|
|
view_rect.size,
|
|
|
|
|
),
|
2024-05-01 19:06:08 +04:00
|
|
|
radius,
|
2024-06-17 09:16:28 +03:00
|
|
|
self.scale,
|
2024-05-01 19:06:08 +04:00
|
|
|
);
|
2024-02-06 19:47:36 +04:00
|
|
|
|
2024-03-19 18:22:25 +04:00
|
|
|
let draw_focus_ring_with_background = if self.effective_border_width().is_some() {
|
|
|
|
|
false
|
|
|
|
|
} else {
|
|
|
|
|
draw_border_with_background
|
|
|
|
|
};
|
2024-05-01 19:06:08 +04:00
|
|
|
let radius = if self.is_fullscreen {
|
|
|
|
|
CornerRadius::default()
|
|
|
|
|
} else if self.effective_border_width().is_some() {
|
|
|
|
|
radius
|
|
|
|
|
} else {
|
|
|
|
|
rules.geometry_corner_radius.unwrap_or_default()
|
|
|
|
|
}
|
|
|
|
|
.expanded_by(self.focus_ring.width() as f32);
|
2024-05-04 11:10:02 +04:00
|
|
|
self.focus_ring.update_render_elements(
|
2024-05-01 19:06:08 +04:00
|
|
|
self.animated_tile_size(),
|
2024-05-04 11:10:02 +04:00
|
|
|
is_active,
|
2024-05-01 19:06:08 +04:00
|
|
|
!draw_focus_ring_with_background,
|
2024-05-04 11:10:02 +04:00
|
|
|
view_rect,
|
2024-05-01 19:06:08 +04:00
|
|
|
radius,
|
2024-06-17 09:16:28 +03:00
|
|
|
self.scale,
|
2024-05-01 19:06:08 +04:00
|
|
|
);
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn scale(&self) -> f64 {
|
|
|
|
|
self.scale
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn render_offset(&self) -> Point<f64, Logical> {
|
2024-04-13 11:07:23 +04:00
|
|
|
let mut offset = Point::from((0., 0.));
|
|
|
|
|
|
2024-04-19 12:44:24 +04:00
|
|
|
if let Some(move_) = &self.move_x_animation {
|
2024-06-17 09:16:28 +03:00
|
|
|
offset.x += move_.from * move_.anim.value();
|
2024-04-19 12:44:24 +04:00
|
|
|
}
|
|
|
|
|
if let Some(move_) = &self.move_y_animation {
|
2024-06-17 09:16:28 +03:00
|
|
|
offset.y += move_.from * move_.anim.value();
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
offset += self.interactive_move_offset;
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
offset
|
2024-02-07 11:32:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn start_open_animation(&mut self) {
|
2024-05-15 19:38:29 +04:00
|
|
|
self.open_animation = Some(OpenAnimation::new(Animation::new(
|
2024-11-24 09:41:43 +03:00
|
|
|
self.clock.clone(),
|
2024-02-07 17:05:15 +04:00
|
|
|
0.,
|
|
|
|
|
1.,
|
2024-03-05 13:32:30 +04:00
|
|
|
0.,
|
2024-05-15 19:38:29 +04:00
|
|
|
self.options.animations.window_open.anim,
|
|
|
|
|
)));
|
2024-04-09 22:37:10 +04:00
|
|
|
}
|
|
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
pub fn resize_animation(&self) -> Option<&Animation> {
|
|
|
|
|
self.resize_animation.as_ref().map(|resize| &resize.anim)
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn animate_move_from(&mut self, from: Point<f64, Logical>) {
|
2024-04-19 12:44:24 +04:00
|
|
|
self.animate_move_x_from(from.x);
|
|
|
|
|
self.animate_move_y_from(from.y);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn animate_move_x_from(&mut self, from: f64) {
|
2024-04-19 12:44:24 +04:00
|
|
|
self.animate_move_x_from_with_config(from, self.options.animations.window_movement.0);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn animate_move_x_from_with_config(&mut self, from: f64, config: niri_config::Animation) {
|
2024-04-19 12:44:24 +04:00
|
|
|
let current_offset = self.render_offset().x;
|
|
|
|
|
|
|
|
|
|
// Preserve the previous config if ongoing.
|
|
|
|
|
let anim = self.move_x_animation.take().map(|move_| move_.anim);
|
|
|
|
|
let anim = anim
|
2024-11-24 09:41:43 +03:00
|
|
|
.map(|anim| anim.restarted(1., 0., 0.))
|
|
|
|
|
.unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));
|
2024-04-19 12:44:24 +04:00
|
|
|
|
|
|
|
|
self.move_x_animation = Some(MoveAnimation {
|
|
|
|
|
anim,
|
|
|
|
|
from: from + current_offset,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn animate_move_y_from(&mut self, from: f64) {
|
2024-04-19 12:44:24 +04:00
|
|
|
self.animate_move_y_from_with_config(from, self.options.animations.window_movement.0);
|
2024-04-15 21:19:09 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn animate_move_y_from_with_config(&mut self, from: f64, config: niri_config::Animation) {
|
2024-04-19 12:44:24 +04:00
|
|
|
let current_offset = self.render_offset().y;
|
2024-04-13 11:07:23 +04:00
|
|
|
|
2024-04-18 00:30:12 +04:00
|
|
|
// Preserve the previous config if ongoing.
|
2024-04-19 12:44:24 +04:00
|
|
|
let anim = self.move_y_animation.take().map(|move_| move_.anim);
|
2024-04-18 00:30:12 +04:00
|
|
|
let anim = anim
|
2024-11-24 09:41:43 +03:00
|
|
|
.map(|anim| anim.restarted(1., 0., 0.))
|
|
|
|
|
.unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));
|
2024-04-18 00:30:12 +04:00
|
|
|
|
2024-04-19 12:44:24 +04:00
|
|
|
self.move_y_animation = Some(MoveAnimation {
|
2024-04-18 00:30:12 +04:00
|
|
|
anim,
|
2024-04-13 11:07:23 +04:00
|
|
|
from: from + current_offset,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
pub fn stop_move_animations(&mut self) {
|
|
|
|
|
self.move_x_animation = None;
|
|
|
|
|
self.move_y_animation = None;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
pub fn window(&self) -> &W {
|
|
|
|
|
&self.window
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-19 14:41:17 +04:00
|
|
|
pub fn window_mut(&mut self) -> &mut W {
|
|
|
|
|
&mut self.window
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
pub fn into_window(self) -> W {
|
|
|
|
|
self.window
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 18:17:49 +04:00
|
|
|
pub fn is_fullscreen(&self) -> bool {
|
|
|
|
|
self.is_fullscreen
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 21:51:42 +04:00
|
|
|
/// Returns `None` if the border is hidden and `Some(width)` if it should be shown.
|
2024-11-02 09:42:46 +03:00
|
|
|
pub fn effective_border_width(&self) -> Option<f64> {
|
2023-12-27 21:51:42 +04:00
|
|
|
if self.is_fullscreen {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.border.is_off() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(self.border.width())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the location of the window's visual geometry within this Tile.
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn window_loc(&self) -> Point<f64, Logical> {
|
|
|
|
|
let mut loc = Point::from((0., 0.));
|
2023-12-27 21:51:42 +04:00
|
|
|
|
|
|
|
|
// In fullscreen, center the window in the given size.
|
|
|
|
|
if self.is_fullscreen {
|
2024-06-17 09:16:28 +03:00
|
|
|
let window_size = self.window_size();
|
2023-12-27 21:51:42 +04:00
|
|
|
let target_size = self.fullscreen_size;
|
|
|
|
|
|
|
|
|
|
// Windows aren't supposed to be larger than the fullscreen size, but in case we get
|
|
|
|
|
// one, leave it at the top-left as usual.
|
|
|
|
|
if window_size.w < target_size.w {
|
2024-06-17 09:16:28 +03:00
|
|
|
loc.x += (target_size.w - window_size.w) / 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
if window_size.h < target_size.h {
|
2024-06-17 09:16:28 +03:00
|
|
|
loc.y += (target_size.h - window_size.h) / 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
2024-06-17 09:16:28 +03:00
|
|
|
|
|
|
|
|
// Round to physical pixels.
|
|
|
|
|
loc = loc
|
|
|
|
|
.to_physical_precise_round(self.scale)
|
|
|
|
|
.to_logical(self.scale);
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(width) = self.effective_border_width() {
|
|
|
|
|
loc += (width, width).into();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn tile_size(&self) -> Size<f64, Logical> {
|
|
|
|
|
let mut size = self.window_size();
|
2023-12-27 21:51:42 +04:00
|
|
|
|
|
|
|
|
if self.is_fullscreen {
|
|
|
|
|
// Normally we'd just return the fullscreen size here, but this makes things a bit
|
|
|
|
|
// nicer if a fullscreen window is bigger than the fullscreen size for some reason.
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = f64::max(size.w, self.fullscreen_size.w);
|
|
|
|
|
size.h = f64::max(size.h, self.fullscreen_size.h);
|
2023-12-27 21:51:42 +04:00
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(width) = self.effective_border_width() {
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w += width * 2.;
|
|
|
|
|
size.h += width * 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn window_size(&self) -> Size<f64, Logical> {
|
|
|
|
|
let mut size = self.window.size().to_f64();
|
|
|
|
|
size = size
|
|
|
|
|
.to_physical_precise_round(self.scale)
|
|
|
|
|
.to_logical(self.scale);
|
|
|
|
|
size
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
fn animated_window_size(&self) -> Size<f64, Logical> {
|
|
|
|
|
let mut size = self.window_size();
|
2024-04-13 11:07:23 +04:00
|
|
|
|
|
|
|
|
if let Some(resize) = &self.resize_animation {
|
|
|
|
|
let val = resize.anim.value();
|
2024-06-17 09:16:28 +03:00
|
|
|
let size_from = resize.size_from.to_f64();
|
2024-04-13 11:07:23 +04:00
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = f64::max(1., size_from.w + (size.w - size_from.w) * val);
|
|
|
|
|
size.h = f64::max(1., size_from.h + (size.h - size_from.h) * val);
|
|
|
|
|
size = size
|
|
|
|
|
.to_physical_precise_round(self.scale)
|
|
|
|
|
.to_logical(self.scale);
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
fn animated_tile_size(&self) -> Size<f64, Logical> {
|
2024-04-13 11:07:23 +04:00
|
|
|
let mut size = self.animated_window_size();
|
|
|
|
|
|
|
|
|
|
if self.is_fullscreen {
|
|
|
|
|
// Normally we'd just return the fullscreen size here, but this makes things a bit
|
|
|
|
|
// nicer if a fullscreen window is bigger than the fullscreen size for some reason.
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = f64::max(size.w, self.fullscreen_size.w);
|
|
|
|
|
size.h = f64::max(size.h, self.fullscreen_size.h);
|
2024-04-13 11:07:23 +04:00
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(width) = self.effective_border_width() {
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w += width * 2.;
|
|
|
|
|
size.h += width * 2.;
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn buf_loc(&self) -> Point<f64, Logical> {
|
|
|
|
|
let mut loc = Point::from((0., 0.));
|
2023-12-27 21:51:42 +04:00
|
|
|
loc += self.window_loc();
|
2024-06-17 09:16:28 +03:00
|
|
|
loc += self.window.buf_loc().to_f64();
|
2023-12-27 21:51:42 +04:00
|
|
|
loc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_in_input_region(&self, mut point: Point<f64, Logical>) -> bool {
|
|
|
|
|
point -= self.window_loc().to_f64();
|
|
|
|
|
self.window.is_in_input_region(point)
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-28 08:45:12 +04:00
|
|
|
pub fn is_in_activation_region(&self, point: Point<f64, Logical>) -> bool {
|
2024-06-17 09:16:28 +03:00
|
|
|
let activation_region = Rectangle::from_loc_and_size((0., 0.), self.tile_size());
|
|
|
|
|
activation_region.contains(point)
|
2023-12-28 08:45:12 +04:00
|
|
|
}
|
|
|
|
|
|
2024-08-22 14:44:11 +03:00
|
|
|
pub fn request_tile_size(
|
|
|
|
|
&mut self,
|
|
|
|
|
mut size: Size<f64, Logical>,
|
|
|
|
|
animate: bool,
|
|
|
|
|
transaction: Option<Transaction>,
|
|
|
|
|
) {
|
2023-12-27 21:51:42 +04:00
|
|
|
// Can't go through effective_border_width() because we might be fullscreen.
|
|
|
|
|
if !self.border.is_off() {
|
|
|
|
|
let width = self.border.width();
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = f64::max(1., size.w - width * 2.);
|
|
|
|
|
size.h = f64::max(1., size.h - width * 2.);
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
// The size request has to be i32 unfortunately, due to Wayland. We floor here instead of
|
|
|
|
|
// round to avoid situations where proportionally-sized columns don't fit on the screen
|
|
|
|
|
// exactly.
|
2024-08-22 14:44:11 +03:00
|
|
|
self.window
|
|
|
|
|
.request_size(size.to_i32_floor(), animate, transaction);
|
2024-06-17 09:16:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn tile_width_for_window_width(&self, size: f64) -> f64 {
|
|
|
|
|
if self.border.is_off() {
|
|
|
|
|
size
|
|
|
|
|
} else {
|
|
|
|
|
size + self.border.width() * 2.
|
|
|
|
|
}
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn tile_height_for_window_height(&self, size: f64) -> f64 {
|
2023-12-27 21:51:42 +04:00
|
|
|
if self.border.is_off() {
|
|
|
|
|
size
|
|
|
|
|
} else {
|
2024-06-17 09:16:28 +03:00
|
|
|
size + self.border.width() * 2.
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn window_width_for_tile_width(&self, size: f64) -> f64 {
|
2023-12-27 21:51:42 +04:00
|
|
|
if self.border.is_off() {
|
|
|
|
|
size
|
|
|
|
|
} else {
|
2024-06-17 09:16:28 +03:00
|
|
|
size - self.border.width() * 2.
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn window_height_for_tile_height(&self, size: f64) -> f64 {
|
2023-12-27 21:51:42 +04:00
|
|
|
if self.border.is_off() {
|
|
|
|
|
size
|
|
|
|
|
} else {
|
2024-06-17 09:16:28 +03:00
|
|
|
size - self.border.width() * 2.
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn request_fullscreen(&mut self, size: Size<f64, Logical>) {
|
2023-12-27 21:51:42 +04:00
|
|
|
self.fullscreen_backdrop.resize(size);
|
|
|
|
|
self.fullscreen_size = size;
|
2024-06-17 09:16:28 +03:00
|
|
|
self.window.request_fullscreen(size.to_i32_round());
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn min_size(&self) -> Size<f64, Logical> {
|
|
|
|
|
let mut size = self.window.min_size().to_f64();
|
2023-12-27 21:51:42 +04:00
|
|
|
|
|
|
|
|
if let Some(width) = self.effective_border_width() {
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w = f64::max(1., size.w);
|
|
|
|
|
size.h = f64::max(1., size.h);
|
2023-12-28 18:53:08 +04:00
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
size.w += width * 2.;
|
|
|
|
|
size.h += width * 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
pub fn max_size(&self) -> Size<f64, Logical> {
|
|
|
|
|
let mut size = self.window.max_size().to_f64();
|
2023-12-27 21:51:42 +04:00
|
|
|
|
|
|
|
|
if let Some(width) = self.effective_border_width() {
|
2024-06-17 09:16:28 +03:00
|
|
|
if size.w > 0. {
|
|
|
|
|
size.w += width * 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
2024-06-17 09:16:28 +03:00
|
|
|
if size.h > 0. {
|
|
|
|
|
size.h += width * 2.;
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-19 18:22:25 +04:00
|
|
|
pub fn draw_border_with_background(&self) -> bool {
|
|
|
|
|
if self.effective_border_width().is_some() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.window
|
|
|
|
|
.rules()
|
|
|
|
|
.draw_border_with_background
|
|
|
|
|
.unwrap_or_else(|| !self.window.has_ssd())
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-07 11:32:02 +04:00
|
|
|
fn render_inner<R: NiriRenderer>(
|
2023-12-27 21:51:42 +04:00
|
|
|
&self,
|
|
|
|
|
renderer: &mut R,
|
2024-06-17 09:16:28 +03:00
|
|
|
location: Point<f64, Logical>,
|
2023-12-27 21:51:42 +04:00
|
|
|
scale: Scale<f64>,
|
2024-02-06 19:47:36 +04:00
|
|
|
focus_ring: bool,
|
2024-03-24 09:03:59 +04:00
|
|
|
target: RenderTarget,
|
2024-02-05 14:05:08 +04:00
|
|
|
) -> impl Iterator<Item = TileRenderElement<R>> {
|
2024-05-01 19:04:47 +04:00
|
|
|
let _span = tracy_client::span!("Tile::render_inner");
|
|
|
|
|
|
2024-03-24 08:30:26 +04:00
|
|
|
let alpha = if self.is_fullscreen {
|
|
|
|
|
1.
|
|
|
|
|
} else {
|
|
|
|
|
self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.)
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
let window_loc = self.window_loc();
|
2024-06-17 09:16:28 +03:00
|
|
|
let window_size = self.window_size().to_f64();
|
2024-04-13 11:07:23 +04:00
|
|
|
let animated_window_size = self.animated_window_size();
|
|
|
|
|
let window_render_loc = location + window_loc;
|
|
|
|
|
let area = Rectangle::from_loc_and_size(window_render_loc, animated_window_size);
|
|
|
|
|
|
2024-05-04 11:43:56 +04:00
|
|
|
let rules = self.window.rules();
|
|
|
|
|
let clip_to_geometry = !self.is_fullscreen && rules.clip_to_geometry == Some(true);
|
|
|
|
|
let radius = rules.geometry_corner_radius.unwrap_or_default();
|
|
|
|
|
|
2024-04-21 20:10:35 +04:00
|
|
|
// If we're resizing, try to render a shader, or a fallback.
|
|
|
|
|
let mut resize_shader = None;
|
2024-05-01 19:02:22 +04:00
|
|
|
let mut resize_popups = None;
|
2024-04-21 20:10:35 +04:00
|
|
|
let mut resize_fallback = None;
|
2024-04-13 11:07:23 +04:00
|
|
|
|
|
|
|
|
if let Some(resize) = &self.resize_animation {
|
2024-05-01 19:02:22 +04:00
|
|
|
resize_popups = Some(
|
|
|
|
|
self.window
|
|
|
|
|
.render_popups(renderer, window_render_loc, scale, alpha, target)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(Into::into),
|
|
|
|
|
);
|
|
|
|
|
|
2024-05-03 21:21:58 +04:00
|
|
|
if ResizeRenderElement::has_shader(renderer) {
|
2024-05-01 19:02:22 +04:00
|
|
|
let gles_renderer = renderer.as_gles_renderer();
|
|
|
|
|
|
2024-04-13 14:16:07 +04:00
|
|
|
if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) {
|
2024-05-01 19:02:22 +04:00
|
|
|
let window_elements = self.window.render_normal(
|
|
|
|
|
gles_renderer,
|
2024-06-17 09:16:28 +03:00
|
|
|
Point::from((0., 0.)),
|
2024-05-01 19:02:22 +04:00
|
|
|
scale,
|
|
|
|
|
1.,
|
|
|
|
|
target,
|
|
|
|
|
);
|
|
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
let current = render_to_encompassing_texture(
|
|
|
|
|
gles_renderer,
|
|
|
|
|
scale,
|
|
|
|
|
Transform::Normal,
|
|
|
|
|
Fourcc::Abgr8888,
|
|
|
|
|
&window_elements,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| warn!("error rendering window to texture: {err:?}"))
|
|
|
|
|
.ok();
|
|
|
|
|
|
2024-05-04 11:54:52 +04:00
|
|
|
// Clip blocked-out resizes unconditionally because they use solid color render
|
|
|
|
|
// elements.
|
|
|
|
|
let clip_to_geometry = if target
|
|
|
|
|
.should_block_out(resize.snapshot.block_out_from)
|
|
|
|
|
&& target.should_block_out(rules.block_out_from)
|
|
|
|
|
{
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
clip_to_geometry
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-13 11:07:23 +04:00
|
|
|
if let Some((texture_current, _sync_point, texture_current_geo)) = current {
|
2024-04-21 20:10:35 +04:00
|
|
|
let elem = ResizeRenderElement::new(
|
2024-04-13 11:07:23 +04:00
|
|
|
area,
|
|
|
|
|
scale,
|
|
|
|
|
texture_from.clone(),
|
|
|
|
|
resize.snapshot.size,
|
|
|
|
|
(texture_current, texture_current_geo),
|
|
|
|
|
window_size,
|
2024-04-21 20:10:35 +04:00
|
|
|
resize.anim.value() as f32,
|
2024-04-13 11:07:23 +04:00
|
|
|
resize.anim.clamped_value().clamp(0., 1.) as f32,
|
2024-05-04 11:43:56 +04:00
|
|
|
radius,
|
2024-05-01 19:06:08 +04:00
|
|
|
clip_to_geometry,
|
2024-04-13 11:07:23 +04:00
|
|
|
alpha,
|
2024-04-29 09:23:03 +04:00
|
|
|
);
|
2024-05-01 19:06:08 +04:00
|
|
|
// FIXME: with split popups, this will use the resize element ID for
|
|
|
|
|
// popups, but we want the real IDs.
|
2024-04-13 11:07:23 +04:00
|
|
|
self.window
|
|
|
|
|
.set_offscreen_element_id(Some(elem.id().clone()));
|
2024-04-21 20:10:35 +04:00
|
|
|
resize_shader = Some(elem.into());
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-21 20:10:35 +04:00
|
|
|
if resize_shader.is_none() {
|
2024-04-13 11:07:23 +04:00
|
|
|
let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]);
|
2024-04-21 20:10:35 +04:00
|
|
|
resize_fallback = Some(
|
2024-04-13 11:07:23 +04:00
|
|
|
SolidColorRenderElement::from_buffer(
|
|
|
|
|
&fallback_buffer,
|
2024-06-17 09:16:28 +03:00
|
|
|
area.loc,
|
2024-04-13 11:07:23 +04:00
|
|
|
alpha,
|
|
|
|
|
Kind::Unspecified,
|
|
|
|
|
)
|
|
|
|
|
.into(),
|
|
|
|
|
);
|
|
|
|
|
self.window.set_offscreen_element_id(None);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're not resizing, render the window itself.
|
2024-05-01 19:06:08 +04:00
|
|
|
let mut window_surface = None;
|
|
|
|
|
let mut window_popups = None;
|
2024-05-02 08:14:21 +04:00
|
|
|
let mut rounded_corner_damage = None;
|
2024-04-21 20:10:35 +04:00
|
|
|
if resize_shader.is_none() && resize_fallback.is_none() {
|
2024-05-01 19:06:08 +04:00
|
|
|
let window = self
|
|
|
|
|
.window
|
|
|
|
|
.render(renderer, window_render_loc, scale, alpha, target);
|
|
|
|
|
|
|
|
|
|
let geo = Rectangle::from_loc_and_size(window_render_loc, window_size);
|
2024-05-04 11:43:56 +04:00
|
|
|
let radius = radius.fit_to(window_size.w as f32, window_size.h as f32);
|
2024-05-01 19:06:08 +04:00
|
|
|
|
|
|
|
|
let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned();
|
2024-05-03 21:21:58 +04:00
|
|
|
let has_border_shader = BorderRenderElement::has_shader(renderer);
|
2024-05-01 19:06:08 +04:00
|
|
|
|
2024-05-02 08:14:21 +04:00
|
|
|
if clip_to_geometry && clip_shader.is_some() {
|
|
|
|
|
let damage = self.rounded_corner_damage.element();
|
|
|
|
|
rounded_corner_damage = Some(damage.with_location(window_render_loc).into());
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:06:08 +04:00
|
|
|
window_surface = Some(window.normal.into_iter().map(move |elem| match elem {
|
|
|
|
|
LayoutElementRenderElement::Wayland(elem) => {
|
|
|
|
|
// If we should clip to geometry, render a clipped window.
|
|
|
|
|
if clip_to_geometry {
|
|
|
|
|
if let Some(shader) = clip_shader.clone() {
|
|
|
|
|
if ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radius) {
|
|
|
|
|
return ClippedSurfaceRenderElement::new(
|
|
|
|
|
elem,
|
|
|
|
|
scale,
|
|
|
|
|
geo,
|
|
|
|
|
shader.clone(),
|
|
|
|
|
radius,
|
|
|
|
|
)
|
|
|
|
|
.into();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, render it normally.
|
|
|
|
|
LayoutElementRenderElement::Wayland(elem).into()
|
|
|
|
|
}
|
|
|
|
|
LayoutElementRenderElement::SolidColor(elem) => {
|
|
|
|
|
// In this branch we're rendering a blocked-out window with a solid
|
|
|
|
|
// color. We need to render it with a rounded corner shader even if
|
|
|
|
|
// clip_to_geometry is false, because in this case we're assuming that
|
|
|
|
|
// the unclipped window CSD already has corners rounded to the
|
|
|
|
|
// user-provided radius, so our blocked-out rendering should match that
|
|
|
|
|
// radius.
|
2024-05-03 21:21:58 +04:00
|
|
|
if radius != CornerRadius::default() && has_border_shader {
|
|
|
|
|
return BorderRenderElement::new(
|
2024-05-04 20:13:53 +04:00
|
|
|
geo.size,
|
2024-06-17 09:16:28 +03:00
|
|
|
Rectangle::from_loc_and_size((0., 0.), geo.size),
|
2024-07-16 10:22:03 +03:00
|
|
|
GradientInterpolation::default(),
|
2024-08-20 11:43:32 +03:00
|
|
|
Color::from_color32f(elem.color()),
|
|
|
|
|
Color::from_color32f(elem.color()),
|
2024-05-03 21:21:58 +04:00
|
|
|
0.,
|
2024-06-17 09:16:28 +03:00
|
|
|
Rectangle::from_loc_and_size((0., 0.), geo.size),
|
2024-05-03 21:21:58 +04:00
|
|
|
0.,
|
|
|
|
|
radius,
|
2024-06-18 12:19:23 +03:00
|
|
|
scale.x as f32,
|
2024-05-03 21:21:58 +04:00
|
|
|
)
|
2024-05-04 20:13:53 +04:00
|
|
|
.with_location(geo.loc)
|
2024-05-03 21:21:58 +04:00
|
|
|
.into();
|
2024-05-01 19:06:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, render the solid color as is.
|
|
|
|
|
LayoutElementRenderElement::SolidColor(elem).into()
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
window_popups = Some(window.popups.into_iter().map(Into::into));
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:02:22 +04:00
|
|
|
let rv = resize_popups
|
2024-02-05 07:58:33 +04:00
|
|
|
.into_iter()
|
2024-05-01 19:02:22 +04:00
|
|
|
.flatten()
|
|
|
|
|
.chain(resize_shader)
|
2024-04-21 20:10:35 +04:00
|
|
|
.chain(resize_fallback)
|
2024-05-01 19:06:08 +04:00
|
|
|
.chain(window_popups.into_iter().flatten())
|
2024-05-02 08:14:21 +04:00
|
|
|
.chain(rounded_corner_damage)
|
2024-05-01 19:06:08 +04:00
|
|
|
.chain(window_surface.into_iter().flatten());
|
2024-02-05 07:58:33 +04:00
|
|
|
|
2024-04-28 06:48:48 +04:00
|
|
|
let elem = self.is_fullscreen.then(|| {
|
|
|
|
|
SolidColorRenderElement::from_buffer(
|
|
|
|
|
&self.fullscreen_backdrop,
|
2024-06-17 09:16:28 +03:00
|
|
|
location,
|
2024-04-28 06:48:48 +04:00
|
|
|
1.,
|
|
|
|
|
Kind::Unspecified,
|
|
|
|
|
)
|
|
|
|
|
.into()
|
|
|
|
|
});
|
|
|
|
|
let rv = rv.chain(elem);
|
|
|
|
|
|
2024-02-21 11:08:48 +04:00
|
|
|
let elem = self.effective_border_width().map(|width| {
|
|
|
|
|
self.border
|
2024-06-17 09:16:28 +03:00
|
|
|
.render(renderer, location + Point::from((width, width)))
|
2024-02-21 11:08:48 +04:00
|
|
|
.map(Into::into)
|
2024-02-05 07:58:33 +04:00
|
|
|
});
|
|
|
|
|
let rv = rv.chain(elem.into_iter().flatten());
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
let elem = focus_ring.then(|| self.focus_ring.render(renderer, location).map(Into::into));
|
2024-04-28 06:48:48 +04:00
|
|
|
rv.chain(elem.into_iter().flatten())
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
2024-02-07 11:32:02 +04:00
|
|
|
|
|
|
|
|
pub fn render<R: NiriRenderer>(
|
|
|
|
|
&self,
|
|
|
|
|
renderer: &mut R,
|
2024-06-17 09:16:28 +03:00
|
|
|
location: Point<f64, Logical>,
|
2024-02-07 11:32:02 +04:00
|
|
|
scale: Scale<f64>,
|
|
|
|
|
focus_ring: bool,
|
2024-03-24 09:03:59 +04:00
|
|
|
target: RenderTarget,
|
2024-02-07 11:32:02 +04:00
|
|
|
) -> impl Iterator<Item = TileRenderElement<R>> {
|
2024-05-01 19:00:54 +04:00
|
|
|
let _span = tracy_client::span!("Tile::render");
|
|
|
|
|
|
2024-05-15 19:38:29 +04:00
|
|
|
let mut open_anim_elem = None;
|
|
|
|
|
let mut window_elems = None;
|
|
|
|
|
|
|
|
|
|
if let Some(open) = &self.open_animation {
|
2024-02-07 11:32:02 +04:00
|
|
|
let renderer = renderer.as_gles_renderer();
|
2024-05-15 19:38:29 +04:00
|
|
|
let elements =
|
2024-06-17 09:16:28 +03:00
|
|
|
self.render_inner(renderer, Point::from((0., 0.)), scale, focus_ring, target);
|
2024-02-07 11:32:02 +04:00
|
|
|
let elements = elements.collect::<Vec<TileRenderElement<_>>>();
|
2024-05-15 19:38:29 +04:00
|
|
|
match open.render(renderer, &elements, self.tile_size(), location, scale) {
|
|
|
|
|
Ok(elem) => {
|
|
|
|
|
self.window()
|
|
|
|
|
.set_offscreen_element_id(Some(elem.id().clone()));
|
|
|
|
|
open_anim_elem = Some(elem.into());
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
warn!("error rendering window opening animation: {err:?}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 11:32:02 +04:00
|
|
|
|
2024-05-15 19:38:29 +04:00
|
|
|
if open_anim_elem.is_none() {
|
2024-02-07 11:32:02 +04:00
|
|
|
self.window().set_offscreen_element_id(None);
|
2024-05-15 19:38:29 +04:00
|
|
|
window_elems = Some(self.render_inner(renderer, location, scale, focus_ring, target));
|
2024-02-07 11:32:02 +04:00
|
|
|
}
|
2024-05-15 19:38:29 +04:00
|
|
|
|
|
|
|
|
open_anim_elem
|
|
|
|
|
.into_iter()
|
|
|
|
|
.chain(window_elems.into_iter().flatten())
|
2024-02-07 11:32:02 +04:00
|
|
|
}
|
2024-04-09 22:37:10 +04:00
|
|
|
|
2024-05-04 11:37:58 +04:00
|
|
|
pub fn store_unmap_snapshot_if_empty(
|
|
|
|
|
&mut self,
|
|
|
|
|
renderer: &mut GlesRenderer,
|
|
|
|
|
scale: Scale<f64>,
|
|
|
|
|
) {
|
2024-05-14 15:00:11 +04:00
|
|
|
if self.unmap_snapshot.is_some() {
|
2024-05-01 19:00:11 +04:00
|
|
|
return;
|
2024-04-10 08:53:35 +04:00
|
|
|
}
|
2024-04-09 22:37:10 +04:00
|
|
|
|
2024-05-14 15:00:11 +04:00
|
|
|
self.unmap_snapshot = Some(self.render_snapshot(renderer, scale));
|
2024-04-10 08:53:35 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:00:11 +04:00
|
|
|
fn render_snapshot(
|
2024-04-10 08:53:35 +04:00
|
|
|
&self,
|
|
|
|
|
renderer: &mut GlesRenderer,
|
|
|
|
|
scale: Scale<f64>,
|
2024-05-01 19:00:11 +04:00
|
|
|
) -> TileRenderSnapshot {
|
|
|
|
|
let _span = tracy_client::span!("Tile::render_snapshot");
|
|
|
|
|
|
2024-05-12 08:42:43 +04:00
|
|
|
let contents = self.render(
|
2024-05-01 19:00:11 +04:00
|
|
|
renderer,
|
2024-06-17 09:16:28 +03:00
|
|
|
Point::from((0., 0.)),
|
2024-05-01 19:00:11 +04:00
|
|
|
scale,
|
|
|
|
|
false,
|
|
|
|
|
RenderTarget::Output,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// A bit of a hack to render blocked out as for screencast, but I think it's fine here.
|
2024-05-12 08:42:43 +04:00
|
|
|
let blocked_out_contents = self.render(
|
2024-05-01 19:00:11 +04:00
|
|
|
renderer,
|
2024-06-17 09:16:28 +03:00
|
|
|
Point::from((0., 0.)),
|
2024-05-01 19:00:11 +04:00
|
|
|
scale,
|
|
|
|
|
false,
|
|
|
|
|
RenderTarget::Screencast,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
RenderSnapshot {
|
|
|
|
|
contents: contents.collect(),
|
|
|
|
|
blocked_out_contents: blocked_out_contents.collect(),
|
|
|
|
|
block_out_from: self.window.rules().block_out_from,
|
2024-04-13 18:05:56 +04:00
|
|
|
size: self.animated_tile_size(),
|
2024-04-13 14:16:07 +04:00
|
|
|
texture: Default::default(),
|
|
|
|
|
blocked_out_texture: Default::default(),
|
2024-05-01 19:00:11 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-14 15:00:11 +04:00
|
|
|
pub fn take_unmap_snapshot(&mut self) -> Option<TileRenderSnapshot> {
|
2024-05-01 19:00:11 +04:00
|
|
|
self.unmap_snapshot.take()
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
|
|
|
|
}
|