dotfiles/hosts/lio/hyprland_customizations.nix
RingOfStorms (Joshua Bell) c007bb72d2 persist workspaces
2025-08-26 23:00:45 -05:00

137 lines
4 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ lib, pkgs, ... }:
let
# Exact descriptions as reported by: hyprctl -j monitors | jq '.[].description'
mainDesc = "ASUSTek COMPUTER INC ASUS PG43U 0x01010101";
secondaryDesc = "Samsung Electric Company C34J79x HTRM900776";
mainMonitor = "desc:${mainDesc}";
secondaryMonitor = "desc:${secondaryDesc}";
hyprlandExtraOptions = {
monitor = [
"${mainMonitor},3840x2160@97.98,0x0,1,transform,0"
"${secondaryMonitor},3440x1440@99.98,-1440x-640,1,transform,1"
];
workspace =
let
inherit (builtins) map toString;
inherit (lib) range;
mkWs = monitor: i: "${toString i},monitor:${monitor},persistent:true";
in
(map (mkWs mainMonitor) (range 1 6)) ++ (map (mkWs secondaryMonitor) (range 7 10));
};
moveScript = pkgs.writeShellScriptBin "hyprland-move-workspaces" ''
#!/usr/bin/env bash
set -euo pipefail
HYPRCTL='${pkgs.hyprland}/bin/hyprctl'
JQ='${pkgs.jq}/bin/jq'
SOCAT='${pkgs.socat}/bin/socat'
MAIN_DESC='${mainDesc}'
SEC_DESC='${secondaryDesc}'
get_socket() {
# socket2 carries the event stream
echo "${"$"}{XDG_RUNTIME_DIR}/hypr/${"$"}{HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock"
}
wait_for_hypr() {
# Wait until hyprctl works (Hyprland is up)
until ''${HYPRCTL} -j monitors >/dev/null 2>&1; do
sleep 0.5
done
}
mon_name_by_desc() {
# Resolve Hyprland "name" (e.g., DP-2) from human-friendly description
local desc="${"$"}1"
''${HYPRCTL} -j monitors \
| ''${JQ} -r --arg d "${"$"}desc" '.[] | select(.description == $d) | .name' \
| head -n1
}
place_workspaces() {
local mainName secName
mainName="$(mon_name_by_desc "${"$"}MAIN_DESC")"
secName="$(mon_name_by_desc "${"$"}SEC_DESC" || true)"
# Always keep 16 on the main monitor
for ws in 1 2 3 4 5 6; do
''${HYPRCTL} dispatch moveworkspacetomonitor "${"$"}ws" "${"$"}mainName" || true
done
if [ -n "${"$"}{secName:-}" ]; then
# Secondary is present put 710 on secondary
for ws in 7 8 9 10; do
''${HYPRCTL} dispatch moveworkspacetomonitor "${"$"}ws" "${"$"}secName" || true
done
else
# No secondary keep 710 on main
for ws in 7 8 9 10; do
''${HYPRCTL} dispatch moveworkspacetomonitor "${"$"}ws" "${"$"}mainName" || true
done
fi
}
watch_events() {
local sock
sock="$(get_socket)"
# If socket2 is missing for some reason, fall back to polling
if [ ! -S "${"$"}sock" ]; then
while :; do
place_workspaces
sleep 5
done
return
fi
# Subscribe to Hyprland events and react to monitor changes
''${SOCAT} - "UNIX-CONNECT:${"$"}sock" | while IFS= read -r line; do
case "${"$"}line" in
monitoradded*|monitorremoved*|activemonitor*|layoutchange*)
place_workspaces
;;
esac
done
}
if [ "${"$"}{1:-}" = "--oneshot" ]; then
wait_for_hypr
place_workspaces
else
wait_for_hypr
place_workspaces
watch_events
fi
'';
in
{
options = { };
config = {
environment.systemPackages = [ moveScript ];
# Pass the options to home-manager for hyprland (as you already do)
ringofstorms_common.desktopEnvironment.hyprland.extraOptions = hyprlandExtraOptions;
# User-level systemd service that follows your Hyprland session and watches for monitor changes
systemd.user.services.hyprland-move-workspaces = {
description = "Keep workspaces 16 on main and 710 on secondary; react to monitor changes";
wants = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
partOf = [ "graphical-session.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${moveScript}/bin/hyprland-move-workspaces";
Restart = "always";
RestartSec = "2s";
};
wantedBy = [ "graphical-session.target" ];
};
};
}