adds overview, removes old stuff
This commit is contained in:
@@ -2,256 +2,65 @@
|
||||
set -euo pipefail
|
||||
|
||||
PRIMARY="eDP-2"
|
||||
RIGHT_EXTERNAL_NAME="DP-2"
|
||||
MIDDLE_EXTERNAL_NAME="HDMI-A-1"
|
||||
DP_OUT="DP-2"
|
||||
HDMI_OUT="HDMI-A-1"
|
||||
|
||||
# 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'
|
||||
|
||||
# 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
|
||||
# eDP-2 at scale 1.5 → logical width 1280px → externals start at x=1280
|
||||
# DP-2 at 1920px wide → HDMI starts at x=1280+1920=3200
|
||||
|
||||
notify() {
|
||||
if command -v notify-send >/dev/null 2>&1; then
|
||||
notify-send -a "Hyprland" "$1" "${2:-}"
|
||||
else
|
||||
[ -n "${2:-}" ] && printf '%s: %s\n' "$1" "$2" >&2 || printf '%s\n' "$1" >&2
|
||||
fi
|
||||
notify-send -a "monitor-toggle" "$1" "${2:-}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
hypr() {
|
||||
local out
|
||||
if ! out="$(hyprctl "$@" 2>&1)"; then
|
||||
notify "hyprctl failed" "$out"
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$out"
|
||||
active_monitors() {
|
||||
hyprctl -j monitors | jq -r '.[] | select(.disabled != true) | .name'
|
||||
}
|
||||
|
||||
hypr_batch() {
|
||||
local out
|
||||
if ! out="$(hyprctl --batch "$1" 2>&1)"; then
|
||||
notify "hyprctl --batch failed" "$out"
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
monjson | jq -r '.[] | select(.disabled != true) | .name'
|
||||
else
|
||||
# Fallback is weaker; jq is strongly recommended
|
||||
hyprctl monitors | awk '
|
||||
/^Monitor /{name=$2}
|
||||
/disabled: false/{print name}
|
||||
'
|
||||
fi
|
||||
}
|
||||
|
||||
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 don’t 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
|
||||
}
|
||||
|
||||
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
|
||||
has_external() {
|
||||
active_monitors | grep -qvx "$PRIMARY"
|
||||
}
|
||||
|
||||
profile_laptop() {
|
||||
# Minimal: disable only currently enabled externals
|
||||
local batch=""
|
||||
batch+="$(batch_disable_enabled_externals)"
|
||||
apply_profile "Laptop-only" "$batch"
|
||||
wlr-randr --output "$DP_OUT" --off 2>/dev/null || true
|
||||
wlr-randr --output "$HDMI_OUT" --off 2>/dev/null || true
|
||||
notify "Laptop only"
|
||||
}
|
||||
|
||||
profile_dual() {
|
||||
# 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"
|
||||
wlr-randr --output "$PRIMARY" --on --mode 1920x1080@144 --pos 0,0 --scale 1
|
||||
wlr-randr --output "$HDMI_OUT" --on --mode 1920x1080@60 --pos 1920,0 --scale 1
|
||||
wlr-randr --output "$DP_OUT" --off 2>/dev/null || true
|
||||
notify "Dual"
|
||||
}
|
||||
|
||||
profile_triple() {
|
||||
# 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
|
||||
wlr-randr --output "$PRIMARY" --on --mode 1920x1080@144 --pos 0,0 --scale 1
|
||||
wlr-randr --output "$HDMI_OUT" --on --mode 1920x1080@60 --pos 1920,0 --scale 1
|
||||
wlr-randr --output "$DP_OUT" --on --mode 1920x1080@60 --pos 3840,0 --scale 1 --transform 90
|
||||
notify "Triple"
|
||||
}
|
||||
|
||||
toggle_externals() {
|
||||
if external_enabled; then
|
||||
toggle() {
|
||||
if has_external; then
|
||||
profile_laptop
|
||||
else
|
||||
if [ -n "$EXT1" ] && [ -n "$EXT2" ]; then
|
||||
profile_triple
|
||||
else
|
||||
profile_dual
|
||||
fi
|
||||
profile_triple
|
||||
fi
|
||||
}
|
||||
|
||||
dpms_toggle_focused() {
|
||||
local name
|
||||
name="$(focused_monitor_name)"
|
||||
[ -z "$name" ] && { notify "No focused monitor" ""; exit 2; }
|
||||
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)"
|
||||
echo "Active monitors:"
|
||||
active_monitors | sed 's/^/ - /'
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
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 ;;
|
||||
laptop) profile_laptop ;;
|
||||
dual) profile_dual ;;
|
||||
triple) profile_triple ;;
|
||||
toggle) toggle ;;
|
||||
status) status ;;
|
||||
*)
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$(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 # 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
|
||||
echo "Usage: $(basename "$0") laptop|dual|triple|toggle|status"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user