diff options
Diffstat (limited to 'src/niri.rs')
| -rw-r--r-- | src/niri.rs | 102 |
1 files changed, 88 insertions, 14 deletions
diff --git a/src/niri.rs b/src/niri.rs index de88da71..3ba1b9a4 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -133,7 +133,7 @@ use crate::ipc::server::IpcServer; use crate::layer::mapped::LayerSurfaceRenderElement; use crate::layer::MappedLayer; use crate::layout::tile::TileRenderElement; -use crate::layout::workspace::WorkspaceId; +use crate::layout::workspace::{Workspace, WorkspaceId}; use crate::layout::{HitType, Layout, LayoutElement as _, MonitorRenderElement}; use crate::niri_render_elements; use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState}; @@ -470,6 +470,7 @@ pub enum KeyboardFocus { LayerShell { surface: WlSurface }, LockScreen { surface: Option<WlSurface> }, ScreenshotUi, + Overview, } #[derive(Default, Clone, PartialEq)] @@ -566,6 +567,7 @@ impl KeyboardFocus { KeyboardFocus::LayerShell { surface } => Some(surface), KeyboardFocus::LockScreen { surface } => surface.as_ref(), KeyboardFocus::ScreenshotUi => None, + KeyboardFocus::Overview => None, } } @@ -575,12 +577,17 @@ impl KeyboardFocus { KeyboardFocus::LayerShell { surface } => Some(surface), KeyboardFocus::LockScreen { surface } => surface, KeyboardFocus::ScreenshotUi => None, + KeyboardFocus::Overview => None, } } pub fn is_layout(&self) -> bool { matches!(self, KeyboardFocus::Layout { .. }) } + + pub fn is_overview(&self) -> bool { + matches!(self, KeyboardFocus::Overview) + } } pub struct State { @@ -1026,13 +1033,18 @@ impl State { let focus_on_layer = |layer| excl_focus_on_layer(layer).or_else(|| on_d_focus_on_layer(layer)); + let is_overview_open = self.niri.layout.is_overview_open(); + let mut surface = grab_on_layer(Layer::Overlay); // FIXME: we shouldn't prioritize the top layer grabs over regular overlay input or a // fullscreen layout window. This will need tracking in grab() to avoid handing it out // in the first place. Or a better way to structure this code. surface = surface.or_else(|| grab_on_layer(Layer::Top)); - surface = surface.or_else(|| grab_on_layer(Layer::Bottom)); - surface = surface.or_else(|| grab_on_layer(Layer::Background)); + + if !is_overview_open { + surface = surface.or_else(|| grab_on_layer(Layer::Bottom)); + surface = surface.or_else(|| grab_on_layer(Layer::Background)); + } surface = surface.or_else(|| focus_on_layer(Layer::Overlay)); @@ -1043,6 +1055,11 @@ impl State { surface = surface.or_else(|| focus_on_layer(Layer::Background)); } else { surface = surface.or_else(|| focus_on_layer(Layer::Top)); + + if is_overview_open { + surface = Some(surface.unwrap_or(KeyboardFocus::Overview)); + } + surface = surface.or_else(|| on_d_focus_on_layer(Layer::Bottom)); surface = surface.or_else(|| on_d_focus_on_layer(Layer::Background)); surface = surface.or_else(layout_focus); @@ -1102,7 +1119,9 @@ impl State { // focused window. // // FIXME: Ideally this should happen inside Layout itself, then there wouldn't be any - // problems with layer-shell, etc. + // problems with layer-shell, etc. Or a similar problem now with the Overview where we + // don't update the previously focused window because the keyboard focus is on the + // Overview rather than on the Layout. if matches!(self.niri.keyboard_focus, KeyboardFocus::Layout { .. }) && matches!(focus, KeyboardFocus::Layout { .. }) { @@ -2910,6 +2929,10 @@ impl Niri { output: &Output, pos_within_output: Point<f64, Logical>, ) -> bool { + if self.layout.is_overview_open() { + return false; + } + // Check if some layer-shell surface is on top. let layers = layer_map_for_output(output); let layer_popup_under = |layer| { @@ -2939,6 +2962,42 @@ impl Niri { false } + /// Returns the workspace under the position to be activated. + /// + /// The return value is an output and a workspace index on it. + pub fn workspace_under( + &self, + extended_bounds: bool, + pos: Point<f64, Logical>, + ) -> Option<(Output, &Workspace<Mapped>)> { + if self.is_locked() || self.screenshot_ui.is_open() { + return None; + } + + let (output, pos_within_output) = self.output_under(pos)?; + + if self.is_sticky_obscured_under(output, pos_within_output) { + return None; + } + + if self.is_layout_obscured_under(output, pos_within_output) { + return None; + } + + let ws = self + .layout + .workspace_under(extended_bounds, output, pos_within_output)?; + Some((output.clone(), ws)) + } + + pub fn workspace_under_cursor( + &self, + extended_bounds: bool, + ) -> Option<(Output, &Workspace<Mapped>)> { + let pos = self.seat.get_pointer().unwrap().current_location(); + self.workspace_under(extended_bounds, pos) + } + /// Returns the window under the position to be activated. /// /// The cursor may be inside the window's activation region, but not within the window's input @@ -3039,6 +3098,8 @@ impl Niri { let mon = self.layout.monitor_for_output(output)?; let (_, geo) = mon.workspace_under(pos_within_output)?; layer_pos_within_output += geo.loc; + // Don't need to deal with zoom here because in the overview background and + // bottom layers don't receive input. } let surface_type = if popup { @@ -3096,6 +3157,8 @@ impl Niri { let mut under = layer_popup_under(Layer::Overlay).or_else(|| layer_toplevel_under(Layer::Overlay)); + let is_overview_open = self.layout.is_overview_open(); + // When rendering above the top layer, we put the regular monitor elements first. // Otherwise, we will render all layer-shell pop-ups and the top layer on top. if mon.render_above_top_layer() { @@ -3111,13 +3174,23 @@ impl Niri { } else { under = under .or_else(|| layer_popup_under(Layer::Top)) - .or_else(|| layer_toplevel_under(Layer::Top)) - .or_else(interactive_moved_window_under) - .or_else(|| layer_popup_under(Layer::Bottom)) - .or_else(|| layer_popup_under(Layer::Background)) - .or_else(window_under) - .or_else(|| layer_toplevel_under(Layer::Bottom)) - .or_else(|| layer_toplevel_under(Layer::Background)); + .or_else(|| layer_toplevel_under(Layer::Top)); + + under = under.or_else(interactive_moved_window_under); + + if !is_overview_open { + under = under + .or_else(|| layer_popup_under(Layer::Bottom)) + .or_else(|| layer_popup_under(Layer::Background)); + } + + under = under.or_else(window_under); + + if !is_overview_open { + under = under + .or_else(|| layer_toplevel_under(Layer::Bottom)) + .or_else(|| layer_toplevel_under(Layer::Background)); + } } let Some((mut surface_and_pos, (window, layer))) = under else { @@ -3576,6 +3649,7 @@ impl Niri { // layer-shell, the layout will briefly draw as active, despite never having focus. KeyboardFocus::LockScreen { .. } => true, KeyboardFocus::ScreenshotUi => true, + KeyboardFocus::Overview => true, }; self.layout.refresh(layout_is_active); @@ -3877,7 +3951,7 @@ impl Niri { // Get monitor elements. let mon = self.layout.monitor_for_output(output).unwrap(); - let zoom = 1.; + let zoom = mon.overview_zoom(); let monitor_elements = Vec::from_iter( mon.render_elements(renderer, target, focus_ring) .map(|(geo, iter)| (geo, Vec::from_iter(iter))), @@ -5560,7 +5634,7 @@ impl Niri { } if let Some(window) = &new_focus.window { - if current_focus.window.as_ref() != Some(window) { + if !self.layout.is_overview_open() && current_focus.window.as_ref() != Some(window) { let (window, hit) = window; // Don't trigger focus-follows-mouse over the tab indicator. @@ -5809,7 +5883,7 @@ fn scale_relocate_crop<E: Element>( niri_render_elements! { OutputRenderElements<R> => { Monitor = MonitorRenderElement<R>, - Tile = TileRenderElement<R>, + RescaledTile = RescaleRenderElement<TileRenderElement<R>>, LayerSurface = LayerSurfaceRenderElement<R>, RelocatedLayerSurface = CropRenderElement<RelocateRenderElement<RescaleRenderElement< LayerSurfaceRenderElement<R> |
