aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-03-19 14:41:17 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-03-19 18:29:13 +0400
commit3963f537a4182dbcd8e1e2f262ee105473facc56 (patch)
tree942f802863d1f8f63bbe68b53ab2d091e3c7dda9 /src
parentf31e105043a9fae0fae3dcfe0feb7ea1193d5f77 (diff)
downloadniri-3963f537a4182dbcd8e1e2f262ee105473facc56.tar.gz
niri-3963f537a4182dbcd8e1e2f262ee105473facc56.tar.bz2
niri-3963f537a4182dbcd8e1e2f262ee105473facc56.zip
Wrap mapped windows in a Mapped
Diffstat (limited to 'src')
-rw-r--r--src/handlers/compositor.rs35
-rw-r--r--src/handlers/mod.rs21
-rw-r--r--src/handlers/xdg_shell.rs25
-rw-r--r--src/input.rs25
-rw-r--r--src/layout/mod.rs177
-rw-r--r--src/layout/tile.rs4
-rw-r--r--src/layout/workspace.rs18
-rw-r--r--src/niri.rs32
-rw-r--r--src/protocols/foreign_toplevel.rs6
-rw-r--r--src/utils/mod.rs4
-rw-r--r--src/window/mapped.rs159
-rw-r--r--src/window/mod.rs3
12 files changed, 285 insertions, 224 deletions
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
index 52c5a193..188ba831 100644
--- a/src/handlers/compositor.rs
+++ b/src/handlers/compositor.rs
@@ -17,8 +17,7 @@ use smithay::wayland::shm::{ShmHandler, ShmState};
use smithay::{delegate_compositor, delegate_shm};
use crate::niri::{ClientState, State};
-use crate::utils::clone2;
-use crate::window::{InitialConfigureState, Unmapped};
+use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
@@ -109,22 +108,22 @@ impl CompositorHandler for State {
window.on_commit();
- let (width, is_full_width, output) =
+ let (rules, width, is_full_width, output) =
if let InitialConfigureState::Configured {
+ rules,
width,
is_full_width,
output,
- ..
} = state
{
// Check that the output is still connected.
let output =
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
- (width, is_full_width, output)
+ (rules, width, is_full_width, output)
} else {
error!("window map must happen after initial configure");
- (None, false, None)
+ (ResolvedWindowRules::default(), None, false, None)
};
let parent = window
@@ -141,29 +140,30 @@ impl CompositorHandler for State {
.filter(|(_, parent_output)| {
output.is_none() || output.as_ref() == Some(*parent_output)
})
- .map(|(window, _)| window.clone());
+ .map(|(mapped, _)| mapped.window.clone());
- let window = window.clone();
- let win = window.clone();
+ let mapped = Mapped::new(window, rules);
+ let window = mapped.window.clone();
let output = if let Some(p) = parent {
// Open dialogs immediately to the right of their parent window.
self.niri
.layout
- .add_window_right_of(&p, win, width, is_full_width)
+ .add_window_right_of(&p, mapped, width, is_full_width)
} else if let Some(output) = &output {
self.niri
.layout
- .add_window_on_output(output, win, width, is_full_width);
+ .add_window_on_output(output, mapped, width, is_full_width);
Some(output)
} else {
- self.niri.layout.add_window(win, width, is_full_width)
+ self.niri.layout.add_window(mapped, width, is_full_width)
};
if let Some(output) = output.cloned() {
self.niri.layout.start_open_animation_for_window(&window);
- let new_active_window = self.niri.layout.active_window().map(|(w, _)| w);
+ let new_active_window =
+ self.niri.layout.active_window().map(|(m, _)| &m.window);
if new_active_window == Some(&window) {
self.maybe_warp_cursor_to_focus();
}
@@ -183,8 +183,9 @@ impl CompositorHandler for State {
}
// This is a commit of a previously-mapped root or a non-toplevel root.
- if let Some(win_out) = self.niri.layout.find_window_and_output(surface) {
- let (window, output) = clone2(win_out);
+ if let Some((mapped, output)) = self.niri.layout.find_window_and_output(surface) {
+ let window = mapped.window.clone();
+ let output = output.clone();
window.on_commit();
@@ -224,7 +225,9 @@ impl CompositorHandler for State {
// This is a commit of a non-root or a non-toplevel root.
let root_window_output = self.niri.layout.find_window_and_output(&root_surface);
- if let Some((window, output)) = root_window_output.map(clone2) {
+ if let Some((mapped, output)) = root_window_output {
+ let window = mapped.window.clone();
+ let output = output.clone();
window.on_commit();
self.niri.layout.update_window(&window);
self.niri.queue_redraw(output);
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
index 4d3a606f..46ee79b6 100644
--- a/src/handlers/mod.rs
+++ b/src/handlers/mod.rs
@@ -144,7 +144,7 @@ impl InputMethodHandler for State {
self.niri
.layout
.find_window_and_output(parent)
- .map(|(window, _)| window.geometry())
+ .map(|(mapped, _)| mapped.window.geometry())
.unwrap_or_default()
}
}
@@ -333,25 +333,24 @@ impl ForeignToplevelHandler for State {
}
fn activate(&mut self, wl_surface: WlSurface) {
- if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
- let window = window.clone();
+ if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
+ let window = mapped.window.clone();
self.niri.layout.activate_window(&window);
self.niri.queue_redraw_all();
}
}
fn close(&mut self, wl_surface: WlSurface) {
- if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
- window.toplevel().expect("no x11 support").send_close();
+ if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
+ mapped.toplevel().send_close();
}
}
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) {
- if let Some((window, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
+ if let Some((mapped, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
{
- if !window
+ if !mapped
.toplevel()
- .expect("no x11 support")
.current_state()
.capabilities
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
@@ -359,7 +358,7 @@ impl ForeignToplevelHandler for State {
return;
}
- let window = window.clone();
+ let window = mapped.window.clone();
if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {
if &requested_output != current_output {
@@ -374,8 +373,8 @@ impl ForeignToplevelHandler for State {
}
fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
- if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
- let window = window.clone();
+ if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
+ let window = mapped.window.clone();
self.niri.layout.set_fullscreen(&window, false);
}
}
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index 66c3dea4..4d31645c 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -29,7 +29,6 @@ use smithay::{
use crate::layout::workspace::ColumnWidth;
use crate::niri::{PopupGrabState, State};
-use crate::utils::clone2;
use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped};
fn window_matches(role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool {
@@ -213,9 +212,7 @@ impl XdgShellHandler for State {
}
let layout_focus = self.niri.layout.focus();
- if Some(&root)
- != layout_focus.map(|win| win.toplevel().expect("no x11 support").wl_surface())
- {
+ if Some(&root) != layout_focus.map(|win| win.toplevel().wl_surface()) {
let _ = PopupManager::dismiss_popup(&root, &popup);
return;
}
@@ -278,12 +275,12 @@ impl XdgShellHandler for State {
) {
let requested_output = wl_output.as_ref().and_then(Output::from_resource);
- if let Some((window, current_output)) = self
+ if let Some((mapped, current_output)) = self
.niri
.layout
.find_window_and_output(toplevel.wl_surface())
{
- let window = window.clone();
+ let window = mapped.window.clone();
if let Some(requested_output) = requested_output {
if &requested_output != current_output {
@@ -358,12 +355,12 @@ impl XdgShellHandler for State {
}
fn unfullscreen_request(&mut self, toplevel: ToplevelSurface) {
- if let Some((window, _)) = self
+ if let Some((mapped, _)) = self
.niri
.layout
.find_window_and_output(toplevel.wl_surface())
{
- let window = window.clone();
+ let window = mapped.window.clone();
self.niri.layout.set_fullscreen(&window, false);
// A configure is required in response to this event regardless if there are pending
@@ -453,14 +450,16 @@ impl XdgShellHandler for State {
.layout
.find_window_and_output(surface.wl_surface());
- let Some((window, output)) = win_out.map(clone2) else {
+ let Some((mapped, output)) = win_out else {
// I have no idea how this can happen, but I saw it happen once, in a weird interaction
// involving laptop going to sleep and resuming.
error!("toplevel missing from both unmapped_windows and layout");
return;
};
+ let window = mapped.window.clone();
+ let output = output.clone();
- let active_window = self.niri.layout.active_window().map(|(w, _)| w);
+ let active_window = self.niri.layout.active_window().map(|(m, _)| &m.window);
let was_active = active_window == Some(&window);
self.niri.layout.remove_window(&window);
@@ -733,8 +732,8 @@ impl State {
};
// Figure out if the root is a window or a layer surface.
- if let Some((window, output)) = self.niri.layout.find_window_and_output(&root) {
- self.unconstrain_window_popup(popup, window, output);
+ if let Some((mapped, output)) = self.niri.layout.find_window_and_output(&root) {
+ self.unconstrain_window_popup(popup, &mapped.window, output);
} else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| {
let map = layer_map_for_output(o);
let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?;
@@ -814,6 +813,8 @@ impl State {
if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state {
*rules = resolve();
}
+ } else if let Some(mapped) = self.niri.layout.find_window_mut(toplevel.wl_surface()) {
+ mapped.rules = resolve();
}
}
}
diff --git a/src/input.rs b/src/input.rs
index 35a612bc..11a84878 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -379,21 +379,24 @@ impl State {
}
Action::ScreenshotWindow => {
let active = self.niri.layout.active_window();
- if let Some((window, output)) = active {
+ if let Some((mapped, output)) = active {
self.backend.with_primary_renderer(|renderer| {
- if let Err(err) = self.niri.screenshot_window(renderer, output, window) {
+ if let Err(err) =
+ self.niri
+ .screenshot_window(renderer, output, &mapped.window)
+ {
warn!("error taking screenshot: {err:?}");
}
});
}
}
Action::CloseWindow => {
- if let Some(window) = self.niri.layout.focus() {
- window.toplevel().expect("no x11 support").send_close();
+ if let Some(mapped) = self.niri.layout.focus() {
+ mapped.toplevel().send_close();
}
}
Action::FullscreenWindow => {
- let focus = self.niri.layout.focus().cloned();
+ let focus = self.niri.layout.focus().map(|m| m.window.clone());
if let Some(window) = focus {
self.niri.layout.toggle_fullscreen(&window);
// FIXME: granular
@@ -1017,8 +1020,8 @@ impl State {
let button_state = event.state();
if ButtonState::Pressed == button_state {
- if let Some(window) = self.niri.window_under_cursor() {
- let window = window.clone();
+ if let Some(mapped) = self.niri.window_under_cursor() {
+ let window = mapped.window.clone();
self.niri.layout.activate_window(&window);
// FIXME: granular.
@@ -1177,8 +1180,8 @@ impl State {
tool.tip_down(serial, event.time_msec());
if let Some(pos) = self.niri.tablet_cursor_location {
- if let Some(window) = self.niri.window_under(pos) {
- let window = window.clone();
+ if let Some(mapped) = self.niri.window_under(pos) {
+ let window = mapped.window.clone();
self.niri.layout.activate_window(&window);
// FIXME: granular.
@@ -1535,8 +1538,8 @@ impl State {
.output_under(touch_location)
.next()
.cloned();
- if let Some(window) = self.niri.window_under(touch_location) {
- let window = window.clone();
+ if let Some(mapped) = self.niri.window_under(touch_location) {
+ let window = mapped.window.clone();
self.niri.layout.activate_window(&window);
// FIXME: granular.
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 24720435..86ce5c85 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -38,21 +38,14 @@ use niri_config::{CenterFocusedColumn, Config, Struts};
use niri_ipc::SizeChange;
use smithay::backend::renderer::element::solid::SolidColorRenderElement;
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
-use smithay::backend::renderer::element::{AsRenderElements, Id};
-use smithay::desktop::space::SpaceElement;
-use smithay::desktop::Window;
+use smithay::backend::renderer::element::Id;
use smithay::output::Output;
-use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
-use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
-use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};
-use smithay::wayland::compositor::{send_surface_state, with_states};
-use smithay::wayland::shell::xdg::SurfaceCachedState;
+use smithay::utils::{Logical, Point, Scale, Size, Transform};
use self::monitor::Monitor;
pub use self::monitor::MonitorRenderElement;
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
-use crate::niri::WindowOffscreenId;
use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::utils::output_size;
@@ -229,148 +222,6 @@ impl Options {
}
}
-impl LayoutElement for Window {
- type Id = Self;
-
- fn id(&self) -> &Self::Id {
- self
- }
-
- fn size(&self) -> Size<i32, Logical> {
- self.geometry().size
- }
-
- fn buf_loc(&self) -> Point<i32, Logical> {
- Point::from((0, 0)) - self.geometry().loc
- }
-
- fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool {
- let surface_local = point + self.geometry().loc.to_f64();
- SpaceElement::is_in_input_region(self, &surface_local)
- }
-
- fn render<R: NiriRenderer>(
- &self,
- renderer: &mut R,
- location: Point<i32, Logical>,
- scale: Scale<f64>,
- ) -> Vec<LayoutElementRenderElement<R>> {
- let buf_pos = location - self.geometry().loc;
- self.render_elements(
- renderer,
- buf_pos.to_physical_precise_round(scale),
- scale,
- 1.,
- )
- }
-
- fn request_size(&self, size: Size<i32, Logical>) {
- self.toplevel()
- .expect("no x11 support")
- .with_pending_state(|state| {
- state.size = Some(size);
- state.states.unset(xdg_toplevel::State::Fullscreen);
- });
- }
-
- fn request_fullscreen(&self, size: Size<i32, Logical>) {
- self.toplevel()
- .expect("no x11 support")
- .with_pending_state(|state| {
- state.size = Some(size);
- state.states.set(xdg_toplevel::State::Fullscreen);
- });
- }
-
- fn min_size(&self) -> Size<i32, Logical> {
- with_states(
- self.toplevel().expect("no x11 support").wl_surface(),
- |state| {
- let curr = state.cached_state.current::<SurfaceCachedState>();
- curr.min_size
- },
- )
- }
-
- fn max_size(&self) -> Size<i32, Logical> {
- with_states(
- self.toplevel().expect("no x11 support").wl_surface(),
- |state| {
- let curr = state.cached_state.current::<SurfaceCachedState>();
- curr.max_size
- },
- )
- }
-
- fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
- self.toplevel().expect("no x11 support").wl_surface() == wl_surface
- }
-
- fn set_preferred_scale_transform(&self, scale: i32, transform: Transform) {
- self.with_surfaces(|surface, data| {
- send_surface_state(surface, data, scale, transform);
- });
- }
-
- fn has_ssd(&self) -> bool {
- self.toplevel()
- .expect("no x11 support")
- .current_state()
- .decoration_mode
- == Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
- }
-
- fn output_enter(&self, output: &Output) {
- let overlap = Rectangle::from_loc_and_size((0, 0), (i32::MAX, i32::MAX));
- SpaceElement::output_enter(self, output, overlap)
- }
-
- fn output_leave(&self, output: &Output) {
- SpaceElement::output_leave(self, output)
- }
-
- fn set_offscreen_element_id(&self, id: Option<Id>) {
- let data = self.user_data().get_or_insert(WindowOffscreenId::default);
- data.0.replace(id);
- }
-
- fn set_activated(&self, active: bool) {
- Window::set_activated(self, active);
- }
-
- fn set_bounds(&self, bounds: Size<i32, Logical>) {
- self.toplevel()
- .expect("no x11 support")
- .with_pending_state(|state| {
- state.bounds = Some(bounds);
- });
- }
-
- fn send_pending_configure(&self) {
- self.toplevel()
- .expect("no x11 support")
- .send_pending_configure();
- }
-
- fn is_fullscreen(&self) -> bool {
- self.toplevel()
- .expect("no x11 support")
- .current_state()
- .states
- .contains(xdg_toplevel::State::Fullscreen)
- }
-
- fn is_pending_fullscreen(&self) -> bool {
- self.toplevel()
- .expect("no x11 support")
- .with_pending_state(|state| state.states.contains(xdg_toplevel::State::Fullscreen))
- }
-
- fn refresh(&self) {
- SpaceElement::refresh(self)
- }
-}
-
impl<W: LayoutElement> Layout<W> {
pub fn new(config: &Config) -> Self {
Self::with_options(Options::from_config(config))
@@ -792,6 +643,29 @@ impl<W: LayoutElement> Layout<W> {
}
}
+ pub fn find_window_mut(&mut self, wl_surface: &WlSurface) -> Option<&mut W> {
+ match &mut self.monitor_set {
+ MonitorSet::Normal { monitors, .. } => {
+ for mon in monitors {
+ for ws in &mut mon.workspaces {
+ if let Some(window) = ws.find_wl_surface_mut(wl_surface) {
+ return Some(window);
+ }
+ }
+ }
+ }
+ MonitorSet::NoOutputs { workspaces } => {
+ for ws in workspaces {
+ if let Some(window) = ws.find_wl_surface_mut(wl_surface) {
+ return Some(window);
+ }
+ }
+ }
+ }
+
+ None
+ }
+
pub fn find_window_and_output(&self, wl_surface: &WlSurface) -> Option<(&W, &Output)> {
if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {
for mon in monitors {
@@ -1866,6 +1740,7 @@ mod tests {
use proptest::prelude::*;
use proptest_derive::Arbitrary;
use smithay::output::{Mode, PhysicalProperties, Subpixel};
+ use smithay::utils::Rectangle;
use super::*;
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index 505a6ccd..589b2fc0 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -121,6 +121,10 @@ impl<W: LayoutElement> Tile<W> {
&self.window
}
+ pub fn window_mut(&mut self) -> &mut W {
+ &mut self.window
+ }
+
pub fn into_window(self) -> W {
self.window
}
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 9a0f10e5..03868dc0 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -10,6 +10,7 @@ use smithay::output::Output;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
+use smithay::wayland::compositor::send_surface_state;
use super::tile::{Tile, TileRenderElement};
use super::{LayoutElement, Options};
@@ -299,6 +300,13 @@ impl<W: LayoutElement> Workspace<W> {
.map(Tile::window)
}
+ pub fn windows_mut(&mut self) -> impl Iterator<Item = &mut W> + '_ {
+ self.columns
+ .iter_mut()
+ .flat_map(|col| col.tiles.iter_mut())
+ .map(Tile::window_mut)
+ }
+
pub fn set_output(&mut self, output: Option<Output>) {
if self.output == output {
return;
@@ -407,7 +415,11 @@ impl<W: LayoutElement> Workspace<W> {
pub fn configure_new_window(&self, window: &Window, width: Option<ColumnWidth>) {
if let Some(output) = self.output.as_ref() {
- set_preferred_scale_transform(window, output);
+ let scale = output.current_scale().integer_scale();
+ let transform = output.current_transform();
+ window.with_surfaces(|surface, data| {
+ send_surface_state(surface, data, scale, transform);
+ });
}
window
@@ -584,6 +596,10 @@ impl<W: LayoutElement> Workspace<W> {
self.windows().find(|win| win.is_wl_surface(wl_surface))
}
+ pub fn find_wl_surface_mut(&mut self, wl_surface: &WlSurface) -> Option<&mut W> {
+ self.windows_mut().find(|win| win.is_wl_surface(wl_surface))
+ }
+
/// Computes the X position of the windows in the given column, in logical coordinates.
fn column_x(&self, column_idx: usize) -> i32 {
let mut x = 0;
diff --git a/src/niri.rs b/src/niri.rs
index b87ce915..13aa6409 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -114,7 +114,7 @@ use crate::utils::spawning::CHILD_ENV;
use crate::utils::{
center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
};
-use crate::window::Unmapped;
+use crate::window::{Mapped, Unmapped};
use crate::{animation, niri_render_elements};
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.];
@@ -138,7 +138,7 @@ pub struct Niri {
// Each workspace corresponds to a Space. Each workspace generally has one Output mapped to it,
// however it may have none (when there are no outputs connected) or mutiple (when mirroring).
- pub layout: Layout<Window>,
+ pub layout: Layout<Mapped>,
// This space does not actually contain any windows, but all outputs are mapped into it
// according to their global position.
@@ -631,7 +631,7 @@ impl State {
self.niri
.layout
.focus()
- .map(|win| win.toplevel().expect("no x11 support").wl_surface().clone())
+ .map(|win| win.toplevel().wl_surface().clone())
.map(|surface| KeyboardFocus::Layout {
surface: Some(surface),
})
@@ -1591,7 +1591,7 @@ impl Niri {
///
/// The cursor may be inside the window's activation region, but not within the window's input
/// region.
- pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Window> {
+ pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Mapped> {
if self.is_locked() || self.screenshot_ui.is_open() {
return None;
}
@@ -1618,7 +1618,7 @@ impl Niri {
///
/// The cursor may be inside the window's activation region, but not within the window's input
/// region.
- pub fn window_under_cursor(&self) -> Option<&Window> {
+ pub fn window_under_cursor(&self) -> Option<&Mapped> {
let pos = self.seat.get_pointer().unwrap().current_location();
self.window_under(pos)
}
@@ -1684,8 +1684,9 @@ impl Niri {
let window_under = || {
self.layout
.window_under(output, pos_within_output)
- .and_then(|(window, win_pos_within_output)| {
+ .and_then(|(mapped, win_pos_within_output)| {
let win_pos_within_output = win_pos_within_output?;
+ let window = &mapped.window;
window
.surface_under(
pos_within_output - win_pos_within_output.to_f64(),
@@ -2409,7 +2410,8 @@ impl Niri {
// The reason to do this at all is that it keeps track of whether the surface is visible or
// not in a unified way with the pointer surfaces, which makes the logic elsewhere simpler.
- for win in self.layout.windows_for_output(output) {
+ for mapped in self.layout.windows_for_output(output) {
+ let win = &mapped.window;
let offscreen_id = win
.user_data()
.get_or_insert(WindowOffscreenId::default)
@@ -2480,8 +2482,8 @@ impl Niri {
// We can unconditionally send the current output's feedback to regular and layer-shell
// surfaces, as they can only be displayed on a single output at a time. Even if a surface
// is currently invisible, this is the DMABUF feedback that it should know about.
- for win in self.layout.windows_for_output(output) {
- win.send_dmabuf_feedback(
+ for mapped in self.layout.windows_for_output(output) {
+ mapped.window.send_dmabuf_feedback(
output,
|_, _| Some(output.clone()),
|surface, _| {
@@ -2600,8 +2602,8 @@ impl Niri {
let frame_callback_time = get_monotonic_time();
- for win in self.layout.windows_for_output(output) {
- win.send_frame(
+ for mapped in self.layout.windows_for_output(output) {
+ mapped.window.send_frame(
output,
frame_callback_time,
FRAME_CALLBACK_THROTTLE,
@@ -2666,8 +2668,8 @@ impl Niri {
let frame_callback_time = get_monotonic_time();
- self.layout.with_windows(|win, _| {
- win.send_frame(
+ self.layout.with_windows(|mapped, _| {
+ mapped.window.send_frame(
output,
frame_callback_time,
FRAME_CALLBACK_THROTTLE,
@@ -2746,8 +2748,8 @@ impl Niri {
);
}
- for win in self.layout.windows_for_output(output) {
- win.take_presentation_feedback(
+ for mapped in self.layout.windows_for_output(output) {
+ mapped.window.take_presentation_feedback(
&mut feedback,
surface_primary_scanout_output,
|surface, _| {
diff --git a/src/protocols/foreign_toplevel.rs b/src/protocols/foreign_toplevel.rs
index 67dff830..af2be711 100644
--- a/src/protocols/foreign_toplevel.rs
+++ b/src/protocols/foreign_toplevel.rs
@@ -95,8 +95,8 @@ pub fn refresh(state: &mut State) {
// Save the focused window for last, this way when the focus changes, we will first deactivate
// the previous window and only then activate the newly focused window.
let mut focused = None;
- state.niri.layout.with_windows(|window, output| {
- let wl_surface = window.toplevel().expect("no x11 support").wl_surface();
+ state.niri.layout.with_windows(|mapped, output| {
+ let wl_surface = mapped.toplevel().wl_surface();
with_states(wl_surface, |states| {
let role = states
@@ -107,7 +107,7 @@ pub fn refresh(state: &mut State) {
.unwrap();
if state.niri.keyboard_focus.surface() == Some(wl_surface) {
- focused = Some((window.clone(), output.cloned()));
+ focused = Some((mapped.window.clone(), output.cloned()));
} else {
refresh_toplevel(protocol_state, wl_surface, &role, output, false);
}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 4902bd3a..acd37b20 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -20,10 +20,6 @@ pub mod watcher;
pub static IS_SYSTEMD_SERVICE: AtomicBool = AtomicBool::new(false);
-pub fn clone2<T: Clone, U: Clone>(t: (&T, &U)) -> (T, U) {
- (t.0.clone(), t.1.clone())
-}
-
pub fn version() -> String {
format!(
"{} ({})",
diff --git a/src/window/mapped.rs b/src/window/mapped.rs
new file mode 100644
index 00000000..4476dede
--- /dev/null
+++ b/src/window/mapped.rs
@@ -0,0 +1,159 @@
+use smithay::backend::renderer::element::{AsRenderElements as _, Id};
+use smithay::desktop::space::SpaceElement as _;
+use smithay::desktop::Window;
+use smithay::output::Output;
+use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
+use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
+use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};
+use smithay::wayland::compositor::{send_surface_state, with_states};
+use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface};
+
+use super::ResolvedWindowRules;
+use crate::layout::{LayoutElement, LayoutElementRenderElement};
+use crate::niri::WindowOffscreenId;
+use crate::render_helpers::renderer::NiriRenderer;
+
+#[derive(Debug)]
+pub struct Mapped {
+ pub window: Window,
+
+ /// Up-to-date rules.
+ pub rules: ResolvedWindowRules,
+}
+
+impl Mapped {
+ pub fn new(window: Window, rules: ResolvedWindowRules) -> Self {
+ Self { window, rules }
+ }
+
+ pub fn toplevel(&self) -> &ToplevelSurface {
+ self.window.toplevel().expect("no X11 support")
+ }
+}
+
+impl LayoutElement for Mapped {
+ type Id = Window;
+
+ fn id(&self) -> &Self::Id {
+ &self.window
+ }
+
+ fn size(&self) -> Size<i32, Logical> {
+ self.window.geometry().size
+ }
+
+ fn buf_loc(&self) -> Point<i32, Logical> {
+ Point::from((0, 0)) - self.window.geometry().loc
+ }
+
+ fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool {
+ let surface_local = point + self.window.geometry().loc.to_f64();
+ self.window.is_in_input_region(&surface_local)
+ }
+
+ fn render<R: NiriRenderer>(
+ &self,
+ renderer: &mut R,
+ location: Point<i32, Logical>,
+ scale: Scale<f64>,
+ ) -> Vec<LayoutElementRenderElement<R>> {
+ let buf_pos = location - self.window.geometry().loc;
+ self.window.render_elements(
+ renderer,
+ buf_pos.to_physical_precise_round(scale),
+ scale,
+ 1.,
+ )
+ }
+
+ fn request_size(&self, size: Size<i32, Logical>) {
+ self.toplevel().with_pending_state(|state| {
+ state.size = Some(size);
+ state.states.unset(xdg_toplevel::State::Fullscreen);
+ }