dot
vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks
| 1 | #!/usr/bin/env bash |
| 2 | # Apps opened at login. Run once per login by autostart-apps.service (see |
| 3 | # modules/home/services.nix), after Waybar, so the tray exists. |
| 4 | # |
| 5 | # OpenDeck, WowUp to the tray |
| 6 | # Faugus its window on workspace 1 |
| 7 | # Signal, Discord workspace 3, Signal on top and Discord below it |
| 8 | # |
| 9 | # Which workspace a window lands on (and Faugus' position) is decided by the |
| 10 | # temporary "startup-*" window rules in hyprland.lua, which also closes the window |
| 11 | # WowUp opens. This script starts the apps and then stacks Signal |
| 12 | # above Discord. Apps that are already running are left alone, so it is safe to run |
| 13 | # again by hand. |
| 14 | |
| 15 | SIGNAL_HEIGHT=802 # px; Discord gets the rest of the (portrait) monitor |
| 16 | |
| 17 | running() { pgrep -f "$1" >/dev/null 2>&1; } |
| 18 | # Each app is started as its own systemd service (app-*.service), with systemd as |
| 19 | # its parent rather than this script: Discord and WowUp run in a bubblewrap |
| 20 | # sandbox that exits with its parent process, so they would close as soon as |
| 21 | # this script finished. `systemctl --user list-units 'app-*'` shows them. |
| 22 | launch() { uwsm app -t service -- "$@" >/dev/null 2>&1; } |
| 23 | dispatch() { hyprctl dispatch "$1" >/dev/null 2>&1; } |
| 24 | |
| 25 | # Tray apps only show an icon if the tray already exists when they start. |
| 26 | for _ in $(seq 40); do |
| 27 | busctl --user status org.kde.StatusNotifierWatcher >/dev/null 2>&1 && break |
| 28 | sleep 0.5 |
| 29 | done |
| 30 | |
| 31 | # OpenDeck starts hidden with its flag. WowUp has one too but shows its window |
| 32 | # anyway; hyprland.lua closes that window (see "startup-wowup" there). |
| 33 | running '(^|/)\.?opendeck(-wrapped)? ' || launch opendeck --hide |
| 34 | running 'wowup-cf' || launch wowup-cf --hidden |
| 35 | running 'faugus\.(launcher|tray_only)' || launch faugus-launcher |
| 36 | running 'signal-desktop.*/app\.asar' || launch signal-desktop # themed, see pkgs/signal-desktop-themed |
| 37 | running '/Discord( |$)' || launch discord |
| 38 | |
| 39 | # --- Stack Signal above Discord ------------------------------------------------ |
| 40 | |
| 41 | # Prints "address y width height" of the first mapped, tiled window whose class is $1, |
| 42 | # ignoring small ones (Discord's updater splash). |
| 43 | window() { |
| 44 | hyprctl clients -j | python3 -c ' |
| 45 | import json, sys |
| 46 | for c in json.load(sys.stdin): |
| 47 | if c["class"] == sys.argv[1] and c.get("mapped", True) and not c["floating"] and c["size"][0] > 500: |
| 48 | print(c["address"], c["at"][1], c["size"][0], c["size"][1]) |
| 49 | break |
| 50 | ' "$1" |
| 51 | } |
| 52 | |
| 53 | for _ in $(seq 180); do |
| 54 | read -r signal_addr signal_y signal_w signal_h <<<"$(window signal)" |
| 55 | read -r discord_addr discord_y _ _ <<<"$(window discord)" |
| 56 | [ -n "$signal_addr" ] && [ -n "$discord_addr" ] && break |
| 57 | sleep 0.5 |
| 58 | done |
| 59 | [ -n "$signal_addr" ] && [ -n "$discord_addr" ] || exit 0 |
| 60 | |
| 61 | if [ "$signal_y" -gt "$discord_y" ]; then |
| 62 | dispatch "hl.dsp.window.move({ direction = \"u\", window = \"address:$signal_addr\" })" |
| 63 | sleep 0.3 |
| 64 | read -r signal_addr signal_y signal_w signal_h <<<"$(window signal)" |
| 65 | fi |
| 66 | if [ "$signal_h" != "$SIGNAL_HEIGHT" ]; then |
| 67 | dispatch "hl.dsp.window.resize({ x = $signal_w, y = $SIGNAL_HEIGHT, window = \"address:$signal_addr\" })" |
| 68 | fi |