A message-only window registers the keyboard and mouse with
RIDEV_INPUTSINK, so every key and button arrives as WM_INPUT whether or
not the client is in front. Raw input only observes, where a
WH_KEYBOARD_LL hook sits in the path of the system input queue and can
swallow a keystroke; it also reports the side buttons and both edges of
every key, which push-to-talk needs.
Keyboard codes stay in each platform's own numbering — X keycodes on
X11, set-1 scan codes with the E0/E1 escape folded into the high byte on
Windows — since that is what the input API reports and the key-naming
call expects. Bindings are per-machine either way, as TeamSpeak's own
per-OS keydefs are. Mouse buttons are unified on the X numbering, so
"Mouse 4" means the same thing on both.
The RAWINPUT decoding lives in its own class so it can be tested off
Windows; the window and its pump have not been run on Windows yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Capture and playback now go through AudioCapture/AudioPlayback interfaces
with two implementations: PipeWire, used for the system default and for
every pw: device when a session is reachable, and Java Sound for the raw
ALSA devices and other platforms. The PipeWire binding is FFM-based
(PipeWireLibrary, PipeWireSession, PipeWireStream, SpaPod) and enumerates
the graph's real devices, so each stream shows up separately in volume
mixers.
Lines are opened with a negotiated channel count instead of a fixed mono
format: OPUS_MUSIC transmits the stereo capture as-is, OPUS_VOICE keeps
transmitting the mono downmix so the pre-processing chain and VAD still
see a single channel. Java Sound enumeration asks only for "a mixer that
can capture/play" so the PipeWire and PulseAudio ALSA plugins are no
longer hidden by their narrow format lists.
The Java Sound classes are renamed to Desktop* to match what they now
are, and JUnit plus a SpaPod round-trip test are wired into the build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>