mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
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.
This commit is contained in:
@@ -1210,6 +1210,8 @@ pub struct Gestures {
|
|||||||
pub dnd_edge_view_scroll: DndEdgeViewScroll,
|
pub dnd_edge_view_scroll: DndEdgeViewScroll,
|
||||||
#[knuffel(child, default)]
|
#[knuffel(child, default)]
|
||||||
pub dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch,
|
pub dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch,
|
||||||
|
#[knuffel(child, default)]
|
||||||
|
pub hot_corners: HotCorners,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
||||||
@@ -1252,6 +1254,12 @@ impl Default for DndEdgeWorkspaceSwitch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
||||||
|
pub struct HotCorners {
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub off: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct Overview {
|
pub struct Overview {
|
||||||
#[knuffel(child, unwrap(argument), default = Self::default().zoom)]
|
#[knuffel(child, unwrap(argument), default = Self::default().zoom)]
|
||||||
@@ -4587,6 +4595,9 @@ mod tests {
|
|||||||
1500.0,
|
1500.0,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
hot_corners: HotCorners {
|
||||||
|
off: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
overview: Overview {
|
overview: Overview {
|
||||||
zoom: FloatOrInt(
|
zoom: FloatOrInt(
|
||||||
|
|||||||
+41
-1
@@ -29,7 +29,7 @@ use smithay::input::touch::{
|
|||||||
};
|
};
|
||||||
use smithay::input::SeatHandler;
|
use smithay::input::SeatHandler;
|
||||||
use smithay::output::Output;
|
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::keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitor;
|
||||||
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
|
||||||
use smithay::wayland::selection::data_device::DnDGrab;
|
use smithay::wayland::selection::data_device::DnDGrab;
|
||||||
@@ -1995,6 +1995,10 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_pointer_motion<I: InputBackend>(&mut self, event: I::PointerMotionEvent) {
|
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.
|
// We need an output to be able to move the pointer.
|
||||||
if self.niri.global_space.outputs().next().is_none() {
|
if self.niri.global_space.outputs().next().is_none() {
|
||||||
return;
|
return;
|
||||||
@@ -2171,6 +2175,22 @@ impl State {
|
|||||||
|
|
||||||
pointer.frame(self);
|
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.
|
// Activate a new confinement if necessary.
|
||||||
self.niri.maybe_activate_pointer_constraint();
|
self.niri.maybe_activate_pointer_constraint();
|
||||||
|
|
||||||
@@ -2195,6 +2215,10 @@ impl State {
|
|||||||
&mut self,
|
&mut self,
|
||||||
event: I::PointerMotionAbsoluteEvent,
|
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(|| {
|
let Some(pos) = self.compute_absolute_location(&event, None).or_else(|| {
|
||||||
self.global_bounding_rectangle().map(|output_geo| {
|
self.global_bounding_rectangle().map(|output_geo| {
|
||||||
event.position_transformed(output_geo.size) + output_geo.loc.to_f64()
|
event.position_transformed(output_geo.size) + output_geo.loc.to_f64()
|
||||||
@@ -2240,6 +2264,22 @@ impl State {
|
|||||||
|
|
||||||
pointer.frame(self);
|
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();
|
self.niri.maybe_activate_pointer_constraint();
|
||||||
|
|
||||||
// We moved the pointer, show it.
|
// We moved the pointer, show it.
|
||||||
|
|||||||
+12
@@ -347,6 +347,7 @@ pub struct Niri {
|
|||||||
/// Used for limiting the notify to once per iteration, so that it's not spammed with high
|
/// Used for limiting the notify to once per iteration, so that it's not spammed with high
|
||||||
/// resolution mice.
|
/// resolution mice.
|
||||||
pub notified_activity_this_iteration: bool,
|
pub notified_activity_this_iteration: bool,
|
||||||
|
pub pointer_inside_hot_corner: bool,
|
||||||
pub tablet_cursor_location: Option<Point<f64, Logical>>,
|
pub tablet_cursor_location: Option<Point<f64, Logical>>,
|
||||||
pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,
|
pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,
|
||||||
pub overview_scroll_swipe_gesture: ScrollSwipeGesture,
|
pub overview_scroll_swipe_gesture: ScrollSwipeGesture,
|
||||||
@@ -2427,6 +2428,7 @@ impl Niri {
|
|||||||
pointer_inactivity_timer: None,
|
pointer_inactivity_timer: None,
|
||||||
pointer_inactivity_timer_got_reset: false,
|
pointer_inactivity_timer_got_reset: false,
|
||||||
notified_activity_this_iteration: false,
|
notified_activity_this_iteration: false,
|
||||||
|
pointer_inside_hot_corner: false,
|
||||||
tablet_cursor_location: None,
|
tablet_cursor_location: None,
|
||||||
gesture_swipe_3f_cumulative: None,
|
gesture_swipe_3f_cumulative: None,
|
||||||
overview_scroll_swipe_gesture: ScrollSwipeGesture::new(),
|
overview_scroll_swipe_gesture: ScrollSwipeGesture::new(),
|
||||||
@@ -2920,6 +2922,11 @@ impl Niri {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hot_corner = Rectangle::from_size(Size::from((1., 1.)));
|
||||||
|
if hot_corner.contains(pos_within_output) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if layer_popup_under(Layer::Top) || layer_toplevel_under(Layer::Top) {
|
if layer_popup_under(Layer::Top) || layer_toplevel_under(Layer::Top) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3175,6 +3182,11 @@ impl Niri {
|
|||||||
.or_else(|| layer_toplevel_under(Layer::Bottom))
|
.or_else(|| layer_toplevel_under(Layer::Bottom))
|
||||||
.or_else(|| layer_toplevel_under(Layer::Background));
|
.or_else(|| layer_toplevel_under(Layer::Background));
|
||||||
} else {
|
} else {
|
||||||
|
let hot_corner = Rectangle::from_size(Size::from((1., 1.)));
|
||||||
|
if hot_corner.contains(pos_within_output) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
under = under
|
under = under
|
||||||
.or_else(|| layer_popup_under(Layer::Top))
|
.or_else(|| layer_popup_under(Layer::Top))
|
||||||
.or_else(|| layer_toplevel_under(Layer::Top));
|
.or_else(|| layer_toplevel_under(Layer::Top));
|
||||||
|
|||||||
Reference in New Issue
Block a user