Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree with a 3-file patch applied (no upstream history). Patch (crates/gpui_linux/src/linux/wayland/): - serial.rs: add SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; add latest_serial_of() - window.rs: activate() uses keyboard-enter serial (Mutter focus gate) Mutter honors window activation only when the token carries the keyboard- focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial. See docs/tray-window-focus-wayland.md in logiguard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
154 lines
4.7 KiB
Rust
154 lines
4.7 KiB
Rust
use audio::{AudioDeviceInfo, AvailableAudioDevices};
|
|
use cpal::DeviceId;
|
|
use gpui::{AnyElement, App, ElementId, ReadGlobal, SharedString, Window};
|
|
use settings::{AudioInputDeviceName, AudioOutputDeviceName, SettingsStore};
|
|
use std::str::FromStr;
|
|
use ui::{ContextMenu, DropdownMenu, DropdownStyle, IconPosition, IntoElement};
|
|
use util::ResultExt;
|
|
|
|
use crate::{SettingField, SettingsFieldMetadata, SettingsUiFile, update_settings_file};
|
|
|
|
pub(crate) const SYSTEM_DEFAULT: &str = "System Default";
|
|
|
|
pub(crate) fn get_current_device(
|
|
current_id: Option<&DeviceId>,
|
|
is_input: bool,
|
|
devices: &[AudioDeviceInfo],
|
|
) -> Option<AudioDeviceInfo> {
|
|
let Some(current_id) = current_id else {
|
|
return None;
|
|
};
|
|
devices
|
|
.iter()
|
|
.find(|d| d.matches(current_id, is_input))
|
|
.cloned()
|
|
}
|
|
|
|
pub(crate) fn render_audio_device_dropdown<F>(
|
|
dropdown_id: impl Into<ElementId>,
|
|
current_device_id: Option<DeviceId>,
|
|
is_input: bool,
|
|
on_select: F,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> AnyElement
|
|
where
|
|
F: Fn(Option<DeviceId>, &mut Window, &mut App) + Clone + 'static,
|
|
{
|
|
audio::ensure_devices_initialized(cx);
|
|
let devices = cx.global::<AvailableAudioDevices>().0.clone();
|
|
let current_device = get_current_device(current_device_id.as_ref(), is_input, &devices);
|
|
|
|
let menu = ContextMenu::build(window, cx, {
|
|
let current_device = current_device.clone();
|
|
move |mut menu, _, _cx| {
|
|
let is_system_default = current_device.is_none();
|
|
menu = menu.toggleable_entry(
|
|
SYSTEM_DEFAULT,
|
|
is_system_default,
|
|
IconPosition::Start,
|
|
None,
|
|
{
|
|
let on_select = on_select.clone();
|
|
move |window, cx| {
|
|
on_select(None, window, cx);
|
|
}
|
|
},
|
|
);
|
|
|
|
for device in devices.iter().filter(|d| d.matches_input(is_input)) {
|
|
let is_current = current_device
|
|
.as_ref()
|
|
.map(|info| info.matches(&device.id, is_input))
|
|
.unwrap_or(false);
|
|
let device_id = device.id.clone();
|
|
|
|
menu = menu.toggleable_entry(
|
|
device.to_string(),
|
|
is_current,
|
|
IconPosition::Start,
|
|
None,
|
|
{
|
|
let on_select = on_select.clone();
|
|
move |window, cx| {
|
|
on_select(Some(device_id.clone()), window, cx);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
menu
|
|
}
|
|
});
|
|
|
|
DropdownMenu::new(
|
|
dropdown_id,
|
|
current_device
|
|
.map(|info| info.desc.name().to_string())
|
|
.unwrap_or(SYSTEM_DEFAULT.to_string()),
|
|
menu,
|
|
)
|
|
.style(DropdownStyle::Outlined)
|
|
.full_width(true)
|
|
.into_any_element()
|
|
}
|
|
|
|
fn render_settings_audio_device_dropdown<T: AsRef<Option<String>> + From<Option<String>> + Send>(
|
|
field: SettingField<T>,
|
|
file: SettingsUiFile,
|
|
is_input: bool,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> AnyElement {
|
|
let (_, current_value): (_, Option<&T>) =
|
|
SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick);
|
|
let current_device_id =
|
|
current_value.and_then(|x| x.as_ref().clone().and_then(|x| DeviceId::from_str(&x).ok()));
|
|
|
|
let dropdown_id: SharedString = if is_input {
|
|
"input-audio-device-dropdown".into()
|
|
} else {
|
|
"output-audio-device-dropdown".into()
|
|
};
|
|
|
|
render_audio_device_dropdown(
|
|
dropdown_id,
|
|
current_device_id,
|
|
is_input,
|
|
move |device_id, window, cx| {
|
|
let value: Option<T> = device_id.map(|id| T::from(Some(id.to_string())));
|
|
update_settings_file(
|
|
file.clone(),
|
|
field.json_path,
|
|
window,
|
|
cx,
|
|
move |settings, app| {
|
|
(field.write)(settings, value, app);
|
|
},
|
|
)
|
|
.log_err();
|
|
},
|
|
window,
|
|
cx,
|
|
)
|
|
}
|
|
|
|
pub fn render_input_audio_device_dropdown(
|
|
field: SettingField<AudioInputDeviceName>,
|
|
file: SettingsUiFile,
|
|
_metadata: Option<&SettingsFieldMetadata>,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> AnyElement {
|
|
render_settings_audio_device_dropdown(field, file, true, window, cx)
|
|
}
|
|
|
|
pub fn render_output_audio_device_dropdown(
|
|
field: SettingField<AudioOutputDeviceName>,
|
|
file: SettingsUiFile,
|
|
_metadata: Option<&SettingsFieldMetadata>,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> AnyElement {
|
|
render_settings_audio_device_dropdown(field, file, false, window, cx)
|
|
}
|