feat(hardware): hide dual-sensor webcam IR node (fixes dark image)
A built-in RGB+IR webcam (common on recent ThinkPads, e.g. the T14s AMD Gen 4) exposes its IR face-unlock sensor as a second, identically-named "Integrated Camera"; apps that pick it show a dark, 8-bit-greyscale image. nomarchy.hardware.camera.hideIrSensor disables that node on the V4L2 PipeWire path (WirePlumber monitor.v4l2.rules) so only the colour camera is offered, matched by card name (overridable irMatch). - libcamera is left untouched: an external camera you plug in is never affected, and surgical internal-only libcamera scoping isn't possible (its distinguishing props bind after the monitor rule runs). - Only the PipeWire node is hidden; the kernel /dev/video* stays open, so Howdy face-unlock still reads the IR sensor directly. - The installer's hardware-db.sh auto-detects a paired RGB+IR webcam from /sys/class/video4linux/*/name and bakes the toggle into system.nix; commented example added to the downstream template. Validated live on a T14s: collapses the four enumerated "Integrated Camera" entries to one colour source; the exact shipped regex disables the IR node (confirmed in the WirePlumber log) and leaves libcamera intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,33 @@ in
|
||||
distro default — for very new hardware whose drivers (a fresh NPU, the
|
||||
`xe` GPU driver, new-platform enablement) only landed recently. Off by
|
||||
default; the default kernel already carries amd-pstate and amdxdna (6.14+)'';
|
||||
|
||||
camera = {
|
||||
hideIrSensor = lib.mkEnableOption ''
|
||||
hiding a dual-sensor webcam's IR (face-unlock) node from PipeWire so
|
||||
apps only ever see the colour camera. Such modules (common on recent
|
||||
ThinkPads) expose the IR sensor as a SECOND, identically-named
|
||||
"Integrated Camera"; selecting it gives a dark, 8-bit-greyscale image —
|
||||
the classic "my webcam is dark" symptom. The installer turns this on
|
||||
when it detects a paired RGB+IR webcam. Only the PipeWire node is
|
||||
disabled — the kernel /dev/video* device stays open, so face-unlock
|
||||
(Howdy) still works. Acts on the V4L2 path only; the libcamera monitor
|
||||
is left untouched, so an external camera you plug in is never affected.
|
||||
See irMatch'';
|
||||
|
||||
irMatch = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "~.*(Integrated I|IR Camera|Infrared).*";
|
||||
description = ''
|
||||
WirePlumber regex (matched against a V4L2 node's api.v4l2.cap.card)
|
||||
selecting the IR sensor to hide. The default catches the common
|
||||
dual-sensor naming ("… Integrated I", "IR Camera", "Infrared"); set it
|
||||
to your camera's IR card name if it differs — find it with
|
||||
`v4l2-ctl --list-devices` or `wpctl inspect`. Only consulted when
|
||||
camera.hideIrSensor is on.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
@@ -174,6 +201,26 @@ in
|
||||
nomarchy.hardware.latestKernel = true.'';
|
||||
})
|
||||
|
||||
# ── Webcam: hide a dual-sensor module's IR node ────────────────────
|
||||
# A built-in RGB+IR webcam exposes its IR (face-unlock) sensor as a second,
|
||||
# identically-named camera; an app that picks it gets a dark greyscale
|
||||
# image. Disable that node on the V4L2 PipeWire path so only the colour
|
||||
# camera is offered. Matched by card name (irMatch). libcamera is left
|
||||
# alone on purpose — an external camera may rely on it, and surgical
|
||||
# internal-only libcamera scoping isn't possible (the distinguishing
|
||||
# device props bind after the monitor rule runs). The kernel /dev/video*
|
||||
# stays open, so Howdy face-unlock still reads the IR sensor directly.
|
||||
(lib.mkIf (cfg.camera.hideIrSensor && config.services.pipewire.wireplumber.enable) {
|
||||
services.pipewire.wireplumber.extraConfig."90-nomarchy-hide-ir-camera" = {
|
||||
"monitor.v4l2.rules" = [
|
||||
{
|
||||
matches = [ { "api.v4l2.cap.card" = cfg.camera.irMatch; } ];
|
||||
actions."update-props"."node.disabled" = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
})
|
||||
|
||||
# ── Sanity ─────────────────────────────────────────────────────────
|
||||
{
|
||||
assertions = [
|
||||
|
||||
Reference in New Issue
Block a user