Skip to content

feat(hardware): mute/mic-mute keyboard LED sync (steelbore-audio-led)#17

Merged
UnbreakableMJ merged 1 commit into
mainfrom
feat/audio-mute-leds
Jun 29, 2026
Merged

feat(hardware): mute/mic-mute keyboard LED sync (steelbore-audio-led)#17
UnbreakableMJ merged 1 commit into
mainfrom
feat/audio-mute-leds

Conversation

@UnbreakableMJ

Copy link
Copy Markdown
Contributor

Problem

On the ThinkPad T490s, the mute (F1) and mic-mute (F4) keyboard LEDs stayed dark under Niri. The kernel audio-mute / audio-micmute triggers follow the ALSA hardware mute, but PipeWire mutes the node in software, so the hardware mute never flips and the LEDs never light. (CapsLock and FnLock already work — kernel input layer and EC/thinkpad_acpi respectively — so they need no changes.)

Solution

A tiny Rust daemon, steelbore-audio-led (pkgs/steelbore-audio-led/, libpulse-binding), that subscribes to default sink/source mute changes and writes the LED brightness nodes directly — so the LEDs track mute no matter how it was toggled (key, GUI, per-app). Keybinds are unchanged.

New module modules/hardware/audio-led.nix (steelbore.hardware.audioLed.enable, enabled on hosts/thinkpad):

  • udev rule sets platform::{mute,micmute} trigger=none so the daemon owns the LEDs (the trigger attr is root-only; brightness is already input-group-writable via the brightnessctl udev rule in modules/desktops/niri.nix).
  • systemd user service running the daemon, ordered after pipewire/wireplumber/pipewire-pulse, Restart=on-failure.

Design: single-threaded (inherently serial workload — documented per Standard §3.2); mimalloc omitted (no alloc hot path); SPDX headers + Rust-guideline lints.

Verification

  • nix build of the toplevel — builds clean.
  • Daemon connects to the live PipeWire session and seeds initial state.
  • End-to-end on hardware: toggling mute via wpctl drives both LEDs 1/0 (mic + speaker) ✅.

Docs: PRD.md §6.4, TODO.md Phase 7.

🤖 Generated with Claude Code

On the T490s the mute (platform::mute) and mic-mute (platform::micmute)
keyboard LEDs stayed dark under Niri: the kernel audio-mute/audio-micmute
triggers follow the ALSA *hardware* mute, but PipeWire mutes the node in
*software*, so the hardware mute never flips. (CapsLock and FnLock already work
via the kernel input layer and the EC/thinkpad_acpi respectively.)

Add steelbore-audio-led (pkgs/steelbore-audio-led/, Rust + libpulse-binding): a
tiny event-driven daemon that subscribes to default sink/source mute changes and
writes the LED brightness nodes directly, so the LEDs track mute regardless of
how it was toggled (key, GUI, per-app). Single-threaded by design (inherently
serial workload); resilience delegated to systemd Restart=on-failure.

New module modules/hardware/audio-led.nix (steelbore.hardware.audioLed.enable):
- udev rule sets platform::{mute,micmute} trigger=none so the daemon owns the
  LEDs (trigger is root-only; brightness is already input-group-writable via the
  brightnessctl udev rule in modules/desktops/niri.nix).
- systemd *user* service running the daemon, ordered after pipewire/wireplumber/
  pipewire-pulse.
Enabled on hosts/thinkpad. PRD.md §6.4 + TODO.md Phase 7 updated.

Verified: package builds; daemon connects to the live session; toggling mute via
wpctl drives both LEDs 1/0 end-to-end (mic + speaker).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@UnbreakableMJ UnbreakableMJ merged commit a48993f into main Jun 29, 2026
1 check passed
@UnbreakableMJ UnbreakableMJ deleted the feat/audio-mute-leds branch June 29, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant