Wrap mapped windows in a Mapped

This commit is contained in:
Ivan Molodetskikh
2024-03-19 14:41:17 +04:00
parent f31e105043
commit 3963f537a4
12 changed files with 285 additions and 224 deletions
+19 -16
View File
@@ -17,8 +17,7 @@ use smithay::wayland::shm::{ShmHandler, ShmState};
use smithay::{delegate_compositor, delegate_shm}; use smithay::{delegate_compositor, delegate_shm};
use crate::niri::{ClientState, State}; use crate::niri::{ClientState, State};
use crate::utils::clone2; use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
use crate::window::{InitialConfigureState, Unmapped};
impl CompositorHandler for State { impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState { fn compositor_state(&mut self) -> &mut CompositorState {
@@ -109,22 +108,22 @@ impl CompositorHandler for State {
window.on_commit(); window.on_commit();
let (width, is_full_width, output) = let (rules, width, is_full_width, output) =
if let InitialConfigureState::Configured { if let InitialConfigureState::Configured {
rules,
width, width,
is_full_width, is_full_width,
output, output,
..
} = state } = state
{ {
// Check that the output is still connected. // Check that the output is still connected.
let output = let output =
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some()); output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
(width, is_full_width, output) (rules, width, is_full_width, output)
} else { } else {
error!("window map must happen after initial configure"); error!("window map must happen after initial configure");
(None, false, None) (ResolvedWindowRules::default(), None, false, None)
}; };
let parent = window let parent = window
@@ -141,29 +140,30 @@ impl CompositorHandler for State {
.filter(|(_, parent_output)| { .filter(|(_, parent_output)| {
output.is_none() || output.as_ref() == Some(*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 mapped = Mapped::new(window, rules);
let win = window.clone(); let window = mapped.window.clone();
let output = if let Some(p) = parent { let output = if let Some(p) = parent {
// Open dialogs immediately to the right of their parent window. // Open dialogs immediately to the right of their parent window.
self.niri self.niri
.layout .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 { } else if let Some(output) = &output {
self.niri self.niri
.layout .layout
.add_window_on_output(output, win, width, is_full_width); .add_window_on_output(output, mapped, width, is_full_width);
Some(output) Some(output)
} else { } 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() { if let Some(output) = output.cloned() {
self.niri.layout.start_open_animation_for_window(&window); 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) { if new_active_window == Some(&window) {
self.maybe_warp_cursor_to_focus(); 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. // 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) { if let Some((mapped, output)) = self.niri.layout.find_window_and_output(surface) {
let (window, output) = clone2(win_out); let window = mapped.window.clone();
let output = output.clone();
window.on_commit(); window.on_commit();
@@ -224,7 +225,9 @@ impl CompositorHandler for State {
// This is a commit of a non-root or a non-toplevel root. // 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); 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(); window.on_commit();
self.niri.layout.update_window(&window); self.niri.layout.update_window(&window);
self.niri.queue_redraw(output); self.niri.queue_redraw(output);
+10 -11
View File
@@ -144,7 +144,7 @@ impl InputMethodHandler for State {
self.niri self.niri
.layout .layout
.find_window_and_output(parent) .find_window_and_output(parent)
.map(|(window, _)| window.geometry()) .map(|(mapped, _)| mapped.window.geometry())
.unwrap_or_default() .unwrap_or_default()
} }
} }
@@ -333,25 +333,24 @@ impl ForeignToplevelHandler for State {
} }
fn activate(&mut self, wl_surface: WlSurface) { fn activate(&mut self, wl_surface: WlSurface) {
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) { if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.activate_window(&window); self.niri.layout.activate_window(&window);
self.niri.queue_redraw_all(); self.niri.queue_redraw_all();
} }
} }
fn close(&mut self, wl_surface: WlSurface) { fn close(&mut self, wl_surface: WlSurface) {
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) { if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
window.toplevel().expect("no x11 support").send_close(); mapped.toplevel().send_close();
} }
} }
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) { 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() .toplevel()
.expect("no x11 support")
.current_state() .current_state()
.capabilities .capabilities
.contains(xdg_toplevel::WmCapabilities::Fullscreen) .contains(xdg_toplevel::WmCapabilities::Fullscreen)
@@ -359,7 +358,7 @@ impl ForeignToplevelHandler for State {
return; 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 let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {
if &requested_output != current_output { if &requested_output != current_output {
@@ -374,8 +373,8 @@ impl ForeignToplevelHandler for State {
} }
fn unset_fullscreen(&mut self, wl_surface: WlSurface) { fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) { if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.set_fullscreen(&window, false); self.niri.layout.set_fullscreen(&window, false);
} }
} }
+13 -12
View File
@@ -29,7 +29,6 @@ use smithay::{
use crate::layout::workspace::ColumnWidth; use crate::layout::workspace::ColumnWidth;
use crate::niri::{PopupGrabState, State}; use crate::niri::{PopupGrabState, State};
use crate::utils::clone2;
use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped}; use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped};
fn window_matches(role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool { fn window_matches(role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool {
@@ -213,9 +212,7 @@ impl XdgShellHandler for State {
} }
let layout_focus = self.niri.layout.focus(); let layout_focus = self.niri.layout.focus();
if Some(&root) if Some(&root) != layout_focus.map(|win| win.toplevel().wl_surface()) {
!= layout_focus.map(|win| win.toplevel().expect("no x11 support").wl_surface())
{
let _ = PopupManager::dismiss_popup(&root, &popup); let _ = PopupManager::dismiss_popup(&root, &popup);
return; return;
} }
@@ -278,12 +275,12 @@ impl XdgShellHandler for State {
) { ) {
let requested_output = wl_output.as_ref().and_then(Output::from_resource); 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 .niri
.layout .layout
.find_window_and_output(toplevel.wl_surface()) .find_window_and_output(toplevel.wl_surface())
{ {
let window = window.clone(); let window = mapped.window.clone();
if let Some(requested_output) = requested_output { if let Some(requested_output) = requested_output {
if &requested_output != current_output { if &requested_output != current_output {
@@ -358,12 +355,12 @@ impl XdgShellHandler for State {
} }
fn unfullscreen_request(&mut self, toplevel: ToplevelSurface) { fn unfullscreen_request(&mut self, toplevel: ToplevelSurface) {
if let Some((window, _)) = self if let Some((mapped, _)) = self
.niri .niri
.layout .layout
.find_window_and_output(toplevel.wl_surface()) .find_window_and_output(toplevel.wl_surface())
{ {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.set_fullscreen(&window, false); self.niri.layout.set_fullscreen(&window, false);
// A configure is required in response to this event regardless if there are pending // A configure is required in response to this event regardless if there are pending
@@ -453,14 +450,16 @@ impl XdgShellHandler for State {
.layout .layout
.find_window_and_output(surface.wl_surface()); .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 // 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. // involving laptop going to sleep and resuming.
error!("toplevel missing from both unmapped_windows and layout"); error!("toplevel missing from both unmapped_windows and layout");
return; 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); let was_active = active_window == Some(&window);
self.niri.layout.remove_window(&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. // 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) { if let Some((mapped, output)) = self.niri.layout.find_window_and_output(&root) {
self.unconstrain_window_popup(popup, window, output); self.unconstrain_window_popup(popup, &mapped.window, output);
} else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| { } else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| {
let map = layer_map_for_output(o); let map = layer_map_for_output(o);
let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?; let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?;
@@ -814,6 +813,8 @@ impl State {
if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state { if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state {
*rules = resolve(); *rules = resolve();
} }
} else if let Some(mapped) = self.niri.layout.find_window_mut(toplevel.wl_surface()) {
mapped.rules = resolve();
} }
} }
} }
+14 -11
View File
@@ -379,21 +379,24 @@ impl State {
} }
Action::ScreenshotWindow => { Action::ScreenshotWindow => {
let active = self.niri.layout.active_window(); 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| { 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:?}"); warn!("error taking screenshot: {err:?}");
} }
}); });
} }
} }
Action::CloseWindow => { Action::CloseWindow => {
if let Some(window) = self.niri.layout.focus() { if let Some(mapped) = self.niri.layout.focus() {
window.toplevel().expect("no x11 support").send_close(); mapped.toplevel().send_close();
} }
} }
Action::FullscreenWindow => { 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 { if let Some(window) = focus {
self.niri.layout.toggle_fullscreen(&window); self.niri.layout.toggle_fullscreen(&window);
// FIXME: granular // FIXME: granular
@@ -1017,8 +1020,8 @@ impl State {
let button_state = event.state(); let button_state = event.state();
if ButtonState::Pressed == button_state { if ButtonState::Pressed == button_state {
if let Some(window) = self.niri.window_under_cursor() { if let Some(mapped) = self.niri.window_under_cursor() {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.activate_window(&window); self.niri.layout.activate_window(&window);
// FIXME: granular. // FIXME: granular.
@@ -1177,8 +1180,8 @@ impl State {
tool.tip_down(serial, event.time_msec()); tool.tip_down(serial, event.time_msec());
if let Some(pos) = self.niri.tablet_cursor_location { if let Some(pos) = self.niri.tablet_cursor_location {
if let Some(window) = self.niri.window_under(pos) { if let Some(mapped) = self.niri.window_under(pos) {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.activate_window(&window); self.niri.layout.activate_window(&window);
// FIXME: granular. // FIXME: granular.
@@ -1535,8 +1538,8 @@ impl State {
.output_under(touch_location) .output_under(touch_location)
.next() .next()
.cloned(); .cloned();
if let Some(window) = self.niri.window_under(touch_location) { if let Some(mapped) = self.niri.window_under(touch_location) {
let window = window.clone(); let window = mapped.window.clone();
self.niri.layout.activate_window(&window); self.niri.layout.activate_window(&window);
// FIXME: granular. // FIXME: granular.
+26 -151
View File
@@ -38,21 +38,14 @@ use niri_config::{CenterFocusedColumn, Config, Struts};
use niri_ipc::SizeChange; use niri_ipc::SizeChange;
use smithay::backend::renderer::element::solid::SolidColorRenderElement; use smithay::backend::renderer::element::solid::SolidColorRenderElement;
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
use smithay::backend::renderer::element::{AsRenderElements, Id}; use smithay::backend::renderer::element::Id;
use smithay::desktop::space::SpaceElement;
use smithay::desktop::Window;
use smithay::output::Output; 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::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform}; use smithay::utils::{Logical, Point, Scale, Size, Transform};
use smithay::wayland::compositor::{send_surface_state, with_states};
use smithay::wayland::shell::xdg::SurfaceCachedState;
use self::monitor::Monitor; use self::monitor::Monitor;
pub use self::monitor::MonitorRenderElement; pub use self::monitor::MonitorRenderElement;
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace}; use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
use crate::niri::WindowOffscreenId;
use crate::niri_render_elements; use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::renderer::NiriRenderer;
use crate::utils::output_size; 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> { impl<W: LayoutElement> Layout<W> {
pub fn new(config: &Config) -> Self { pub fn new(config: &Config) -> Self {
Self::with_options(Options::from_config(config)) 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)> { pub fn find_window_and_output(&self, wl_surface: &WlSurface) -> Option<(&W, &Output)> {
if let MonitorSet::Normal { monitors, .. } = &self.monitor_set { if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {
for mon in monitors { for mon in monitors {
@@ -1866,6 +1740,7 @@ mod tests {
use proptest::prelude::*; use proptest::prelude::*;
use proptest_derive::Arbitrary; use proptest_derive::Arbitrary;
use smithay::output::{Mode, PhysicalProperties, Subpixel}; use smithay::output::{Mode, PhysicalProperties, Subpixel};
use smithay::utils::Rectangle;
use super::*; use super::*;
+4
View File
@@ -121,6 +121,10 @@ impl<W: LayoutElement> Tile<W> {
&self.window &self.window
} }
pub fn window_mut(&mut self) -> &mut W {
&mut self.window
}
pub fn into_window(self) -> W { pub fn into_window(self) -> W {
self.window self.window
} }
+17 -1
View File
@@ -10,6 +10,7 @@ use smithay::output::Output;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
use smithay::wayland::compositor::send_surface_state;
use super::tile::{Tile, TileRenderElement}; use super::tile::{Tile, TileRenderElement};
use super::{LayoutElement, Options}; use super::{LayoutElement, Options};
@@ -299,6 +300,13 @@ impl<W: LayoutElement> Workspace<W> {
.map(Tile::window) .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>) { pub fn set_output(&mut self, output: Option<Output>) {
if self.output == output { if self.output == output {
return; return;
@@ -407,7 +415,11 @@ impl<W: LayoutElement> Workspace<W> {
pub fn configure_new_window(&self, window: &Window, width: Option<ColumnWidth>) { pub fn configure_new_window(&self, window: &Window, width: Option<ColumnWidth>) {
if let Some(output) = self.output.as_ref() { 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 window
@@ -584,6 +596,10 @@ impl<W: LayoutElement> Workspace<W> {
self.windows().find(|win| win.is_wl_surface(wl_surface)) 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. /// Computes the X position of the windows in the given column, in logical coordinates.
fn column_x(&self, column_idx: usize) -> i32 { fn column_x(&self, column_idx: usize) -> i32 {
let mut x = 0; let mut x = 0;
+17 -15
View File
@@ -114,7 +114,7 @@ use crate::utils::spawning::CHILD_ENV;
use crate::utils::{ use crate::utils::{
center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8, 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}; use crate::{animation, niri_render_elements};
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.]; 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, // 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). // 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 // This space does not actually contain any windows, but all outputs are mapped into it
// according to their global position. // according to their global position.
@@ -631,7 +631,7 @@ impl State {
self.niri self.niri
.layout .layout
.focus() .focus()
.map(|win| win.toplevel().expect("no x11 support").wl_surface().clone()) .map(|win| win.toplevel().wl_surface().clone())
.map(|surface| KeyboardFocus::Layout { .map(|surface| KeyboardFocus::Layout {
surface: Some(surface), 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 /// The cursor may be inside the window's activation region, but not within the window's input
/// region. /// 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() { if self.is_locked() || self.screenshot_ui.is_open() {
return None; 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 /// The cursor may be inside the window's activation region, but not within the window's input
/// region. /// 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(); let pos = self.seat.get_pointer().unwrap().current_location();
self.window_under(pos) self.window_under(pos)
} }
@@ -1684,8 +1684,9 @@ impl Niri {
let window_under = || { let window_under = || {
self.layout self.layout
.window_under(output, pos_within_output) .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 win_pos_within_output = win_pos_within_output?;
let window = &mapped.window;
window window
.surface_under( .surface_under(
pos_within_output - win_pos_within_output.to_f64(), 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 // 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. // 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 let offscreen_id = win
.user_data() .user_data()
.get_or_insert(WindowOffscreenId::default) .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 // 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 // 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. // is currently invisible, this is the DMABUF feedback that it should know about.
for win in self.layout.windows_for_output(output) { for mapped in self.layout.windows_for_output(output) {
win.send_dmabuf_feedback( mapped.window.send_dmabuf_feedback(
output, output,
|_, _| Some(output.clone()), |_, _| Some(output.clone()),
|surface, _| { |surface, _| {
@@ -2600,8 +2602,8 @@ impl Niri {
let frame_callback_time = get_monotonic_time(); let frame_callback_time = get_monotonic_time();
for win in self.layout.windows_for_output(output) { for mapped in self.layout.windows_for_output(output) {
win.send_frame( mapped.window.send_frame(
output, output,
frame_callback_time, frame_callback_time,
FRAME_CALLBACK_THROTTLE, FRAME_CALLBACK_THROTTLE,
@@ -2666,8 +2668,8 @@ impl Niri {
let frame_callback_time = get_monotonic_time(); let frame_callback_time = get_monotonic_time();
self.layout.with_windows(|win, _| { self.layout.with_windows(|mapped, _| {
win.send_frame( mapped.window.send_frame(
output, output,
frame_callback_time, frame_callback_time,
FRAME_CALLBACK_THROTTLE, FRAME_CALLBACK_THROTTLE,
@@ -2746,8 +2748,8 @@ impl Niri {
); );
} }
for win in self.layout.windows_for_output(output) { for mapped in self.layout.windows_for_output(output) {
win.take_presentation_feedback( mapped.window.take_presentation_feedback(
&mut feedback, &mut feedback,
surface_primary_scanout_output, surface_primary_scanout_output,
|surface, _| { |surface, _| {
+3 -3
View File
@@ -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 // 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. // the previous window and only then activate the newly focused window.
let mut focused = None; let mut focused = None;
state.niri.layout.with_windows(|window, output| { state.niri.layout.with_windows(|mapped, output| {
let wl_surface = window.toplevel().expect("no x11 support").wl_surface(); let wl_surface = mapped.toplevel().wl_surface();
with_states(wl_surface, |states| { with_states(wl_surface, |states| {
let role = states let role = states
@@ -107,7 +107,7 @@ pub fn refresh(state: &mut State) {
.unwrap(); .unwrap();
if state.niri.keyboard_focus.surface() == Some(wl_surface) { if state.niri.keyboard_focus.surface() == Some(wl_surface) {
focused = Some((window.clone(), output.cloned())); focused = Some((mapped.window.clone(), output.cloned()));
} else { } else {
refresh_toplevel(protocol_state, wl_surface, &role, output, false); refresh_toplevel(protocol_state, wl_surface, &role, output, false);
} }
-4
View File
@@ -20,10 +20,6 @@ pub mod watcher;
pub static IS_SYSTEMD_SERVICE: AtomicBool = AtomicBool::new(false); 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 { pub fn version() -> String {
format!( format!(
"{} ({})", "{} ({})",
+159
View File
@@ -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);
});
}
fn request_fullscreen(&self, size: Size<i32, Logical>) {
self.toplevel().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().wl_surface(), |state| {
let curr = state.cached_state.current::<SurfaceCachedState>();
curr.min_size
})
}
fn max_size(&self) -> Size<i32, Logical> {
with_states(self.toplevel().wl_surface(), |state| {
let curr = state.cached_state.current::<SurfaceCachedState>();
curr.max_size
})
}
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
self.toplevel().wl_surface() == wl_surface
}
fn set_preferred_scale_transform(&self, scale: i32, transform: Transform) {
self.window.with_surfaces(|surface, data| {
send_surface_state(surface, data, scale, transform);
});
}
fn has_ssd(&self) -> bool {
self.toplevel().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));
self.window.output_enter(output, overlap)
}
fn output_leave(&self, output: &Output) {
self.window.output_leave(output)
}
fn set_offscreen_element_id(&self, id: Option<Id>) {
let data = self
.window
.user_data()
.get_or_insert(WindowOffscreenId::default);
data.0.replace(id);
}
fn set_activated(&self, active: bool) {
self.window.set_activated(active);
}
fn set_bounds(&self, bounds: Size<i32, Logical>) {
self.toplevel().with_pending_state(|state| {
state.bounds = Some(bounds);
});
}
fn send_pending_configure(&self) {
self.toplevel().send_pending_configure();
}
fn is_fullscreen(&self) -> bool {
self.toplevel()
.current_state()
.states
.contains(xdg_toplevel::State::Fullscreen)
}
fn is_pending_fullscreen(&self) -> bool {
self.toplevel()
.with_pending_state(|state| state.states.contains(xdg_toplevel::State::Fullscreen))
}
fn refresh(&self) {
self.window.refresh();
}
}
+3
View File
@@ -1,5 +1,8 @@
use crate::layout::workspace::ColumnWidth; use crate::layout::workspace::ColumnWidth;
pub mod mapped;
pub use mapped::Mapped;
pub mod unmapped; pub mod unmapped;
pub use unmapped::{InitialConfigureState, Unmapped}; pub use unmapped::{InitialConfigureState, Unmapped};