From 784996c378a5fbcc898ba86a2a52e7faee8d9bd1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 17 Jul 2026 18:25:39 -0700 Subject: [PATCH] Scale webcam overlay to monitor resolution --- bin/omarchy-capture-webcam-resize | 79 ++++++++++++++++++---------- default/hypr/apps/webcam-overlay.lua | 15 +++--- test/shell.d/screenrecording-test.sh | 36 ++++++++++--- 3 files changed, 86 insertions(+), 44 deletions(-) diff --git a/bin/omarchy-capture-webcam-resize b/bin/omarchy-capture-webcam-resize index 623103a9..ebe9840f 100755 --- a/bin/omarchy-capture-webcam-resize +++ b/bin/omarchy-capture-webcam-resize @@ -8,9 +8,6 @@ set -euo pipefail readonly MARGIN=40 -readonly SMALL_WIDTH=240 -readonly MEDIUM_WIDTH=360 -readonly LARGE_WIDTH=480 usage() { echo "Usage: omarchy-capture-webcam-resize " >&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 ((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 exit 0 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 +# 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_y=$((monitor_y + monitor_height - target_height - MARGIN)) diff --git a/default/hypr/apps/webcam-overlay.lua b/default/hypr/apps/webcam-overlay.lua index 27fcc64d..a8312e5f 100644 --- a/default/hypr/apps/webcam-overlay.lua +++ b/default/hypr/apps/webcam-overlay.lua @@ -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$", { - size = { 240, 270 }, - move = { "(monitor_w-240-40)", "(monitor_h-270-40)" }, + size = { "(monitor_h*4/25)", "(monitor_h*9/50)" }, + move = { "(monitor_w-monitor_h*4/25-40)", "(monitor_h-monitor_h*9/50-40)" }, }) o.window("^WebcamOverlay-medium$", { - size = { 360, 405 }, - move = { "(monitor_w-360-40)", "(monitor_h-405-40)" }, + size = { "(monitor_h*2/9)", "(monitor_h/4)" }, + move = { "(monitor_w-monitor_h*2/9-40)", "(monitor_h-monitor_h/4-40)" }, }) o.window("^WebcamOverlay-large$", { - size = { 480, 540 }, - move = { "(monitor_w-480-40)", "(monitor_h-540-40)" }, + size = { "(monitor_h*3/10)", "(monitor_h*27/80)" }, + 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, diff --git a/test/shell.d/screenrecording-test.sh b/test/shell.d/screenrecording-test.sh index 2e8ec40b..2abadec2 100644 --- a/test/shell.d/screenrecording-test.sh +++ b/test/shell.d/screenrecording-test.sh @@ -84,11 +84,14 @@ case $1 in clients) printf '[{"address":"0xabc","title":"%s","size":[%s,%s],"monitor":2}]\n' \ "${OMARCHY_TEST_CLIENT_TITLE:-WebcamOverlay}" \ - "${OMARCHY_TEST_CLIENT_WIDTH:-360}" \ - "${OMARCHY_TEST_CLIENT_HEIGHT:-405}" + "${OMARCHY_TEST_CLIENT_WIDTH:-178}" \ + "${OMARCHY_TEST_CLIENT_HEIGHT:-200}" ;; 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) 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" printf '%s\n' \ - 'dispatch hl.dsp.window.resize({ window = "address:0xabc", x = 240, y = 270 })' \ - 'dispatch hl.dsp.window.move({ window = "address:0xabc", x = 2280, y = 390 })' >"$expected_hyprctl_args" + 'dispatch hl.dsp.window.resize({ window = "address:0xabc", x = 128, y = 144 })' \ + '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 fail "webcam resize preserves its aspect ratio and corner anchor" "$(diff -u "$expected_hyprctl_args" "$OMARCHY_TEST_HYPRCTL_ARGS")" fi 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_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" 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" -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" -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" pass "webcam size rules place the initial window in its final corner"