From 0823f6173f978c822d14d1692d3eedc46216799a Mon Sep 17 00:00:00 2001 From: Mollusk Date: Sat, 22 Aug 2026 05:44:08 -0400 Subject: [PATCH] ux: make safe desktop audio the default --- src/app/mod.rs | 105 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 653d12e..f292d55 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1150,9 +1150,9 @@ pub struct AppState { /// Apps currently producing audio, shown in the share picker. Populated from /// `UiEvent::AudioAppsListed` after the picker requests an enumeration. share_audio_apps: Vec, - /// The picker's current typed selection. Desktop-shared remains the legacy - /// fallback; desktop-excluding is shown only when the resolved PixelPass - /// advertises it. + /// The picker's current typed selection. Desktop-excluding is the normal + /// whole-desktop choice; desktop-shared remains an internal compatibility + /// fallback for an older PixelPass that does not advertise exclusion. share_audio_selection: ShareAudioSelection, /// Session-only quality override for the next screen-share start. share_quality_selection: ShareQuality, @@ -2188,12 +2188,14 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { // Open the audio picker instead of sharing immediately, so the // user chooses which app's audio to capture rather than the whole // desktop (which echoes the call back to viewers, A23). Default - // selection is legacy "All system audio". Kick off a fresh + // to the fail-closed desktop-exclusion contract while the fresh + // capability probe runs; an old PixelPass response replaces it + // with the visibly warned legacy fallback. Kick off a fresh // enumeration so the list reflects what's playing right now. // Suppressed while a start is already in flight (`share_starting`) // so the picker can't be reopened during the startup window. state.share_picker_open = true; - state.share_audio_selection = ShareAudioSelection::DesktopShared; + state.share_audio_selection = ShareAudioSelection::DesktopExcluding; // NB: do NOT reset `share_quality_selection` here. It is the // per-call override set by the inline quality dropdown next to // the Share button, and the picker has no quality control of its @@ -2535,6 +2537,11 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { // Only meaningful while the picker is open; if the user // already cancelled, drop it. if state.share_picker_open { + let desktop_fallback = if desktop_audio_exclusion_supported { + ShareAudioSelection::DesktopExcluding + } else { + ShareAudioSelection::DesktopShared + }; state.share_app_audio_supported = app_audio_supported; state.share_desktop_audio_exclusion_supported = desktop_audio_exclusion_supported; @@ -2545,7 +2552,7 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { &state.share_audio_selection && !apps.iter().any(|a| a == sel) { - state.share_audio_selection = ShareAudioSelection::DesktopShared; + state.share_audio_selection = desktop_fallback.clone(); } state.share_audio_apps = apps; } else { @@ -2556,16 +2563,21 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { state.share_audio_selection, ShareAudioSelection::Application(_) ) { - state.share_audio_selection = ShareAudioSelection::DesktopShared; + state.share_audio_selection = desktop_fallback.clone(); } } - if !desktop_audio_exclusion_supported - && matches!( - state.share_audio_selection, - ShareAudioSelection::DesktopExcluding - ) - { - state.share_audio_selection = ShareAudioSelection::DesktopShared; + match state.share_audio_selection { + ShareAudioSelection::DesktopShared + if desktop_audio_exclusion_supported => + { + state.share_audio_selection = ShareAudioSelection::DesktopExcluding; + } + ShareAudioSelection::DesktopExcluding + if !desktop_audio_exclusion_supported => + { + state.share_audio_selection = ShareAudioSelection::DesktopShared; + } + _ => {} } } } @@ -8856,10 +8868,10 @@ fn audio_exclusion_status_message(status: &AudioExclusionStatus) -> Option` to pixelpass. +/// capture all safe system audio or one specific app. With a capable PixelPass, +/// "All system audio" uses desktop exclusion so PeerSpeak playback is never +/// included. An older PixelPass gets the same row backed by legacy capture and +/// an explicit echo warning. Picking an app passes `--app=` to pixelpass. fn with_share_picker<'a>( base: Element<'a, AppMessage>, state: &'a AppState, @@ -8926,29 +8938,37 @@ fn with_share_picker<'a>( }) }; - // Keep the legacy whole-desktop choice visible with its echo warning. The - // new exclusion row exists only when this exact PixelPass advertised it. - let mut options = column![opt_row( - matches!( + // Present one whole-desktop row. A current PixelPass backs it with safe + // exclusion; only an older binary sees the legacy implementation/warning. + // Treat the optimistic pre-probe DesktopExcluding selection as the safe row + // too; the core re-probes the exact binary before constructing its argv. + let safe_desktop = state.share_desktop_audio_exclusion_supported + || matches!( state.share_audio_selection, - ShareAudioSelection::DesktopShared - ), - "All system audio".to_string(), - Some("⚠ may echo the call back to viewers"), - AppMessage::SelectShareAudio(ShareAudioSelection::DesktopShared), - )] - .spacing(4); - if state.share_desktop_audio_exclusion_supported { - options = options.push(opt_row( + ShareAudioSelection::DesktopExcluding + ); + let mut options = if safe_desktop { + column![opt_row( matches!( state.share_audio_selection, ShareAudioSelection::DesktopExcluding ), - "System audio except PeerSpeak".to_string(), + "All system audio".to_string(), Some("Excludes call and watched-share playback"), AppMessage::SelectShareAudio(ShareAudioSelection::DesktopExcluding), - )); + )] + } else { + column![opt_row( + matches!( + state.share_audio_selection, + ShareAudioSelection::DesktopShared + ), + "All system audio".to_string(), + Some("⚠ may echo the call back to viewers"), + AppMessage::SelectShareAudio(ShareAudioSelection::DesktopShared), + )] } + .spacing(4); for app in &state.share_audio_apps { let selected = matches!( &state.share_audio_selection, @@ -10330,6 +10350,20 @@ mod tests { ); } + #[test] + fn share_picker_defaults_whole_desktop_to_safe_exclusion() { + let mut state = AppState::default(); + + let _ = update(&mut state, AppMessage::ToggleScreenShare); + + assert!(state.share_picker_open); + assert_eq!( + state.share_audio_selection, + ShareAudioSelection::DesktopExcluding, + "the picker must not expose legacy echoing capture as its normal desktop default" + ); + } + #[test] fn share_start_failure_clears_in_flight_flag() { // A failed spawn surfaces as UiEvent::Error (not ScreenShareStopped); the @@ -10577,6 +10611,11 @@ mod tests { assert!(state.share_app_audio_supported); assert!(state.share_desktop_audio_exclusion_supported); assert_eq!(state.share_audio_apps.len(), 2); + assert_eq!( + state.share_audio_selection, + ShareAudioSelection::DesktopExcluding, + "a capable PixelPass must make the single desktop row use exclusion" + ); } #[test]