From af30cc8df68b29973c8b9eec290f9e6b93463929 Mon Sep 17 00:00:00 2001 From: yrkv Date: Sat, 16 Aug 2025 01:42:08 -0700 Subject: niri-ipc: Add window positions and sizes (#1265) * Add window sizes and positions to the IPC * basic fixes * report window_loc instead of window pos * clean ups * make scrolling indices 1-based * add printing to niri msg windows * don't include render offset in floating tile pos --------- Co-authored-by: Ivan Molodetskikh --- src/layout/floating.rs | 18 +++++++++++++++++- src/layout/mod.rs | 19 ++++++++++++------- src/layout/scrolling.rs | 18 +++++++++++++++++- src/layout/tile.rs | 14 ++++++++++++++ src/layout/workspace.rs | 8 +++++++- 5 files changed, 67 insertions(+), 10 deletions(-) (limited to 'src/layout') diff --git a/src/layout/floating.rs b/src/layout/floating.rs index 98927c0d..bd976d7d 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -3,7 +3,7 @@ use std::iter::zip; use std::rc::Rc; use niri_config::{PresetSize, RelativeTo}; -use niri_ipc::{PositionChange, SizeChange}; +use niri_ipc::{PositionChange, SizeChange, WindowLayout}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size}; @@ -322,6 +322,22 @@ impl FloatingSpace { }) } + pub fn tiles_with_ipc_layouts(&self) -> impl Iterator, WindowLayout)> { + let scale = self.scale; + self.tiles_with_offsets().map(move |(tile, offset)| { + // Do not include animated render offset here to avoid IPC spam. + let pos = offset; + // Round to physical pixels. + let pos = pos.to_physical_precise_round(scale).to_logical(scale); + + let layout = WindowLayout { + tile_pos_in_workspace_view: Some(pos.into()), + ..tile.ipc_layout_template() + }; + (tile, layout) + }) + } + pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size { let border_config = rules.border.resolve_against(self.options.border); compute_toplevel_bounds(border_config, self.working_area.size) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7bf25b42..52e3173b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -42,7 +42,7 @@ use niri_config::{ CenterFocusedColumn, Config, CornerRadius, FloatOrInt, PresetSize, Struts, Workspace as WorkspaceConfig, WorkspaceReference, }; -use niri_ipc::{ColumnDisplay, PositionChange, SizeChange}; +use niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout}; use scrolling::{Column, ColumnWidth}; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::utils::RescaleRenderElement; @@ -1742,25 +1742,30 @@ impl Layout { moving_window.chain(mon_windows) } - pub fn with_windows(&self, mut f: impl FnMut(&W, Option<&Output>, Option)) { + pub fn with_windows( + &self, + mut f: impl FnMut(&W, Option<&Output>, Option, WindowLayout), + ) { if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move { - f(move_.tile.window(), Some(&move_.output), None); + // We don't fill any positions for interactively moved windows. + let layout = move_.tile.ipc_layout_template(); + f(move_.tile.window(), Some(&move_.output), None, layout); } match &self.monitor_set { MonitorSet::Normal { monitors, .. } => { for mon in monitors { for ws in &mon.workspaces { - for win in ws.windows() { - f(win, Some(&mon.output), Some(ws.id())); + for (tile, layout) in ws.tiles_with_ipc_layouts() { + f(tile.window(), Some(&mon.output), Some(ws.id()), layout); } } } } MonitorSet::NoOutputs { workspaces } => { for ws in workspaces { - for win in ws.windows() { - f(win, None, Some(ws.id())); + for (tile, layout) in ws.tiles_with_ipc_layouts() { + f(tile.window(), None, Some(ws.id()), layout); } } } diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index a60581af..551fa692 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use std::time::Duration; use niri_config::{CenterFocusedColumn, PresetSize, Struts}; -use niri_ipc::{ColumnDisplay, SizeChange}; +use niri_ipc::{ColumnDisplay, SizeChange, WindowLayout}; use ordered_float::NotNan; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size}; @@ -2366,6 +2366,22 @@ impl ScrollingSpace { }) } + pub fn tiles_with_ipc_layouts(&self) -> impl Iterator, WindowLayout)> { + self.columns + .iter() + .enumerate() + .flat_map(move |(col_idx, col)| { + col.tiles().enumerate().map(move |(tile_idx, (tile, _))| { + let layout = WindowLayout { + // Our indices are 1-based, consistent with the actions. + pos_in_scrolling_layout: Some((col_idx + 1, tile_idx + 1)), + ..tile.ipc_layout_template() + }; + (tile, layout) + }) + }) + } + pub(super) fn insert_hint_area( &self, position: InsertPosition, diff --git a/src/layout/tile.rs b/src/layout/tile.rs index a4fe8a57..1555b129 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -2,6 +2,7 @@ use core::f64; use std::rc::Rc; use niri_config::{Color, CornerRadius, GradientInterpolation}; +use niri_ipc::WindowLayout; use smithay::backend::renderer::element::{Element, Kind}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; @@ -688,6 +689,19 @@ impl Tile { loc } + /// Returns a partially-filled [`WindowLayout`]. + /// + /// Only the sizing properties that a [`Tile`] can fill are filled. + pub fn ipc_layout_template(&self) -> WindowLayout { + WindowLayout { + pos_in_scrolling_layout: None, + tile_size: self.tile_size().into(), + window_size: self.window().size().into(), + tile_pos_in_workspace_view: None, + window_offset_in_tile: self.window_loc().into(), + } + } + fn is_in_input_region(&self, mut point: Point) -> bool { point -= self.window_loc().to_f64(); self.window.is_in_input_region(point) diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 588a6971..c01df8fd 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -5,7 +5,7 @@ use std::time::Duration; use niri_config::{ CenterFocusedColumn, CornerRadius, OutputName, PresetSize, Workspace as WorkspaceConfig, }; -use niri_ipc::{ColumnDisplay, PositionChange, SizeChange}; +use niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::desktop::{layer_map_for_output, Window}; use smithay::output::Output; @@ -1427,6 +1427,12 @@ impl Workspace { floating.chain(scrolling) } + pub fn tiles_with_ipc_layouts(&self) -> impl Iterator, WindowLayout)> { + let scrolling = self.scrolling.tiles_with_ipc_layouts(); + let floating = self.floating.tiles_with_ipc_layouts(); + floating.chain(scrolling) + } + pub fn active_tile_visual_rectangle(&self) -> Option> { if self.floating_is_active.get() { self.floating.active_tile_visual_rectangle() -- cgit