Previously returned the first channel percentage found, which is
incorrect when channels are unbalanced. Average across all channels
matches what OS volume sliders display and avoids a misleading reading
in edge cases such as hard-panned mono (e.g. left=100%, right=0%
would have incorrectly reported 100% rather than 50%).
Add a test covering the unbalanced case.
Switch from reading `brightness` (software-requested) to `actual_brightness`
(hardware-reported), which is the correct file on AMD GPU backlights where
the two values can diverge.
Replace the first-found device selection with a scored preference so that
when multiple backlight devices are present (e.g. amdgpu_bl2 and nvidia_0)
the internal laptop panel is chosen rather than a dGPU output that may
report a fixed ceiling value. Priority: amdgpu/intel (3) > acpi/platform
(2) > unknown (1) > nvidia (0).
Add a Rust daemon that connects to QMK keyboards over their Raw HID
interface (VID 0x3434, usage page 0xFF60 / usage 0x0061) and provides:
- Bi-directional layer sync: CMD_LAYER_SYNC (0x40) packets are bridged
between all connected keyboards so their active layers stay in sync.
On connect, a FLAG_QUERY packet requests the current state from each
keyboard. A second keyboard (numpad) is stubbed and ready to enable.
- Volume monitoring: polls `pactl get-sink-volume @DEFAULT_SINK@` every
2 s and pushes CMD_VOLUME (0x41) packets on change. Compatible with
both PulseAudio and PipeWire on KDE6/Wayland.
- Brightness monitoring: polls /sys/class/backlight on the same interval
and pushes CMD_BRIGHTNESS (0x42) packets on change.
- Active-app stub: CMD_ACTIVE_APP (0x43) is reserved with detailed notes
on KDE6/Wayland implementation approaches (KWin D-Bus, foreign
toplevel protocol) for a future commit.
Each keyboard runs a single combined read/write IO thread using a 10 ms
read timeout to interleave HID reads and queued writes without needing
two file handles. Protocol constants in src/protocol.rs mirror
hid_protocol.h in the QMK firmware exactly.
Licensed under GPL-3.0-or-later.