Your one-stop-cake-shop for everything Freshly Baked has to offer

fix(swayidle): move startup to systemd

Previously we were starting swayidle on niri. Unfortunately, this caused
a race condition where niri idle inhibitions were not respected. As niri
gets idle inhibitions from, say, browsers when playing video, this meant
we had to do nasty hacks such as manually systemd-inhibiting via a shell

By moving startup to systemd, we can start swayidle later - and in much
the same way as starting our SSH agent later avoids its race conditions,
starting swayidle later fixes this issue...

Changed files
+258 -243
packetmix
homes
+1 -1
packetmix/homes/coded/niri.nix
··· 5 5 { 6 6 ingredient.niri.enable = true; 7 7 8 - ingredient.niri.niri.timers = { 8 + ingredient.niri.swayidle.timers = { 9 9 lock = 900; 10 10 sleep = 1800; 11 11 };
+215 -242
packetmix/homes/niri/niri.nix
··· 33 33 ''; 34 34 }; 35 35 }; 36 - timers = { 37 - lock = lib.mkOption { 38 - type = lib.types.int; 39 - description = "How long while idling before locking the device (in seconds)"; 40 - default = 300; 41 - }; 42 - sleep = lib.mkOption { 43 - type = lib.types.addCheck lib.types.int (x: x >= config.ingredient.niri.niri.timers.lock); 44 - description = "How long while idling before sleeping the device (in seconds)"; 45 - default = 450; 46 - }; 36 + lockCommand = lib.mkOption { 37 + type = lib.types.str; 38 + description = "The command run when you lock your computer, or when it is locked automatically"; 39 + default = ''${config.programs.niri.package}/bin/niri msg action do-screen-transition && ${pkgs.swaylock}/bin/swaylock -i ${config.ingredient.niri.niri.lockscreen} -s fill -f''; 47 40 }; 48 41 overviewBackground = lib.mkOption { 49 42 type = lib.types.path; ··· 62 55 }; 63 56 64 57 config = { 65 - programs.niri = 66 - let 67 - lock = ''${config.programs.niri.package}/bin/niri msg action do-screen-transition && ${pkgs.swaylock}/bin/swaylock -i ${config.ingredient.niri.niri.lockscreen} -s fill -f''; 68 - in 69 - { 70 - enable = true; 58 + programs.niri = { 59 + enable = true; 71 60 72 - package = pkgs.niri; 61 + package = pkgs.niri; 73 62 74 - settings = { 75 - xwayland-satellite = { 76 - enable = true; 77 - path = "${pkgs.xwayland-satellite}/bin/xwayland-satellite"; 78 - }; 79 - environment.NIXOS_OZONE_WL = "1"; 63 + settings = { 64 + xwayland-satellite = { 65 + enable = true; 66 + path = "${pkgs.xwayland-satellite}/bin/xwayland-satellite"; 67 + }; 68 + environment.NIXOS_OZONE_WL = "1"; 80 69 81 - input.keyboard = { 82 - track-layout = "window"; 83 - repeat-delay = 200; 84 - repeat-rate = 25; 70 + input.keyboard = { 71 + track-layout = "window"; 72 + repeat-delay = 200; 73 + repeat-rate = 25; 85 74 86 - xkb = lib.mkIf (config.home.keyboard != null) { 87 - layout = if config.home.keyboard.layout == null then "" else config.home.keyboard.layout; 88 - model = if config.home.keyboard.model == null then "" else config.home.keyboard.model; 89 - options = builtins.concatStringsSep "," config.home.keyboard.options; 90 - variant = if config.home.keyboard.variant == null then "" else config.home.keyboard.variant; 91 - }; 75 + xkb = lib.mkIf (config.home.keyboard != null) { 76 + layout = if config.home.keyboard.layout == null then "" else config.home.keyboard.layout; 77 + model = if config.home.keyboard.model == null then "" else config.home.keyboard.model; 78 + options = builtins.concatStringsSep "," config.home.keyboard.options; 79 + variant = if config.home.keyboard.variant == null then "" else config.home.keyboard.variant; 92 80 }; 81 + }; 93 82 94 - input.touchpad.natural-scroll = true; 95 - input.touchpad.click-method = "clickfinger"; 83 + input.touchpad.natural-scroll = true; 84 + input.touchpad.click-method = "clickfinger"; 96 85 97 - input.warp-mouse-to-focus.enable = true; 98 - input.focus-follows-mouse = { 99 - enable = true; 100 - max-scroll-amount = "0%"; 101 - }; 86 + input.warp-mouse-to-focus.enable = true; 87 + input.focus-follows-mouse = { 88 + enable = true; 89 + max-scroll-amount = "0%"; 90 + }; 102 91 103 - input.power-key-handling.enable = false; 92 + input.power-key-handling.enable = false; 104 93 105 - binds = 106 - let 107 - mod = "Super"; 108 - mod1 = "Alt"; 94 + binds = 95 + let 96 + mod = "Super"; 97 + mod1 = "Alt"; 109 98 110 - generateWorkspaceBindings = workspaceNumber: { 111 - "${mod}+${builtins.toString (lib.mod workspaceNumber 10)}".action.focus-workspace = [ 112 - workspaceNumber 113 - ]; 114 - "${mod}+Shift+${builtins.toString (lib.mod workspaceNumber 10)}".action.move-column-to-workspace = [ 115 - workspaceNumber 116 - ]; 117 - }; 118 - joinAttrsetList = listOfAttrsets: lib.fold (a: b: a // b) { } listOfAttrsets; 119 - in 120 - { 121 - # General Keybinds 122 - "${mod}+Q".action.close-window = [ ]; 123 - "${mod}+Shift+Q".action.quit = [ ]; 124 - "${mod}+Return".action.spawn = "${pkgs.ghostty}/bin/ghostty"; 125 - "${mod}+L".action.spawn = [ 126 - "sh" 127 - "-c" 128 - lock 99 + generateWorkspaceBindings = workspaceNumber: { 100 + "${mod}+${builtins.toString (lib.mod workspaceNumber 10)}".action.focus-workspace = [ 101 + workspaceNumber 102 + ]; 103 + "${mod}+Shift+${builtins.toString (lib.mod workspaceNumber 10)}".action.move-column-to-workspace = [ 104 + workspaceNumber 129 105 ]; 130 - "${mod}+P".action.power-off-monitors = [ ]; 106 + }; 107 + joinAttrsetList = listOfAttrsets: lib.fold (a: b: a // b) { } listOfAttrsets; 108 + in 109 + { 110 + # General Keybinds 111 + "${mod}+Q".action.close-window = [ ]; 112 + "${mod}+Shift+Q".action.quit = [ ]; 113 + "${mod}+Return".action.spawn = "${pkgs.ghostty}/bin/ghostty"; 114 + "${mod}+L".action.spawn = [ 115 + "sh" 116 + "-c" 117 + config.ingredient.niri.niri.lockCommand 118 + ]; 119 + "${mod}+P".action.power-off-monitors = [ ]; 131 120 132 - "${mod}+R".action.screenshot = [ ]; 133 - "${mod}+Ctrl+R".action.screenshot-screen = [ ]; 134 - "${mod}+Shift+R".action.screenshot-window = [ ]; 135 - "Print".action.screenshot = [ ]; 136 - "Ctrl+Print".action.screenshot-screen = [ ]; 137 - "Shift+Print".action.screenshot-window = [ ]; 121 + "${mod}+R".action.screenshot = [ ]; 122 + "${mod}+Ctrl+R".action.screenshot-screen = [ ]; 123 + "${mod}+Shift+R".action.screenshot-window = [ ]; 124 + "Print".action.screenshot = [ ]; 125 + "Ctrl+Print".action.screenshot-screen = [ ]; 126 + "Shift+Print".action.screenshot-window = [ ]; 138 127 139 - "${mod}+Space".action.switch-layout = [ "next" ]; 140 - "${mod}+Shift+Space".action.switch-layout = [ "prev" ]; 128 + "${mod}+Space".action.switch-layout = [ "next" ]; 129 + "${mod}+Shift+Space".action.switch-layout = [ "prev" ]; 141 130 142 - "${mod}+Shift+Slash".action.show-hotkey-overlay = [ ]; 131 + "${mod}+Shift+Slash".action.show-hotkey-overlay = [ ]; 143 132 144 - "${mod}+V".action.set-dynamic-cast-monitor = [ ]; 145 - "${mod}+W".action.set-dynamic-cast-window = [ ]; 146 - "${mod}+Shift+V".action.clear-dynamic-cast-target = [ ]; 147 - "${mod}+Shift+W".action.clear-dynamic-cast-target = [ ]; 133 + "${mod}+V".action.set-dynamic-cast-monitor = [ ]; 134 + "${mod}+W".action.set-dynamic-cast-window = [ ]; 135 + "${mod}+Shift+V".action.clear-dynamic-cast-target = [ ]; 136 + "${mod}+Shift+W".action.clear-dynamic-cast-target = [ ]; 148 137 149 - "${mod}+N".action.spawn = [ 150 - "sh" 151 - "-c" 152 - "${pkgs.systemd}/bin/systemctl --user start swaync && ${pkgs.swaynotificationcenter}/bin/swaync-client -t" 153 - ]; 154 - # We need to ensure swaync is started, since as it isn't usually until we get a notification 155 - } 156 - // 157 - # Workspace Keybinds 158 - (lib.pipe (lib.range 1 10) [ 159 - (map generateWorkspaceBindings) 160 - joinAttrsetList 161 - ]) 162 - // 163 - # Window Manipulation Bindings 164 - ({ 165 - "${mod}+BracketLeft".action.consume-or-expel-window-left = [ ]; 166 - "${mod}+BracketRight".action.consume-or-expel-window-right = [ ]; 167 - "${mod}+Shift+BracketLeft".action.consume-window-into-column = [ ]; 168 - "${mod}+Shift+BracketRight".action.expel-window-from-column = [ ]; 169 - "${mod}+Slash".action.switch-preset-column-width = [ ]; 170 - "${mod}+${mod1}+F".action.fullscreen-window = [ ]; 171 - "${mod}+${mod1}+Shift+F".action.toggle-windowed-fullscreen = [ ]; 138 + "${mod}+N".action.spawn = [ 139 + "sh" 140 + "-c" 141 + "${pkgs.systemd}/bin/systemctl --user start swaync && ${pkgs.swaynotificationcenter}/bin/swaync-client -t" 142 + ]; 143 + # We need to ensure swaync is started, since as it isn't usually until we get a notification 144 + } 145 + // 146 + # Workspace Keybinds 147 + (lib.pipe (lib.range 1 10) [ 148 + (map generateWorkspaceBindings) 149 + joinAttrsetList 150 + ]) 151 + // 152 + # Window Manipulation Bindings 153 + ({ 154 + "${mod}+BracketLeft".action.consume-or-expel-window-left = [ ]; 155 + "${mod}+BracketRight".action.consume-or-expel-window-right = [ ]; 156 + "${mod}+Shift+BracketLeft".action.consume-window-into-column = [ ]; 157 + "${mod}+Shift+BracketRight".action.expel-window-from-column = [ ]; 158 + "${mod}+Slash".action.switch-preset-column-width = [ ]; 159 + "${mod}+${mod1}+F".action.fullscreen-window = [ ]; 160 + "${mod}+${mod1}+Shift+F".action.toggle-windowed-fullscreen = [ ]; 172 161 173 - # Focus 174 - "${mod}+Up".action.focus-window-or-workspace-up = [ ]; 175 - "${mod}+Down".action.focus-window-or-workspace-down = [ ]; 162 + # Focus 163 + "${mod}+Up".action.focus-window-or-workspace-up = [ ]; 164 + "${mod}+Down".action.focus-window-or-workspace-down = [ ]; 176 165 177 - # Non Jump Movement 178 - "${mod}+Shift+Up".action.move-window-up-or-to-workspace-up = [ ]; 179 - "${mod}+Shift+Down".action.move-window-down-or-to-workspace-down = [ ]; 180 - "${mod}+Shift+Left".action.consume-or-expel-window-left = [ ]; 181 - "${mod}+Shift+Right".action.consume-or-expel-window-right = [ ]; 166 + # Non Jump Movement 167 + "${mod}+Shift+Up".action.move-window-up-or-to-workspace-up = [ ]; 168 + "${mod}+Shift+Down".action.move-window-down-or-to-workspace-down = [ ]; 169 + "${mod}+Shift+Left".action.consume-or-expel-window-left = [ ]; 170 + "${mod}+Shift+Right".action.consume-or-expel-window-right = [ ]; 182 171 183 - # To Monitor 184 - "${mod}+Shift+Ctrl+Up".action.move-window-to-monitor-up = [ ]; 185 - "${mod}+Shift+Ctrl+Down".action.move-window-to-monitor-down = [ ]; 186 - "${mod}+Shift+Ctrl+Left".action.move-window-to-monitor-left = [ ]; 187 - "${mod}+Shift+Ctrl+Right".action.move-window-to-monitor-right = [ ]; 172 + # To Monitor 173 + "${mod}+Shift+Ctrl+Up".action.move-window-to-monitor-up = [ ]; 174 + "${mod}+Shift+Ctrl+Down".action.move-window-to-monitor-down = [ ]; 175 + "${mod}+Shift+Ctrl+Left".action.move-window-to-monitor-left = [ ]; 176 + "${mod}+Shift+Ctrl+Right".action.move-window-to-monitor-right = [ ]; 188 177 189 - # To Workspace 190 - "${mod}+Ctrl+Up".action.move-window-to-workspace-up = [ ]; 191 - "${mod}+Ctrl+Down".action.move-window-to-workspace-down = [ ]; 178 + # To Workspace 179 + "${mod}+Ctrl+Up".action.move-window-to-workspace-up = [ ]; 180 + "${mod}+Ctrl+Down".action.move-window-to-workspace-down = [ ]; 192 181 193 - # Sizing 194 - "${mod}+Equal".action.set-window-height = [ "+5%" ]; 195 - "${mod}+Minus".action.set-window-height = [ "-5%" ]; 196 - }) 197 - // 198 - # Column Manipulation Bindings 199 - ({ 200 - # Focus 201 - "${mod}+Left".action.focus-column-left = [ ]; 202 - "${mod}+Right".action.focus-column-right = [ ]; 203 - "${mod}+${mod1}+C".action.center-column = [ ]; 204 - "${mod}+F".action.maximize-column = [ ]; 182 + # Sizing 183 + "${mod}+Equal".action.set-window-height = [ "+5%" ]; 184 + "${mod}+Minus".action.set-window-height = [ "-5%" ]; 185 + }) 186 + // 187 + # Column Manipulation Bindings 188 + ({ 189 + # Focus 190 + "${mod}+Left".action.focus-column-left = [ ]; 191 + "${mod}+Right".action.focus-column-right = [ ]; 192 + "${mod}+${mod1}+C".action.center-column = [ ]; 193 + "${mod}+F".action.maximize-column = [ ]; 205 194 206 - # Non Monitor Movement 207 - "${mod}+${mod1}+Shift+Up".action.move-column-to-workspace-up = [ ]; 208 - "${mod}+${mod1}+Shift+Down".action.move-column-to-workspace-down = [ ]; 209 - "${mod}+${mod1}+Shift+Left".action.move-column-left = [ ]; 210 - "${mod}+${mod1}+Shift+Right".action.move-column-right = [ ]; 195 + # Non Monitor Movement 196 + "${mod}+${mod1}+Shift+Up".action.move-column-to-workspace-up = [ ]; 197 + "${mod}+${mod1}+Shift+Down".action.move-column-to-workspace-down = [ ]; 198 + "${mod}+${mod1}+Shift+Left".action.move-column-left = [ ]; 199 + "${mod}+${mod1}+Shift+Right".action.move-column-right = [ ]; 211 200 212 - # To Monitor 213 - "${mod}+${mod1}+Shift+Ctrl+Up".action.move-column-to-monitor-up = [ ]; 214 - "${mod}+${mod1}+Shift+Ctrl+Down".action.move-column-to-monitor-down = [ ]; 215 - "${mod}+${mod1}+Shift+Ctrl+Left".action.move-column-to-monitor-left = [ ]; 216 - "${mod}+${mod1}+Shift+Ctrl+Right".action.move-column-to-monitor-right = [ ]; 201 + # To Monitor 202 + "${mod}+${mod1}+Shift+Ctrl+Up".action.move-column-to-monitor-up = [ ]; 203 + "${mod}+${mod1}+Shift+Ctrl+Down".action.move-column-to-monitor-down = [ ]; 204 + "${mod}+${mod1}+Shift+Ctrl+Left".action.move-column-to-monitor-left = [ ]; 205 + "${mod}+${mod1}+Shift+Ctrl+Right".action.move-column-to-monitor-right = [ ]; 217 206 218 - # Sizing 219 - "${mod}+${mod1}+Equal".action.set-column-width = [ "+5%" ]; 220 - "${mod}+${mod1}+Minus".action.set-column-width = [ "-5%" ]; 221 - }) 222 - // 223 - # Workspace Manipulation Bindings 224 - ({ 225 - # Focus 226 - "${mod}+Page_Up".action.focus-workspace-up = [ ]; 227 - "${mod}+Page_Down".action.focus-workspace-down = [ ]; 207 + # Sizing 208 + "${mod}+${mod1}+Equal".action.set-column-width = [ "+5%" ]; 209 + "${mod}+${mod1}+Minus".action.set-column-width = [ "-5%" ]; 210 + }) 211 + // 212 + # Workspace Manipulation Bindings 213 + ({ 214 + # Focus 215 + "${mod}+Page_Up".action.focus-workspace-up = [ ]; 216 + "${mod}+Page_Down".action.focus-workspace-down = [ ]; 228 217 229 - # Within Itself 230 - "${mod}+Shift+Page_Up".action.move-workspace-up = [ ]; 231 - "${mod}+Shift+Page_Down".action.move-workspace-down = [ ]; 218 + # Within Itself 219 + "${mod}+Shift+Page_Up".action.move-workspace-up = [ ]; 220 + "${mod}+Shift+Page_Down".action.move-workspace-down = [ ]; 232 221 233 - # To Monitor 234 - "${mod}+Shift+Ctrl+Page_Up".action.move-workspace-to-monitor-up = [ ]; 235 - "${mod}+Shift+Ctrl+Page_Down".action.move-workspace-to-monitor-down = [ ]; 236 - "${mod}+Shift+Ctrl+Home".action.move-workspace-to-monitor-left = [ ]; 237 - "${mod}+Shift+Ctrl+End".action.move-workspace-to-monitor-right = [ ]; 222 + # To Monitor 223 + "${mod}+Shift+Ctrl+Page_Up".action.move-workspace-to-monitor-up = [ ]; 224 + "${mod}+Shift+Ctrl+Page_Down".action.move-workspace-to-monitor-down = [ ]; 225 + "${mod}+Shift+Ctrl+Home".action.move-workspace-to-monitor-left = [ ]; 226 + "${mod}+Shift+Ctrl+End".action.move-workspace-to-monitor-right = [ ]; 238 227 239 - "${mod}+Space" = { 240 - action.toggle-overview = [ ]; 241 - repeat = false; 242 - }; 243 - }) 244 - // { 245 - # Audio 246 - "XF86AudioRaiseVolume" = { 247 - allow-when-locked = true; 248 - action.spawn = [ 249 - "${pkgs.wireplumber}/bin/wpctl" 250 - "set-volume" 251 - "@DEFAULT_AUDIO_SINK@" 252 - "0.05+" 253 - ]; 254 - }; 255 - "XF86AudioLowerVolume" = { 256 - allow-when-locked = true; 257 - action.spawn = [ 258 - "${pkgs.wireplumber}/bin/wpctl" 259 - "set-volume" 260 - "@DEFAULT_AUDIO_SINK@" 261 - "0.05-" 262 - ]; 263 - }; 264 - "XF86AudioMute" = { 265 - allow-when-locked = true; 266 - action.spawn = [ 267 - "${pkgs.wireplumber}/bin/wpctl" 268 - "set-mute" 269 - "@DEFAULT_AUDIO_SINK@" 270 - "toggle" 271 - ]; 272 - }; 273 - "XF86AudioMicMute" = { 274 - allow-when-locked = true; 275 - action.spawn = [ 276 - "${pkgs.wireplumber}/bin/wpctl" 277 - "set-mute" 278 - "@DEFAULT_AUDIO_SOURCE@" 279 - "toggle" 280 - ]; 228 + "${mod}+Space" = { 229 + action.toggle-overview = [ ]; 230 + repeat = false; 281 231 }; 232 + }) 233 + // { 234 + # Audio 235 + "XF86AudioRaiseVolume" = { 236 + allow-when-locked = true; 237 + action.spawn = [ 238 + "${pkgs.wireplumber}/bin/wpctl" 239 + "set-volume" 240 + "@DEFAULT_AUDIO_SINK@" 241 + "0.05+" 242 + ]; 282 243 }; 244 + "XF86AudioLowerVolume" = { 245 + allow-when-locked = true; 246 + action.spawn = [ 247 + "${pkgs.wireplumber}/bin/wpctl" 248 + "set-volume" 249 + "@DEFAULT_AUDIO_SINK@" 250 + "0.05-" 251 + ]; 252 + }; 253 + "XF86AudioMute" = { 254 + allow-when-locked = true; 255 + action.spawn = [ 256 + "${pkgs.wireplumber}/bin/wpctl" 257 + "set-mute" 258 + "@DEFAULT_AUDIO_SINK@" 259 + "toggle" 260 + ]; 261 + }; 262 + "XF86AudioMicMute" = { 263 + allow-when-locked = true; 264 + action.spawn = [ 265 + "${pkgs.wireplumber}/bin/wpctl" 266 + "set-mute" 267 + "@DEFAULT_AUDIO_SOURCE@" 268 + "toggle" 269 + ]; 270 + }; 271 + }; 283 272 284 - layout = { 285 - gaps = 16; 273 + layout = { 274 + gaps = 16; 286 275 287 - center-focused-column = "on-overflow"; 276 + center-focused-column = "on-overflow"; 288 277 289 - preset-column-widths = [ 290 - { proportion = 1. / 4.; } 291 - { proportion = 1. / 3.; } 292 - { proportion = 1. / 2.; } 293 - { proportion = 2. / 3.; } 294 - { proportion = 9. / 10.; } 295 - ]; # TODO: clicks to PR a docs update for niri-flake 296 - }; 278 + preset-column-widths = [ 279 + { proportion = 1. / 4.; } 280 + { proportion = 1. / 3.; } 281 + { proportion = 1. / 2.; } 282 + { proportion = 2. / 3.; } 283 + { proportion = 9. / 10.; } 284 + ]; # TODO: clicks to PR a docs update for niri-flake 285 + }; 297 286 298 - prefer-no-csd = true; # No "client-side-decorations" (i.e. client-side window open/close buttons) 299 - hotkey-overlay.skip-at-startup = true; 300 - screenshot-path = null; 287 + prefer-no-csd = true; # No "client-side-decorations" (i.e. client-side window open/close buttons) 288 + hotkey-overlay.skip-at-startup = true; 289 + screenshot-path = null; 301 290 302 - spawn-at-startup = [ 303 - { 304 - command = [ 305 - "${pkgs.swaybg}/bin/swaybg" 306 - "-i" 307 - "${config.ingredient.niri.niri.wallpaper}" 308 - "-m" 309 - "fill" 310 - ]; 311 - } 312 - { 313 - command = [ 314 - "${pkgs.swayidle}/bin/swayidle" 315 - "-w" 316 - "timeout" 317 - (toString config.ingredient.niri.niri.timers.lock) 318 - lock 319 - "timeout" 320 - (toString config.ingredient.niri.niri.timers.sleep) 321 - "niri msg action power-off-monitors" 322 - "resume" 323 - "niri msg action power-on-monitors" # Not sure if this is really needed - niri normally powers on monitors on a movement action anyway, but maybe this can affect resuming in different ways? 324 - "before-sleep" 325 - lock 326 - ]; 327 - } 328 - ]; 329 - }; 291 + spawn-at-startup = [ 292 + { 293 + command = [ 294 + "${pkgs.swaybg}/bin/swaybg" 295 + "-i" 296 + "${config.ingredient.niri.niri.wallpaper}" 297 + "-m" 298 + "fill" 299 + ]; 300 + } 301 + ]; 330 302 }; 303 + }; 331 304 332 305 programs.bash.profileExtra = lib.mkBefore '' 333 306 if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
+42
packetmix/homes/niri/swayidle.nix
··· 1 + { 2 + pkgs, 3 + lib, 4 + config, 5 + ... 6 + }: 7 + { 8 + options.ingredient.niri.swayidle.timers = { 9 + lock = lib.mkOption { 10 + type = lib.types.int; 11 + description = "How long while idling before locking the device (in seconds)"; 12 + default = 300; 13 + }; 14 + sleep = lib.mkOption { 15 + type = lib.types.addCheck lib.types.int (x: x >= config.ingredient.niri.swayidle.timers.lock); 16 + description = "How long while idling before sleeping the device (in seconds)"; 17 + default = 450; 18 + }; 19 + }; 20 + 21 + config.systemd.user.services.swayidle = { 22 + Unit.After = [ "niri.service" ]; 23 + Install.WantedBy = [ "niri.service" ]; 24 + 25 + Service.ExecStart = builtins.concatStringsSep " " ( 26 + map (arg: "'${arg}'") [ 27 + "${pkgs.swayidle}/bin/swayidle" 28 + "-w" 29 + "timeout" 30 + (toString config.ingredient.niri.swayidle.timers.lock) 31 + config.ingredient.niri.niri.lockCommand 32 + "timeout" 33 + (toString config.ingredient.niri.swayidle.timers.sleep) 34 + "niri msg action power-off-monitors" 35 + "resume" 36 + "niri msg action power-on-monitors" # Not sure if this is really needed - niri normally powers on monitors on a movement action anyway, but maybe this can affect resuming in different ways? 37 + "before-sleep" 38 + config.ingredient.niri.niri.lockCommand 39 + ] 40 + ); # There's some nastiness here around "what happens if your commands contain single quotes"... at the moment, don't :) 41 + }; 42 + }