ux: make safe desktop audio the default
This commit is contained in:
+72
-33
@@ -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<String>,
|
||||
/// 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<AppMessage> {
|
||||
// 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<AppMessage> {
|
||||
// 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<AppMessage> {
|
||||
&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<AppMessage> {
|
||||
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<Strin
|
||||
}
|
||||
|
||||
/// Overlay the screen-share **audio picker** when open (A23). Lets the user
|
||||
/// capture a single app's audio instead of the whole desktop sink — the default
|
||||
/// whole-desktop capture contains our own call playout, so a viewer would
|
||||
/// otherwise hear themselves echoed back. "All system audio" keeps the legacy
|
||||
/// behavior (with a warning); picking an app passes `--app=<name>` 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=<name>` 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]
|
||||
|
||||
Reference in New Issue
Block a user