mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-24 02:01:18 +07:00
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 <yalterz@gmail.com> * Integrate code review Move is_inside_hot_corner * fixes --------- Co-authored-by: Aadniz <8147434+Aadniz@users.noreply.github.com> Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
This commit is contained in:
@@ -23,6 +23,10 @@ gestures {
|
|||||||
|
|
||||||
hot-corners {
|
hot-corners {
|
||||||
// off
|
// off
|
||||||
|
top-left
|
||||||
|
// top-right
|
||||||
|
// bottom-left
|
||||||
|
// bottom-right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -94,3 +98,18 @@ gestures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<sup>Since: next release</sup> You can choose specific hot corners by name: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
||||||
|
If no corners are explicitly set, the top-left corner will be active by default.
|
||||||
|
|
||||||
|
```kdl
|
||||||
|
// Enable the top-right and bottom-right hot corners.
|
||||||
|
gestures {
|
||||||
|
hot-corners {
|
||||||
|
top-right
|
||||||
|
bottom-right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also customize hot corners per-output [in the output config](./Configuration:-Outputs.md#hot-corners).
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ output "eDP-1" {
|
|||||||
focus-at-startup
|
focus-at-startup
|
||||||
background-color "#003300"
|
background-color "#003300"
|
||||||
backdrop-color "#001100"
|
backdrop-color "#001100"
|
||||||
|
|
||||||
|
hot-corners {
|
||||||
|
// off
|
||||||
|
top-left
|
||||||
|
// top-right
|
||||||
|
// bottom-left
|
||||||
|
// bottom-right
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output "HDMI-A-1" {
|
output "HDMI-A-1" {
|
||||||
@@ -217,3 +225,31 @@ output "HDMI-A-1" {
|
|||||||
backdrop-color "#001100"
|
backdrop-color "#001100"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `hot-corners`
|
||||||
|
|
||||||
|
<sup>Since: next release</sup>
|
||||||
|
|
||||||
|
Customize the hot corners for this output.
|
||||||
|
By default, hot corners [in the gestures settings](./Configuration:-Gestures.md#hot-corners) are used for all outputs.
|
||||||
|
|
||||||
|
Hot corners toggle the overview when you put your mouse at the very corner of a monitor.
|
||||||
|
|
||||||
|
`off` will disable the hot corners on this output, and writing specific corners will enable only those hot corners on this output.
|
||||||
|
|
||||||
|
```kdl
|
||||||
|
// Enable the bottom-left and bottom-right hot corners on HDMI-A-1.
|
||||||
|
output "HDMI-A-1" {
|
||||||
|
hot-corners {
|
||||||
|
bottom-left
|
||||||
|
bottom-right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable the hot corners on DP-2.
|
||||||
|
output "DP-2" {
|
||||||
|
hot-corners {
|
||||||
|
off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -54,4 +54,12 @@ impl Default for DndEdgeWorkspaceSwitch {
|
|||||||
pub struct HotCorners {
|
pub struct HotCorners {
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub off: bool,
|
pub off: bool,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub top_left: bool,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub top_right: bool,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub bottom_left: bool,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub bottom_right: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,6 +348,13 @@ mod tests {
|
|||||||
mode "1920x1080@144"
|
mode "1920x1080@144"
|
||||||
variable-refresh-rate on-demand=true
|
variable-refresh-rate on-demand=true
|
||||||
background-color "rgba(25, 25, 102, 1.0)"
|
background-color "rgba(25, 25, 102, 1.0)"
|
||||||
|
hot-corners {
|
||||||
|
off
|
||||||
|
top-left
|
||||||
|
top-right
|
||||||
|
bottom-left
|
||||||
|
bottom-right
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout {
|
layout {
|
||||||
@@ -742,6 +749,15 @@ mod tests {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
backdrop_color: None,
|
backdrop_color: None,
|
||||||
|
hot_corners: Some(
|
||||||
|
HotCorners {
|
||||||
|
off: true,
|
||||||
|
top_left: true,
|
||||||
|
top_right: true,
|
||||||
|
bottom_left: true,
|
||||||
|
bottom_right: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -1158,6 +1174,10 @@ mod tests {
|
|||||||
},
|
},
|
||||||
hot_corners: HotCorners {
|
hot_corners: HotCorners {
|
||||||
off: false,
|
off: false,
|
||||||
|
top_left: false,
|
||||||
|
top_right: false,
|
||||||
|
bottom_left: false,
|
||||||
|
bottom_right: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
overview: Overview {
|
overview: Overview {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use niri_ipc::{ConfiguredMode, Transform};
|
use niri_ipc::{ConfiguredMode, Transform};
|
||||||
|
|
||||||
|
use crate::gestures::HotCorners;
|
||||||
use crate::{Color, FloatOrInt};
|
use crate::{Color, FloatOrInt};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
@@ -27,6 +28,8 @@ pub struct Output {
|
|||||||
pub background_color: Option<Color>,
|
pub background_color: Option<Color>,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub backdrop_color: Option<Color>,
|
pub backdrop_color: Option<Color>,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub hot_corners: Option<HotCorners>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
@@ -56,6 +59,7 @@ impl Default for Output {
|
|||||||
variable_refresh_rate: None,
|
variable_refresh_rate: None,
|
||||||
background_color: None,
|
background_color: None,
|
||||||
backdrop_color: None,
|
backdrop_color: None,
|
||||||
|
hot_corners: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-10
@@ -3118,6 +3118,49 @@ impl Niri {
|
|||||||
Some((output, pos_within_output))
|
Some((output, pos_within_output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_inside_hot_corner(&self, output: &Output, pos: Point<f64, Logical>) -> bool {
|
||||||
|
let config = self.config.borrow();
|
||||||
|
let hot_corners = output
|
||||||
|
.user_data()
|
||||||
|
.get::<OutputName>()
|
||||||
|
.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<f64, Logical>| {
|
||||||
|
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(
|
pub fn is_sticky_obscured_under(
|
||||||
&self,
|
&self,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
@@ -3161,13 +3204,9 @@ impl Niri {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hot_corners = self.config.borrow().gestures.hot_corners;
|
if self.is_inside_hot_corner(output, pos_within_output) {
|
||||||
if !hot_corners.off {
|
|
||||||
let hot_corner = Rectangle::from_size(Size::from((1., 1.)));
|
|
||||||
if hot_corner.contains(pos_within_output) {
|
|
||||||
return true;
|
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;
|
||||||
@@ -3438,14 +3477,10 @@ 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_corners = self.config.borrow().gestures.hot_corners;
|
if self.is_inside_hot_corner(output, pos_within_output) {
|
||||||
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;
|
rv.hot_corner = true;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
under = under
|
under = under
|
||||||
.or_else(|| layer_popup_under(Layer::Top))
|
.or_else(|| layer_popup_under(Layer::Top))
|
||||||
|
|||||||
Reference in New Issue
Block a user