aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-07-16 14:36:58 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-08-21 12:26:32 +0300
commitd662811bf6d004382061380f537ffdeb0bffe892 (patch)
treeee8a973a4e5599a34725c9dd12da09b528520a26
parent05337ce85560efc4d20cc23e9452a1e1f360007d (diff)
downloadniri-d662811bf6d004382061380f537ffdeb0bffe892.tar.gz
niri-d662811bf6d004382061380f537ffdeb0bffe892.tar.bz2
niri-d662811bf6d004382061380f537ffdeb0bffe892.zip
Unfocus layout when exit confirm dialog is open
Screen readers expect closing a modal dialog to reannounce the previous focus. This makes the exit confirm dialog more modal in this sense: it will unfocus the layout and then focus it back when closed, giving the desired behavior.
-rw-r--r--src/handlers/xdg_shell.rs6
-rw-r--r--src/input/mod.rs11
-rw-r--r--src/niri.rs21
3 files changed, 27 insertions, 11 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index a9eceb67..b7123e5f 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -303,7 +303,11 @@ impl XdgShellHandler for State {
// We need to hand out the grab in a way consistent with what update_keyboard_focus()
// thinks the current focus is, otherwise it will desync and cause weird issues with
// keyboard focus being at the wrong place.
- if self.niri.is_locked() {
+ if self.niri.exit_confirm_dialog.is_open() {
+ trace!("ignoring popup grab because the exit confirm dialog is open");
+ let _ = PopupManager::dismiss_popup(&root, &popup);
+ return;
+ } else if self.niri.is_locked() {
if Some(&root) != self.niri.lock_surface_focus().as_ref() {
trace!("ignoring popup grab because the session is locked");
let _ = PopupManager::dismiss_popup(&root, &popup);
diff --git a/src/input/mod.rs b/src/input/mod.rs
index e753c42d..6921eaa1 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -375,12 +375,13 @@ impl State {
let modified = keysym.modified_sym();
let raw = keysym.raw_latin_sym_or_raw_current_sym();
- if this.niri.exit_confirm_dialog.is_open() && pressed && raw == Some(Keysym::Return)
- {
- info!("quitting after confirming exit dialog");
- this.niri.stop_signal.stop();
+ if this.niri.exit_confirm_dialog.is_open() && pressed {
+ if raw == Some(Keysym::Return) {
+ info!("quitting after confirming exit dialog");
+ this.niri.stop_signal.stop();
+ }
- // Don't send this Enter press to any clients.
+ // Don't send this press to any clients.
this.niri.suppressed_keys.insert(key_code);
return FilterResult::Intercept(None);
}
diff --git a/src/niri.rs b/src/niri.rs
index 5b270f48..f1a1f3c0 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -512,6 +512,7 @@ pub enum KeyboardFocus {
LayerShell { surface: WlSurface },
LockScreen { surface: Option<WlSurface> },
ScreenshotUi,
+ ExitConfirmDialog,
Overview,
}
@@ -611,6 +612,7 @@ impl KeyboardFocus {
KeyboardFocus::LayerShell { surface } => Some(surface),
KeyboardFocus::LockScreen { surface } => surface.as_ref(),
KeyboardFocus::ScreenshotUi => None,
+ KeyboardFocus::ExitConfirmDialog => None,
KeyboardFocus::Overview => None,
}
}
@@ -621,6 +623,7 @@ impl KeyboardFocus {
KeyboardFocus::LayerShell { surface } => Some(surface),
KeyboardFocus::LockScreen { surface } => surface,
KeyboardFocus::ScreenshotUi => None,
+ KeyboardFocus::ExitConfirmDialog => None,
KeyboardFocus::Overview => None,
}
}
@@ -944,7 +947,10 @@ impl State {
let pointer = &self.niri.seat.get_pointer().unwrap();
let location = pointer.current_location();
- if !self.niri.is_locked() && !self.niri.screenshot_ui.is_open() {
+ if !self.niri.exit_confirm_dialog.is_open()
+ && !self.niri.is_locked()
+ && !self.niri.screenshot_ui.is_open()
+ {
// Don't refresh cursor focus during transitions.
if let Some((output, _)) = self.niri.output_under(location) {
let monitor = self.niri.layout.monitor_for_output(output).unwrap();
@@ -1063,7 +1069,9 @@ impl State {
}
// Compute the current focus.
- let focus = if self.niri.is_locked() {
+ let focus = if self.niri.exit_confirm_dialog.is_open() {
+ KeyboardFocus::ExitConfirmDialog
+ } else if self.niri.is_locked() {
KeyboardFocus::LockScreen {
surface: self.niri.lock_surface_focus(),
}
@@ -3193,7 +3201,7 @@ impl Niri {
extended_bounds: bool,
pos: Point<f64, Logical>,
) -> Option<(Output, &Workspace<Mapped>)> {
- if self.is_locked() || self.screenshot_ui.is_open() {
+ if self.exit_confirm_dialog.is_open() || self.is_locked() || self.screenshot_ui.is_open() {
return None;
}
@@ -3226,7 +3234,7 @@ impl Niri {
/// The cursor may be inside the window's activation region, but not within the window's input
/// region.
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Mapped> {
- if self.is_locked() || self.screenshot_ui.is_open() {
+ if self.exit_confirm_dialog.is_open() || self.is_locked() || self.screenshot_ui.is_open() {
return None;
}
@@ -3278,7 +3286,9 @@ impl Niri {
// The ordering here must be consistent with the ordering in render() so that input is
// consistent with the visuals.
- if self.is_locked() {
+ if self.exit_confirm_dialog.is_open() {
+ return rv;
+ } else if self.is_locked() {
let Some(state) = self.output_state.get(output) else {
return rv;
};
@@ -3909,6 +3919,7 @@ impl Niri {
// layer-shell, the layout will briefly draw as active, despite never having focus.
KeyboardFocus::LockScreen { .. } => true,
KeyboardFocus::ScreenshotUi => true,
+ KeyboardFocus::ExitConfirmDialog => true,
KeyboardFocus::Overview => true,
};