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 <noreply@anthropic.com>

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aditya Garud
2026-08-13 12:23:48 +02:00
committed by GitHub
co-authored by Claude Fable 5 David Heinemeier Hansson
parent 860503f56a
commit 8b9e43d3e5
5 changed files with 119 additions and 26 deletions
+1 -1
View File
@@ -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
@@ -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
+33
View File
@@ -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
+1 -1
View File
@@ -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) ]]
+83 -9
View File
@@ -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