This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / desktops / sway / default.nix
21 kB 605 lines
1{ 2 pkgs, 3 lib, 4 self, 5 ... 6}: 7let 8 9 term = "/run/current-system/sw/bin/kitty"; 10 11 # Unified script: switch to workspace and move it to current output 12 gotoWorkspace = pkgs.writeShellApplication { 13 name = "goto-workspace"; 14 runtimeInputs = [ 15 pkgs.sway 16 pkgs.jq 17 ]; 18 text = '' 19 WS="$1" 20 CURRENT_OUTPUT=$(swaymsg -r -t get_outputs | jq -r '.[] | select(.focused == true).name') 21 swaymsg workspace "$WS" 22 swaymsg move workspace to output "$CURRENT_OUTPUT" 23 ''; 24 }; 25 26 # Switch screens by number. mod+F<n> focuses the screen bound to slot n: 27 # the output assigned via mod+ctrl+F<n> while one exists (so a bound 28 # screen stays reachable after hot-plug renumbering), else the nth 29 # output by position (1 = primary, 2 = first secondary, ...). 30 # mod+ctrl+F<n> binds the currently focused screen to slot n. 31 screenSwitch = pkgs.writeShellApplication { 32 name = "sway-screen-switch"; 33 runtimeInputs = [ 34 pkgs.sway 35 pkgs.jq 36 pkgs.coreutils 37 pkgs.libnotify 38 ]; 39 text = '' 40 MARKS="$HOME/.config/sway-screen-marks" 41 mkdir -p "$MARKS" 42 43 if [ "$1" = "mark" ]; then 44 SLOT="$2" 45 CURRENT=$(swaymsg -r -t get_outputs | jq -r '.[] | select(.focused == true) | .name') 46 if [ -n "$CURRENT" ]; then 47 echo "$CURRENT" > "$MARKS/$SLOT" 48 notify-send "Screen" "F$SLOT -> $CURRENT" 2>/dev/null 49 fi 50 exit 0 51 fi 52 53 MARKED=$(cat "$MARKS/$1" 2>/dev/null || true) 54 55 if [ -n "$MARKED" ]; then 56 swaymsg focus output "$MARKED" 57 exit 0 58 fi 59 60 swaymsg -r -t get_outputs | jq -r 'sort_by(.x, .y) | .[].name' | sed -n "''${1}p" | while read -r TARGET; do 61 [ -n "$TARGET" ] && swaymsg focus output "$TARGET" 62 done 63 ''; 64 }; 65 66 # mod+F<n> = switch to screen slot n, mod+ctrl+F<n> = bind current screen to slot n. 67 screenBindings = lib.concatStringsSep "\n" ( 68 lib.map 69 (n: '' 70 bindsym $mod+F${toString n} exec ${lib.getExe screenSwitch} ${toString n} 71 bindsym $mod+ctrl+F${toString n} exec ${lib.getExe screenSwitch} mark ${toString n} 72 '') 73 [ 74 1 75 2 76 3 77 4 78 5 79 6 80 7 81 8 82 9 83 ] 84 ); 85 86 # Mark a window and jump to it later, per slot. mod+ctrl+<digit> marks 87 # the focused window (by container id) into that slot. mod+<digit> jumps 88 # to the marked window: if its workspace is hidden it is moved to the 89 # current screen, if it is shown on another screen focus switches there. 90 # mod+shift+<digit> always brings the marked window's workspace to the 91 # current screen. A stale mark (window closed) is dropped with a note. 92 windowMark = pkgs.writeShellApplication { 93 name = "sway-window-mark"; 94 runtimeInputs = [ 95 pkgs.sway 96 pkgs.jq 97 pkgs.coreutils 98 pkgs.libnotify 99 ]; 100 text = '' 101 MARKS="$HOME/.config/sway-window-marks" 102 mkdir -p "$MARKS" 103 104 if [ "$1" = "mark" ]; then 105 SLOT="$2" 106 INFO=$(swaymsg -r -t get_tree | jq -r ' 107 [ .. | objects | select(.type == "con" and .focused == true) ] 108 | .[0] // empty 109 | [ .id, (.name // "unknown") ] | @tsv') 110 if [ -z "$INFO" ]; then 111 notify-send "Window" "no window focused" 2>/dev/null 112 exit 0 113 fi 114 IFS=$'\t' read -r ID NAME <<< "$INFO" 115 echo "$ID" > "$MARKS/$SLOT" 116 notify-send "Window" "slot $SLOT: $NAME" 2>/dev/null 117 exit 0 118 fi 119 120 SLOT="$1" 121 ID=$(cat "$MARKS/$SLOT" 2>/dev/null || true) 122 if [ -z "$ID" ]; then 123 notify-send "Window" "no window in slot $SLOT" 2>/dev/null 124 exit 0 125 fi 126 127 # Workspace that currently contains the marked window (empty if it died). 128 WS=$(swaymsg -r -t get_tree | jq -r --argjson id "$ID" ' 129 . as $t 130 | [ path(.. | objects | select(.id == $id)) ] | .[0] as $p 131 | if $p == null then empty 132 else [ range(1; ($p | length) + 1) as $i 133 | $t | getpath($p[0:$i]) 134 | objects | select(.type == "workspace") | .name 135 ] | .[0] // empty 136 end') 137 if [ -z "$WS" ]; then 138 rm -f "$MARKS/$SLOT" 139 notify-send "Window" "slot $SLOT: marked window is gone" 2>/dev/null 140 exit 0 141 fi 142 143 # Screen the user is typing on, captured BEFORE the switch: switching 144 # a workspace moves seat focus to the target's output, so the 145 # focused output after the switch would be the wrong answer. 146 CUR=$(swaymsg -r -t get_outputs | jq -r '.[] | select(.focused == true) | .name') 147 # Shown on any output? A plain jump only brings the workspace when 148 # it is hidden; "bring" always moves it here. 149 SHOWN=$(swaymsg -r -t get_workspaces | jq -r --arg ws "$WS" '[.[] | select(.name == $ws) | .visible] | any') 150 MOVE=false 151 if [ $# -gt 1 ] && [ "$2" = "bring" ]; then 152 MOVE=true 153 elif [ "$SHOWN" != "true" ]; then 154 MOVE=true 155 fi 156 swaymsg workspace "$WS" 157 # Sway 1.12: nameless "move workspace to output <out>" moves the 158 # focused (just-switched) workspace. A named first argument is 159 # parsed as a container move to a workspace literally named 160 # "<ws> to output <out>". 161 if [ "$MOVE" = "true" ]; then 162 swaymsg move workspace to output "$CUR" 163 fi 164 swaymsg "[con_id=$ID] focus" 165 ''; 166 }; 167 168 # mod+<digit> = jump to window slot, mod+shift+<digit> = bring its 169 # workspace here, mod+ctrl+<digit> = mark focused window into slot. 170 windowBindings = lib.concatStringsSep "\n" ( 171 lib.map 172 (n: '' 173 bindsym $mod+${toString n} exec ${lib.getExe windowMark} ${toString n} 174 bindsym $mod+shift+${toString n} exec ${lib.getExe windowMark} ${toString n} bring 175 bindsym $mod+ctrl+${toString n} exec ${lib.getExe windowMark} mark ${toString n} 176 '') 177 [ 178 1 179 2 180 3 181 4 182 5 183 6 184 7 185 8 186 9 187 ] 188 ); 189 190in 191{ 192 # Make goto-workspace available system-wide for workspace switching 193 environment.systemPackages = [ gotoWorkspace ]; 194 195 # Firefox as a persistent systemd user service — starts after workspace-manager 196 # so the sway rule to hide the anchor window is already registered 197 systemd.user.services.firefox = { 198 description = "Firefox Web Browser"; 199 partOf = [ "sway-session.target" ]; 200 wantedBy = [ "sway-session.target" ]; 201 after = [ 202 "sway-session.target" 203 "workspace-manager.service" 204 ]; 205 # Never restart on `nixos-rebuild switch` — that would kill all open 206 # browser windows. A new Firefox build is only picked up at the next 207 # sway session start. 208 restartIfChanged = false; 209 stopIfChanged = false; 210 serviceConfig = { 211 Type = "simple"; 212 ExecStart = lib.getExe self.packages.${pkgs.system}.firefox; 213 Restart = "always"; 214 RestartSec = 2; 215 }; 216 }; 217 imports = [ 218 ../lib/wayland.nix 219 ./noctalia.nix 220 ]; 221 programs.sway = { 222 enable = true; 223 wrapperFeatures.gtk = true; 224 # Do NOT hardcode SWAYSOCK here: sway >=1.11 ignores an inherited value and 225 # creates its own per-pid socket (sway-ipc.$uid.$pid.sock). Exporting a 226 # fixed path baked a dead socket into the firefox/workspace-manager user 227 # services, breaking all swaymsg calls (and thus tab restore). Let sway 228 # advertise the real socket; the import-environment below propagates it. 229 }; 230 231 # Enable realtime scheduling for sway 232 security.pam.loginLimits = [ 233 { 234 domain = "@users"; 235 item = "rtprio"; 236 type = "-"; 237 value = 1; 238 } 239 ]; 240 241 systemd.user.services.swayidle = { 242 description = "Idle manager for Wayland"; 243 documentation = [ "man:swayidle(1)" ]; 244 partOf = [ "sway-session.target" ]; 245 wantedBy = [ "sway-session.target" ]; 246 serviceConfig = { 247 Type = "simple"; 248 ExecStart = '' 249 ${pkgs.swayidle}/bin/swayidle -w \ 250 timeout 120 '${lib.getExe' pkgs.systemd "systemctl"} --user start lock.target' \ 251 timeout 300 '${lib.getExe' pkgs.systemd "systemctl"} suspend-then-hibernate' \ 252 before-sleep '${lib.getExe' pkgs.systemd "systemctl"} --user start lock.target' 253 ''; 254 Restart = "on-failure"; 255 RestartSec = 1; 256 }; 257 }; 258 259 # Keep new windows on the workspace of the process that spawned them instead 260 # of the focused one (sway only tracks this for its own `exec`, and only for 261 # 60s). 262 systemd.user.services.sway-spawn-workspace = { 263 description = "Place new windows on their spawning process's workspace"; 264 partOf = [ "sway-session.target" ]; 265 wantedBy = [ "sway-session.target" ]; 266 serviceConfig = { 267 Type = "simple"; 268 ExecStart = lib.getExe self.packages.${pkgs.system}.sway-spawn-workspace; 269 Restart = "on-failure"; 270 RestartSec = 2; 271 }; 272 }; 273 274 # Inhibit idle when audio is playing (prevents suspend during calls/music) 275 systemd.user.services.sway-audio-idle-inhibit = { 276 description = "Inhibit idle when audio is playing"; 277 partOf = [ "sway-session.target" ]; 278 wantedBy = [ "sway-session.target" ]; 279 serviceConfig = { 280 Type = "simple"; 281 ExecStart = "${pkgs.sway-audio-idle-inhibit}/bin/sway-audio-idle-inhibit"; 282 Restart = "on-failure"; 283 RestartSec = 1; 284 }; 285 }; 286 287 systemd.user.services.sway-urgent-rumble = { 288 description = "Trigger rumble on urgent windows"; 289 partOf = [ "sway-session.target" ]; 290 wantedBy = [ "sway-session.target" ]; 291 serviceConfig = { 292 Type = "simple"; 293 ExecStart = lib.getExe ( 294 pkgs.writeShellApplication { 295 name = "sway-urgent-rumble"; 296 runtimeInputs = [ 297 pkgs.sway 298 pkgs.jq 299 self.packages.${pkgs.system}.gpd-rumble 300 ]; 301 text = '' 302 # Find first force-feedback capable device 303 FF_DEVICE="" 304 for ev in /sys/class/input/event*; do 305 if [ -f "$ev/device/capabilities/ff" ] && [ "$(cat "$ev/device/capabilities/ff")" != "0" ]; then 306 FF_DEVICE="/dev/input/''${ev##*/}" 307 break 308 fi 309 done 310 311 if [ -z "$FF_DEVICE" ]; then 312 echo "No force-feedback device found, exiting" 313 exit 0 314 fi 315 316 echo "Using force-feedback device: $FF_DEVICE" 317 318 swaymsg -t subscribe -m '["window"]' | \ 319 jq --unbuffered -r 'select(.change == "urgent" and .container.urgent == true) | "urgent"' | \ 320 while read -r _; do 321 gpd-rumble 200 100 "$FF_DEVICE" & 322 done 323 ''; 324 } 325 ); 326 Restart = "on-failure"; 327 RestartSec = 1; 328 }; 329 }; 330 331 environment.etc."sway/config".text = '' 332 # Default config for sway 333 # 334 # Copy this to ~/.config/sway/config and edit it to your liking. 335 # 336 # Read `man 5 sway` for a complete reference. 337 338 ### Variables 339 # 340 # Logo key. Use Mod1 for Alt. 341 set $mod Mod4 342 # Home row direction keys, like vim 343 set $left h 344 set $down j 345 set $up k 346 set $right l 347 # Your preferred terminal emulator 348 set $term ${term} 349 # Your preferred application launcher 350 # Note: pass the final command to swaymsg so that the resulting window can be opened 351 # on the original workspace that the command was run on. 352 set $menu ${pkgs.dmenu}/bin/dmenu_path | menu | xargs swaymsg exec -- 353 354 ### Output configuration 355 # 356 # Default wallpaper (more resolutions are available in @datadir@/backgrounds/sway/) 357 # output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill 358 # 359 # Example configuration: 360 # 361 # output HDMI-A-1 resolution 1920x1080 position 1920,0 362 # 363 # You can get the names of your outputs by running: swaymsg -t get_outputs 364 365 # Per-machine output overrides (scale, resolution, position) live here. 366 include /etc/sway/config.d/*.conf 367 368 ### Idle configuration 369 # 370 # Example configuration: 371 # 372 # exec swayidle -w \ 373 # timeout 300 'swaylock -f -c 000000' \ 374 # timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \ 375 # before-sleep 'swaylock -f -c 000000' 376 # 377 # This will lock your screen after 300 seconds of inactivity, then turn off 378 # your displays after another 300 seconds, and turn your screens back on when 379 # resumed. It will also lock your screen before your computer goes to sleep. 380 381 ### Input configuration 382 # 383 # Example configuration: 384 # 385 input "2321:21128:HTIX5288:00_0911:5288_Touchpad" { 386 tap enabled 387 } 388 input "2362:628:PIXA3854:00_093A:0274_Touchpad" { 389 tap enabled 390 } 391 # 392 # You can get the names of your inputs by running: swaymsg -t get_inputs 393 # Read `man 5 sway-input` for more information about this section. 394 395 ### Key bindings 396 # 397 # Basics: 398 # 399 # Start a terminal 400 bindsym $mod+Shift+Return exec $term 401 402 # Kill focused window 403 bindsym $mod+Shift+c kill 404 405 # Start your launcher 406 bindsym $mod+d exec $menu 407 408 # Drag floating windows by holding down $mod and left mouse button. 409 # Resize them with right mouse button + $mod. 410 # Despite the name, also works for non-floating windows. 411 # Change normal to inverse to use left mouse button for resizing and right 412 # mouse button for dragging. 413 floating_modifier $mod normal 414 415 # Reload the configuration file 416 bindsym $mod+Shift+r reload 417 418 # Exit sway (logs you out of your Wayland session) 419 bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' 420 # 421 # Moving around: 422 # 423 # Move your focus around 424 bindsym $mod+Left focus left 425 bindsym $mod+Down focus down 426 bindsym $mod+Up focus up 427 bindsym $mod+Right focus right 428 429 # Move the focused window with the same, but add Shift 430 bindsym $mod+Shift+Left move left 431 bindsym $mod+Shift+Down move down 432 bindsym $mod+Shift+Up move up 433 bindsym $mod+Shift+Right move right 434 # Layout stuff: 435 # 436 # Split bindings removed to avoid accidental sub-containers. 437 # Use $mod+w to flatten into tabs, $mod+e to toggle split direction. 438 bindsym $mod+n split none 439 440 # Switch the current container between different layout styles 441 bindsym $mod+s layout stacking 442 bindsym $mod+w layout tabbed 443 bindsym $mod+e layout toggle split 444 445 # Make the current focus fullscreen 446 bindsym $mod+f fullscreen 447 448 # Toggle the current focus between tiling and floating mode 449 bindsym $mod+Shift+space floating toggle 450 451 # Swap focus between the tiling area and the floating area 452 bindsym $mod+a focus mode_toggle 453 454 # Workspace manager menu 455 bindsym $mod+space exec ${lib.getExe self.packages.${pkgs.system}.workspace-menu} 456 457 # Move focus to the parent container 458 bindsym $mod+Shift+a focus parent 459 # 460 # Scratchpad: 461 # 462 # Sway has a "scratchpad", which is a bag of holding for windows. 463 # You can send windows there and get them back later. 464 465 # Move the currently focused window to the scratchpad 466 bindsym $mod+Shift+minus move scratchpad 467 468 # Show the next scratchpad window or hide the focused scratchpad window. 469 # If there are multiple scratchpad windows, this command cycles through them. 470 bindsym $mod+minus scratchpad show 471 # 472 # Resizing containers: 473 # 474 mode "resize" { 475 # left will shrink the containers width 476 # right will grow the containers width 477 # up will shrink the containers height 478 # down will grow the containers height 479 bindsym $left resize shrink width 10px 480 bindsym $down resize grow height 10px 481 bindsym $up resize shrink height 10px 482 bindsym $right resize grow width 10px 483 484 # Ditto, with arrow keys 485 bindsym Left resize shrink width 10px 486 bindsym Down resize grow height 10px 487 bindsym Up resize shrink height 10px 488 bindsym Right resize grow width 10px 489 490 # Return to default mode 491 bindsym Return mode "default" 492 bindsym Escape mode "default" 493 } 494 bindsym $mod+r mode "resize" 495 496 default_border pixel 1 497 default_floating_border none 498 smart_borders on 499 500 bindsym $mod+q exec ${pkgs.writers.writeDash "goto_workspace" '' 501 set -efux 502 WS=$(swaymsg -r -t get_workspaces | 503 jq -r '.[].name' | 504 menu -p 'Workspace name: ' 505 ) 506 if [ -n "$WS" ]; then 507 ${lib.getExe gotoWorkspace} "$WS" 508 fi 509 ''} 510 511 bindsym $mod+Shift+q exec ${pkgs.writers.writeDash "moveto_workspace" '' 512 set -efux 513 WS=$(swaymsg -r -t get_workspaces | 514 jq -r '.[].name' | 515 menu -p 'Workspace name: ' 516 ) 517 if [ -n "$WS" ]; then 518 swaymsg move container to workspace "$WS" 519 fi 520 ''} 521 522 bindsym $mod+y exec /run/current-system/sw/bin/switch-theme toggle 523 524 bindsym $mod+Tab exec ${pkgs.sway-overfocus}/bin/sway-overfocus split-rw group-rw split-dw group-dw 525 bindsym $mod+Escape workspace back_and_forth 526 527 # Screen switching: mod+F<n> jumps to screen n (1 = primary, 2 = first 528 # secondary, ...), mod+ctrl+F<n> marks the current screen as the target. 529 # While a mark exists, any mod+F<n> jumps to the marked screen. 530 ${screenBindings} 531 532 # Window marks: mod+<digit> jumps to the window marked into that slot, 533 # mod+shift+<digit> brings its workspace to the current screen, 534 # mod+ctrl+<digit> marks the focused window into that slot. 535 ${windowBindings} 536 537 # screenlock (random live video/image from ~/wallpaper via swaylock-plugin) 538 bindsym $mod+F11 exec ${lib.getExe' pkgs.systemd "systemctl"} --user start lock.target 539 bindsym $mod+l exec ${lib.getExe' pkgs.systemd "systemctl"} --user start lock.target 540 541 # kill window (xkill-like) 542 mode "kill" { 543 bindsym --whole-window button1 kill, mode "default" 544 bindsym --whole-window button2 mode "default" 545 bindsym --whole-window button3 mode "default" 546 bindsym Escape mode "default" 547 } 548 bindsym $mod+x mode "kill" 549 550 # media buttons 551 bindsym XF86AudioMute exec ${pkgs.pulseaudio.out}/bin/pactl -- set-sink-mute @DEFAULT_SINK@ toggle 552 bindsym XF86AudioRaiseVolume exec ${pkgs.pulseaudio.out}/bin/pactl -- set-sink-volume @DEFAULT_SINK@ +4% 553 bindsym XF86AudioLowerVolume exec ${pkgs.pulseaudio.out}/bin/pactl -- set-sink-volume @DEFAULT_SINK@ -4% 554 555 # brightness keys 556 bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- 557 bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ 558 559 # background programs 560 exec ${pkgs.copyq}/bin/copyq --start-server 561 exec ydotoold 562 exec_always pkill kanshi; exec ${pkgs.kanshi}/bin/kanshi 563 564 565 # theme and env specific stuff 566 exec_always ${pkgs.writers.writeDash "dbus-sway-environment" '' 567 set -efux 568 # XDG_SESSION_TYPE=wayland must be exported here: with DISPLAY set but 569 # XDG_SESSION_TYPE unset, Firefox (and other apps) started from the systemd 570 # user manager pick the X11/XWayland screencast path, which captures a black 571 # screen under sway. Setting it explicitly makes them use the PipeWire portal. 572 dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP=sway XDG_SESSION_TYPE=wayland 573 systemctl --user set-environment XDG_SESSION_TYPE=wayland 574 systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP 575 systemctl --user start --no-block sway-session.target 576 systemctl --user stop xdg-desktop-portal xdg-desktop-portal-wlr 577 systemctl --user start xdg-desktop-portal xdg-desktop-portal-wlr 578 ''} 579 580 exec_always ${pkgs.writers.writeDash "gsettings" '' 581 set -efux 582 export XDG_DATA_DIRS=${pkgs.gsettings-desktop-schemas}:$XDG_DATA_DIRS 583 gnome_schema=org.gnome.desktop.interface 584 gsettings set $gnome_schema gtk-theme 'adw-gtk3-dark' 585 ''} 586 587 # keyboard 588 input type:keyboard { 589 xkb_layout us 590 xkb_variant altgr-intl 591 xkb_options caps:hyper 592 } 593 594 # Hide the workspace-manager Firefox anchor window. Registered statically 595 # here (not just by the daemon at runtime) so the rule exists before the 596 # firefox.service starts, otherwise the anchor flashes onto the current 597 # workspace and gets captured as a phantom tab. 598 for_window [title="^workspace-anchor"] move scratchpad 599 600 # flameshot 601 for_window [app_id="flameshot"] border pixel 0, floating enable, fullscreen disable, move absolute position 0 0 602 exec ${pkgs.flameshot}/bin/flameshot 603 ''; 604 605}