aboutsummaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-04-25 10:08:26 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-04-25 02:00:18 -0700
commite6d82d3ee34a7ac225d07ca3f4f4365cf27292d1 (patch)
tree36880baab7b89832ced2f38ab79e8b93b6cac687 /src/input
parentfae3a276418ef1f6f6baad465f160e33a6ac9d8a (diff)
downloadniri-e6d82d3ee34a7ac225d07ca3f4f4365cf27292d1.tar.gz
niri-e6d82d3ee34a7ac225d07ca3f4f4365cf27292d1.tar.bz2
niri-e6d82d3ee34a7ac225d07ca3f4f4365cf27292d1.zip
Implement top-left hot corner to toggle the Overview
Compared to third-party implementations such as waycorner: - It works during interactive window move (no surfaces receive pointer focus in this case, so this cannot work through layer-shell). - It works during drag-and-drop. - It disables itself over fullscreen windows. - It does not prevent direct scanout.
Diffstat (limited to 'src/input')
-rw-r--r--src/input/mod.rs42
1 files changed, 41 insertions, 1 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs
index 8ab9bde1..e7c2e38f 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -29,7 +29,7 @@ use smithay::input::touch::{
};
use smithay::input::SeatHandler;
use smithay::output::Output;
-use smithay::utils::{Logical, Point, Rectangle, Transform, SERIAL_COUNTER};
+use smithay::utils::{Logical, Point, Rectangle, Size, Transform, SERIAL_COUNTER};
use smithay::wayland::keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitor;
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
use smithay::wayland::selection::data_device::DnDGrab;
@@ -1995,6 +1995,10 @@ impl State {
}
fn on_pointer_motion<I: InputBackend>(&mut self, event: I::PointerMotionEvent) {
+ let was_inside_hot_corner = self.niri.pointer_inside_hot_corner;
+ // Any of the early returns here mean that the pointer is not inside the hot corner.
+ self.niri.pointer_inside_hot_corner = false;
+
// We need an output to be able to move the pointer.
if self.niri.global_space.outputs().next().is_none() {
return;
@@ -2171,6 +2175,22 @@ impl State {
pointer.frame(self);
+ // contents_under() will return no surface when the hot corner should trigger.
+ let hot_corners = self.niri.config.borrow().gestures.hot_corners;
+ if !hot_corners.off
+ && pointer.current_focus().is_none()
+ && !self.niri.screenshot_ui.is_open()
+ {
+ let hot_corner = Rectangle::from_size(Size::from((1., 1.)));
+ if let Some((_, pos_within_output)) = self.niri.output_under(pos) {
+ let inside_hot_corner = hot_corner.contains(pos_within_output);
+ if inside_hot_corner && !was_inside_hot_corner {
+ self.niri.layout.toggle_overview();
+ }
+ self.niri.pointer_inside_hot_corner = inside_hot_corner;
+ }
+ }
+
// Activate a new confinement if necessary.
self.niri.maybe_activate_pointer_constraint();
@@ -2195,6 +2215,10 @@ impl State {
&mut self,
event: I::PointerMotionAbsoluteEvent,
) {
+ let was_inside_hot_corner = self.niri.pointer_inside_hot_corner;
+ // Any of the early returns here mean that the pointer is not inside the hot corner.
+ self.niri.pointer_inside_hot_corner = false;
+
let Some(pos) = self.compute_absolute_location(&event, None).or_else(|| {
self.global_bounding_rectangle().map(|output_geo| {
event.position_transformed(output_geo.size) + output_geo.loc.to_f64()
@@ -2240,6 +2264,22 @@ impl State {
pointer.frame(self);
+ // contents_under() will return no surface when the hot corner should trigger.
+ let hot_corners = self.niri.config.borrow().gestures.hot_corners;
+ if !hot_corners.off
+ && pointer.current_focus().is_none()
+ && !self.niri.screenshot_ui.is_open()
+ {
+ let hot_corner = Rectangle::from_size(Size::from((1., 1.)));
+ if let Some((_, pos_within_output)) = self.niri.output_under(pos) {
+ let inside_hot_corner = hot_corner.contains(pos_within_output);
+ if inside_hot_corner && !was_inside_hot_corner {
+ self.niri.layout.toggle_overview();
+ }
+ self.niri.pointer_inside_hot_corner = inside_hot_corner;
+ }
+ }
+
self.niri.maybe_activate_pointer_constraint();
// We moved the pointer, show it.