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:
@@ -36,6 +36,7 @@ partial class MainForm
|
||||
private FlowLayoutPanel flpVoiceTop = null!;
|
||||
private FlowLayoutPanel flpVoiceBottom = null!;
|
||||
private Button btnMicToggle = null!;
|
||||
private Button btnScreenShareToggle = null!;
|
||||
private CheckBox chkMute = null!;
|
||||
private CheckBox chkDeafen = null!;
|
||||
private RadioButton radioVad = null!;
|
||||
@@ -81,6 +82,7 @@ partial class MainForm
|
||||
flpVoiceTop = new FlowLayoutPanel();
|
||||
flpVoiceBottom = new FlowLayoutPanel();
|
||||
btnMicToggle = new Button();
|
||||
btnScreenShareToggle = new Button();
|
||||
chkMute = new CheckBox();
|
||||
chkDeafen = new CheckBox();
|
||||
radioVad = new RadioButton();
|
||||
@@ -227,6 +229,20 @@ partial class MainForm
|
||||
btnMicToggle.Margin = new Padding(0, 2, 6, 0);
|
||||
btnMicToggle.TabIndex = 0;
|
||||
|
||||
// Screen-audio share — independent of mic voice (can share without joining voice and
|
||||
// vice versa). The core's WASAPI loopback path (VOICECAT_HAS_LOOPBACK, always on for
|
||||
// the windows-client preset) captures the default render endpoint; see docs/voice.md
|
||||
// §9. Whole-device loopback inherently re-captures this app's own incoming voice mix —
|
||||
// an accepted self-echo characteristic, not a bug.
|
||||
btnScreenShareToggle.Text = "Share Screen &Audio";
|
||||
btnScreenShareToggle.AccessibleName = "Share screen audio";
|
||||
btnScreenShareToggle.AccessibleDescription =
|
||||
"Start or stop sharing your computer's audio (desktop/system audio) with the channel. " +
|
||||
"Independent of the microphone. Captures everything playing through your default speakers.";
|
||||
btnScreenShareToggle.AutoSize = true;
|
||||
btnScreenShareToggle.Margin = new Padding(0, 2, 12, 0);
|
||||
btnScreenShareToggle.TabIndex = 10;
|
||||
|
||||
chkMute.Text = "&Mute mic";
|
||||
chkMute.AutoSize = true;
|
||||
chkMute.Enabled = false;
|
||||
@@ -276,6 +292,7 @@ partial class MainForm
|
||||
flpVoiceTop.AutoSize = false;
|
||||
flpVoiceTop.Padding = new Padding(4, 2, 4, 0);
|
||||
flpVoiceTop.Controls.Add(btnMicToggle);
|
||||
flpVoiceTop.Controls.Add(btnScreenShareToggle);
|
||||
flpVoiceTop.Controls.Add(chkMute);
|
||||
flpVoiceTop.Controls.Add(chkDeafen);
|
||||
flpVoiceTop.Controls.Add(lblMode);
|
||||
|
||||
@@ -23,6 +23,7 @@ public partial class MainForm : Form
|
||||
|
||||
// Voice state
|
||||
private uint _micStreamId; // 0 = not started
|
||||
private uint _screenStreamId; // 0 = not sharing screen audio
|
||||
private Keys _pttKey = Keys.F8;
|
||||
private bool _serverMuted;
|
||||
private bool _serverDeafened;
|
||||
@@ -60,6 +61,7 @@ public partial class MainForm : Form
|
||||
|
||||
// Voice controls
|
||||
btnMicToggle.Click += BtnMicToggle_Click;
|
||||
btnScreenShareToggle.Click += BtnScreenShareToggle_Click;
|
||||
chkMute.CheckedChanged += (_, _) => ApplySelfMute();
|
||||
chkDeafen.CheckedChanged += (_, _) => ApplySelfMute();
|
||||
radioVad.CheckedChanged += RadioVad_CheckedChanged;
|
||||
@@ -404,9 +406,11 @@ public partial class MainForm : Form
|
||||
_talkingUsers.Clear();
|
||||
_currentChannelId = 0;
|
||||
_micStreamId = 0;
|
||||
_screenStreamId = 0;
|
||||
txtCompose.Enabled = false;
|
||||
btnSend.Enabled = false;
|
||||
btnMicToggle.Enabled = false;
|
||||
btnScreenShareToggle.Enabled = false;
|
||||
}
|
||||
|
||||
// ── Level meter ───────────────────────────────────────────────────────────
|
||||
@@ -604,6 +608,35 @@ public partial class MainForm : Form
|
||||
private void ApplySelfMute() =>
|
||||
_client.SetSelfMute(chkMute.Checked, chkDeafen.Checked);
|
||||
|
||||
// Screen-audio share — independent of mic voice. The core bypasses VAD/PTT/self-mute/
|
||||
// server-mute for non-MIC kinds (client.cpp on_capture_frame), so no input-mode or mute
|
||||
// state applies here. WASAPI loopback captures the default render endpoint (whole-device,
|
||||
// not process-specific — docs/voice.md §9).
|
||||
private void BtnScreenShareToggle_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (_screenStreamId == 0)
|
||||
{
|
||||
var (result, streamId) = _client.StartStream(VcStreamKind.ScreenAudio, "Desktop audio");
|
||||
if (result == VcResult.Ok)
|
||||
{
|
||||
_screenStreamId = streamId;
|
||||
btnScreenShareToggle.Text = "Stop Screen &Audio";
|
||||
AddActivity("Started sharing screen audio");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddActivity($"Failed to start screen audio: {result}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_client.StopStream(_screenStreamId);
|
||||
_screenStreamId = 0;
|
||||
btnScreenShareToggle.Text = "Share Screen &Audio";
|
||||
AddActivity("Stopped sharing screen audio");
|
||||
}
|
||||
}
|
||||
|
||||
private void RadioVad_CheckedChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (!radioVad.Checked) return;
|
||||
@@ -916,6 +949,11 @@ public partial class MainForm : Form
|
||||
_pumpTimer.Stop();
|
||||
_client.LevelChanged -= OnLevelChanged;
|
||||
_client.EventReceived -= OnEvent;
|
||||
// Stop any active local streams before tearing down — the core stops loopback in
|
||||
// stream_stop/destroy, but explicit stops ensure clean StreamStop protocol messages
|
||||
// go out before Disconnect closes the control channel.
|
||||
if (_screenStreamId != 0) _client.StopStream(_screenStreamId);
|
||||
if (_micStreamId != 0) _client.StopStream(_micStreamId);
|
||||
_client.Disconnect();
|
||||
_client.Dispose();
|
||||
base.OnFormClosed(e);
|
||||
|
||||
Reference in New Issue
Block a user