diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-17 09:16:28 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-06-18 14:01:28 +0300 |
| commit | 1dae45c58d7eabeda21ef490d712915890bf6cff (patch) | |
| tree | 62c473ab1662a1161ed522517ea57b7bd8db340c /src/layout | |
| parent | 997119c44338ad96a40b4a1d6e958f77062a37ef (diff) | |
| download | niri-1dae45c58d7eabeda21ef490d712915890bf6cff.tar.gz niri-1dae45c58d7eabeda21ef490d712915890bf6cff.tar.bz2 niri-1dae45c58d7eabeda21ef490d712915890bf6cff.zip | |
Refactor layout to fractional-logical
Lets borders, gaps, and everything else stay pixel-perfect even with
fractional scale. Allows setting fractional border widths, gaps,
struts.
See the new wiki .md for more details.
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/closing_window.rs | 25 | ||||
| -rw-r--r-- | src/layout/focus_ring.rs | 73 | ||||
| -rw-r--r-- | src/layout/mod.rs | 204 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 56 | ||||
| -rw-r--r-- | src/layout/opening_window.rs | 18 | ||||
| -rw-r--r-- | src/layout/tile.rs | 221 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 560 |
7 files changed, 708 insertions, 449 deletions
diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs index 8945d2aa..06b5927c 100644 --- a/src/layout/closing_window.rs +++ b/src/layout/closing_window.rs @@ -34,10 +34,10 @@ pub struct ClosingWindow { block_out_from: Option<BlockOutFrom>, /// Size of the window geometry. - geo_size: Size<i32, Logical>, + geo_size: Size<f64, Logical>, /// Position in the workspace. - pos: Point<i32, Logical>, + pos: Point<f64, Logical>, /// How much the texture should be offset. buffer_offset: Point<f64, Logical>, @@ -64,8 +64,8 @@ impl ClosingWindow { renderer: &mut GlesRenderer, snapshot: RenderSnapshot<E, E>, scale: Scale<f64>, - geo_size: Size<i32, Logical>, - pos: Point<i32, Logical>, + geo_size: Size<f64, Logical>, + pos: Point<f64, Logical>, anim: Animation, ) -> anyhow::Result<Self> { let _span = tracy_client::span!("ClosingWindow::new"); @@ -123,7 +123,7 @@ impl ClosingWindow { pub fn render( &self, renderer: &mut GlesRenderer, - view_rect: Rectangle<i32, Logical>, + view_rect: Rectangle<f64, Logical>, scale: Scale<f64>, target: RenderTarget, ) -> ClosingWindowRenderElement { @@ -140,7 +140,12 @@ impl ClosingWindow { 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); - let geo_loc = Vec2::new(self.pos.x as f32, self.pos.y as f32); + // Round to physical pixels relative to the view position. This is similar to what + // happens when rendering normal windows. + let relative = self.pos - view_rect.loc; + let pos = view_rect.loc + relative.to_physical_precise_round(scale).to_logical(scale); + + let geo_loc = Vec2::new(pos.x as f32, pos.y as f32); let geo_size = Vec2::new(self.geo_size.w as f32, self.geo_size.h as f32); let input_to_geo = Mat3::from_scale(area_size / geo_size) @@ -171,7 +176,7 @@ impl ClosingWindow { HashMap::from([(String::from("niri_tex"), buffer.texture().clone())]), Kind::Unspecified, ) - .with_location(Point::from((0, 0))) + .with_location(Point::from((0., 0.))) .into(); } @@ -186,15 +191,15 @@ impl ClosingWindow { let elem = PrimaryGpuTextureRenderElement(elem); - let center = self.geo_size.to_point().to_f64().downscale(2.); + let center = self.geo_size.to_point().downscale(2.); let elem = RescaleRenderElement::from_element( elem, (center - offset).to_physical_precise_round(scale), ((1. - clamped_progress) / 5. + 0.8).max(0.), ); - let mut location = self.pos.to_f64() + offset; - location.x -= view_rect.loc.x as f64; + let mut location = self.pos + offset; + location.x -= view_rect.loc.x; let elem = RelocateRenderElement::from_element( elem, location.to_physical_precise_round(scale), diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs index b198e838..e7c0388b 100644 --- a/src/layout/focus_ring.rs +++ b/src/layout/focus_ring.rs @@ -1,23 +1,22 @@ -use std::cmp::{max, min}; use std::iter::zip; use arrayvec::ArrayVec; 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}; +use smithay::utils::{Logical, Point, Rectangle, Size}; use crate::niri_render_elements; use crate::render_helpers::border::BorderRenderElement; use crate::render_helpers::renderer::NiriRenderer; +use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; #[derive(Debug)] pub struct FocusRing { buffers: [SolidColorBuffer; 8], - locations: [Point<i32, Logical>; 8], - sizes: [Size<i32, Logical>; 8], + locations: [Point<f64, Logical>; 8], + sizes: [Size<f64, Logical>; 8], borders: [BorderRenderElement; 8], - full_size: Size<i32, Logical>, + full_size: Size<f64, Logical>, is_border: bool, use_border_shader: bool, config: niri_config::FocusRing, @@ -56,14 +55,15 @@ impl FocusRing { pub fn update_render_elements( &mut self, - win_size: Size<i32, Logical>, + win_size: Size<f64, Logical>, is_active: bool, is_border: bool, - view_rect: Rectangle<i32, Logical>, + view_rect: Rectangle<f64, Logical>, radius: CornerRadius, + scale: f64, ) { - let width = i32::from(self.config.width); - self.full_size = win_size + Size::from((width * 2, width * 2)); + let width = self.config.width.0; + self.full_size = win_size + Size::from((width, width)).upscale(2.); let color = if is_active { self.config.active_color @@ -107,39 +107,48 @@ impl FocusRing { 0. }; + let ceil = |logical: f64| (logical * scale).ceil() / scale; + + // All of this stuff should end up aligned to physical pixels because: + // * Window size and border width are rounded to physical pixels before being passed to this + // function. + // * We will ceil the corner radii below. + // * We do not divide anything, only add, subtract and multiply by integers. + // * At rendering time, tile positions are rounded to physical pixels. + if is_border { - let top_left = max(width, radius.top_left.ceil() as i32); - let top_right = min( + let top_left = f64::max(width, ceil(f64::from(radius.top_left))); + let top_right = f64::min( self.full_size.w - top_left, - max(width, radius.top_right.ceil() as i32), + f64::max(width, ceil(f64::from(radius.top_right))), ); - let bottom_left = min( + let bottom_left = f64::min( self.full_size.h - top_left, - max(width, radius.bottom_left.ceil() as i32), + f64::max(width, ceil(f64::from(radius.bottom_left))), ); - let bottom_right = min( + let bottom_right = f64::min( self.full_size.h - top_right, - min( + f64::min( self.full_size.w - bottom_left, - max(width, radius.bottom_right.ceil() as i32), + f64::max(width, ceil(f64::from(radius.bottom_right))), ), ); // Top edge. - self.sizes[0] = Size::from((win_size.w + width * 2 - top_left - top_right, width)); + self.sizes[0] = Size::from((win_size.w + width * 2. - top_left - top_right, width)); self.locations[0] = Point::from((-width + top_left, -width)); // Bottom edge. self.sizes[1] = - Size::from((win_size.w + width * 2 - bottom_left - bottom_right, width)); + Size::from((win_size.w + width * 2. - bottom_left - bottom_right, width)); self.locations[1] = Point::from((-width + bottom_left, win_size.h)); // Left edge. - self.sizes[2] = Size::from((width, win_size.h + width * 2 - top_left - bottom_left)); + self.sizes[2] = Size::from((width, win_size.h + width * 2. - top_left - bottom_left)); self.locations[2] = Point::from((-width, -width + top_left)); // Right edge. - self.sizes[3] = Size::from((width, win_size.h + width * 2 - top_right - bottom_right)); + self.sizes[3] = Size::from((width, win_size.h + width * 2. - top_right - bottom_right)); self.locations[3] = Point::from((win_size.w, -width + top_right)); // Top-left corner. @@ -203,8 +212,7 @@ impl FocusRing { pub fn render( &self, renderer: &mut impl NiriRenderer, - location: Point<i32, Logical>, - scale: Scale<f64>, + location: Point<f64, Logical>, ) -> impl Iterator<Item = FocusRingRenderElement> { let mut rv = ArrayVec::<_, 8>::new(); @@ -215,24 +223,17 @@ impl FocusRing { let border_width = -self.locations[0].y; // If drawing as a border with width = 0, then there's nothing to draw. - if self.is_border && border_width == 0 { + if self.is_border && border_width == 0. { return rv.into_iter(); } let has_border_shader = BorderRenderElement::has_shader(renderer); - let mut push = |buffer, border: &BorderRenderElement, location: Point<i32, Logical>| { + let mut push = |buffer, border: &BorderRenderElement, location: Point<f64, Logical>| { 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), - scale, - 1., - Kind::Unspecified, - ) - .into() + SolidColorRenderElement::from_buffer(buffer, location, 1., Kind::Unspecified).into() }; rv.push(elem); }; @@ -252,8 +253,8 @@ impl FocusRing { rv.into_iter() } - pub fn width(&self) -> i32 { - self.config.width.into() + pub fn width(&self) -> f64 { + self.config.width.0 } pub fn is_off(&self) -> bool { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 135f694f..ffc5e0e8 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -34,9 +34,8 @@ use std::mem; use std::rc::Rc; use std::time::Duration; -use niri_config::{CenterFocusedColumn, Config, Struts, Workspace as WorkspaceConfig}; +use niri_config::{CenterFocusedColumn, Config, FloatOrInt, Struts, Workspace as WorkspaceConfig}; use niri_ipc::SizeChange; -use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::Id; use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture}; @@ -50,9 +49,10 @@ use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Works use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; 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::{output_size, ResizeEdge}; +use crate::utils::{output_size, round_logical_in_physical_max1, ResizeEdge}; use crate::window::ResolvedWindowRules; pub mod closing_window; @@ -63,7 +63,7 @@ pub mod tile; pub mod workspace; /// Size changes up to this many pixels don't animate. -pub const RESIZE_ANIMATION_THRESHOLD: i32 = 10; +pub const RESIZE_ANIMATION_THRESHOLD: f64 = 10.; niri_render_elements! { LayoutElementRenderElement<R> => { @@ -110,7 +110,7 @@ pub trait LayoutElement { fn render<R: NiriRenderer>( &self, renderer: &mut R, - location: Point<i32, Logical>, + location: Point<f64, Logical>, scale: Scale<f64>, alpha: f32, target: RenderTarget, @@ -120,7 +120,7 @@ pub trait LayoutElement { fn render_normal<R: NiriRenderer>( &self, renderer: &mut R, - location: Point<i32, Logical>, + location: Point<f64, Logical>, scale: Scale<f64>, alpha: f32, target: RenderTarget, @@ -132,7 +132,7 @@ pub trait LayoutElement { fn render_popups<R: NiriRenderer>( &self, renderer: &mut R, - location: Point<i32, Logical>, + location: Point<f64, Logical>, scale: Scale<f64>, alpha: f32, target: RenderTarget, @@ -206,10 +206,10 @@ enum MonitorSet<W: LayoutElement> { }, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Options { /// Padding around windows in logical pixels. - pub gaps: i32, + pub gaps: f64, /// Extra padding around the working area in logical pixels. pub struts: Struts, pub focus_ring: niri_config::FocusRing, @@ -225,7 +225,7 @@ pub struct Options { impl Default for Options { fn default() -> Self { Self { - gaps: 16, + gaps: 16., struts: Default::default(), focus_ring: Default::default(), border: Default::default(), @@ -265,7 +265,7 @@ impl Options { .unwrap_or(Some(ColumnWidth::Proportion(0.5))); Self { - gaps: layout.gaps.into(), + gaps: layout.gaps.0, struts: layout.struts, focus_ring: layout.focus_ring, border: layout.border, @@ -275,6 +275,16 @@ impl Options { animations: config.animations.clone(), } } + + fn adjusted_for_scale(mut self, scale: f64) -> Self { + let round = |logical: f64| round_logical_in_physical_max1(scale, logical); + + self.gaps = round(self.gaps); + self.focus_ring.width = FloatOrInt(round(self.focus_ring.width.0)); + self.border.width = FloatOrInt(round(self.border.width.0)); + + self + } } impl<W: LayoutElement> Layout<W> { @@ -486,12 +496,12 @@ impl<W: LayoutElement> Layout<W> { width: Option<ColumnWidth>, is_full_width: bool, ) -> Option<&Output> { - let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w)); + let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w))); if let ColumnWidth::Fixed(w) = &mut width { let rules = window.rules(); let border_config = rules.border.resolve_against(self.options.border); if !border_config.off { - *w += border_config.width as i32 * 2; + *w += border_config.width.0 * 2.; } } @@ -575,12 +585,12 @@ impl<W: LayoutElement> Layout<W> { width: Option<ColumnWidth>, is_full_width: bool, ) -> Option<&Output> { - let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w)); + let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w))); if let ColumnWidth::Fixed(w) = &mut width { let rules = window.rules(); let border_config = rules.border.resolve_against(self.options.border); if !border_config.off { - *w += border_config.width as i32 * 2; + *w += border_config.width.0 * 2.; } } @@ -633,12 +643,12 @@ impl<W: LayoutElement> Layout<W> { width: Option<ColumnWidth>, is_full_width: bool, ) -> Option<&Output> { - let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w)); + let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w))); if let ColumnWidth::Fixed(w) = &mut width { let rules = window.rules(); let border_config = rules.border.resolve_against(self.options.border); if !border_config.off { - *w += border_config.width as i32 * 2; + *w += border_config.width.0 * 2.; } } @@ -671,12 +681,12 @@ impl<W: LayoutElement> Layout<W> { width: Option<ColumnWidth>, is_full_width: bool, ) { - let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w)); + let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w))); if let ColumnWidth::Fixed(w) = &mut width { let rules = window.rules(); let border_config = rules.border.resolve_against(self.options.border); if !border_config.off { - *w += border_config.width as i32 * 2; + *w += border_config.width.0 * 2.; } } @@ -887,7 +897,7 @@ impl<W: LayoutElement> Layout<W> { None } - pub fn window_loc(&self, window: &W::Id) -> Option<Point<i32, Logical>> { + pub fn window_loc(&self, window: &W::Id) -> Option<Point<f64, Logical>> { match &self.monitor_set { MonitorSet::Normal { monitors, .. } => { for mon in monitors { @@ -1440,7 +1450,7 @@ impl<W: LayoutElement> Layout<W> { &self, output: &Output, pos_within_output: Point<f64, Logical>, - ) -> Option<(&W, Option<Point<i32, Logical>>)> { + ) -> Option<(&W, Option<Point<f64, Logical>>)> { let MonitorSet::Normal { monitors, .. } = &self.monitor_set else { return None; }; @@ -1485,8 +1495,15 @@ impl<W: LayoutElement> Layout<W> { ); assert_eq!( - workspace.options, self.options, - "workspace options must be synchronized with layout" + workspace.base_options, self.options, + "workspace base options must be synchronized with layout" + ); + + let options = Options::clone(&workspace.base_options) + .adjusted_for_scale(workspace.scale().fractional_scale()); + assert_eq!( + &*workspace.options, &options, + "workspace options must be base options adjusted for workspace scale" ); assert!( @@ -1589,10 +1606,17 @@ impl<W: LayoutElement> Layout<W> { for workspace in &monitor.workspaces { assert_eq!( - workspace.options, self.options, + workspace.base_options, self.options, "workspace options must be synchronized with layout" ); + let options = Options::clone(&workspace.base_options) + .adjusted_for_scale(workspace.scale().fractional_scale()); + assert_eq!( + &*workspace.options, &options, + "workspace options must be base options adjusted for workspace scale" + ); + assert!( seen_workspace_id.insert(workspace.id()), "workspace id must be unique" @@ -2368,13 +2392,14 @@ impl<W: LayoutElement> Default for MonitorSet<W> { mod tests { use std::cell::Cell; - use niri_config::WorkspaceName; + use niri_config::{FloatOrInt, WorkspaceName}; use proptest::prelude::*; use proptest_derive::Arbitrary; use smithay::output::{Mode, PhysicalProperties, Subpixel}; use smithay::utils::Rectangle; use super::*; + use crate::utils::round_logical_in_physical; impl<W: LayoutElement> Default for Layout<W> { fn default() -> Self { @@ -2459,7 +2484,7 @@ mod tests { fn render<R: NiriRenderer>( &self, _renderer: &mut R, - _location: Point<i32, Logical>, + _location: Point<f64, Logical>, _scale: Scale<f64>, _alpha: f32, _target: RenderTarget, @@ -2595,9 +2620,19 @@ mod tests { ] } + fn arbitrary_scale() -> impl Strategy<Value = f64> { + prop_oneof![Just(1.), Just(1.5), Just(2.),] + } + #[derive(Debug, Clone, Copy, Arbitrary)] enum Op { AddOutput(#[proptest(strategy = "1..=5usize")] usize), + AddScaledOutput { + #[proptest(strategy = "1..=5usize")] + id: usize, + #[proptest(strategy = "arbitrary_scale()")] + scale: f64, + }, RemoveOutput(#[proptest(strategy = "1..=5usize")] usize), FocusOutput(#[proptest(strategy = "1..=5usize")] usize), AddNamedWorkspace { @@ -2769,6 +2804,32 @@ mod tests { ); layout.add_output(output.clone()); } + Op::AddScaledOutput { id, scale } => { + let name = format!("output{id}"); + if layout.outputs().any(|o| o.name() == name) { + return; + } + + let output = Output::new( + name, + PhysicalProperties { + size: Size::from((1280, 720)), + subpixel: Subpixel::Unknown, + make: String::new(), + model: String::new(), + }, + ); + output.change_current_state( + Some(Mode { + size: Size::from((1280, 720)), + refresh: 60000, + }), + None, + Some(smithay::output::Scale::Fractional(scale)), + None, + ); + layout.add_output(output.clone()); + } Op::RemoveOutput(id) => { let name = format!("output{id}"); let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else { @@ -3560,7 +3621,7 @@ mod tests { let mut options = Options::default(); options.border.off = false; - options.border.width = 1; + options.border.width = FloatOrInt(1.); check_ops_with_options(options, &ops); } @@ -3578,7 +3639,7 @@ mod tests { let mut options = Options::default(); options.border.off = false; - options.border.width = 1; + options.border.width = FloatOrInt(1.); check_ops_with_options(options, &ops); } @@ -3916,7 +3977,7 @@ mod tests { fn config_change_updates_cached_sizes() { let mut config = Config::default(); config.layout.border.off = false; - config.layout.border.width = 2; + config.layout.border.width = FloatOrInt(2.); let mut layout = Layout::new(&config); @@ -3927,18 +3988,83 @@ mod tests { } .apply(&mut layout); - config.layout.border.width = 4; + config.layout.border.width = FloatOrInt(4.); layout.update_config(&config); layout.verify_invariants(); } - fn arbitrary_spacing() -> impl Strategy<Value = u16> { + #[test] + fn working_area_starts_at_physical_pixel() { + let struts = Struts { + left: FloatOrInt(0.5), + right: FloatOrInt(1.), + top: FloatOrInt(0.75), + bottom: FloatOrInt(1.), + }; + + let output = Output::new( + String::from("output"), + PhysicalProperties { + size: Size::from((1280, 720)), + subpixel: Subpixel::Unknown, + make: String::new(), + model: String::new(), + }, + ); + output.change_current_state( + Some(Mode { + size: Size::from((1280, 720)), + refresh: 60000, + }), + None, + None, + None, + ); + + let area = compute_working_area(&output, struts); + + assert_eq!(round_logical_in_physical(1., area.loc.x), area.loc.x); + assert_eq!(round_logical_in_physical(1., area.loc.y), area.loc.y); + } + + #[test] + fn large_fractional_strut() { + let struts = Struts { + left: FloatOrInt(0.), + right: FloatOrInt(0.), + top: FloatOrInt(50000.5), + bottom: FloatOrInt(0.), + }; + + let output = Output::new( + String::from("output"), + PhysicalProperties { + size: Size::from((1280, 720)), + subpixel: Subpixel::Unknown, + make: String::new(), + model: String::new(), + }, + ); + output.change_current_state( + Some(Mode { + size: Size::from((1280, 720)), + refresh: 60000, + }), + None, + None, + None, + ); + + compute_working_area(&output, struts); + } + + fn arbitrary_spacing() -> impl Strategy<Value = f64> { // Give equal weight to: // - 0: the element is disabled // - 4: some reasonable value // - random value, likely unreasonably big - prop_oneof![Just(0), Just(4), (1..=u16::MAX)] + prop_oneof![Just(0.), Just(4.), ((1.)..=65535.)] } fn arbitrary_struts() -> impl Strategy<Value = Struts> { @@ -3949,10 +4075,10 @@ mod tests { arbitrary_spacing(), ) .prop_map(|(left, right, top, bottom)| Struts { - left, - right, - top, - bottom, + left: FloatOrInt(left), + right: FloatOrInt(right), + top: FloatOrInt(top), + bottom: FloatOrInt(bottom), }) } @@ -3971,7 +4097,7 @@ mod tests { ) -> niri_config::FocusRing { niri_config::FocusRing { off, - width, + width: FloatOrInt(width), ..Default::default() } } @@ -3984,7 +4110,7 @@ mod tests { ) -> niri_config::Border { niri_config::Border { off, - width, + width: FloatOrInt(width), ..Default::default() } } @@ -3999,7 +4125,7 @@ mod tests { center_focused_column in arbitrary_center_focused_column(), ) -> Options { Options { - gaps: gaps.into(), + gaps, struts, center_focused_column, focus_ring, diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index b166879b..eb72242f 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -7,7 +7,7 @@ use smithay::backend::renderer::element::utils::{ CropRenderElement, Relocate, RelocateRenderElement, }; use smithay::output::Output; -use smithay::utils::{Logical, Point, Rectangle, Scale}; +use smithay::utils::{Logical, Point, Rectangle}; use super::workspace::{ compute_working_area, Column, ColumnWidth, OutputId, Workspace, WorkspaceId, @@ -19,7 +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::{output_size, ResizeEdge}; +use crate::utils::{output_size, to_physical_precise_round, ResizeEdge}; /// Amount of touchpad movement to scroll the height of one workspace. const WORKSPACE_GESTURE_MOVEMENT: f64 = 300.; @@ -761,16 +761,16 @@ impl<W: LayoutElement> Monitor<W> { /// Returns the geometry of the active tile relative to and clamped to the output. /// /// During animations, assumes the final view position. - pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<i32, Logical>> { + pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> { let mut rect = self.active_workspace_ref().active_tile_visual_rectangle()?; if let Some(switch) = &self.workspace_switch { - let size = output_size(&self.output); + let size = output_size(&self.output).to_f64(); let offset = switch.target_idx() - self.active_workspace_idx as f64; - let offset = (offset * size.h as f64).round() as i32; + let offset = offset * size.h; - let clip_rect = Rectangle::from_loc_and_size((0, -offset), size); + let clip_rect = Rectangle::from_loc_and_size((0., -offset), size); rect = rect.intersection(clip_rect)?; } @@ -780,16 +780,16 @@ impl<W: LayoutElement> Monitor<W> { pub fn window_under( &self, pos_within_output: Point<f64, Logical>, - ) -> Option<(&W, Option<Point<i32, Logical>>)> { + ) -> Option<(&W, Option<Point<f64, Logical>>)> { match &self.workspace_switch { Some(switch) => { - let size = output_size(&self.output); + let size = output_size(&self.output).to_f64(); let render_idx = switch.current_idx(); let before_idx = render_idx.floor(); let after_idx = render_idx.ceil(); - let offset = ((render_idx - before_idx) * size.h as f64).round() as i32; + let offset = (render_idx - before_idx) * size.h; if after_idx < 0. || before_idx as usize >= self.workspaces.len() { return None; @@ -797,22 +797,22 @@ impl<W: LayoutElement> Monitor<W> { let after_idx = after_idx as usize; - let (idx, ws_offset) = if pos_within_output.y < (size.h - offset) as f64 { + let (idx, ws_offset) = if pos_within_output.y < size.h - offset { if before_idx < 0. { return None; } - (before_idx as usize, Point::from((0, offset))) + (before_idx as usize, Point::from((0., offset))) } else { if after_idx >= self.workspaces.len() { return None; } - (after_idx, Point::from((0, -size.h + offset))) + (after_idx, Point::from((0., -size.h + offset))) }; let ws = &self.workspaces[idx]; - let (win, win_pos) = ws.window_under(pos_within_output + ws_offset.to_f64())?; + let (win, win_pos) = ws.window_under(pos_within_output + ws_offset)?; Some((win, win_pos.map(|p| p - ws_offset))) } None => { @@ -831,7 +831,7 @@ impl<W: LayoutElement> Monitor<W> { let before_idx = render_idx.floor(); let after_idx = render_idx.ceil(); - let offset = ((render_idx - before_idx) * size.h as f64).round() as i32; + let offset = (render_idx - before_idx) * size.h; if after_idx < 0. || before_idx as usize >= self.workspaces.len() { return None; @@ -839,22 +839,22 @@ impl<W: LayoutElement> Monitor<W> { let after_idx = after_idx as usize; - let (idx, ws_offset) = if pos_within_output.y < (size.h - offset) as f64 { + let (idx, ws_offset) = if pos_within_output.y < size.h - offset { if before_idx < 0. { return None; } - (before_idx as usize, Point::from((0, offset))) + (before_idx as usize, Point::from((0., offset))) } else { if after_idx >= self.workspaces.len() { return None; } - (after_idx, Point::from((0, -size.h + offset))) + (after_idx, Point::from((0., -size.h + offset))) }; let ws = &self.workspaces[idx]; - ws.resize_edges_under(pos_within_output + ws_offset.to_f64()) + ws.resize_edges_under(pos_within_output + ws_offset) } None => { let ws = &self.workspaces[self.active_workspace_idx]; @@ -880,10 +880,8 @@ impl<W: LayoutElement> Monitor<W> { ) -> Vec<MonitorRenderElement<R>> { let _span = tracy_client::span!("Monitor::render_elements"); - let output_scale = Scale::from(self.output.current_scale().fractional_scale()); - let output_transform = self.output.current_transform(); - let output_mode = self.output.current_mode().unwrap(); - let size = output_transform.transform_size(output_mode.size); + let scale = self.output.current_scale().fractional_scale(); + let size = output_size(&self.output); match &self.workspace_switch { Some(switch) => { @@ -891,7 +889,7 @@ impl<W: LayoutElement> Monitor<W> { let before_idx = render_idx.floor(); let after_idx = render_idx.ceil(); - let offset = ((render_idx - before_idx) * size.h as f64).round() as i32; + let offset = (render_idx - before_idx) * size.h; if after_idx < 0. || before_idx as usize >= self.workspaces.len() { return vec![]; @@ -904,7 +902,7 @@ impl<W: LayoutElement> Monitor<W> { Some(RelocateRenderElement::from_element( CropRenderElement::from_element( elem, - output_scale, + scale, // HACK: crop to infinite bounds for all sides except the side // where the workspaces join, // otherwise it will cut pixel shaders and mess up @@ -914,7 +912,7 @@ impl<W: LayoutElement> Monitor<W> { (i32::MAX / 2, i32::MAX / 2), ), )?, - (0, -offset + size.h), + Point::from((0., -offset + size.h)).to_physical_precise_round(scale), Relocate::Relative, )) }); @@ -934,13 +932,13 @@ impl<W: LayoutElement> Monitor<W> { Some(RelocateRenderElement::from_element( CropRenderElement::from_element( elem, - output_scale, + scale, Rectangle::from_extemities( (-i32::MAX / 2, -i32::MAX / 2), - (i32::MAX / 2, size.h), + (i32::MAX / 2, to_physical_precise_round(scale, size.h)), |
