use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::mem::size_of;
use core::ptr::addr_of_mut;
use anyhow::Context;
use log::warn;
use objc2_app_kit::{NSApplicationActivationPolicy, NSWorkspace};
use objc2_core_audio::{
AudioObjectID, kAudioHardwarePropertyProcessObjectList, kAudioObjectSystemObject,
kAudioProcessPropertyPID,
};
use crate::common::result::Result;
use crate::common::source::{AudioSource, AudioSources};
use crate::platform::mac::wrapper::{get_prop_data, get_prop_size};
pub fn list() -> Result<AudioSources> {
let mut sources = vec![];
for object_id in list_audio_objects()? {
match get_pid(object_id) {
Ok(id) => {
if let Some(name) = get_friendly_name(id) {
sources.push(AudioSource { id: id.into(), name: name.into() });
}
}
Err(e) => warn!("failed to get PID from audio source: {e:?}"),
}
}
Ok(sources.into())
}
fn list_audio_objects() -> Result<Vec<AudioObjectID>> {
let system_object = kAudioObjectSystemObject as AudioObjectID;
let query = query!(kAudioHardwarePropertyProcessObjectList);
let data_size = get_prop_size(system_object, &query)
.context("Failed to get system object property size")?;
let num_objects = data_size / size_of::<AudioObjectID>() as u32;
if num_objects == 0 {
return Ok(vec![]);
}
let mut object_ids = vec![0; num_objects as usize];
get_prop_data(
system_object,
&query,
None,
data_size,
object_ids.as_mut_ptr(),
).context("Failed to get system object property data")?;
Ok(object_ids)
}
fn get_pid(object_id: AudioObjectID) -> Result<u32> {
let mut pid = 0;
get_prop_data(
object_id,
&query!(kAudioProcessPropertyPID),
None,
size_of::<AudioObjectID>() as u32,
addr_of_mut!(pid),
).context("Failed to get PID from AudioObjectID")?;
Ok(pid)
}
fn get_friendly_name(pid: u32) -> Option<String> {
let workspace = NSWorkspace::sharedWorkspace();
let apps = workspace.runningApplications();
for app in &apps {
if app.activationPolicy() == NSApplicationActivationPolicy::Regular
&& app.processIdentifier() == pid.cast_signed() {
return Some(app.localizedName()?.to_string());
}
}
None
}