diff --git a/home-manager/fred.nix b/home-manager/fred.nix index 7220787..2b23159 100644 --- a/home-manager/fred.nix +++ b/home-manager/fred.nix @@ -215,10 +215,19 @@ in }; # Upstream ships no .desktop file (TUI only), so the launcher can't find it. + # Hyprland parks the instance started at login on the special:music workspace + # (see settings/hyprland.nix), so this toggles that window rather than opening + # a second terminal — a fresh process would mean another ~20s wait for its + # Spotify Connect device. Relaunch first if the window is gone, otherwise the + # toggle just reveals an empty workspace. xdg.desktopEntries.spotify-player = lib.mkIf isDesktop { name = "Spotify Player"; genericName = "Music Player"; - exec = "ghostty --title=spotify-player -e spotify_player"; + exec = "${pkgs.writeShellScript "spotify-player-toggle" '' + hyprctl clients | grep -q "title: spotify-player" \ + || ghostty --title=spotify-player -e spotify_player + hyprctl dispatch togglespecialworkspace music + ''}"; icon = "spotify"; terminal = false; categories = [ "Audio" "Music" "Player" "AudioVideo" ]; diff --git a/settings/hyprland.nix b/settings/hyprland.nix index b02c0cb..1125d4c 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -272,6 +272,10 @@ in hl.exec_cmd("wl-paste --type text --watch cliphist store") hl.exec_cmd("wl-paste --type image --watch cliphist store") hl.exec_cmd("hyprctl setcursor Bibata-Modern-Ice 24") + -- Warms spotify-player's Spotify Connect device while you're + -- doing something else; the window rule above swallows it onto + -- special:music silently, so it never steals focus. + hl.exec_cmd("ghostty --title=spotify-player -e spotify_player") -- swayosd removed; volume/brightness OSD handled by quickshell ${lib.optionalString isMacbook ''hl.exec_cmd("hypridle")''} end) @@ -310,6 +314,19 @@ in workspace = "special silent", }) + -- spotify-player lives on a special workspace for its whole + -- session. Its librespot device takes ~20s to be accepted by + -- Spotify's backend, and that cost is per *process*, so relaunching + -- a terminal for it means waiting every time. Parking one instance + -- here pays it once at login; SUPER+M slides it in and out. + -- Matched on title, not class: gtk-single-instance means every + -- ghostty window is the same app-id, and --title is authoritative + -- (ghostty ignores the program's own title escapes once it is set). + hl.window_rule({ + match = { title = "^spotify-player$" }, + workspace = "special:music silent", + }) + -- Games stall on other workspaces: no frame callbacks = no render = -- game loop blocks. Keep them ticking in the background at -- misc:render_unfocused_fps (15). @@ -362,6 +379,7 @@ in hl.bind(mod .. " + F", hl.dsp.window.fullscreen()) hl.bind(mod .. " + P", hl.dsp.window.pseudo()) hl.bind(mod .. " + S", hl.dsp.layout("togglesplit")) + hl.bind(mod .. " + M", hl.dsp.workspace.toggle_special("music")) -- Focus hl.bind(mod .. " + left", hl.dsp.focus({ direction = "left" }))