swadhinbiswas Add Monitor Manager app (visual layout, drag arrange, rotate, scale) + SUPER+CTRL+M keybinding · 8/22/2026
9da17d3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/bin/bash
# omarchy:summary=Interactive multi-monitor management via rofi
# omarchy:group=monitor
set -euo pipefail
ROT=("0°" "90°" "180°" "270°")
rofi_menu() { rofi -dmenu -i -p "$1" -width 600 2>/dev/null; }
backlight_dev() { find /sys/class/backlight -maxdepth 1 -mindepth 1 -type d 2>/dev/null | head -1; }
brightness_pct() {
local dev b m; dev=$(backlight_dev) || return 1
b=$(<"/sys/class/backlight/$dev/brightness"); m=$(<"/sys/class/backlight/$dev/max_brightness")
awk "BEGIN{printf\"%.0f\",$b/$m*100}"
}
set_brightness() {
local dev m; dev=$(backlight_dev) || return 1
m=$(<"/sys/class/backlight/$dev/max_brightness")
echo "$(( $1 * m / 100 ))" >"/sys/class/backlight/$dev/brightness"
}
field() { echo "$1" | jq -r "$2"; }
list_monitors() {
local data="$1" i=0 count; count=$(echo "$data" | jq 'length')
while (( i < count )); do
local n w h x y t d f
n=$(field "$data" ".[$i].name"); w=$(field "$data" ".[$i].width"); h=$(field "$data" ".[$i].height")
x=$(field "$data" ".[$i].x"); y=$(field "$data" ".[$i].y"); t=$(field "$data" ".[$i].transform")
d=$(field "$data" ".[$i].disabled"); f=$(field "$data" ".[$i].focused")
local tag=""; [[ $d == "true" ]] && tag=" [off]"; [[ $f == "true" ]] && tag=" [*]"
printf '%s\t%sx%s @ (%s,%s) rot:%s%s\n' "$n" "$w" "$h" "$x" "$y" "${ROT[$t]}" "$tag"
(( i++ ))
done
}
apply_cfg() {
local name="$1" data="$2" w h r x y t s d
w=$(field "$data" '.width'); h=$(field "$data" '.height'); r=$(field "$data" '.refreshRate')
x=$(field "$data" '.x'); y=$(field "$data" '.y'); t=$(field "$data" '.transform')
s=$(field "$data" '.scale'); d=$(field "$data" '.disabled')
if [[ $d == "true" ]]; then
hyprctl keyword monitor "$name,disable" >/dev/null 2>&1
else
hyprctl keyword monitor "$name,${w}x${h}@${r},${s},${x}x${y},${ROT[$t]}" >/dev/null 2>&1
fi
}
find_adjacent() {
local cur="$1" dir="$2" data="$3" cx cy cw ch best="" bestd=999999 i=0
cx=$(jq -r --arg n "$cur" '.[]|select(.name==$n)|.x' <<< "$data")
cy=$(jq -r --arg n "$cur" '.[]|select(.name==$n)|.y' <<< "$data")
cw=$(jq -r --arg n "$cur" '.[]|select(.name==$n)|.width' <<< "$data")
ch=$(jq -r --arg n "$cur" '.[]|select(.name==$n)|.height' <<< "$data")
while (( i < $(jq 'length' <<< "$data") )); do
local nn nx ny nw nh d=-1
nn=$(jq -r ".[$i].name" <<< "$data"); (( i++ ))
[[ $nn == "$cur" ]] && continue
nx=$(jq -r ".[$i].x" <<< "$data"); ny=$(jq -r ".[$i].y" <<< "$data")
nw=$(jq -r ".[$i].width" <<< "$data"); nh=$(jq -r ".[$i].height" <<< "$data")
case "$dir" in
left) d=$(( cx - nx - nw )) ;; right) d=$(( nx - cx - cw )) ;;
up) d=$(( cy - ny - nh )) ;; down) d=$(( ny - cy - ch )) ;;
esac
(( d >= 0 && d < bestd )) && { bestd=$d; best=$nn; }
done
[[ -n $best ]] && echo "$best"
}
do_actions() {
local name="$1" data="$2"
local mon; mon=$(jq -r --arg n "$name" '.[]|select(.name==$n)' <<< "$data")
while true; do
local choice
choice=$(printf '%s\n' "Rotate 90°" "Move Left" "Move Right" "Move Up" "Move Down" \
"Scale 1x" "Scale 1.5x" "Scale 2x" "Scale 2.5x" "Brightness" "Back" | rofi_menu "Monitor: $name") || return 0
case "$choice" in
"Rotate 90°")
mon=$(jq --argjson t "$(( ($(jq -r '.transform' <<< "$mon") + 1) % 4 ))" '.transform=$t' <<< "$mon")
apply_cfg "$name" "$mon";;
"Move"*)
local dir; dir=$(echo "${choice#Move }" | tr A-Z a-z)
local tgt; tgt=$(find_adjacent "$name" "$dir" "$data") || {
omarchy-notification-send -i dialog-error -t 3000 "Monitor Manager" "No monitor to the $dir"; continue; }
local tm tx ty tw th nx ny
tm=$(jq -r --arg n "$tgt" '.[]|select(.name==$n)' <<< "$data")
tx=$(jq -r '.x' <<< "$tm"); ty=$(jq -r '.y' <<< "$tm")
tw=$(jq -r '.width' <<< "$tm"); th=$(jq -r '.height' <<< "$tm")
case "$dir" in
left) nx=$tx; ny=$ty ;; right) nx=$((tx+tw)); ny=$ty ;;
up) nx=$tx; ny=$ty ;; down) nx=$tx; ny=$((ty+th)) ;;
esac
mon=$(jq --argjson x "$nx" --argjson y "$ny" '.x=$x|.y=$y' <<< "$mon")
apply_cfg "$name" "$mon";;
"Scale"*)
local s; s="${choice#Scale }"; s="${s%x}"
mon=$(jq --argjson s "$s" '.scale=$s' <<< "$mon"); apply_cfg "$name" "$mon";;
"Brightness")
local dev; dev=$(backlight_dev) || {
omarchy-notification-send -i dialog-error -t 3000 "Monitor Manager" "No backlight device"; continue; }
local cur; cur=$(brightness_pct) || continue
local p; p=$(printf '25%%\n50%%\n75%%\n100%%\nCurrent: %s%%\n' "$cur" | rofi_menu "Brightness ($dev)") || continue
[[ $p == Current:* ]] || set_brightness "${p%%%}";;
"Back") return 1;;
esac
data=$(hyprctl monitors all -j 2>/dev/null) || break
mon=$(jq -r --arg n "$name" '.[]|select(.name==$n)' <<< "$data")
done
}
main() {
local data; data=$(hyprctl monitors all -j 2>/dev/null) || {
omarchy-notification-send -i dialog-error -t 3000 "Monitor Manager" "Cannot query monitors"; exit 1; }
while true; do
local list; list=$(list_monitors "$data") || {
omarchy-notification-send -i dialog-information -t 3000 "Monitor Manager" "No monitors"; break; }
local choice; choice=$(echo "$list" | rofi_menu "Select Monitor") || break
local name; name=$(echo "$choice" | cut -f1)
do_actions "$name" "$data" || continue
data=$(hyprctl monitors all -j 2>/dev/null) || break
done
}
main "$@"