Scale webcam overlay to monitor resolution

This commit is contained in:
David Heinemeier Hansson
2026-07-17 18:28:44 -07:00
parent ca0cf8bff9
commit 784996c378
3 changed files with 86 additions and 44 deletions
+50 -29
View File
@@ -8,9 +8,6 @@
set -euo pipefail set -euo pipefail
readonly MARGIN=40 readonly MARGIN=40
readonly SMALL_WIDTH=240
readonly MEDIUM_WIDTH=360
readonly LARGE_WIDTH=480
usage() { usage() {
echo "Usage: omarchy-capture-webcam-resize <smaller|larger|reset|small|medium|large>" >&2 echo "Usage: omarchy-capture-webcam-resize <smaller|larger|reset|small|medium|large>" >&2
@@ -41,32 +38,6 @@ read -r address current_width current_height monitor_id < <(
[[ -n $address && $current_width =~ ^[0-9]+$ && $current_height =~ ^[0-9]+$ && $monitor_id =~ ^[0-9]+$ ]] || exit 0 [[ -n $address && $current_width =~ ^[0-9]+$ && $current_height =~ ^[0-9]+$ && $monitor_id =~ ^[0-9]+$ ]] || exit 0
((current_width > 0 && current_height > 0)) || exit 0 ((current_width > 0 && current_height > 0)) || exit 0
target_width=$current_width
case $action in
small) target_width=$SMALL_WIDTH ;;
medium | reset) target_width=$MEDIUM_WIDTH ;;
large) target_width=$LARGE_WIDTH ;;
smaller)
for width in $LARGE_WIDTH $MEDIUM_WIDTH $SMALL_WIDTH; do
if ((width < current_width)); then
target_width=$width
break
fi
done
;;
larger)
for width in $SMALL_WIDTH $MEDIUM_WIDTH $LARGE_WIDTH; do
if ((width > current_width)); then
target_width=$width
break
fi
done
;;
esac
# The camera is center-cropped to a consistent 8:9 portrait frame.
target_height=$(((target_width * 9 + 4) / 8))
if ! monitor=$(hyprctl monitors -j 2>/dev/null | jq -cer --argjson id "$monitor_id" 'first(.[] | select(.id == $id)) // empty' 2>/dev/null); then if ! monitor=$(hyprctl monitors -j 2>/dev/null | jq -cer --argjson id "$monitor_id" 'first(.[] | select(.id == $id)) // empty' 2>/dev/null); then
exit 0 exit 0
fi fi
@@ -86,6 +57,56 @@ read -r monitor_x monitor_y monitor_width monitor_height < <(
[[ $monitor_x =~ ^-?[0-9]+$ && $monitor_y =~ ^-?[0-9]+$ && $monitor_width =~ ^[0-9]+$ && $monitor_height =~ ^[0-9]+$ ]] || exit 0 [[ $monitor_x =~ ^-?[0-9]+$ && $monitor_y =~ ^-?[0-9]+$ && $monitor_width =~ ^[0-9]+$ && $monitor_height =~ ^[0-9]+$ ]] || exit 0
# Scale the 8:9 portrait presets from monitor height so they occupy the same
# proportion of a 1080p, HiDPI, ultrawide, or 6K recording.
small_height=$(((monitor_height * 9 + 25) / 50))
small_width=$(((small_height * 8 + 4) / 9))
medium_height=$(((monitor_height + 2) / 4))
medium_width=$(((medium_height * 8 + 4) / 9))
large_height=$(((monitor_height * 27 + 40) / 80))
large_width=$(((large_height * 8 + 4) / 9))
target_width=$current_width
target_height=$current_height
case $action in
small)
target_width=$small_width
target_height=$small_height
;;
medium | reset)
target_width=$medium_width
target_height=$medium_height
;;
large)
target_width=$large_width
target_height=$large_height
;;
smaller)
if ((large_width < current_width)); then
target_width=$large_width
target_height=$large_height
elif ((medium_width < current_width)); then
target_width=$medium_width
target_height=$medium_height
elif ((small_width < current_width)); then
target_width=$small_width
target_height=$small_height
fi
;;
larger)
if ((small_width > current_width)); then
target_width=$small_width
target_height=$small_height
elif ((medium_width > current_width)); then
target_width=$medium_width
target_height=$medium_height
elif ((large_width > current_width)); then
target_width=$large_width
target_height=$large_height
fi
;;
esac
target_x=$((monitor_x + monitor_width - target_width - MARGIN)) target_x=$((monitor_x + monitor_width - target_width - MARGIN))
target_y=$((monitor_y + monitor_height - target_height - MARGIN)) target_y=$((monitor_y + monitor_height - target_height - MARGIN))
+8 -7
View File
@@ -1,15 +1,16 @@
-- Initial sizes mirror the live presets in omarchy-capture-webcam-resize. -- The 8:9 portrait presets scale from monitor height so they occupy the same
-- share of anything from a 1080p monitor to a scaled 6K display.
o.window("^WebcamOverlay-small$", { o.window("^WebcamOverlay-small$", {
size = { 240, 270 }, size = { "(monitor_h*4/25)", "(monitor_h*9/50)" },
move = { "(monitor_w-240-40)", "(monitor_h-270-40)" }, move = { "(monitor_w-monitor_h*4/25-40)", "(monitor_h-monitor_h*9/50-40)" },
}) })
o.window("^WebcamOverlay-medium$", { o.window("^WebcamOverlay-medium$", {
size = { 360, 405 }, size = { "(monitor_h*2/9)", "(monitor_h/4)" },
move = { "(monitor_w-360-40)", "(monitor_h-405-40)" }, move = { "(monitor_w-monitor_h*2/9-40)", "(monitor_h-monitor_h/4-40)" },
}) })
o.window("^WebcamOverlay-large$", { o.window("^WebcamOverlay-large$", {
size = { 480, 540 }, size = { "(monitor_h*3/10)", "(monitor_h*27/80)" },
move = { "(monitor_w-480-40)", "(monitor_h-540-40)" }, move = { "(monitor_w-monitor_h*3/10-40)", "(monitor_h-monitor_h*27/80-40)" },
}) })
-- The dedicated app id keeps this out of mpv's generic centered floating rules, -- The dedicated app id keeps this out of mpv's generic centered floating rules,
+28 -8
View File
@@ -84,11 +84,14 @@ case $1 in
clients) clients)
printf '[{"address":"0xabc","title":"%s","size":[%s,%s],"monitor":2}]\n' \ printf '[{"address":"0xabc","title":"%s","size":[%s,%s],"monitor":2}]\n' \
"${OMARCHY_TEST_CLIENT_TITLE:-WebcamOverlay}" \ "${OMARCHY_TEST_CLIENT_TITLE:-WebcamOverlay}" \
"${OMARCHY_TEST_CLIENT_WIDTH:-360}" \ "${OMARCHY_TEST_CLIENT_WIDTH:-178}" \
"${OMARCHY_TEST_CLIENT_HEIGHT:-405}" "${OMARCHY_TEST_CLIENT_HEIGHT:-200}"
;; ;;
monitors) monitors)
printf '[{"id":2,"x":1280,"y":-100,"width":2560,"height":1600,"scale":2}]\n' printf '[{"id":2,"x":1280,"y":-100,"width":%s,"height":%s,"scale":%s}]\n' \
"${OMARCHY_TEST_MONITOR_WIDTH:-2560}" \
"${OMARCHY_TEST_MONITOR_HEIGHT:-1600}" \
"${OMARCHY_TEST_MONITOR_SCALE:-2}"
;; ;;
dispatch) dispatch)
printf '%s\n' "$*" >>"$OMARCHY_TEST_HYPRCTL_ARGS" printf '%s\n' "$*" >>"$OMARCHY_TEST_HYPRCTL_ARGS"
@@ -103,14 +106,31 @@ export OMARCHY_TEST_HYPRCTL_ARGS="$tmp_dir/hyprctl-args"
expected_hyprctl_args="$tmp_dir/expected-hyprctl-args" expected_hyprctl_args="$tmp_dir/expected-hyprctl-args"
printf '%s\n' \ printf '%s\n' \
'dispatch hl.dsp.window.resize({ window = "address:0xabc", x = 240, y = 270 })' \ 'dispatch hl.dsp.window.resize({ window = "address:0xabc", x = 128, y = 144 })' \
'dispatch hl.dsp.window.move({ window = "address:0xabc", x = 2280, y = 390 })' >"$expected_hyprctl_args" 'dispatch hl.dsp.window.move({ window = "address:0xabc", x = 2392, y = 516 })' >"$expected_hyprctl_args"
if ! cmp -s "$OMARCHY_TEST_HYPRCTL_ARGS" "$expected_hyprctl_args"; then if ! cmp -s "$OMARCHY_TEST_HYPRCTL_ARGS" "$expected_hyprctl_args"; then
fail "webcam resize preserves its aspect ratio and corner anchor" "$(diff -u "$expected_hyprctl_args" "$OMARCHY_TEST_HYPRCTL_ARGS")" fail "webcam resize preserves its aspect ratio and corner anchor" "$(diff -u "$expected_hyprctl_args" "$OMARCHY_TEST_HYPRCTL_ARGS")"
fi fi
pass "webcam resize preserves its aspect ratio and corner anchor" pass "webcam resize preserves its aspect ratio and corner anchor"
: >"$OMARCHY_TEST_HYPRCTL_ARGS"
OMARCHY_TEST_MONITOR_WIDTH=1920 \
OMARCHY_TEST_MONITOR_HEIGHT=1080 \
OMARCHY_TEST_MONITOR_SCALE=1 \
OMARCHY_TEST_CLIENT_WIDTH=128 \
OMARCHY_TEST_CLIENT_HEIGHT=144 \
"$ROOT/bin/omarchy-capture-webcam-resize" reset
printf '%s\n' \
'dispatch hl.dsp.window.resize({ window = "address:0xabc", x = 240, y = 270 })' \
'dispatch hl.dsp.window.move({ window = "address:0xabc", x = 2920, y = 670 })' >"$expected_hyprctl_args"
if ! cmp -s "$OMARCHY_TEST_HYPRCTL_ARGS" "$expected_hyprctl_args"; then
fail "webcam default size adapts to monitor resolution" "$(diff -u "$expected_hyprctl_args" "$OMARCHY_TEST_HYPRCTL_ARGS")"
fi
pass "webcam default size adapts to monitor resolution"
: >"$OMARCHY_TEST_HYPRCTL_ARGS" : >"$OMARCHY_TEST_HYPRCTL_ARGS"
OMARCHY_TEST_CLIENT_TITLE="Other Window" "$ROOT/bin/omarchy-capture-webcam-resize" larger OMARCHY_TEST_CLIENT_TITLE="Other Window" "$ROOT/bin/omarchy-capture-webcam-resize" larger
@@ -129,10 +149,10 @@ grep -F -- '--wayland-app-id="WebcamOverlay-$WEBCAM_SIZE"' \
"$ROOT/bin/omarchy-capture-screenrecording" >/dev/null || fail "webcam uses a dedicated size-specific app id" "$ROOT/bin/omarchy-capture-screenrecording" >/dev/null || fail "webcam uses a dedicated size-specific app id"
webcam_rules="$ROOT/default/hypr/apps/webcam-overlay.lua" webcam_rules="$ROOT/default/hypr/apps/webcam-overlay.lua"
grep -F 'move = { "(monitor_w-240-40)", "(monitor_h-270-40)" }' "$webcam_rules" >/dev/null || \ grep -F 'move = { "(monitor_w-monitor_h*4/25-40)", "(monitor_h-monitor_h*9/50-40)" }' "$webcam_rules" >/dev/null || \
fail "small webcam starts at its final corner position" fail "small webcam starts at its final corner position"
grep -F 'move = { "(monitor_w-360-40)", "(monitor_h-405-40)" }' "$webcam_rules" >/dev/null || \ grep -F 'move = { "(monitor_w-monitor_h*2/9-40)", "(monitor_h-monitor_h/4-40)" }' "$webcam_rules" >/dev/null || \
fail "medium webcam starts at its final corner position" fail "medium webcam starts at its final corner position"
grep -F 'move = { "(monitor_w-480-40)", "(monitor_h-540-40)" }' "$webcam_rules" >/dev/null || \ grep -F 'move = { "(monitor_w-monitor_h*3/10-40)", "(monitor_h-monitor_h*27/80-40)" }' "$webcam_rules" >/dev/null || \
fail "large webcam starts at its final corner position" fail "large webcam starts at its final corner position"
pass "webcam size rules place the initial window in its final corner" pass "webcam size rules place the initial window in its final corner"