feat(windows): wire screen-audio sharing into the WinForms client

The core already supported SCREEN_AUDIO capture on Windows (post-M3 WASAPI
loopback via VOICECAT_HAS_LOOPBACK) and the C# Interop layer was complete
(VcStreamKind.ScreenAudio, StartStream/StopStream/SetRemoteStream). Only the
UI was missing -- no core, proto, or C ABI changes needed.

Adds a 'Share Screen Audio' toggle to the voice panel, independent of mic
voice (can share without joining voice). Disconnect/teardown now stops the
screen stream cleanly. New smoke test exercises the full StartStream ->
StreamStarted -> StopStream -> StreamStopped path through P/Invoke.
This commit is contained in:
2026-06-17 22:47:28 +02:00
parent 2185d9d15c
commit a88656f2fa
4 changed files with 140 additions and 0 deletions

View File

@@ -10,6 +10,44 @@ up instantly. Newest status at the top.
## ▶ Where we left off / next action
- **Done:** **Screen-audio sharing wired into the Windows WinForms client** (2026-06-17).
The core already fully supported `SCREEN_AUDIO` capture on Windows (post-M3 WASAPI
loopback via `VOICECAT_HAS_LOOPBACK`, always on for the `windows-client` preset —
`core/CMakeLists.txt:85`; loopback start/stop at `client.cpp:1093`/`audio_engine.cpp:529`;
VAD/PTT/self-mute/server-mute correctly bypassed for non-MIC kinds at `client.cpp:862-879`)
and the C# Interop layer was already complete (`VcStreamKind.ScreenAudio`,
`StartStream`/`StopStream`/`SetRemoteStream`/`ListUserStreams` all generic). The gap was
purely UI wiring. **No core, proto, or C ABI changes were needed** — confirming the
"the core should support it already" assessment.
- `MainForm.Designer.cs` — new `btnScreenShareToggle` button in the voice panel top row
(`flpVoiceTop`), right after `btnMicToggle`, with full `AccessibleName`/
`AccessibleDescription` per the existing accessibility convention.
- `MainForm.cs` — new `_screenStreamId` field; `BtnScreenShareToggle_Click` handler
mirroring `BtnMicToggle_Click` but with no device picker / VAD / PTT / mode / mute
(screen audio bypasses all of those in the core). Independent of mic — can share
without joining voice and vice versa. `HandleDisconnected` now resets
`_screenStreamId` and disables the screen toggle. `OnFormClosed` now explicitly stops
both mic and screen streams before `Disconnect()` (clean `StreamStop` messages go out
before the control channel closes). `HandleStreamStarted` already labeled
`ScreenAudio => "screen audio"`; `PerUserTuningDialog.ApplySettings` already iterates
all of a peer's streams — both unchanged, peers can independently volume-tune a
screen-audio stream vs that user's mic.
- `VoiceCatClientSmokeTests.cs` — new `ScreenAudioStream_Starts_And_Stops` test:
connects + TOFU + guest auth, `StartStream(ScreenAudio)`, asserts
`VC_EVENT_STREAM_STARTED` arrives with matching `StreamId`, `StopStream`, asserts
`VC_EVENT_STREAM_STOPPED`. Passes headless (the `StreamAnnounce` succeeds regardless
of whether the loopback device initializes on a CI box).
- `dotnet build` — 0 warnings/errors. `dotnet test`**4/4 tests green**
(3 existing + 1 new). Event trace confirms the full
`StreamStarted → UserUpdated → StreamStopped` path through P/Invoke against a live
`voicecat-server.exe`.
- **Not yet confirmed audible by ear** — pending manual two-instance live test (one
shares screen audio while something plays on the default render endpoint, the other
hears it). This is the observable-behavior exit criterion per `AGENTS.md`.
- **Documented caveat** (`docs/voice.md §9`, unchanged): whole-device WASAPI loopback
inherently re-captures this app's own incoming voice mix (self-echo loop) — accepted
characteristic, not a bug. Process-specific loopback (Windows 10 2004+
`AUDIOCLIENT_ACTIVATION_PARAMS`) is a future enhancement; miniaudio doesn't expose it.
- **In progress:** **M5 — moderation & admin** (2026-06-17). Server-side and C ABI are
implemented and tested: permissions, kick/ban/move/server-mute, channel CRUD, in-app account
management. Four new tests pass: `test_m5_permissions`, `test_m5_kick_ban_move_mute`,