diff --git a/src/input.rs b/src/input.rs index 2178b71..9e76516 100644 --- a/src/input.rs +++ b/src/input.rs @@ -7,6 +7,7 @@ use std::{collections::HashMap, mem::Discriminant, time::Duration}; use crate::{ focus::{keyboard::KeyboardFocusTarget, pointer::PointerFocusTarget}, state::WithState, + window::WindowElement, }; use pinnacle_api_defs::pinnacle::input::v0alpha1::{ set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse, @@ -181,87 +182,88 @@ impl State { .output_geometry(output) .expect("called output_geometry on unmapped output"); - let layers = layer_map_for_output(output); + let mut fullscreen_and_up_split_at = 0; - let top_fullscreen_window = output - .with_state(|state| state.focus_stack.stack.clone()) - .into_iter() - .rev() - .find(|win| { - win.with_state(|state| { - state.fullscreen_or_maximized.is_fullscreen() - && output.with_state(|op_state| { - op_state - .focused_tags() - .any(|op_tag| state.tags.contains(op_tag)) - }) - }) - }); - - if let Some(window) = top_fullscreen_window { - let loc = self - .space - .element_location(&window) - .expect("called elem loc on unmapped win") - - window.geometry().loc; - - window - .surface_under(point - loc.to_f64(), WindowSurfaceType::ALL) - .map(|(surf, surf_loc)| (PointerFocusTarget::WlSurface(surf), surf_loc + loc)) - } else if let (Some(layer), _) | (None, Some(layer)) = ( - layers.layer_under(wlr_layer::Layer::Overlay, point), - layers.layer_under(wlr_layer::Layer::Top, point), - ) { - let layer_loc = layers.layer_geometry(layer).expect("no layer geo").loc; - - layer - .surface_under( - point - layer_loc.to_f64() - output_geo.loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(surf, surf_loc)| { - ( - PointerFocusTarget::WlSurface(surf), - surf_loc + layer_loc + output_geo.loc, - ) - }) - } else if let Some((surface, loc)) = self + for (i, win) in self .space .elements() .rev() .filter(|win| win.is_on_active_tag()) - .find_map(|win| { - let loc = self - .space - .element_location(win) - .expect("called elem loc on unmapped win") - - win.geometry().loc; - - win.surface_under(point - loc.to_f64(), WindowSurfaceType::ALL) - .map(|(surf, surf_loc)| (surf, surf_loc + loc)) - }) + .enumerate() { - Some((PointerFocusTarget::WlSurface(surface), loc)) - } else if let (Some(layer), _) | (None, Some(layer)) = ( - layers.layer_under(wlr_layer::Layer::Overlay, point), - layers.layer_under(wlr_layer::Layer::Top, point), - ) { - let layer_loc = layers.layer_geometry(layer).expect("no layer geo").loc; - - layer - .surface_under( - point - layer_loc.to_f64() - output_geo.loc.to_f64(), - WindowSurfaceType::ALL, - ) - .map(|(surf, surf_loc)| { - ( - PointerFocusTarget::WlSurface(surf), - surf_loc + layer_loc + output_geo.loc, - ) - }) - } else { - None + if win.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) { + fullscreen_and_up_split_at = i + 1; + } } + + let layer_under = + |layers: &[wlr_layer::Layer]| -> Option<(PointerFocusTarget, Point)> { + let layer_map = layer_map_for_output(output); + let layer = layers + .iter() + .find_map(|layer| layer_map.layer_under(*layer, point))?; + + let layer_loc = layer_map.layer_geometry(layer)?.loc; + + layer + .surface_under( + point - layer_loc.to_f64() - output_geo.loc.to_f64(), + WindowSurfaceType::ALL, + ) + .map(|(surf, surf_loc)| { + ( + PointerFocusTarget::WlSurface(surf), + surf_loc + layer_loc + output_geo.loc, + ) + }) + }; + + let window_under = + |windows: &[WindowElement]| -> Option<(PointerFocusTarget, Point)> { + windows.iter().find_map(|win| { + let loc = self + .space + .element_location(win) + .expect("called elem loc on unmapped win") + - win.geometry().loc; + + win.surface_under(point - loc.to_f64(), WindowSurfaceType::ALL) + .map(|(surf, surf_loc)| { + (PointerFocusTarget::WlSurface(surf), surf_loc + loc) + }) + }) + }; + + // Input and rendering go, from top to bottom, + // - All windows down to the bottom-most fullscreen window (this mimics Awesome) + // - Overlay and Top layer surfaces + // - The rest of the windows + // - Bottom and background layer surfaces + + window_under( + &self + .space + .elements() + .rev() + .filter(|win| win.is_on_active_tag()) + .take(fullscreen_and_up_split_at) + .cloned() + .collect::>(), + ) + .or_else(|| layer_under(&[wlr_layer::Layer::Overlay, wlr_layer::Layer::Top])) + .or_else(|| { + window_under( + &self + .space + .elements() + .rev() + .filter(|win| win.is_on_active_tag()) + .skip(fullscreen_and_up_split_at) + .cloned() + .collect::>(), + ) + }) + .or_else(|| layer_under(&[wlr_layer::Layer::Bottom, wlr_layer::Layer::Background])) } /// Update the pointer focus if it's different from the previous one. diff --git a/src/render.rs b/src/render.rs index 0d1c94c..c6182fe 100644 --- a/src/render.rs +++ b/src/render.rs @@ -22,10 +22,7 @@ use smithay::{ }, input::pointer::{CursorImageAttributes, CursorImageStatus}, output::Output, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::protocol::wl_surface::WlSurface, - }, + reexports::wayland_server::protocol::wl_surface::WlSurface, render_elements, utils::{Logical, Physical, Point, Scale}, wayland::{compositor, shell::wlr_layer}, @@ -129,22 +126,38 @@ where } /// Get render elements for windows on active tags. -fn tag_render_elements( +/// +/// ret.1 contains render elements for the windows at and above the first fullscreen window. +/// ret.2 contains the rest. +#[allow(clippy::type_complexity)] +fn window_render_elements( output: &Output, windows: &[WindowElement], space: &Space, renderer: &mut R, scale: Scale, -) -> Vec>> +) -> ( + Vec>>, + Vec>>, +) where R: Renderer + ImportAll + ImportMem, ::TextureId: 'static, { - let elements = windows + // bot wwwwwFFww top + // rev wwFFwwwww + + let mut last_fullscreen_split_at = 0; + + let mut elements = windows .iter() .rev() // rev because I treat the focus stack backwards vs how the renderer orders it .filter(|win| win.is_on_active_tag()) - .map(|win| { + .enumerate() + .map(|(i, win)| { + if win.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) { + last_fullscreen_split_at = i + 1; + } // subtract win.geometry().loc to align decorations correctly let loc = ( space.element_location(win) .unwrap_or((0, 0).into()) @@ -159,7 +172,7 @@ where }); (win.render_elements::>(renderer, loc, scale, 1.0), elem_geo) - }).flat_map(|(elems, rect)| { + }).map(|(elems, rect)| { // We're cropping everything down to its expected size to stop any sussy windows that // don't want to cooperate. Unfortunately this also truncates shadows and other // external decorations. @@ -171,10 +184,15 @@ where }, None => elems.into_iter().map(OutputRenderElements::from).collect(), } - }) - .collect::>(); + }).collect::>(); - elements + let rest = elements.split_off(last_fullscreen_split_at); + + // PERF: bunch of allocations here + ( + elements.into_iter().flatten().collect(), + rest.into_iter().flatten().collect(), + ) } pub fn pointer_render_elements( @@ -293,67 +311,35 @@ where // | base it on if it's a descendant or not output_render_elements.extend(o_r_elements.map(OutputRenderElements::from)); - let top_fullscreen_window = windows.iter().rev().find(|win| { - let is_wayland_actually_fullscreen = { - if let Some(toplevel) = win.toplevel() { - toplevel - .current_state() - .states - .contains(xdg_toplevel::State::Fullscreen) - } else { - true - } - }; + let LayerRenderElements { + background, + bottom, + top, + overlay, + } = layer_render_elements(output, renderer, scale); - win.with_state(|state| { - state.fullscreen_or_maximized.is_fullscreen() - && output.with_state(|op_state| { - op_state - .focused_tags() - .any(|op_tag| state.tags.contains(op_tag)) - }) - }) && is_wayland_actually_fullscreen - }); + let (fullscreen_and_up_elements, rest_of_window_elements) = + window_render_elements::(output, &windows, space, renderer, scale); - // If fullscreen windows exist, render only the topmost one - if let Some(window) = top_fullscreen_window { - let window_render_elements: Vec> = - window.render_elements(renderer, (0, 0).into(), scale, 1.0); + // Elements render from top to bottom - output_render_elements.extend( - window_render_elements - .into_iter() - .map(OutputRenderElements::from), - ); - } else { - let LayerRenderElements { - background, - bottom, - top, - overlay, - } = layer_render_elements(output, renderer, scale); + output_render_elements.extend(fullscreen_and_up_elements); - let window_render_elements = - tag_render_elements::(output, &windows, space, renderer, scale); + output_render_elements.extend( + overlay + .into_iter() + .chain(top) + .map(OutputRenderElements::from), + ); - // Elements render from top to bottom + output_render_elements.extend(rest_of_window_elements); - output_render_elements.extend( - overlay - .into_iter() - .chain(top) - .map(OutputRenderElements::from), - ); - - output_render_elements.extend(window_render_elements); - - output_render_elements.extend( - bottom - .into_iter() - .chain(background) - .map(OutputRenderElements::from), - ); - } + output_render_elements.extend( + bottom + .into_iter() + .chain(background) + .map(OutputRenderElements::from), + ); output_render_elements } diff --git a/src/window.rs b/src/window.rs index 0bbb07b..bce6e4a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -18,7 +18,7 @@ use self::window_state::WindowElementState; pub mod window_state; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct WindowElement(Window); impl Deref for WindowElement {