diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-27 09:31:32 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-30 20:12:37 +0300 |
| commit | fc99724aba1c0043eda3ad1b9829da8ae802d310 (patch) | |
| tree | 2a0face72bccbbf86da931bd92a7a5885b58e040 | |
| parent | 88fbc62b1d3cb245453b9ac5a8faae2b9159866a (diff) | |
| download | niri-fc99724aba1c0043eda3ad1b9829da8ae802d310.tar.gz niri-fc99724aba1c0043eda3ad1b9829da8ae802d310.tar.bz2 niri-fc99724aba1c0043eda3ad1b9829da8ae802d310.zip | |
Add open-focused window rule
| -rw-r--r-- | niri-config/src/lib.rs | 4 | ||||
| -rw-r--r-- | src/handlers/compositor.rs | 42 | ||||
| -rw-r--r-- | src/window/mod.rs | 8 | ||||
| -rw-r--r-- | wiki/Configuration:-Window-Rules.md | 30 |
4 files changed, 68 insertions, 16 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 8d755c78..607d8f37 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -985,6 +985,8 @@ pub struct WindowRule { pub open_fullscreen: Option<bool>, #[knuffel(child, unwrap(argument))] pub open_floating: Option<bool>, + #[knuffel(child, unwrap(argument))] + pub open_focused: Option<bool>, // Rules applied dynamically. #[knuffel(child, unwrap(argument))] @@ -3142,6 +3144,7 @@ mod tests { open-maximized true open-fullscreen false open-floating false + open-focused true focus-ring { off @@ -3421,6 +3424,7 @@ mod tests { open_maximized: Some(true), open_fullscreen: Some(false), open_floating: Some(false), + open_focused: Some(true), focus_ring: BorderRule { off: true, width: Some(FloatOrInt(3.)), diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 3856849f..c0dc9428 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -126,6 +126,32 @@ impl CompositorHandler for State { // moment, that is here. let is_floating = rules.compute_open_floating(toplevel); + // Figure out if we should activate the window. + let activate = rules.open_focused.map(|focus| { + if focus { + ActivateWindow::Yes + } else { + ActivateWindow::No + } + }); + let activate = activate.unwrap_or_else(|| { + // Check the token timestamp again in case the window took a while between + // requesting activation and mapping. + let token = activation_token_data.filter(|token| { + token.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT + }); + if token.is_some() { + ActivateWindow::Yes + } else { + let config = self.niri.config.borrow(); + if config.debug.strict_new_window_focus_policy { + ActivateWindow::No + } else { + ActivateWindow::Smart + } + } + }); + let parent = toplevel .parent() .and_then(|parent| self.niri.layout.find_window_and_output(&parent)) @@ -146,22 +172,6 @@ impl CompositorHandler for State { let mapped = Mapped::new(window, rules, hook); let window = mapped.window.clone(); - // Check the token timestamp again in case the window took a while between - // requesting activation and mapping. - let activate = match activation_token_data - .filter(|token| token.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT) - { - Some(_) => ActivateWindow::Yes, - None => { - let config = self.niri.config.borrow(); - if config.debug.strict_new_window_focus_policy { - ActivateWindow::No - } else { - ActivateWindow::Smart - } - } - }; - let target = if let Some(p) = &parent { // Open dialogs next to their parent window. AddWindowTarget::NextTo(p) diff --git a/src/window/mod.rs b/src/window/mod.rs index 9a44319a..0b0b5f1e 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -49,6 +49,9 @@ pub struct ResolvedWindowRules { /// Whether the window should open floating. pub open_floating: Option<bool>, + /// Whether the window should open focused. + pub open_focused: Option<bool>, + /// Extra bound on the minimum window width. pub min_width: Option<u16>, /// Extra bound on the minimum window height. @@ -116,6 +119,7 @@ impl ResolvedWindowRules { open_maximized: None, open_fullscreen: None, open_floating: None, + open_focused: None, min_width: None, min_height: None, max_width: None, @@ -208,6 +212,10 @@ impl ResolvedWindowRules { resolved.open_floating = Some(x); } + if let Some(x) = rule.open_focused { + resolved.open_focused = Some(x); + } + if let Some(x) = rule.min_width { resolved.min_width = Some(x); } diff --git a/wiki/Configuration:-Window-Rules.md b/wiki/Configuration:-Window-Rules.md index ccf5bb84..84e4d694 100644 --- a/wiki/Configuration:-Window-Rules.md +++ b/wiki/Configuration:-Window-Rules.md @@ -42,6 +42,7 @@ window-rule { open-maximized true open-fullscreen true open-floating true + open-focused false // Properties that apply continuously. draw-border-with-background false @@ -338,6 +339,35 @@ window-rule { } ``` +#### `open-focused` + +<sup>Since: next release</sup> + +Set this to `false` to prevent this window from being automatically focused upon opening. + +```kdl +// Don't give focus to the GIMP startup splash screen. +window-rule { + match app-id="^gimp" title="^GIMP Startup$" + + open-focused false +} +``` + +You can also set this to `true` to focus the window, even if normally it wouldn't get auto-focused. + +```kdl +// Always focus the KeePassXC-Browser unlock dialog. +// +// This dialog opens parented to the KeePassXC window rather than the browser, +// so it doesn't get auto-focused by default. +window-rule { + match app-id=r#"^org\.keepassxc\.KeePassXC$"# title="^Unlock Database - KeePassXC$" + + open-focused true +} +``` + ### Dynamic Properties These properties apply continuously to open windows. |
