@@ -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_name s | 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