From 8b9e43d3e54f7b790788aa9fbb04ea61a78de5bd Mon Sep 17 00:00:00 2001 From: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Date: Thu, 13 Aug 2026 15:53:48 +0530 Subject: [PATCH] Only offer video capture devices as webcams (#6732) * Only offer video capture devices as webcams V4L2 exposes raw processing and output-only nodes beside usable cameras. The webcam picker and automatic recorder selection treated the first /dev/video node as a camera, so IPU6 laptops opened a black overlay instead of their loopback capture device.\n\nShare one device lister across detection, selection, and recording, and keep only groups whose first video node advertises Video Capture in Device Caps. Cover raw IPU nodes, ordinary capture devices, and capture-less systems. * Fall through to a later capture-capable node in a webcam group A group whose first video node is not capture-capable vanished entirely, even when a later node in the same group could capture. Probe each node until one qualifies, still emitting at most one device per group. Also exit zero explicitly: a trailing filtered device used to leak the failed capability check as the script's exit status. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: David Heinemeier Hansson Co-authored-by: Claude Fable 5 --- bin/omarchy-capture-screenrecording | 2 +- ...marchy-capture-screenrecording-with-webcam | 16 +--- bin/omarchy-capture-webcam-list | 33 +++++++ bin/omarchy-hw-webcam | 2 +- test/shell.d/screenrecording-test.sh | 92 +++++++++++++++++-- 5 files changed, 119 insertions(+), 26 deletions(-) create mode 100755 bin/omarchy-capture-webcam-list diff --git a/bin/omarchy-capture-screenrecording b/bin/omarchy-capture-screenrecording index 4335080b..7666e1e6 100755 --- a/bin/omarchy-capture-screenrecording +++ b/bin/omarchy-capture-screenrecording @@ -64,7 +64,7 @@ start_webcam_overlay() { # Auto-detect first available webcam if none specified if [[ -z $WEBCAM_DEVICE ]]; then - WEBCAM_DEVICE=$(v4l2-ctl --list-devices 2>/dev/null | grep -m1 "^[[:space:]]*/dev/video" | tr -d '\t') + WEBCAM_DEVICE=$(omarchy-capture-webcam-list | sed -n '1s/[[:space:]].*//p') if [[ -z $WEBCAM_DEVICE ]]; then omarchy-notification-send -u critical -t 3000 "No webcam devices found" return 1 diff --git a/bin/omarchy-capture-screenrecording-with-webcam b/bin/omarchy-capture-screenrecording-with-webcam index fb35cb8e..e5b91bfd 100755 --- a/bin/omarchy-capture-screenrecording-with-webcam +++ b/bin/omarchy-capture-screenrecording-with-webcam @@ -3,21 +3,7 @@ # omarchy:summary=Pick a webcam and start a screen recording with it # omarchy:examples=omarchy capture screenrecording-with-webcam -set -o pipefail - -webcam_list() { - v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do - if [[ $line != $'\t'* && -n $line ]]; then - local name="$line" - local device - IFS= read -r device || break - device=$(tr -d '\t' <<<"$device" | head -1) - [[ -n $device ]] && echo "$device $name" - fi - done -} - -mapfile -t devices < <(webcam_list) +mapfile -t devices < <(omarchy-capture-webcam-list) if (( ${#devices[@]} == 0 )); then omarchy-notification-send "No webcam devices found" -u critical -t 3000 exit 1 diff --git a/bin/omarchy-capture-webcam-list b/bin/omarchy-capture-webcam-list new file mode 100755 index 00000000..bc01f6b9 --- /dev/null +++ b/bin/omarchy-capture-webcam-list @@ -0,0 +1,33 @@ +#!/bin/bash + +# omarchy:summary=List webcam devices that support video capture +# omarchy:hidden=true + +capture_capable() { + local device="$1" + + v4l2-ctl --device "$device" --info 2>/dev/null | awk ' + /^[[:space:]]*Device Caps[[:space:]]*:/ { inspect = 1; next } + inspect && /^[[:space:]]*Video Capture/ { found = 1 } + END { exit !found } + ' +} + +name="" +emitted=0 + +while IFS= read -r line; do + if [[ -n $line && $line != [[:space:]]* ]]; then + name="$line" + emitted=0 + elif (( ! emitted )); then + device="${line#"${line%%[![:space:]]*}"}" + + if [[ $device == /dev/video* ]] && capture_capable "$device"; then + emitted=1 + printf '%s %s\n' "$device" "$name" + fi + fi +done < <(v4l2-ctl --list-devices 2>/dev/null) + +exit 0 diff --git a/bin/omarchy-hw-webcam b/bin/omarchy-hw-webcam index 4874dd77..7b25be70 100755 --- a/bin/omarchy-hw-webcam +++ b/bin/omarchy-hw-webcam @@ -2,4 +2,4 @@ # omarchy:summary=Check whether a webcam is available -v4l2-ctl --list-devices 2>/dev/null | grep -q '^[[:space:]]*/dev/video' +[[ -n $(omarchy-capture-webcam-list) ]] diff --git a/test/shell.d/screenrecording-test.sh b/test/shell.d/screenrecording-test.sh index cb8ca7e1..7859ec14 100644 --- a/test/shell.d/screenrecording-test.sh +++ b/test/shell.d/screenrecording-test.sh @@ -15,12 +15,44 @@ cat >"$stub_bin/v4l2-ctl" <<'SH' [[ ${OMARCHY_TEST_NO_WEBCAM:-false} == "true" ]] && exit 0 -printf '%s\n' "Built-in Webcam: Integrated Camera" -printf '\t%s\n' "/dev/video0" -printf '\t%s\n' "/dev/video1" -printf '\n' -printf '%s\n' "USB Capture Card: External Camera" -printf '\t%s\n' "/dev/video2" +case "$1" in +--list-devices) + printf '%s\n' "ipu6 (PCI:0000:00:05.0):" + printf '\t%s\n' "/dev/video0" + printf '\t%s\n' "/dev/video1" + + if [[ ${OMARCHY_TEST_RAW_WEBCAM:-false} != "true" ]]; then + printf '\n%s\n' "Built-in Webcam: Integrated Camera" + printf '\t%s\n' "/dev/video42" + printf '\t%s\n' "/dev/video43" + printf '\n%s\n' "USB Capture Card: External Camera" + printf '\t%s\n' "/dev/video2" + fi + + if [[ ${OMARCHY_TEST_DUAL_NODE_WEBCAM:-false} == "true" ]]; then + printf '\n%s\n' "Dual Node Camera: ISP Wrapper" + printf '\t%s\n' "/dev/video7" + printf '\t%s\n' "/dev/video8" + printf '\n%s\n' "Metadata Only: Sensor" + printf '\t%s\n' "/dev/video9" + fi + ;; +--device) + case "$2" in + /dev/video0) device_capability="Video Output" ;; + /dev/video1) device_capability="Metadata Capture" ;; + /dev/video7 | /dev/video9) device_capability="Video Output" ;; + *) device_capability="Video Capture" ;; + esac + + printf '%s\n' \ + "Driver Info:" \ + $'\tCapabilities : 0x84a00001' \ + $'\t\tVideo Capture' \ + $'\tDevice Caps : 0x04200001' \ + $'\t\t'"$device_capability" + ;; +esac SH cat >"$stub_bin/omarchy-menu-select" <<'SH' @@ -51,10 +83,39 @@ export OMARCHY_TEST_MENU_ARGS="$tmp_dir/menu-args" export OMARCHY_TEST_RECORDER_ARGS="$tmp_dir/recorder-args" export OMARCHY_TEST_NOTIFICATION_ARGS="$tmp_dir/notification-args" +mapfile -t capture_devices < <(omarchy-capture-webcam-list) +expected_capture_devices=( + "/dev/video42 Built-in Webcam: Integrated Camera" + "/dev/video2 USB Capture Card: External Camera" +) + +if [[ ${capture_devices[*]} != "${expected_capture_devices[*]}" ]]; then + fail "webcam detection filters output-only devices and collapses each capture group" \ + "expected: ${expected_capture_devices[*]}\nactual: ${capture_devices[*]}" +fi +pass "webcam detection filters output-only devices and collapses each capture group" + +dual_node=$(OMARCHY_TEST_DUAL_NODE_WEBCAM=true omarchy-capture-webcam-list) || + fail "webcam listing exits zero when the trailing device is filtered" +pass "webcam listing exits zero when the trailing device is filtered" + +expected_dual_node="/dev/video42 Built-in Webcam: Integrated Camera +/dev/video2 USB Capture Card: External Camera +/dev/video8 Dual Node Camera: ISP Wrapper" +[[ $dual_node == "$expected_dual_node" ]] || + fail "webcam detection falls through to a later capture-capable node in a group" "$dual_node" +pass "webcam detection falls through to a later capture-capable node in a group" + if "$ROOT/bin/omarchy-hw-webcam"; then - pass "webcam hardware detection succeeds when a video device is available" + pass "webcam hardware detection succeeds when a capture device is available" else - fail "webcam hardware detection succeeds when a video device is available" + fail "webcam hardware detection succeeds when a capture device is available" +fi + +if OMARCHY_TEST_RAW_WEBCAM=true "$ROOT/bin/omarchy-hw-webcam"; then + fail "webcam hardware detection rejects output-only video devices" +else + pass "webcam hardware detection rejects output-only video devices" fi if OMARCHY_TEST_NO_WEBCAM=true "$ROOT/bin/omarchy-hw-webcam"; then @@ -63,12 +124,19 @@ else pass "webcam hardware detection fails when no video device is available" fi +if OMARCHY_TEST_RAW_WEBCAM=true "$ROOT/bin/omarchy-capture-screenrecording-with-webcam"; then + fail "screenrecording webcam picker rejects output-only video devices" +fi +grep -Fx 'No webcam devices found' "$OMARCHY_TEST_NOTIFICATION_ARGS" >/dev/null || \ + fail "screenrecording webcam picker reports no capture-capable device" +pass "screenrecording webcam picker rejects output-only video devices" + "$ROOT/bin/omarchy-capture-screenrecording-with-webcam" expected_menu_args="$tmp_dir/expected-menu-args" printf '%s\n' \ "Select Webcam" \ - "/dev/video0 Built-in Webcam: Integrated Camera" \ + "/dev/video42 Built-in Webcam: Integrated Camera" \ "/dev/video2 USB Capture Card: External Camera" \ "--" \ "--width" \ @@ -93,6 +161,12 @@ if ! cmp -s "$OMARCHY_TEST_RECORDER_ARGS" "$expected_recorder_args"; then fi pass "screenrecording webcam picker starts recording with selected device" +first_webcam=$(omarchy-capture-webcam-list | sed -n '1s/[[:space:]].*//p') +[[ $first_webcam == "/dev/video42" ]] || fail "screenrecording auto-detection selects the first capture device" +grep -F 'WEBCAM_DEVICE=$(omarchy-capture-webcam-list' "$ROOT/bin/omarchy-capture-screenrecording" >/dev/null || \ + fail "screenrecording auto-detection uses capture-capable webcams" +pass "screenrecording auto-detection uses the first capture-capable webcam" + cat >"$stub_bin/hyprctl" <<'SH' #!/bin/bash