From 7131041107d283f0b543f966862a57580b8906a5 Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Sun, 13 Sep 2026 23:45:42 +0800 Subject: [PATCH] feat(hypridle): define config in home-manager with per-host timeouts Replace the static hypridle.conf with services.hypridle.settings and add required timeout options so each host tunes its own values: - idols-ai (PC): 15 min keyboard, 20 min screen-off, 30 min lock. - 12kingdoms-shoukei (laptop): 3 / 6 / 20 min (previous values). The options have no defaults because the right values depend on the machine. Update the home-manager eval test to inspect the settings option instead of the old config file text. --- home/hosts/linux/12kingdoms-shoukei.nix | 7 +++ home/hosts/linux/idols-ai.nix | 7 +++ home/linux/gui/base/hypridle/default.nix | 63 ++++++++++++++++++- home/linux/gui/base/hypridle/hypridle.conf | 47 -------------- .../aarch64-linux/tests/home-manager/expr.nix | 21 +++---- 5 files changed, 81 insertions(+), 64 deletions(-) delete mode 100644 home/linux/gui/base/hypridle/hypridle.conf diff --git a/home/hosts/linux/12kingdoms-shoukei.nix b/home/hosts/linux/12kingdoms-shoukei.nix index 3eb0439e..116d369f 100644 --- a/home/hosts/linux/12kingdoms-shoukei.nix +++ b/home/hosts/linux/12kingdoms-shoukei.nix @@ -14,6 +14,13 @@ in modules.desktop.gaming.enable = false; modules.desktop.niri.enable = true; + # Laptop defaults: backlight 3 min, screen off 6 min, lock 20 min. + modules.desktop.hypridle = { + keyboardBacklightTimeout = 180; + screenOffTimeout = 360; + lockTimeout = 1200; + }; + xdg.configFile."niri/niri-hardware.kdl".source = mkSymlink "${config.home.homeDirectory}/nix-config/hosts/12kingdoms-shoukei/niri-hardware.kdl"; diff --git a/home/hosts/linux/idols-ai.nix b/home/hosts/linux/idols-ai.nix index d60aa5b1..d82b3180 100644 --- a/home/hosts/linux/idols-ai.nix +++ b/home/hosts/linux/idols-ai.nix @@ -10,6 +10,13 @@ in modules.desktop.gaming.enable = true; modules.desktop.niri.enable = true; + # PC: keep the screen on longer than on the laptop. + modules.desktop.hypridle = { + keyboardBacklightTimeout = 900; + screenOffTimeout = 1200; + lockTimeout = 1800; + }; + programs.zed-editor.userSettings = { ui_font_size = 18.0; buffer_font_size = 17.0; diff --git a/home/linux/gui/base/hypridle/default.nix b/home/linux/gui/base/hypridle/default.nix index f1500a04..25e018ff 100644 --- a/home/linux/gui/base/hypridle/default.nix +++ b/home/linux/gui/base/hypridle/default.nix @@ -1,9 +1,66 @@ { + config, + lib, ... }: +let + cfg = config.modules.desktop.hypridle; +in { - xdg.configFile."hypr/hypridle.conf".source = ./hypridle.conf; + options.modules.desktop.hypridle = { + keyboardBacklightTimeout = lib.mkOption { + type = lib.types.int; + description = "Seconds of idle before turning off the keyboard backlight."; + }; + screenOffTimeout = lib.mkOption { + type = lib.types.int; + description = "Seconds of idle before turning off the monitors (DPMS)."; + }; + lockTimeout = lib.mkOption { + type = lib.types.int; + description = "Seconds of idle before locking the screen."; + }; + }; - # Hyprland idle daemon - services.hypridle.enable = true; + config = { + # Hyprland/niri idle daemon. Declared in Nix (instead of a raw + # hypridle.conf) so each host can tune the timeouts below. + services.hypridle = { + enable = true; + + settings = { + general = { + lock_cmd = "noctalia-shell ipc call lockScreen lock"; + before_sleep_cmd = "noctalia-shell ipc call lockScreen lock"; + ignore_dbus_inhibit = false; + }; + + listener = [ + { + timeout = cfg.keyboardBacklightTimeout; + # brightnessctl --list / -d kbd_backlight + on-timeout = "brightnessctl --save --device=kbd_backlight set 0"; + on-resume = "brightnessctl --restore --device=kbd_backlight"; + } + { + timeout = cfg.screenOffTimeout; + ignore_inhibit = true; + # Some apps keep idle-inhibit active even when they are only sitting + # on a page, which can prevent screen-off forever. Ignore those + # inhibitors for screen-off, but skip while an MPRIS player reports + # active playback. + condition_cmd = "! playerctl -a status 2>/dev/null | grep -q '^Playing$'"; + condition_retry = 30; + on-timeout = "niri msg action power-off-monitors"; + on-resume = "niri msg action power-on-monitors"; + } + { + timeout = cfg.lockTimeout; + ignore_inhibit = true; + on-timeout = "noctalia-shell ipc call lockScreen lock"; + } + ]; + }; + }; + }; } diff --git a/home/linux/gui/base/hypridle/hypridle.conf b/home/linux/gui/base/hypridle/hypridle.conf deleted file mode 100644 index 4b653abe..00000000 --- a/home/linux/gui/base/hypridle/hypridle.conf +++ /dev/null @@ -1,47 +0,0 @@ -general { - lock_cmd = noctalia-shell ipc call lockScreen lock # avoid starting multiple instances - before_sleep_cmd = noctalia-shell ipc call lockScreen lock # lock before suspend - ignore_dbus_inhibit = false # whether to ignore dbus-sent idle-inhibit requests -} - -listener { - timeout = 180 # 3 minutes - # List devices: brightnessctl --list - # Adjust keyboard backlight: brightnessctl -d kbd_backlight set 50% - on-timeout = brightnessctl --save --device=kbd_backlight set 0 # turn off keyboard backlight. - on-resume = brightnessctl --restore --device=kbd_backlight # turn on keyboard backlight. -} - -# NOTE: intentionally no 10min screen-dim listener here. Writing brightnessctl -# directly fights the ambient-light auto-brightness daemon (wluma on shoukei): -# wluma is adaptive, and hypridle's periodic brightness writes get learned as if -# the user had made those adjustments. niri (26.04) has no built-in idle timers, -# so hypridle stays responsible for keyboard backlight, dpms and locking below. - -# Some apps keep idle-inhibit active even when they are only sitting on a page, -# which can prevent screen-off forever. Ignore those inhibitors for screen-off, -# but skip the action while an MPRIS player reports active playback. -listener { - timeout = 360 # 6 minutes - ignore_inhibit = true - condition_cmd = ! playerctl -a status 2>/dev/null | grep -q '^Playing$' - condition_retry = 30 - on-timeout = niri msg action power-off-monitors # DPMS off via niri - on-resume = niri msg action power-on-monitors # DPMS on via niri -} - -listener { - timeout = 1200 # 20 minutes - ignore_inhibit = true - on-timeout = noctalia-shell ipc call lockScreen lock # lock screen -} - -# listener { -# timeout = 1660 # 31 minutes -# on-resume = brightnessctl -r # monitor wake up & screen on -# } - -# listener { -# timeout = 1800 # 30min -# on-timeout = systemctl suspend # suspend pc -# } diff --git a/outputs/aarch64-linux/tests/home-manager/expr.nix b/outputs/aarch64-linux/tests/home-manager/expr.nix index ceeaa107..ee23a91c 100644 --- a/outputs/aarch64-linux/tests/home-manager/expr.nix +++ b/outputs/aarch64-linux/tests/home-manager/expr.nix @@ -13,22 +13,15 @@ lib.genAttrs hosts ( name: let hm = outputs.nixosConfigurations.${name}.config.home-manager.users.${username}; - hypridleConfig = builtins.readFile hm.xdg.configFile."hypr/hypridle.conf".source; - hasHypridleLines = lines: lib.hasInfix (lib.concatStringsSep "\n" lines) hypridleConfig; + listeners = hm.services.hypridle.settings.listener; + findByAction = action: lib.findFirst (l: (l."on-timeout" or "") == action) { } listeners; + screenOff = findByAction "niri msg action power-off-monitors"; + lock = findByAction "noctalia-shell ipc call lockScreen lock"; in { homeDirectory = hm.home.homeDirectory; - hypridleScreenOffIgnoresInhibitors = hasHypridleLines [ - " timeout = 360 # 6 minutes" - " ignore_inhibit = true" - ]; - hypridleScreenOffSkipsPlayingMedia = hasHypridleLines [ - " condition_cmd = ! playerctl -a status 2>/dev/null | grep -q '^Playing$'" - " condition_retry = 30" - ]; - hypridleLockIgnoresInhibitors = hasHypridleLines [ - " timeout = 1200 # 20 minutes" - " ignore_inhibit = true" - ]; + hypridleScreenOffIgnoresInhibitors = screenOff.ignore_inhibit or false; + hypridleScreenOffSkipsPlayingMedia = (screenOff.condition_cmd or "") != ""; + hypridleLockIgnoresInhibitors = lock.ignore_inhibit or false; } )