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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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.

View File

@ -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::*;

View File

@ -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
}

View File

@ -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;

View File

@ -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, _| {

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
// 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);
}

View File

@ -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!(
"{} ({})",

159
src/window/mapped.rs Normal file
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();
}
}

View File

@ -1,5 +1,8 @@
use crate::layout::workspace::ColumnWidth;
pub mod mapped;
pub use mapped::Mapped;
pub mod unmapped;
pub use unmapped::{InitialConfigureState, Unmapped};