feat(windows): system-wide push-to-talk via Raw Input

PTT was focus-scoped (WinForms KeyDown/KeyUp) so it died the moment the
window lost focus. Add an optional system-wide path using the Raw Input
API (RegisterRawInputDevices + WM_INPUT with RIDEV_INPUTSINK) instead of
a WH_KEYBOARD_LL low-level hook -- the latter is the keylogger pattern AV
heuristics flag, which is worse for our unsigned MinGW binary. Raw Input
involves no DLL injection or global hook and passes keys through.

- New VoiceCat.App/Native/RawInput.cs: P/Invoke + structs; register the
  keyboard sink, decode WM_INPUT to vkey/up-down, GetAsyncKeyState helper.
- MainForm overrides OnHandleCreated/OnHandleDestroyed/WndProc to manage
  the sink and route WM_INPUT to PTT; gates the focus-scoped KeyDown/KeyUp
  off when system-wide is on; makes the Deactivate force-release
  conditional; adds a GetAsyncKeyState watchdog on the pump timer so a
  missed key-up (RDP/lock-screen) cannot leave PTT stuck.
- VoiceSettings.SystemWidePtt (default ON) + system-wide checkbox in the
  Audio settings PTT section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 13:14:01 +02:00
parent 65736df464
commit 2baefddbe4
5 changed files with 310 additions and 18 deletions

View File

@@ -26,6 +26,21 @@ up instantly. Newest status at the top.
send cushion could be reduced or removed. Shared-core change → add a test and re-verify
desktop↔desktop stays low-latency (steady sender ⇒ ~0 arrival jitter ⇒ no regression).
- **Done (2026-06-24):** **Windows PTT can now work system-wide (in the background).** Previously
the PTT key was focus-scoped (WinForms `KeyDown`/`KeyUp`, dead the moment the window lost
focus). Added an AV-safe global path using the **Raw Input API** (`RegisterRawInputDevices` +
`WM_INPUT` with `RIDEV_INPUTSINK`) — *not* a `WH_KEYBOARD_LL` low-level hook, which is the
keylogger pattern AV heuristics flag (worse for our unsigned MinGW binary). New
`clients/windows/VoiceCat.App/Native/RawInput.cs` (P/Invoke + structs); `MainForm` overrides
`OnHandleCreated`/`OnHandleDestroyed`/`WndProc` to register the keyboard sink and handle
`WM_INPUT`, gates the focus-scoped `KeyDown`/`KeyUp` handlers off when system-wide is on, makes
the `Deactivate` force-release conditional, and adds a `GetAsyncKeyState` watchdog on the pump
timer so a missed key-up (RDP/lock-screen focus switch) can't leave PTT stuck. New
`VoiceSettings.SystemWidePtt` (default ON) with a "Works in the background (system-wide)"
checkbox in the Audio settings PTT section. Build green (`dotnet build`, 0 warnings). **Next
(manual):** verify background PTT against a live server, and confirm the binary trips no AV
keyboard-hook detection.
- **Done (2026-06-23):** **Fixed: receive-side noise reduction silently skipped on stereo mic
streams (regression from stereo-mic capture below).** The per-listener NR toggle
(`vc_set_remote_stream(... noise_reduction)`) did nothing on Windows/macOS/iOS — the UI and
@@ -896,7 +911,8 @@ text, device pickers, level meter on each platform.
**Windows** (`clients/windows/`): `VoiceCat.Interop` (P/Invoke, `[UnmanagedCallersOnly]`),
`VoiceCat.App` (ConnectDialog, ServerIdentityDialog, MainForm with full M5 moderation UI,
PerUserTuningDialog, PttKeyCaptureDialog), `VoiceCat.Interop.Tests`. PTT is focus-scoped.
PerUserTuningDialog, PttKeyCaptureDialog), `VoiceCat.Interop.Tests`. PTT can be system-wide
(Raw Input / WM_INPUT) or focus-scoped, toggled in Audio settings (default system-wide).
**macOS** (`clients/apple/macOS/VoiceCatMac.xcodeproj`): NSOutlineView channel tree,
NSTableView user list, NSTextView chat, voice controls, full VoiceOver accessibility, admin