use std::{
collections::HashMap,
os::unix::prelude::{AsRawFd, RawFd},
};
use zvariant::{Fd, Value};
use zvariant_derive::{DeserializeDict, SerializeDict, TypeDict};
use super::{HandleToken, DESTINATION, PATH};
use crate::{
helpers::{call_basic_response_method, call_method},
Error,
};
#[derive(SerializeDict, DeserializeDict, TypeDict, Clone, Debug, Default)]
struct CameraAccessOptions {
handle_token: HandleToken,
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Camera")]
pub struct CameraProxy<'a>(zbus::azync::Proxy<'a>);
impl<'a> CameraProxy<'a> {
pub async fn new(connection: &zbus::azync::Connection) -> Result<CameraProxy<'a>, Error> {
let proxy = zbus::azync::ProxyBuilder::new_bare(connection)
.interface("org.freedesktop.portal.Camera")?
.path(PATH)?
.destination(DESTINATION)?
.build()
.await?;
Ok(Self(proxy))
}
pub fn inner(&self) -> &zbus::azync::Proxy<'_> {
&self.0
}
#[doc(alias = "AccessCamera")]
pub async fn access_camera(&self) -> Result<(), Error> {
let options = CameraAccessOptions::default();
call_basic_response_method(&self.0, &options.handle_token, "AccessCamera", &(&options))
.await
}
#[doc(alias = "OpenPipeWireRemote")]
pub async fn open_pipe_wire_remote(&self) -> Result<RawFd, Error> {
let options: HashMap<&str, Value<'_>> = HashMap::new();
let fd: Fd = call_method(&self.0, "OpenPipeWireRemote", &(options)).await?;
Ok(fd.as_raw_fd())
}
#[doc(alias = "IsCameraPresent")]
pub async fn is_camera_present(&self) -> Result<bool, Error> {
self.0
.get_property::<bool>("IsCameraPresent")
.await
.map_err(From::from)
}
}
#[cfg(feature = "feature_pipewire")]
pub async fn pipewire_node_id() -> Result<u32, pw::Error> {
let (sender, receiver) = futures::channel::oneshot::channel();
let sender = std::sync::Arc::new(std::sync::Mutex::new(Some(sender)));
std::thread::spawn(move || {
let inner_sender = sender.clone();
if let Err(err) = pipewire_node_id_inner(move |node_id| {
if let Ok(mut guard) = inner_sender.lock() {
if let Some(inner_sender) = guard.take() {
let _result = inner_sender.send(Ok(node_id));
}
}
}) {
tracing::error!("Failed to get pipewire node id {:#?}", err);
let mut guard = sender.lock().unwrap();
if let Some(sender) = guard.take() {
let _ = sender.send(Err(err));
}
}
});
receiver.await.unwrap()
}
#[cfg(feature = "feature_pipewire")]
fn pipewire_node_id_inner<F: FnOnce(u32) + Clone + 'static>(callback: F) -> Result<(), pw::Error> {
use pw::prelude::*;
let mainloop = pw::MainLoop::new()?;
let context = pw::Context::new(&mainloop)?;
let core = context.connect(None)?;
let registry = core.get_registry()?;
let loop_clone = mainloop.clone();
let _listener_reg = registry
.add_listener_local()
.global(move |global| {
if let Some(props) = &global.props {
tracing::info!("found properties: {:#?}", props);
if props.get("media.role") == Some("Camera") {
callback.clone()(global.id);
loop_clone.quit();
}
}
})
.register();
mainloop.run();
Ok(())
}