fixes waybar, adds cursors, fixes some hyprland stuff

This commit is contained in:
2026-02-15 21:03:00 +01:00
parent 73d6ca5bec
commit 5e55b51220
12 changed files with 247 additions and 86 deletions

View File

@@ -1,26 +1,25 @@
#!/usr/bin/env bash
# Quick display control for Hyprland
# Profiles:
# laptop -> keep eDP-2, disable all externals
# dual -> eDP-2 + primary external
# triple -> eDP-2 + both externals
# Toggles:
# toggle-externals -> if any external enabled => laptop; else => triple (or dual if only one preset)
# dpms-toggle-focused -> blank/unblank the currently focused monitor (does NOT change layout)
set -euo pipefail
PRIMARY="eDP-2"
RIGHT_EXTERNAL_NAME="DP-2"
MIDDLE_EXTERNAL_NAME="HDMI-A-1"
# --- exact enable lines (copy from hyprland.conf) ---
# Exact enable lines (from your config)
EXT1='desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1'
EXT2='desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, auto-right, 1, transform, 1'
# Optional fallback by connector (harmless if not present)
EXT_FALLBACK='HDMI-A-1, highres@highrr, auto-right, 1'
# Which external is the "main" one you prefer for Dual:
# In dual mode, which one do we prefer?
DUAL_MAIN="$EXT1"
# Optional: restart Waybar after layout changes (0=off, 1=on)
RESTART_WAYBAR=1
# Dock/MST settle timing (tune if needed)
SETTLE_SECS=1.0
DPMS_KICK_RETRIES=2
DPMS_KICK_SLEEP=0.35
notify() {
if command -v notify-send >/dev/null 2>&1; then
notify-send -a "Hyprland" "$1" "${2:-}"
@@ -35,62 +34,182 @@ hypr() {
notify "hyprctl failed" "$out"
exit 1
fi
printf '%s' "$out"
}
jqok(){ command -v jq >/dev/null; }
monjson(){ hyprctl -j monitors 2>/dev/null || true; }
hypr_batch() {
local out
if ! out="$(hyprctl --batch "$1" 2>&1)"; then
notify "hyprctl --batch failed" "$out"
exit 1
fi
printf '%s' "$out"
}
any_external_enabled() {
if jqok; then
monjson | jq -e --arg P "$PRIMARY" '.[] | select(.name!=$P and (.disabled!=true))' >/dev/null
else
hyprctl monitors | awk -v P="$PRIMARY" '/^Monitor /{gsub(/^Monitor[ \t]+/, "", $0); split($0,a," "); if (a[1]!=P){print; exit 0}}' | grep -q .
restart_waybar() {
[ "${RESTART_WAYBAR:-0}" -eq 1 ] || return 0
if pgrep -x waybar >/dev/null 2>&1; then
pkill -SIGUSR2 waybar >/dev/null 2>&1 || true
fi
}
disable_all_externals() {
jqok() { command -v jq >/dev/null 2>&1; }
monjson() { hyprctl -j monitors 2>/dev/null || true; }
sleep_s() { python - <<PY 2>/dev/null || sleep 1
import time
time.sleep(float("$1"))
PY
}
# --- monitor discovery helpers ---
enabled_monitor_names() {
if jqok; then
mapfile -t names < <(monjson | jq -r --arg P "$PRIMARY" '.[] | select(.name!=$P) | .name')
monjson | jq -r '.[] | select(.disabled != true) | .name'
else
mapfile -t names < <(hyprctl monitors | awk -v P="$PRIMARY" '/^Monitor /{gsub(/^Monitor[ \t]+/, "", $0); split($0,a," "); if (a[1]!=P) print a[1]}')
# Fallback is weaker; jq is strongly recommended
hyprctl monitors | awk '
/^Monitor /{name=$2}
/disabled: false/{print name}
'
fi
for n in "${names[@]:-}"; do
hypr keyword monitor "$n, disable"
}
focused_monitor_name() {
if jqok; then
monjson | jq -r '.[] | select(.focused==true) | .name // empty'
else
hyprctl monitors | awk '
/^Monitor /{name=$2}
/focused: yes/{print name; exit}
'
fi
}
external_enabled() {
local n
while IFS= read -r n; do
[ "$n" != "$PRIMARY" ] && return 0
done < <(enabled_monitor_names)
return 1
}
# We dont try to parse desc lines; we just treat them as “enable rules”
enable_rule() {
local rule="$1"
[ -n "$rule" ] && printf 'keyword monitor %s; ' "$rule"
}
disable_by_name() {
local name="$1"
[ -n "$name" ] && printf 'keyword monitor %s, disable; ' "$name"
}
# Disable all *currently enabled* externals by name (minimal churn)
batch_disable_enabled_externals() {
local batch="" n=""
while IFS= read -r n; do
[ "$n" = "$PRIMARY" ] && continue
batch+="$(disable_by_name "$n")"
done < <(enabled_monitor_names)
printf '%s' "$batch"
}
# Dock settle wait: give MST/alt-mode time to re-enumerate before we apply rules / kick DPMS
dock_settle() {
sleep_s "$SETTLE_SECS"
}
# DPMS kick for all enabled externals (works well for dock hotplug weirdness)
kick_externals() {
local names=() n=""
if jqok; then
mapfile -t names < <(monjson | jq -r --arg P "$PRIMARY" '
.[] | select(.name != $P and (.disabled != true)) | .name
')
else
mapfile -t names < <(enabled_monitor_names | awk -v P="$PRIMARY" '$0!=P')
fi
[ "${#names[@]}" -eq 0 ] && return 0
for _ in $(seq 1 "$DPMS_KICK_RETRIES"); do
for n in "${names[@]}"; do hypr dispatch dpms off "$n" >/dev/null 2>&1 || true; done
sleep_s "$DPMS_KICK_SLEEP"
for n in "${names[@]}"; do hypr dispatch dpms on "$n" >/dev/null 2>&1 || true; done
sleep_s "$DPMS_KICK_SLEEP"
done
}
enable_line() {
local line="$1"
[ -n "$line" ] && hypr keyword monitor "$line"
enforce_triple_order() {
# Keep HDMI between the laptop panel and DP-2, with DP-2 always on the right.
enabled_monitor_names | grep -qx "$MIDDLE_EXTERNAL_NAME" || return 0
enabled_monitor_names | grep -qx "$RIGHT_EXTERNAL_NAME" || return 0
local py=0 px=0 pspan=1920 mspan=1920 mx rx
if jqok; then
read -r px py pspan < <(monjson | jq -r --arg N "$PRIMARY" '
.[] | select(.name == $N) |
(.transform // 0) as $t |
"\(.x // 0) \(.y // 0) \(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)"
')
read -r mspan < <(monjson | jq -r --arg N "$MIDDLE_EXTERNAL_NAME" '
.[] | select(.name == $N) |
(.transform // 0) as $t |
"\(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)"
')
fi
mx=$((px + pspan))
rx=$((mx + mspan))
hypr keyword monitor "$MIDDLE_EXTERNAL_NAME, highres@highrr, ${mx}x${py}, 1, transform, 1" >/dev/null 2>&1 || true
hypr keyword monitor "$RIGHT_EXTERNAL_NAME, highres@highrr, ${rx}x${py}, 1" >/dev/null 2>&1 || true
}
apply_profile() {
local label="$1"
local batch="$2"
dock_settle
[ -n "$batch" ] && hypr_batch "$batch" >/dev/null
dock_settle
kick_externals
notify "Profile: $label" ""
restart_waybar
}
profile_laptop() {
disable_all_externals
notify "Profile: Laptop-only" "$PRIMARY active, externals disabled"
# Minimal: disable only currently enabled externals
local batch=""
batch+="$(batch_disable_enabled_externals)"
apply_profile "Laptop-only" "$batch"
}
profile_dual() {
# Ensure only one external is on
disable_all_externals
enable_line "$DUAL_MAIN"
notify "Profile: Dual" "Enabled main external with preferred transform"
# Disable enabled externals, then enable preferred main external rule
local batch=""
batch+="$(batch_disable_enabled_externals)"
batch+="$(enable_rule "$DUAL_MAIN")"
apply_profile "Dual" "$batch"
}
profile_triple() {
# Ensure both externals are on (and only those two)
disable_all_externals
enable_line "$EXT2" # ext2 first so it appears in the middle
enable_line "$EXT1"
# You can drop EXT_FALLBACK if you don't actually need it:
# enable_line "$EXT_FALLBACK"
notify "Profile: Triple" "Enabled both externals"
# Disable enabled externals, then enable both rules
# Order: EXT2 first so it tends to appear “middle” with auto-right
local batch=""
batch+="$(batch_disable_enabled_externals)"
batch+="$(enable_rule "$EXT2")"
batch+="$(enable_rule "$EXT1")"
apply_profile "Triple" "$batch"
dock_settle
enforce_triple_order
}
toggle_externals() {
if any_external_enabled; then
if external_enabled; then
profile_laptop
else
# Prefer triple if we have two presets, else dual
if [ -n "$EXT1" ] && [ -n "$EXT2" ]; then
profile_triple
else
@@ -100,32 +219,38 @@ toggle_externals() {
}
dpms_toggle_focused() {
# Blank/unblank the currently focused output, layout unchanged
local name
if jqok; then
name="$(monjson | jq -r '.[] | select(.focused==true) | .name // empty')"
else
name="$(hyprctl monitors | awk '$1=="Monitor"{gsub(/^Monitor[ \t]+/,""); split($0,a," "); name=a[1]} /focused: yes/{print name; exit}')"
fi
name="$(focused_monitor_name)"
[ -z "$name" ] && { notify "No focused monitor" ""; exit 2; }
hypr dispatch dpms toggle "$name"
hypr dispatch dpms toggle "$name" >/dev/null
notify "DPMS toggle" "$name"
}
status() {
echo "Enabled monitors:"
enabled_monitor_names | sed 's/^/ - /'
echo
echo "Focused: $(focused_monitor_name || true)"
}
case "${1:-}" in
laptop) profile_laptop ;;
dual) profile_dual ;;
triple) profile_triple ;;
toggle-externals) toggle_externals ;;
dpms-toggle-focused) dpms_toggle_focused ;;
laptop) profile_laptop ;;
dual) profile_dual ;;
triple) profile_triple ;;
toggle-externals) toggle_externals ;;
dpms-toggle-focused) dpms_toggle_focused ;;
kick-externals) dock_settle; kick_externals; notify "Kicked externals (DPMS)" "" ;;
status) status ;;
*)
cat <<EOF
Usage:
$(basename "$0") laptop # eDP-2 only
$(basename "$0") dual # eDP-2 + main external
$(basename "$0") laptop # eDP-2 only (disable enabled externals)
$(basename "$0") dual # eDP-2 + preferred external
$(basename "$0") triple # eDP-2 + both externals
$(basename "$0") toggle-externals # flip between laptop-only and (dual/triple)
$(basename "$0") dpms-toggle-focused # blank/unblank focused output (no layout change)
$(basename "$0") toggle-externals # laptop-only <-> (dual/triple)
$(basename "$0") dpms-toggle-focused # blank/unblank focused output (layout unchanged)
$(basename "$0") kick-externals # DPMS off/on for enabled externals
$(basename "$0") status # print enabled + focused
EOF
exit 2
;;