From 08f5c6fecb3c5c81d63a0bf7248c85ae3299a4a5 Mon Sep 17 00:00:00 2001 From: Kai Koehler <45439844+Fireye04@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:10:01 -0700 Subject: Make hot corners configurable, including per-output (#2108) * Add corner selection in config * Add hot corner docs * Working per-monitor hot corners Handle defaults * run cargo fmt --all * Fix hot corners in is_sticky_obscured_under * Change default to fall back to gesture hot corners if output hot corners are unset * Add hot corner output config docs * Support fractional scaling * Trigger hot corners over widgets * Improve float handling Fixed YaLTeR/niri/pull/2108 * Refactor * Bug Fixes * Amend docs Fix styling Co-authored-by: Ivan Molodetskikh * Integrate code review Move is_inside_hot_corner * fixes --------- Co-authored-by: Aadniz <8147434+Aadniz@users.noreply.github.com> Co-authored-by: Ivan Molodetskikh --- src/niri.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/niri.rs b/src/niri.rs index 612d5417..cb309830 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -3118,6 +3118,49 @@ impl Niri { Some((output, pos_within_output)) } + fn is_inside_hot_corner(&self, output: &Output, pos: Point) -> bool { + let config = self.config.borrow(); + let hot_corners = output + .user_data() + .get::() + .and_then(|name| config.outputs.find(name)) + .and_then(|c| c.hot_corners) + .unwrap_or(config.gestures.hot_corners); + + if hot_corners.off { + return false; + } + + // Use size from the ceiled output geometry, since that's what we currently use for pointer + // motion clamping. + let geom = self.global_space.output_geometry(output).unwrap(); + let size = geom.size.to_f64(); + + let contains = move |corner: Point| { + Rectangle::new(corner, Size::new(1., 1.)).contains(pos) + }; + + if hot_corners.top_right && contains(Point::new(size.w - 1., 0.)) { + return true; + } + if hot_corners.bottom_left && contains(Point::new(0., size.h - 1.)) { + return true; + } + if hot_corners.bottom_right && contains(Point::new(size.w - 1., size.h - 1.)) { + return true; + } + + // If the user didn't explicitly set any corners, we default to top-left. + if (hot_corners.top_left + || !(hot_corners.top_right || hot_corners.bottom_right || hot_corners.bottom_left)) + && contains(Point::new(0., 0.)) + { + return true; + } + + false + } + pub fn is_sticky_obscured_under( &self, output: &Output, @@ -3161,12 +3204,8 @@ impl Niri { return false; } - let hot_corners = self.config.borrow().gestures.hot_corners; - if !hot_corners.off { - let hot_corner = Rectangle::from_size(Size::from((1., 1.))); - if hot_corner.contains(pos_within_output) { - return true; - } + if self.is_inside_hot_corner(output, pos_within_output) { + return true; } if layer_popup_under(Layer::Top) || layer_toplevel_under(Layer::Top) { @@ -3438,13 +3477,9 @@ impl Niri { .or_else(|| layer_toplevel_under(Layer::Bottom)) .or_else(|| layer_toplevel_under(Layer::Background)); } else { - let hot_corners = self.config.borrow().gestures.hot_corners; - if !hot_corners.off { - let hot_corner = Rectangle::from_size(Size::from((1., 1.))); - if hot_corner.contains(pos_within_output) { - rv.hot_corner = true; - return rv; - } + if self.is_inside_hot_corner(output, pos_within_output) { + rv.hot_corner = true; + return rv; } under = under -- cgit