aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-12-27 09:31:32 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-12-30 20:12:37 +0300
commitfc99724aba1c0043eda3ad1b9829da8ae802d310 (patch)
tree2a0face72bccbbf86da931bd92a7a5885b58e040
parent88fbc62b1d3cb245453b9ac5a8faae2b9159866a (diff)
downloadniri-fc99724aba1c0043eda3ad1b9829da8ae802d310.tar.gz
niri-fc99724aba1c0043eda3ad1b9829da8ae802d310.tar.bz2
niri-fc99724aba1c0043eda3ad1b9829da8ae802d310.zip
Add open-focused window rule
-rw-r--r--niri-config/src/lib.rs4
-rw-r--r--src/handlers/compositor.rs42
-rw-r--r--src/window/mod.rs8
-rw-r--r--wiki/Configuration:-Window-Rules.md30
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.