atagen

monolith

public
Star0
Watch0

Commit 8926d3a4e56b

tarball

atagen <boss@atagen.co>2026-10-19 10:59 UTC

2 files changed+35-12
input: keep resize rings off layer surfaces, popups and X11 menus

edge_at only knew whether a surface was under the pointer, so a floating window next to a bar or under a menu took the click on the ring that overlapped them. surface_under now says whether the hit is a window's own surface, and the rings only apply over one of those or over nothing.
Modified file: crates/monolith/src/input.rs+21 additions−8 deletions
@@ -70,9 +70,12 @@ impl State {
7070 return;
7171 };
7272 focus_on_hover(self, pos, self.input.warping);
73- let under = self.surface_under(pos);
73+ let (under, cover) = match self.cover_under(pos) {
74+ Some((s, p, c)) => (Some((s, p)), Some(c)),
75+ None => (None, None),
76+ };
7477 let before = pointer.current_focus().and_then(|s| s.client());
75- let hover = self.edge_at(pos, under.is_some()).map(|(_, e)| e);
78+ let hover = self.edge_at(pos, cover).map(|(_, e)| e);
7679 if std::mem::replace(&mut self.input.hover, hover) != hover {
7780 self.dirty.cursor = true;
7881 }
@@ -136,8 +139,9 @@ impl State {
136139 self.apply_layout();
137140 }
138141 let pos = self.input.pointer_pos;
139- let over = self.surface_under(pos).is_some();
140- self.input.hover = self.edge_at(pos, over).map(|(_, e)| e);
142+ self.input.hover = self
143+ .edge_at(pos, self.cover_under(pos).map(|c| c.2))
144+ .map(|(_, e)| e);
141145 self.dirty.cursor = true;
142146 }
143147 if ended.is_some_and(|d| d.button.is_none()) {
@@ -466,6 +470,14 @@ impl Edges {
466470 }
467471 }
468472
473+/// What the pointer is over. Layer surfaces, popups and X11 menus keep the
474+/// resize rings around windows from taking their clicks.
475+#[derive(Clone, Copy, PartialEq, Eq)]
476+pub enum Cover {
477+ Window,
478+ Other,
479+}
480+
469481 /// A floating window being moved or resized, either with Mod and a mouse
470482 /// button or because its client asked while holding `button`.
471483 pub struct Drag {
@@ -496,10 +508,11 @@ impl State {
496508 /// The window and edges of a resize grab at pos. Rings sit just outside
497509 /// windows, so the client's own surface is never taken over, except
498510 /// that a floating window's ring wins over a tile it overlaps.
499- fn edge_at(&self, pos: Point<f64, Logical>, over_surface: bool) -> Option<(WindowId, Edges)> {
500- if self.services.lock.active() {
511+ fn edge_at(&self, pos: Point<f64, Logical>, cover: Option<Cover>) -> Option<(WindowId, Edges)> {
512+ if self.services.lock.active() || cover == Some(Cover::Other) {
501513 return None;
502514 }
515+ let over_surface = cover.is_some();
503516 let layout = &self.wm.layout;
504517 let (o, out) = layout
505518 .outputs()
@@ -557,8 +570,8 @@ fn start_drag(state: &mut State, button: u32) -> bool {
557570 .is_some_and(|m| crate::commands::held(&m).has(state.settings.config.general.mod_key));
558571 if !held {
559572 let pos = state.input.pointer_pos;
560- let over = state.surface_under(pos).is_some();
561- let Some((id, edges)) = state.edge_at(pos, over).filter(|_| button == BTN_LEFT) else {
573+ let cover = state.cover_under(pos).map(|c| c.2);
574+ let Some((id, edges)) = state.edge_at(pos, cover).filter(|_| button == BTN_LEFT) else {
562575 return false;
563576 };
564577 if !state.wm.layout.is_focused(id) {
Modified file: crates/monolith/src/state.rs+14 additions−4 deletions
@@ -3,6 +3,7 @@ use crate::backend::logical_area;
33 use crate::commands::Binds;
44 use crate::focus::Focus;
55 use crate::globals::Protocols;
6+use crate::input::Cover;
67 use crate::wm::{Kind, Wm, configure};
78 use monolith_config::Config;
89 use monolith_layout::{Layout, OutputId, Rect, WindowId};
@@ -529,8 +530,16 @@ impl State {
529530 &self,
530531 pos: Point<f64, Logical>,
531532 ) -> Option<(WlSurface, Point<f64, Logical>)> {
533+ self.cover_under(pos).map(|(s, p, _)| (s, p))
534+ }
535+
536+ pub fn cover_under(
537+ &self,
538+ pos: Point<f64, Logical>,
539+ ) -> Option<(WlSurface, Point<f64, Logical>, Cover)> {
540+ let other = |(s, p)| (s, p, Cover::Other);
532541 if self.services.lock.active() {
533- return self.lock_surface_under(pos);
542+ return self.lock_surface_under(pos).map(other);
534543 }
535544 let unmanaged = self
536545 .services
@@ -548,7 +557,7 @@ impl State {
548557 WindowSurfaceType::ALL,
549558 )
550559 {
551- return Some((s, (p + geo.loc).to_f64()));
560+ return Some(other((s, (p + geo.loc).to_f64())));
552561 }
553562 }
554563 let fullscreen = self.fullscreen_at(pos).is_some();
@@ -569,6 +578,7 @@ impl State {
569578 }
570579 self.lower_popup_under(pos)
571580 })
581+ .map(other)
572582 .or_else(|| {
573583 self.wm
574584 .space
@@ -576,10 +586,10 @@ impl State {
576586 .and_then(|(window, location)| {
577587 window
578588 .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL)
579- .map(|(s, p)| (s, (p + location).to_f64()))
589+ .map(|(s, p)| (s, (p + location).to_f64(), Cover::Window))
580590 })
581591 })
582- .or_else(|| layer(&[Layer::Bottom, Layer::Background]))
592+ .or_else(|| layer(&[Layer::Bottom, Layer::Background]).map(other))
583593 }
584594 }