From 73cc0079d628e66b1d64168ed1a10b13b23ab5d0 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 4 May 2024 11:10:02 +0400 Subject: Split update_render_elements() from advance_animations() advance_animations() is called from places like input, whereas update_render_elements() is strictly for rendering. --- src/layout/focus_ring.rs | 189 ++++++++++++++++++++++------------------------- src/layout/mod.rs | 33 +++++++-- src/layout/monitor.rs | 33 ++++++++- src/layout/tile.rs | 62 +++++++--------- src/layout/workspace.rs | 55 +++++++++----- 5 files changed, 208 insertions(+), 164 deletions(-) (limited to 'src/layout') diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs index a0d0acfe..b198e838 100644 --- a/src/layout/focus_ring.rs +++ b/src/layout/focus_ring.rs @@ -1,9 +1,8 @@ -use std::cell::RefCell; use std::cmp::{max, min}; use std::iter::zip; use arrayvec::ArrayVec; -use niri_config::{CornerRadius, GradientRelativeTo}; +use niri_config::{CornerRadius, Gradient, GradientRelativeTo}; use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; use smithay::backend::renderer::element::Kind; use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; @@ -17,11 +16,10 @@ pub struct FocusRing { buffers: [SolidColorBuffer; 8], locations: [Point; 8], sizes: [Size; 8], - borders: RefCell<[BorderRenderElement; 8]>, + borders: [BorderRenderElement; 8], full_size: Size, - is_active: bool, is_border: bool, - radius: CornerRadius, + use_border_shader: bool, config: niri_config::FocusRing, } @@ -40,9 +38,8 @@ impl FocusRing { sizes: Default::default(), borders: Default::default(), full_size: Default::default(), - is_active: false, is_border: false, - radius: Default::default(), + use_border_shader: false, config, } } @@ -52,18 +49,64 @@ impl FocusRing { } pub fn update_shaders(&mut self) { - let mut borders = self.borders.borrow_mut(); - for elem in &mut *borders { + for elem in &mut self.borders { elem.damage_all(); } } - pub fn update(&mut self, win_size: Size, is_border: bool, radius: CornerRadius) { + pub fn update_render_elements( + &mut self, + win_size: Size, + is_active: bool, + is_border: bool, + view_rect: Rectangle, + radius: CornerRadius, + ) { let width = i32::from(self.config.width); self.full_size = win_size + Size::from((width * 2, width * 2)); + let color = if is_active { + self.config.active_color + } else { + self.config.inactive_color + }; + + for buf in &mut self.buffers { + buf.set_color(color.into()); + } + let radius = radius.fit_to(self.full_size.w as f32, self.full_size.h as f32); + let gradient = if is_active { + self.config.active_gradient + } else { + self.config.inactive_gradient + }; + + self.use_border_shader = radius != CornerRadius::default() || gradient.is_some(); + + // Set the defaults for solid color + rounded corners. + let gradient = gradient.unwrap_or(Gradient { + from: color, + to: color, + angle: 0, + relative_to: GradientRelativeTo::Window, + }); + + let full_rect = Rectangle::from_loc_and_size((-width, -width), self.full_size); + let gradient_area = match gradient.relative_to { + GradientRelativeTo::Window => full_rect, + GradientRelativeTo::WorkspaceView => view_rect, + }; + + let rounded_corner_border_width = if self.is_border { + // HACK: increase the border width used for the inner rounded corners a tiny bit to + // reduce background bleed. + width as f32 + 0.5 + } else { + 0. + }; + if is_border { let top_left = max(width, radius.top_left.ceil() as i32); let top_right = min( @@ -121,28 +164,40 @@ impl FocusRing { for (buf, size) in zip(&mut self.buffers, self.sizes) { buf.resize(size); } + + for (border, (loc, size)) in zip(&mut self.borders, zip(self.locations, self.sizes)) { + border.update( + size, + Rectangle::from_loc_and_size(gradient_area.loc - loc, gradient_area.size), + gradient.from.into(), + gradient.to.into(), + ((gradient.angle as f32) - 90.).to_radians(), + Rectangle::from_loc_and_size(full_rect.loc - loc, full_rect.size), + rounded_corner_border_width, + radius, + ); + } } else { self.sizes[0] = self.full_size; self.buffers[0].resize(self.sizes[0]); self.locations[0] = Point::from((-width, -width)); - } - - self.is_border = is_border; - self.radius = radius; - } - pub fn set_active(&mut self, is_active: bool) { - let color = if is_active { - self.config.active_color.into() - } else { - self.config.inactive_color.into() - }; - - for buf in &mut self.buffers { - buf.set_color(color); + self.borders[0].update( + self.sizes[0], + Rectangle::from_loc_and_size( + gradient_area.loc - self.locations[0], + gradient_area.size, + ), + gradient.from.into(), + gradient.to.into(), + ((gradient.angle as f32) - 90.).to_radians(), + Rectangle::from_loc_and_size(full_rect.loc - self.locations[0], full_rect.size), + rounded_corner_border_width, + radius, + ); } - self.is_active = is_active; + self.is_border = is_border; } pub fn render( @@ -150,7 +205,6 @@ impl FocusRing { renderer: &mut impl NiriRenderer, location: Point, scale: Scale, - view_size: Size, ) -> impl Iterator { let mut rv = ArrayVec::<_, 8>::new(); @@ -165,75 +219,12 @@ impl FocusRing { return rv.into_iter(); } - let gradient = if self.is_active { - self.config.active_gradient - } else { - self.config.inactive_gradient - }; - let color = if self.is_active { - self.config.active_color - } else { - self.config.inactive_color - }; - - let offset = Point::from((border_width, border_width)); - let full_rect = Rectangle::from_loc_and_size(location - offset, self.full_size); - let view_rect = Rectangle::from_loc_and_size((0, 0), view_size); - - let border_width = if self.is_border { - // HACK: increase the border width used for the inner rounded corners a tiny bit to - // reduce background bleed. - border_width as f32 + 0.5 / scale.x as f32 - } else { - 0. - }; let has_border_shader = BorderRenderElement::has_shader(renderer); - let mut borders = self.borders.borrow_mut(); - - let mut push = |buffer, - border: &mut BorderRenderElement, - location: Point, - size: Size| { - let full_rect = Rectangle::from_loc_and_size(full_rect.loc - location, full_rect.size); - let view_rect = Rectangle::from_loc_and_size(view_rect.loc - location, view_rect.size); - - let elem = if has_border_shader { - if let Some(gradient) = gradient { - let gradient_area = match gradient.relative_to { - GradientRelativeTo::Window => full_rect, - GradientRelativeTo::WorkspaceView => view_rect, - }; - border.update( - size, - gradient_area, - gradient.from.into(), - gradient.to.into(), - ((gradient.angle as f32) - 90.).to_radians(), - full_rect, - border_width, - self.radius, - ); - Some(border.clone().with_location(location).into()) - } else if self.radius != CornerRadius::default() { - border.update( - size, - full_rect, - color.into(), - color.into(), - 0., - full_rect, - border_width, - self.radius, - ); - Some(border.clone().with_location(location).into()) - } else { - None - } - } else { - None - }; - let elem = elem.unwrap_or_else(|| { + let mut push = |buffer, border: &BorderRenderElement, location: Point| { + let elem = if self.use_border_shader && has_border_shader { + border.clone().with_location(location).into() + } else { SolidColorRenderElement::from_buffer( buffer, location.to_physical_precise_round(scale), @@ -242,23 +233,19 @@ impl FocusRing { Kind::Unspecified, ) .into() - }); + }; rv.push(elem); }; if self.is_border { - for ((buf, border), (loc, size)) in zip( - zip(&self.buffers, &mut *borders), - zip(self.locations, self.sizes), - ) { - push(buf, border, location + loc, size); + for ((buf, border), loc) in zip(zip(&self.buffers, &self.borders), self.locations) { + push(buf, border, location + loc); } } else { push( &self.buffers[0], - &mut borders[0], + &self.borders[0], location + self.locations[0], - self.sizes[0], ); } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b670b9f5..b93dfb4d 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1354,23 +1354,40 @@ impl Layout { let _span = tracy_client::span!("Layout::advance_animations"); match &mut self.monitor_set { - MonitorSet::Normal { - monitors, - active_monitor_idx, - .. - } => { - for (idx, mon) in monitors.iter_mut().enumerate() { - mon.advance_animations(current_time, idx == *active_monitor_idx); + MonitorSet::Normal { monitors, .. } => { + for mon in monitors { + mon.advance_animations(current_time); } } MonitorSet::NoOutputs { workspaces, .. } => { for ws in workspaces { - ws.advance_animations(current_time, false); + ws.advance_animations(current_time); } } } } + pub fn update_render_elements(&mut self, output: &Output) { + let _span = tracy_client::span!("Layout::update_render_elements"); + + let MonitorSet::Normal { + monitors, + active_monitor_idx, + .. + } = &mut self.monitor_set + else { + error!("update_render_elements called with no monitors"); + return; + }; + + for (idx, mon) in monitors.iter_mut().enumerate() { + if mon.output == *output { + mon.update_render_elements(idx == *active_monitor_idx); + return; + } + } + } + pub fn update_shaders(&mut self) { match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => { diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index f1e8d542..8d695139 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -523,7 +523,7 @@ impl Monitor { Some(column.tiles[column.active_tile_idx].window()) } - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { + pub fn advance_animations(&mut self, current_time: Duration) { if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch { anim.set_current_time(current_time); if anim.is_done() { @@ -533,7 +533,7 @@ impl Monitor { } for ws in &mut self.workspaces { - ws.advance_animations(current_time, is_active); + ws.advance_animations(current_time); } } @@ -552,6 +552,35 @@ impl Monitor { .any(|ws| ws.are_transitions_ongoing()) } + pub fn update_render_elements(&mut self, is_active: bool) { + match &self.workspace_switch { + Some(switch) => { + let render_idx = switch.current_idx(); + let before_idx = render_idx.floor(); + let after_idx = render_idx.ceil(); + + if after_idx < 0. || before_idx as usize >= self.workspaces.len() { + return; + } + + let after_idx = after_idx as usize; + if after_idx < self.workspaces.len() { + self.workspaces[after_idx].update_render_elements(is_active); + + if before_idx < 0. { + return; + } + } + + let before_idx = before_idx as usize; + self.workspaces[before_idx].update_render_elements(is_active); + } + None => { + self.workspaces[self.active_workspace_idx].update_render_elements(is_active); + } + } + } + pub fn update_config(&mut self, options: Rc) { for ws in &mut self.workspaces { ws.update_config(options.clone()); diff --git a/src/layout/tile.rs b/src/layout/tile.rs index b5b40df0..be9829a9 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -201,7 +201,7 @@ impl Tile { self.rounded_corner_damage.set_size(window_size); } - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { + pub fn advance_animations(&mut self, current_time: Duration) { if let Some(anim) = &mut self.open_animation { anim.set_current_time(current_time); if anim.is_done() { @@ -228,6 +228,17 @@ impl Tile { self.move_y_animation = None; } } + } + + 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() + } + + pub fn update(&mut self, is_active: bool, mut view_rect: Rectangle) { + view_rect.loc -= self.render_offset(); let rules = self.window.rules(); @@ -244,12 +255,16 @@ impl Tile { radius.expanded_by(border_width as f32) }) }; - self.border.update( + self.border.update_render_elements( self.animated_window_size(), + is_active, !draw_border_with_background, + Rectangle::from_loc_and_size( + view_rect.loc - Point::from((border_width, border_width)), + view_rect.size, + ), radius, ); - self.border.set_active(is_active); let draw_focus_ring_with_background = if self.effective_border_width().is_some() { false @@ -264,19 +279,13 @@ impl Tile { rules.geometry_corner_radius.unwrap_or_default() } .expanded_by(self.focus_ring.width() as f32); - self.focus_ring.update( + self.focus_ring.update_render_elements( self.animated_tile_size(), + is_active, !draw_focus_ring_with_background, + view_rect, radius, ); - self.focus_ring.set_active(is_active); - } - - 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() } pub fn render_offset(&self) -> Point { @@ -568,7 +577,6 @@ impl Tile { renderer: &mut R, location: Point, scale: Scale, - view_size: Size, focus_ring: bool, target: RenderTarget, ) -> impl Iterator> { @@ -766,19 +774,14 @@ impl Tile { let elem = self.effective_border_width().map(|width| { self.border - .render( - renderer, - location + Point::from((width, width)), - scale, - view_size, - ) + .render(renderer, location + Point::from((width, width)), scale) .map(Into::into) }); let rv = rv.chain(elem.into_iter().flatten()); let elem = focus_ring.then(|| { self.focus_ring - .render(renderer, location, scale, view_size) + .render(renderer, location, scale) .map(Into::into) }); rv.chain(elem.into_iter().flatten()) @@ -789,7 +792,6 @@ impl Tile { renderer: &mut R, location: Point, scale: Scale, - view_size: Size, focus_ring: bool, target: RenderTarget, ) -> impl Iterator> { @@ -797,8 +799,7 @@ impl Tile { if let Some(anim) = &self.open_animation { let renderer = renderer.as_gles_renderer(); - let elements = - self.render_inner(renderer, location, scale, view_size, focus_ring, target); + let elements = self.render_inner(renderer, location, scale, focus_ring, target); let elements = elements.collect::>>(); let elem = OffscreenRenderElement::new( @@ -826,31 +827,24 @@ impl Tile { } else { self.window().set_offscreen_element_id(None); - let elements = - self.render_inner(renderer, location, scale, view_size, focus_ring, target); + let elements = self.render_inner(renderer, location, scale, focus_ring, target); None.into_iter().chain(Some(elements).into_iter().flatten()) } } - pub fn store_unmap_snapshot_if_empty( - &self, - renderer: &mut GlesRenderer, - scale: Scale, - view_size: Size, - ) { + pub fn store_unmap_snapshot_if_empty(&self, renderer: &mut GlesRenderer, scale: Scale) { let mut snapshot = self.unmap_snapshot.borrow_mut(); if snapshot.is_some() { return; } - *snapshot = Some(self.render_snapshot(renderer, scale, view_size)); + *snapshot = Some(self.render_snapshot(renderer, scale)); } fn render_snapshot( &self, renderer: &mut GlesRenderer, scale: Scale, - view_size: Size, ) -> TileRenderSnapshot { let _span = tracy_client::span!("Tile::render_snapshot"); @@ -858,7 +852,6 @@ impl Tile { renderer, Point::from((0, 0)), scale, - view_size, false, RenderTarget::Output, ); @@ -868,7 +861,6 @@ impl Tile { renderer, Point::from((0, 0)), scale, - view_size, false, RenderTarget::Screencast, ); diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 3205d82b..05d5f280 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -289,7 +289,7 @@ impl Workspace { self.id } - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { + pub fn advance_animations(&mut self, current_time: Duration) { if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { anim.set_current_time(current_time); self.view_offset = anim.value().round() as i32; @@ -300,9 +300,8 @@ impl Workspace { self.view_offset = gesture.current_view_offset.round() as i32; } - for (col_idx, col) in self.columns.iter_mut().enumerate() { - let is_active = is_active && col_idx == self.active_column_idx; - col.advance_animations(current_time, is_active); + for col in &mut self.columns { + col.advance_animations(current_time); } self.closing_windows.retain_mut(|closing| { @@ -325,6 +324,16 @@ impl Workspace { || !self.closing_windows.is_empty() } + pub fn update_render_elements(&mut self, is_active: bool) { + let mut view_rect = Rectangle::from_loc_and_size((self.view_pos(), 0), self.view_size); + + for (col_idx, col) in self.columns.iter_mut().enumerate() { + let is_active = is_active && col_idx == self.active_column_idx; + col.update_render_elements(is_active, view_rect); + view_rect.loc.x -= col.width() + self.options.gaps; + } + } + pub fn update_config(&mut self, options: Rc) { for column in &mut self.columns { column.update_config(options.clone()); @@ -1198,7 +1207,7 @@ impl Workspace { .map(|o| Scale::from(o.current_scale().fractional_scale())) .unwrap_or(Scale::from(1.)); - tile.store_unmap_snapshot_if_empty(renderer, output_scale, self.view_size); + tile.store_unmap_snapshot_if_empty(renderer, output_scale); } pub fn clear_unmap_snapshot(&mut self, window: &W::Id) { @@ -1947,15 +1956,8 @@ impl Workspace { first = false; rv.extend( - tile.render( - renderer, - tile_pos, - output_scale, - self.view_size, - focus_ring, - target, - ) - .map(Into::into), + tile.render(renderer, tile_pos, output_scale, focus_ring, target) + .map(Into::into), ); } @@ -2298,7 +2300,7 @@ impl Column { self.update_tile_sizes(true); } - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { + pub fn advance_animations(&mut self, current_time: Duration) { match &mut self.move_animation { Some(anim) => { anim.set_current_time(current_time); @@ -2309,9 +2311,8 @@ impl Column { None => (), } - for (tile_idx, tile) in self.tiles.iter_mut().enumerate() { - let is_active = is_active && tile_idx == self.active_tile_idx; - tile.advance_animations(current_time, is_active); + for tile in &mut self.tiles { + tile.advance_animations(current_time); } } @@ -2319,6 +2320,24 @@ impl Column { self.move_animation.is_some() || self.tiles.iter().any(Tile::are_animations_ongoing) } + pub fn update_render_elements( + &mut self, + is_active: bool, + mut view_rect: Rectangle, + ) { + view_rect.loc -= self.render_offset(); + + if !self.is_fullscreen { + view_rect.loc.y -= self.working_area.loc.y + self.options.gaps; + } + + for (tile_idx, tile) in self.tiles.iter_mut().enumerate() { + let is_active = is_active && tile_idx == self.active_tile_idx; + tile.update(is_active, view_rect); + view_rect.loc.y -= tile.tile_size().h + self.options.gaps; + } + } + pub fn render_offset(&self) -> Point { let mut offset = Point::from((0., 0.)); -- cgit