mirror of
https://github.com/YaLTeR/niri.git
synced 2024-09-11 20:37:25 +03:00
Compare commits
11 Commits
0c57815fbf
...
4740682904
Author | SHA1 | Date | |
---|---|---|---|
|
4740682904 | ||
|
df9d721f74 | ||
|
d970abead8 | ||
|
4f6ed9dfc9 | ||
|
84302796dc | ||
|
a39e703fc3 | ||
|
a55db6c6c4 | ||
|
a011b385d8 | ||
|
2984722f80 | ||
|
118773e17d | ||
|
741bee461c |
16
README.md
16
README.md
@ -146,6 +146,14 @@ For example, `niri msg --json outputs`.
|
||||
For programmatic access, check the [niri-ipc sub-crate](./niri-ipc/) which defines the types.
|
||||
The communication over the IPC socket happens in JSON.
|
||||
|
||||
## Configuration
|
||||
|
||||
Niri will load configuration from `$XDG_CONFIG_HOME/.config/niri/config.kdl` or `~/.config/niri/config.kdl`.
|
||||
If this fails, it will load [the default configuration file](resources/default-config.kdl).
|
||||
Please use the default configuration file as the starting point for your custom configuration.
|
||||
|
||||
Niri will live-reload most of the configuration settings, like key binds or gaps or output modes, as you change the config file.
|
||||
|
||||
## Default Hotkeys
|
||||
|
||||
When running on a TTY, the Mod key is <kbd>Super</kbd>.
|
||||
@ -195,14 +203,6 @@ The general system is: if a hotkey switches somewhere, then adding <kbd>Ctrl</kb
|
||||
| <kbd>Ctrl</kbd><kbd>PrtSc</kbd> | Take a screenshot of the focused monitor to clipboard and to `~/Pictures/Screenshots/` |
|
||||
| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>E</kbd> | Exit niri |
|
||||
|
||||
## Configuration
|
||||
|
||||
Niri will load configuration from `$XDG_CONFIG_HOME/.config/niri/config.kdl` or `~/.config/niri/config.kdl`.
|
||||
If this fails, it will load [the default configuration file](resources/default-config.kdl).
|
||||
Please use the default configuration file as the starting point for your custom configuration.
|
||||
|
||||
Niri will live-reload most of the configuration settings, like key binds or gaps or output modes, as you change the config file.
|
||||
|
||||
## Contact
|
||||
|
||||
We have a Matrix chat, feel free to join and ask a question: https://matrix.to/#/#niri:matrix.org
|
||||
|
@ -69,6 +69,10 @@ pub struct Input {
|
||||
pub touch: Touch,
|
||||
#[knuffel(child)]
|
||||
pub disable_power_key_handling: bool,
|
||||
#[knuffel(child)]
|
||||
pub warp_mouse_to_focus: bool,
|
||||
#[knuffel(child)]
|
||||
pub focus_follows_mouse: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)]
|
||||
@ -1592,6 +1596,9 @@ mod tests {
|
||||
}
|
||||
|
||||
disable-power-key-handling
|
||||
|
||||
warp-mouse-to-focus
|
||||
focus-follows-mouse
|
||||
}
|
||||
|
||||
output "eDP-1" {
|
||||
@ -1731,6 +1738,8 @@ mod tests {
|
||||
map_to_output: Some("eDP-1".to_owned()),
|
||||
},
|
||||
disable_power_key_handling: true,
|
||||
warp_mouse_to_focus: true,
|
||||
focus_follows_mouse: true,
|
||||
},
|
||||
outputs: vec![Output {
|
||||
off: false,
|
||||
|
@ -67,6 +67,12 @@ input {
|
||||
// Uncomment this if you would like to configure the power button elsewhere
|
||||
// (i.e. logind.conf).
|
||||
// disable-power-key-handling
|
||||
|
||||
// Uncomment this to make the mouse warp to the center of newly focused windows.
|
||||
// warp-mouse-to-focus
|
||||
|
||||
// Focus windows and outputs automatically when moving the mouse into them.
|
||||
// focus-follows-mouse
|
||||
}
|
||||
|
||||
// You can configure outputs by their name, which you can find
|
||||
|
@ -143,6 +143,7 @@ impl CompositorHandler for State {
|
||||
})
|
||||
.map(|(window, _)| window.clone());
|
||||
|
||||
let window = window.clone();
|
||||
let win = window.clone();
|
||||
|
||||
let output = if let Some(p) = parent {
|
||||
@ -161,6 +162,12 @@ impl CompositorHandler for State {
|
||||
|
||||
if let Some(output) = output.cloned() {
|
||||
self.niri.layout.start_open_animation_for_window(&window);
|
||||
|
||||
let new_active_window = self.niri.layout.active_window().map(|(w, _)| w);
|
||||
if new_active_window == Some(&window) {
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
}
|
||||
|
||||
self.niri.queue_redraw(output);
|
||||
}
|
||||
return;
|
||||
|
@ -460,7 +460,15 @@ impl XdgShellHandler for State {
|
||||
return;
|
||||
};
|
||||
|
||||
let active_window = self.niri.layout.active_window().map(|(w, _)| w);
|
||||
let was_active = active_window == Some(&window);
|
||||
|
||||
self.niri.layout.remove_window(&window);
|
||||
|
||||
if was_active {
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
}
|
||||
|
||||
self.niri.queue_redraw(output);
|
||||
}
|
||||
|
||||
|
140
src/input.rs
140
src/input.rs
@ -411,139 +411,166 @@ impl State {
|
||||
}
|
||||
Action::MoveColumnLeft => {
|
||||
self.niri.layout.move_left();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnRight => {
|
||||
self.niri.layout.move_right();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToFirst => {
|
||||
self.niri.layout.move_column_to_first();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToLast => {
|
||||
self.niri.layout.move_column_to_last();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowDown => {
|
||||
self.niri.layout.move_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowUp => {
|
||||
self.niri.layout.move_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowDownOrToWorkspaceDown => {
|
||||
self.niri.layout.move_down_or_to_workspace_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowUpOrToWorkspaceUp => {
|
||||
self.niri.layout.move_up_or_to_workspace_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::ConsumeOrExpelWindowLeft => {
|
||||
self.niri.layout.consume_or_expel_window_left();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::ConsumeOrExpelWindowRight => {
|
||||
self.niri.layout.consume_or_expel_window_right();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusColumnLeft => {
|
||||
self.niri.layout.focus_left();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusColumnRight => {
|
||||
self.niri.layout.focus_right();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusColumnFirst => {
|
||||
self.niri.layout.focus_column_first();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusColumnLast => {
|
||||
self.niri.layout.focus_column_last();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWindowDown => {
|
||||
self.niri.layout.focus_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWindowUp => {
|
||||
self.niri.layout.focus_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWindowOrWorkspaceDown => {
|
||||
self.niri.layout.focus_window_or_workspace_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWindowOrWorkspaceUp => {
|
||||
self.niri.layout.focus_window_or_workspace_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowToWorkspaceDown => {
|
||||
self.niri.layout.move_to_workspace_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowToWorkspaceUp => {
|
||||
self.niri.layout.move_to_workspace_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveWindowToWorkspace(idx) => {
|
||||
let idx = idx.saturating_sub(1) as usize;
|
||||
self.niri.layout.move_to_workspace(idx);
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToWorkspaceDown => {
|
||||
self.niri.layout.move_column_to_workspace_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToWorkspaceUp => {
|
||||
self.niri.layout.move_column_to_workspace_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::MoveColumnToWorkspace(idx) => {
|
||||
let idx = idx.saturating_sub(1) as usize;
|
||||
self.niri.layout.move_column_to_workspace(idx);
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWorkspaceDown => {
|
||||
self.niri.layout.switch_workspace_down();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWorkspaceUp => {
|
||||
self.niri.layout.switch_workspace_up();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::FocusWorkspace(idx) => {
|
||||
let idx = idx.saturating_sub(1) as usize;
|
||||
self.niri.layout.switch_workspace(idx);
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
@ -559,11 +586,14 @@ impl State {
|
||||
}
|
||||
Action::ConsumeWindowIntoColumn => {
|
||||
self.niri.layout.consume_into_column();
|
||||
// This does not cause immediate focus or window size change, so warping mouse to
|
||||
// focus won't do anything here.
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
Action::ExpelWindowFromColumn => {
|
||||
self.niri.layout.expel_from_column();
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
// FIXME: granular
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
@ -581,81 +611,105 @@ impl State {
|
||||
Action::FocusMonitorLeft => {
|
||||
if let Some(output) = self.niri.output_left() {
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::FocusMonitorRight => {
|
||||
if let Some(output) = self.niri.output_right() {
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::FocusMonitorDown => {
|
||||
if let Some(output) = self.niri.output_down() {
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::FocusMonitorUp => {
|
||||
if let Some(output) = self.niri.output_up() {
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWindowToMonitorLeft => {
|
||||
if let Some(output) = self.niri.output_left() {
|
||||
self.niri.layout.move_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWindowToMonitorRight => {
|
||||
if let Some(output) = self.niri.output_right() {
|
||||
self.niri.layout.move_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWindowToMonitorDown => {
|
||||
if let Some(output) = self.niri.output_down() {
|
||||
self.niri.layout.move_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWindowToMonitorUp => {
|
||||
if let Some(output) = self.niri.output_up() {
|
||||
self.niri.layout.move_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveColumnToMonitorLeft => {
|
||||
if let Some(output) = self.niri.output_left() {
|
||||
self.niri.layout.move_column_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveColumnToMonitorRight => {
|
||||
if let Some(output) = self.niri.output_right() {
|
||||
self.niri.layout.move_column_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveColumnToMonitorDown => {
|
||||
if let Some(output) = self.niri.output_down() {
|
||||
self.niri.layout.move_column_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveColumnToMonitorUp => {
|
||||
if let Some(output) = self.niri.output_up() {
|
||||
self.niri.layout.move_column_to_output(&output);
|
||||
self.niri.layout.focus_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::SetColumnWidth(change) => {
|
||||
@ -672,25 +726,33 @@ impl State {
|
||||
Action::MoveWorkspaceToMonitorLeft => {
|
||||
if let Some(output) = self.niri.output_left() {
|
||||
self.niri.layout.move_workspace_to_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWorkspaceToMonitorRight => {
|
||||
if let Some(output) = self.niri.output_right() {
|
||||
self.niri.layout.move_workspace_to_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWorkspaceToMonitorDown => {
|
||||
if let Some(output) = self.niri.output_down() {
|
||||
self.niri.layout.move_workspace_to_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::MoveWorkspaceToMonitorUp => {
|
||||
if let Some(output) = self.niri.output_up() {
|
||||
self.niri.layout.move_workspace_to_output(&output);
|
||||
self.move_cursor_to_output(&output);
|
||||
if !self.maybe_warp_cursor_to_focus_centered() {
|
||||
self.move_cursor_to_output(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -716,12 +778,11 @@ impl State {
|
||||
|
||||
// Check if we have an active pointer constraint.
|
||||
let mut pointer_confined = None;
|
||||
if let Some(focus) = self.niri.pointer_focus.as_ref() {
|
||||
let focus_surface_loc = focus.surface.1;
|
||||
let pos_within_surface = pos.to_i32_round() - focus_surface_loc;
|
||||
if let Some(focus) = &self.niri.pointer_focus.surface {
|
||||
let pos_within_surface = pos.to_i32_round() - focus.1;
|
||||
|
||||
let mut pointer_locked = false;
|
||||
with_pointer_constraint(&focus.surface.0, &pointer, |constraint| {
|
||||
with_pointer_constraint(&focus.0, &pointer, |constraint| {
|
||||
let Some(constraint) = constraint else { return };
|
||||
if !constraint.is_active() {
|
||||
return;
|
||||
@ -739,7 +800,7 @@ impl State {
|
||||
pointer_locked = true;
|
||||
}
|
||||
PointerConstraint::Confined(confine) => {
|
||||
pointer_confined = Some((focus.surface.clone(), confine.region().cloned()));
|
||||
pointer_confined = Some((focus.clone(), confine.region().cloned()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -748,7 +809,7 @@ impl State {
|
||||
if pointer_locked {
|
||||
pointer.relative_motion(
|
||||
self,
|
||||
Some(focus.surface.clone()),
|
||||
Some(focus.clone()),
|
||||
&RelativeMotionEvent {
|
||||
delta: event.delta(),
|
||||
delta_unaccel: event.delta_unaccel(),
|
||||
@ -813,7 +874,7 @@ impl State {
|
||||
let mut prevent = false;
|
||||
|
||||
// Prevent the pointer from leaving the focused surface.
|
||||
if Some(&focus_surface.0) != under.as_ref().map(|x| &x.surface.0) {
|
||||
if Some(&focus_surface.0) != under.surface.as_ref().map(|(s, _)| s) {
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
@ -842,15 +903,16 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
self.niri.handle_focus_follows_mouse(&under);
|
||||
|
||||
// Activate a new confinement if necessary.
|
||||
self.niri.maybe_activate_pointer_constraint(new_pos, &under);
|
||||
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
under.clone(),
|
||||
under.surface.clone(),
|
||||
&MotionEvent {
|
||||
location: new_pos,
|
||||
serial,
|
||||
@ -860,7 +922,7 @@ impl State {
|
||||
|
||||
pointer.relative_motion(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&RelativeMotionEvent {
|
||||
delta: event.delta(),
|
||||
delta_unaccel: event.delta_unaccel(),
|
||||
@ -907,13 +969,15 @@ impl State {
|
||||
}
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
|
||||
self.niri.handle_focus_follows_mouse(&under);
|
||||
|
||||
self.niri.maybe_activate_pointer_constraint(pos, &under);
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&MotionEvent {
|
||||
location: pos,
|
||||
serial,
|
||||
@ -1051,7 +1115,6 @@ impl State {
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
let tablet_seat = self.niri.seat.tablet_seat();
|
||||
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));
|
||||
@ -1078,7 +1141,7 @@ impl State {
|
||||
|
||||
tool.motion(
|
||||
pos,
|
||||
under,
|
||||
under.surface,
|
||||
&tablet,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
event.time_msec(),
|
||||
@ -1133,7 +1196,6 @@ impl State {
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
let tablet_seat = self.niri.seat.tablet_seat();
|
||||
let tool = tablet_seat.add_tool::<Self>(&self.niri.display_handle, &event.tool());
|
||||
@ -1141,7 +1203,7 @@ impl State {
|
||||
if let Some(tablet) = tablet {
|
||||
match event.state() {
|
||||
ProximityState::In => {
|
||||
if let Some(under) = under {
|
||||
if let Some(under) = under.surface {
|
||||
tool.proximity_in(
|
||||
pos,
|
||||
under,
|
||||
@ -1476,13 +1538,10 @@ impl State {
|
||||
};
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let under = self
|
||||
.niri
|
||||
.surface_under_and_global_space(touch_location)
|
||||
.map(|under| under.surface);
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
handle.down(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&DownEvent {
|
||||
slot: evt.slot(),
|
||||
location: touch_location,
|
||||
@ -1512,13 +1571,10 @@ impl State {
|
||||
let Some(touch_location) = self.compute_touch_location(&evt) else {
|
||||
return;
|
||||
};
|
||||
let under = self
|
||||
.niri
|
||||
.surface_under_and_global_space(touch_location)
|
||||
.map(|under| under.surface);
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
handle.motion(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&TouchMotionEvent {
|
||||
slot: evt.slot(),
|
||||
location: touch_location,
|
||||
|
@ -67,6 +67,13 @@ impl WorkspaceSwitch {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_idx(&self) -> f64 {
|
||||
match self {
|
||||
WorkspaceSwitch::Animation(anim) => anim.to(),
|
||||
WorkspaceSwitch::Gesture(gesture) => gesture.current_idx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the workspace switch is [`Animation`].
|
||||
///
|
||||
/// [`Animation`]: WorkspaceSwitch::Animation
|
||||
@ -583,6 +590,25 @@ impl<W: LayoutElement> Monitor<W> {
|
||||
self.clean_up_workspaces();
|
||||
}
|
||||
|
||||
/// Returns the geometry of the active tile relative to and clamped to the output.
|
||||
///
|
||||
/// During animations, assumes the final view position.
|
||||
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<i32, Logical>> {
|
||||
let mut rect = self.active_workspace_ref().active_tile_visual_rectangle()?;
|
||||
|
||||
if let Some(switch) = &self.workspace_switch {
|
||||
let size = output_size(&self.output);
|
||||
|
||||
let offset = switch.target_idx() - self.active_workspace_idx as f64;
|
||||
let offset = (offset * size.h as f64).round() as i32;
|
||||
|
||||
let clip_rect = Rectangle::from_loc_and_size((0, -offset), size);
|
||||
rect = rect.intersection(clip_rect)?;
|
||||
}
|
||||
|
||||
Some(rect)
|
||||
}
|
||||
|
||||
pub fn window_under(
|
||||
&self,
|
||||
pos_within_output: Point<f64, Logical>,
|
||||
|
@ -181,6 +181,15 @@ impl OutputId {
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewOffsetAdjustment {
|
||||
pub fn target_view_offset(&self) -> f64 {
|
||||
match self {
|
||||
ViewOffsetAdjustment::Animation(anim) => anim.to(),
|
||||
ViewOffsetAdjustment::Gesture(gesture) => gesture.current_view_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ColumnWidth {
|
||||
fn resolve(self, options: &Options, view_width: i32) -> i32 {
|
||||
match self {
|
||||
@ -1124,6 +1133,31 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
first.chain(rest)
|
||||
}
|
||||
|
||||
fn active_column_ref(&self) -> Option<&Column<W>> {
|
||||
if self.columns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(&self.columns[self.active_column_idx])
|
||||
}
|
||||
|
||||
/// Returns the geometry of the active tile relative to and clamped to the view.
|
||||
///
|
||||
/// During animations, assumes the final view position.
|
||||
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<i32, Logical>> {
|
||||
let col = self.active_column_ref()?;
|
||||
let view_pos = self
|
||||
.view_offset_adj
|
||||
.as_ref()
|
||||
.map_or(self.view_offset, |adj| adj.target_view_offset() as i32);
|
||||
|
||||
let tile_pos = Point::from((-view_pos, col.tile_y(col.active_tile_idx)));
|
||||
let tile_size = col.active_tile_ref().tile_size();
|
||||
let tile_rect = Rectangle::from_loc_and_size(tile_pos, tile_size);
|
||||
|
||||
let view = Rectangle::from_loc_and_size((0, 0), self.view_size);
|
||||
view.intersection(tile_rect)
|
||||
}
|
||||
|
||||
pub fn window_under(
|
||||
&self,
|
||||
pos: Point<f64, Logical>,
|
||||
@ -2010,6 +2044,10 @@ impl<W: LayoutElement> Column<W> {
|
||||
pos
|
||||
})
|
||||
}
|
||||
|
||||
fn active_tile_ref(&self) -> &Tile<W> {
|
||||
&self.tiles[self.active_tile_idx]
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_new_view_offset(
|
||||
|
268
src/niri.rs
268
src/niri.rs
@ -112,7 +112,7 @@ use crate::ui::hotkey_overlay::HotkeyOverlay;
|
||||
use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
||||
use crate::utils::spawning::CHILD_ENV;
|
||||
use crate::utils::{
|
||||
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
||||
center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
||||
};
|
||||
use crate::window::Unmapped;
|
||||
use crate::{animation, niri_render_elements};
|
||||
@ -193,11 +193,7 @@ pub struct Niri {
|
||||
pub seat: Seat<State>,
|
||||
/// Scancodes of the keys to suppress.
|
||||
pub suppressed_keys: HashSet<u32>,
|
||||
// This is always a toplevel surface focused as far as niri's logic is concerned, even when
|
||||
// popup grabs are active (which means the real keyboard focus is on a popup descending from
|
||||
// this toplevel surface).
|
||||
pub keyboard_focus: Option<WlSurface>,
|
||||
|
||||
pub keyboard_focus: KeyboardFocus,
|
||||
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||
pub is_fdo_idle_inhibited: Arc<AtomicBool>,
|
||||
|
||||
@ -205,7 +201,7 @@ pub struct Niri {
|
||||
pub cursor_texture_cache: CursorTextureCache,
|
||||
pub cursor_shape_manager_state: CursorShapeManagerState,
|
||||
pub dnd_icon: Option<WlSurface>,
|
||||
pub pointer_focus: Option<PointerFocus>,
|
||||
pub pointer_focus: PointerFocus,
|
||||
pub tablet_cursor_location: Option<Point<f64, Logical>>,
|
||||
pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,
|
||||
|
||||
@ -284,10 +280,26 @@ pub struct PopupGrabState {
|
||||
pub grab: PopupGrab<State>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
// The surfaces here are always toplevel surfaces focused as far as niri's logic is concerned, even
|
||||
// when popup grabs are active (which means the real keyboard focus is on a popup descending from
|
||||
// that toplevel surface).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum KeyboardFocus {
|
||||
// Layout is focused by default if there's nothing else to focus.
|
||||
Layout { surface: Option<WlSurface> },
|
||||
LayerShell { surface: WlSurface },
|
||||
LockScreen { surface: Option<WlSurface> },
|
||||
ScreenshotUi,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq)]
|
||||
pub struct PointerFocus {
|
||||
pub output: Output,
|
||||
pub surface: (WlSurface, Point<i32, Logical>),
|
||||
// Output under pointer.
|
||||
pub output: Option<Output>,
|
||||
// Surface under pointer and its location in global coordinate space.
|
||||
pub surface: Option<(WlSurface, Point<i32, Logical>)>,
|
||||
// If surface belongs to a window, this is that window.
|
||||
pub window: Option<Window>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -314,6 +326,11 @@ struct SurfaceFrameThrottlingState {
|
||||
last_sent_at: RefCell<Option<(Output, u32)>>,
|
||||
}
|
||||
|
||||
pub enum CenterCoords {
|
||||
Seperately,
|
||||
Both,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WindowOffscreenId(pub RefCell<Option<Id>>);
|
||||
|
||||
@ -325,6 +342,30 @@ impl Default for SurfaceFrameThrottlingState {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardFocus {
|
||||
pub fn surface(&self) -> Option<&WlSurface> {
|
||||
match self {
|
||||
KeyboardFocus::Layout { surface } => surface.as_ref(),
|
||||
KeyboardFocus::LayerShell { surface } => Some(surface),
|
||||
KeyboardFocus::LockScreen { surface } => surface.as_ref(),
|
||||
KeyboardFocus::ScreenshotUi => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_surface(self) -> Option<WlSurface> {
|
||||
match self {
|
||||
KeyboardFocus::Layout { surface } => surface,
|
||||
KeyboardFocus::LayerShell { surface } => Some(surface),
|
||||
KeyboardFocus::LockScreen { surface } => surface,
|
||||
KeyboardFocus::ScreenshotUi => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_layout(&self) -> bool {
|
||||
matches!(self, KeyboardFocus::Layout { .. })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub backend: Backend,
|
||||
pub niri: Niri,
|
||||
@ -384,12 +425,11 @@ impl State {
|
||||
self.niri
|
||||
.maybe_activate_pointer_constraint(location, &under);
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
pointer.motion(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&MotionEvent {
|
||||
location,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
@ -401,6 +441,86 @@ impl State {
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
|
||||
/// Moves cursor within the specified rectangle, only adjusting coordinates if needed.
|
||||
fn move_cursor_to_rect(&mut self, rect: Rectangle<f64, Logical>, mode: CenterCoords) -> bool {
|
||||
let pointer = &self.niri.seat.get_pointer().unwrap();
|
||||
let cur_loc = pointer.current_location();
|
||||
let x_in_bound = cur_loc.x >= rect.loc.x && cur_loc.x <= rect.loc.x + rect.size.w;
|
||||
let y_in_bound = cur_loc.y >= rect.loc.y && cur_loc.y <= rect.loc.y + rect.size.h;
|
||||
|
||||
let p = match mode {
|
||||
CenterCoords::Seperately => {
|
||||
if x_in_bound && y_in_bound {
|
||||
return false;
|
||||
} else if y_in_bound {
|
||||
// adjust x
|
||||
Point::from((rect.loc.x + rect.size.w / 2.0, cur_loc.y))
|
||||
} else if x_in_bound {
|
||||
// adjust y
|
||||
Point::from((cur_loc.x, rect.loc.y + rect.size.h / 2.0))
|
||||
} else {
|
||||
// adjust x and y
|
||||
center_f64(rect)
|
||||
}
|
||||
}
|
||||
CenterCoords::Both => {
|
||||
if x_in_bound && y_in_bound {
|
||||
return false;
|
||||
} else {
|
||||
// adjust x and y
|
||||
center_f64(rect)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.move_cursor(p);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn move_cursor_to_focused_tile(&mut self, mode: CenterCoords) -> bool {
|
||||
if !self.niri.keyboard_focus.is_layout() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.niri.tablet_cursor_location.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(output) = self.niri.layout.active_output() else {
|
||||
return false;
|
||||
};
|
||||
let output = output.clone();
|
||||
let monitor = self.niri.layout.monitor_for_output(&output).unwrap();
|
||||
|
||||
let mut rv = false;
|
||||
let rect = monitor.active_tile_visual_rectangle();
|
||||
|
||||
if let Some(rect) = rect {
|
||||
let output_geo = self.niri.global_space.output_geometry(&output).unwrap();
|
||||
let mut rect = rect;
|
||||
rect.loc += output_geo.loc;
|
||||
rv = self.move_cursor_to_rect(rect.to_f64(), mode);
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn maybe_warp_cursor_to_focus(&mut self) -> bool {
|
||||
if !self.niri.config.borrow().input.warp_mouse_to_focus {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.move_cursor_to_focused_tile(CenterCoords::Seperately)
|
||||
}
|
||||
|
||||
pub fn maybe_warp_cursor_to_focus_centered(&mut self) -> bool {
|
||||
if !self.niri.config.borrow().input.warp_mouse_to_focus {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.move_cursor_to_focused_tile(CenterCoords::Both)
|
||||
}
|
||||
|
||||
pub fn refresh_pointer_focus(&mut self) {
|
||||
let _span = tracy_client::span!("Niri::refresh_pointer_focus");
|
||||
|
||||
@ -443,11 +563,10 @@ impl State {
|
||||
.maybe_activate_pointer_constraint(location, &under);
|
||||
|
||||
self.niri.pointer_focus.clone_from(&under);
|
||||
let under = under.map(|u| u.surface);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
under,
|
||||
under.surface,
|
||||
&MotionEvent {
|
||||
location,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
@ -486,9 +605,11 @@ impl State {
|
||||
|
||||
pub fn update_keyboard_focus(&mut self) {
|
||||
let focus = if self.niri.is_locked() {
|
||||
self.niri.lock_surface_focus()
|
||||
KeyboardFocus::LockScreen {
|
||||
surface: self.niri.lock_surface_focus(),
|
||||
}
|
||||
} else if self.niri.screenshot_ui.is_open() {
|
||||
None
|
||||
KeyboardFocus::ScreenshotUi
|
||||
} else if let Some(output) = self.niri.layout.active_output() {
|
||||
let mon = self.niri.layout.monitor_for_output(output).unwrap();
|
||||
let layers = layer_map_for_output(output);
|
||||
@ -501,7 +622,9 @@ impl State {
|
||||
.map(|l| (&g.root, l.layer()))
|
||||
});
|
||||
let grab_on_layer = |layer: Layer| {
|
||||
layer_grab.and_then(move |(s, l)| if l == layer { Some(s.clone()) } else { None })
|
||||
layer_grab
|
||||
.and_then(move |(s, l)| if l == layer { Some(s.clone()) } else { None })
|
||||
.map(|surface| KeyboardFocus::LayerShell { surface })
|
||||
};
|
||||
|
||||
let layout_focus = || {
|
||||
@ -509,11 +632,15 @@ impl State {
|
||||
.layout
|
||||
.focus()
|
||||
.map(|win| win.toplevel().expect("no x11 support").wl_surface().clone())
|
||||
.map(|surface| KeyboardFocus::Layout {
|
||||
surface: Some(surface),
|
||||
})
|
||||
};
|
||||
let layer_focus = |surface: &LayerSurface| {
|
||||
surface
|
||||
.can_receive_keyboard_focus()
|
||||
.then(|| surface.wl_surface().clone())
|
||||
.map(|surface| KeyboardFocus::LayerShell { surface })
|
||||
};
|
||||
|
||||
let mut surface = grab_on_layer(Layer::Overlay);
|
||||
@ -532,9 +659,9 @@ impl State {
|
||||
surface = surface.or_else(layout_focus);
|
||||
}
|
||||
|
||||
surface
|
||||
surface.unwrap_or(KeyboardFocus::Layout { surface: None })
|
||||
} else {
|
||||
None
|
||||
KeyboardFocus::Layout { surface: None }
|
||||
};
|
||||
|
||||
let keyboard = self.niri.seat.get_keyboard().unwrap();
|
||||
@ -546,7 +673,7 @@ impl State {
|
||||
);
|
||||
|
||||
if let Some(grab) = self.niri.popup_grab.as_mut() {
|
||||
if Some(&grab.root) != focus.as_ref() {
|
||||
if Some(&grab.root) != focus.surface() {
|
||||
trace!(
|
||||
"grab root {:?} is not the new focus {:?}, ungrabbing",
|
||||
grab.root,
|
||||
@ -570,7 +697,7 @@ impl State {
|
||||
|
||||
let mut new_layout = current_layout;
|
||||
// Store the currently active layout for the surface.
|
||||
if let Some(current_focus) = self.niri.keyboard_focus.as_ref() {
|
||||
if let Some(current_focus) = self.niri.keyboard_focus.surface() {
|
||||
with_states(current_focus, |data| {
|
||||
let cell = data
|
||||
.data_map
|
||||
@ -579,7 +706,7 @@ impl State {
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(focus) = focus.as_ref() {
|
||||
if let Some(focus) = focus.surface() {
|
||||
new_layout = with_states(focus, |data| {
|
||||
let cell = data.data_map.get_or_insert::<Cell<KeyboardLayout>, _>(|| {
|
||||
// The default layout is effectively the first layout in the
|
||||
@ -589,7 +716,7 @@ impl State {
|
||||
cell.get()
|
||||
});
|
||||
}
|
||||
if new_layout != current_layout && focus.is_some() {
|
||||
if new_layout != current_layout && focus.surface().is_some() {
|
||||
keyboard.set_focus(self, None, SERIAL_COUNTER.next_serial());
|
||||
keyboard.with_xkb_state(self, |mut context| {
|
||||
context.set_layout(new_layout);
|
||||
@ -598,7 +725,7 @@ impl State {
|
||||
}
|
||||
|
||||
self.niri.keyboard_focus.clone_from(&focus);
|
||||
keyboard.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
||||
keyboard.set_focus(self, focus.into_surface(), SERIAL_COUNTER.next_serial());
|
||||
|
||||
// FIXME: can be more granular.
|
||||
self.niri.queue_redraw_all();
|
||||
@ -1093,14 +1220,14 @@ impl Niri {
|
||||
gamma_control_manager_state,
|
||||
|
||||
seat,
|
||||
keyboard_focus: None,
|
||||
keyboard_focus: KeyboardFocus::Layout { surface: None },
|
||||
idle_inhibiting_surfaces: HashSet::new(),
|
||||
is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)),
|
||||
cursor_manager,
|
||||
cursor_texture_cache: Default::default(),
|
||||
cursor_shape_manager_state,
|
||||
dnd_icon: None,
|
||||
pointer_focus: None,
|
||||
pointer_focus: PointerFocus::default(),
|
||||
tablet_cursor_location: None,
|
||||
gesture_swipe_3f_cumulative: None,
|
||||
|
||||
@ -1497,31 +1624,39 @@ impl Niri {
|
||||
/// Pointer needs location in global space, and focused window location compatible with that
|
||||
/// global space. We don't have a global space for all windows, but this function converts the
|
||||
/// window location temporarily to the current global space.
|
||||
pub fn surface_under_and_global_space(
|
||||
&mut self,
|
||||
pos: Point<f64, Logical>,
|
||||
) -> Option<PointerFocus> {
|
||||
let (output, pos_within_output) = self.output_under(pos)?;
|
||||
pub fn surface_under_and_global_space(&mut self, pos: Point<f64, Logical>) -> PointerFocus {
|
||||
let mut rv = PointerFocus::default();
|
||||
|
||||
let Some((output, pos_within_output)) = self.output_under(pos) else {
|
||||
return rv;
|
||||
};
|
||||
rv.output = Some(output.clone());
|
||||
let output_pos_in_global_space = self.global_space.output_geometry(output).unwrap().loc;
|
||||
|
||||
if self.is_locked() {
|
||||
let state = self.output_state.get(output)?;
|
||||
let surface = state.lock_surface.as_ref()?;
|
||||
// We put lock surfaces at (0, 0).
|
||||
let point = pos_within_output;
|
||||
let (surface, point) = under_from_surface_tree(
|
||||
let Some(state) = self.output_state.get(output) else {
|
||||
return rv;
|
||||
};
|
||||
let Some(surface) = state.lock_surface.as_ref() else {
|
||||
return rv;
|
||||
};
|
||||
|
||||
rv.surface = under_from_surface_tree(
|
||||
surface.wl_surface(),
|
||||
point,
|
||||
pos_within_output,
|
||||
// We put lock surfaces at (0, 0).
|
||||
(0, 0),
|
||||
WindowSurfaceType::ALL,
|
||||
)?;
|
||||
return Some(PointerFocus {
|
||||
output: output.clone(),
|
||||
surface: (surface, point),
|
||||
)
|
||||
.map(|(surface, pos_within_output)| {
|
||||
(surface, pos_within_output + output_pos_in_global_space)
|
||||
});
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
if self.screenshot_ui.is_open() {
|
||||
return None;
|
||||
return rv;
|
||||
}
|
||||
|
||||
let layers = layer_map_for_output(output);
|
||||
@ -1539,6 +1674,7 @@ impl Niri {
|
||||
(surface, pos_within_layer + layer_pos_within_output)
|
||||
})
|
||||
})
|
||||
.map(|s| (s, None))
|
||||
};
|
||||
|
||||
let window_under = || {
|
||||
@ -1554,6 +1690,7 @@ impl Niri {
|
||||
.map(|(s, pos_within_window)| {
|
||||
(s, pos_within_window + win_pos_within_output)
|
||||
})
|
||||
.map(|s| (s, Some(window.clone())))
|
||||
})
|
||||
};
|
||||
|
||||
@ -1571,17 +1708,18 @@ impl Niri {
|
||||
.or_else(window_under);
|
||||
}
|
||||
|
||||
let (surface, surface_pos_within_output) = under
|
||||
let Some(((surface, surface_pos_within_output), window)) = under
|
||||
.or_else(|| layer_surface_under(Layer::Bottom))
|
||||
.or_else(|| layer_surface_under(Layer::Background))?;
|
||||
.or_else(|| layer_surface_under(Layer::Background))
|
||||
else {
|
||||
return rv;
|
||||
};
|
||||
|
||||
let output_pos_in_global_space = self.global_space.output_geometry(output).unwrap().loc;
|
||||
let surface_loc_in_global_space = surface_pos_within_output + output_pos_in_global_space;
|
||||
|
||||
Some(PointerFocus {
|
||||
output: output.clone(),
|
||||
surface: (surface, surface_loc_in_global_space),
|
||||
})
|
||||
rv.surface = Some((surface, surface_loc_in_global_space));
|
||||
rv.window = window;
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn output_under_cursor(&self) -> Option<Output> {
|
||||
@ -3106,11 +3244,13 @@ impl Niri {
|
||||
pub fn maybe_activate_pointer_constraint(
|
||||
&self,
|
||||
new_pos: Point<f64, Logical>,
|
||||
new_under: &Option<PointerFocus>,
|
||||
new_under: &PointerFocus,
|
||||
) {
|
||||
let Some(under) = new_under else { return };
|
||||
let Some((surface, surface_loc)) = &new_under.surface else {
|
||||
return;
|
||||
};
|
||||
let pointer = &self.seat.get_pointer().unwrap();
|
||||
with_pointer_constraint(&under.surface.0, pointer, |constraint| {
|
||||
with_pointer_constraint(surface, pointer, |constraint| {
|
||||
let Some(constraint) = constraint else { return };
|
||||
if constraint.is_active() {
|
||||
return;
|
||||
@ -3118,7 +3258,7 @@ impl Niri {
|
||||
|
||||
// Constraint does not apply if not within region.
|
||||
if let Some(region) = constraint.region() {
|
||||
let new_pos_within_surface = new_pos.to_i32_round() - under.surface.1;
|
||||
let new_pos_within_surface = new_pos.to_i32_round() - *surface_loc;
|
||||
if !region.contains(new_pos_within_surface) {
|
||||
return;
|
||||
}
|
||||
@ -3165,6 +3305,28 @@ impl Niri {
|
||||
warn!("error spawning a thread to send MonitorsChanged: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointerFocus) {
|
||||
if !self.config.borrow().input.focus_follows_mouse {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.seat.get_pointer().unwrap().is_grabbed() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(output) = &new_focus.output {
|
||||
if self.pointer_focus.output.as_ref() != Some(output) {
|
||||
self.layout.focus_output(output);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(window) = &new_focus.window {
|
||||
if self.pointer_focus.window.as_ref() != Some(window) {
|
||||
self.layout.activate_window(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
|
@ -106,7 +106,7 @@ pub fn refresh(state: &mut State) {
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
if state.niri.keyboard_focus.as_ref() == Some(wl_surface) {
|
||||
if state.niri.keyboard_focus.surface() == Some(wl_surface) {
|
||||
focused = Some((window.clone(), output.cloned()));
|
||||
} else {
|
||||
refresh_toplevel(protocol_state, wl_surface, &role, output, false);
|
||||
|
@ -40,6 +40,10 @@ pub fn center(rect: Rectangle<i32, Logical>) -> Point<i32, Logical> {
|
||||
rect.loc + rect.size.downscale(2).to_point()
|
||||
}
|
||||
|
||||
pub fn center_f64(rect: Rectangle<f64, Logical>) -> Point<f64, Logical> {
|
||||
rect.loc + rect.size.downscale(2.0).to_point()
|
||||
}
|
||||
|
||||
pub fn output_size(output: &Output) -> Size<i32, Logical> {
|
||||
let output_scale = output.current_scale().integer_scale();
|
||||
let output_transform = output.current_transform();
|
||||
|
Loading…
Reference in New Issue
Block a user