use std::{
collections::HashMap,
os::unix::prelude::{IntoRawFd, RawFd},
};
use zbus::zvariant::{DeserializeDict, OwnedFd, SerializeDict, Type, Value};
use super::{HandleToken, DESTINATION, PATH};
use crate::{
helpers::{call_basic_response_method, call_method},
Error,
};
#[derive(SerializeDict, DeserializeDict, Type, Clone, Debug, Default)]
#[zvariant(signature = "dict")]
struct CameraAccessOptions {
handle_token: HandleToken,
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Camera")]
pub struct CameraProxy<'a>(zbus::Proxy<'a>);
impl<'a> CameraProxy<'a> {
pub async fn new(connection: &zbus::Connection) -> Result<CameraProxy<'a>, Error> {
let proxy = zbus::ProxyBuilder::new_bare(connection)
.interface("org.freedesktop.portal.Camera")?
.path(PATH)?
.destination(DESTINATION)?
.build()
.await?;
Ok(Self(proxy))
}
pub fn inner(&self) -> &zbus::Proxy<'_> {
&self.0
}
#[doc(alias = "AccessCamera")]
#[doc(alias = "xdp_portal_access_camera")]
pub async fn access_camera(&self) -> Result<(), Error> {
let options = CameraAccessOptions::default();
call_basic_response_method(
self.inner(),
&options.handle_token,
"AccessCamera",
&(&options),
)
.await
}
#[doc(alias = "OpenPipeWireRemote")]
#[doc(alias = "xdp_portal_open_pipewire_remote_for_camera")]
pub async fn open_pipe_wire_remote(&self) -> Result<RawFd, Error> {
let options: HashMap<&str, Value<'_>> = HashMap::new();
let fd: OwnedFd = call_method(self.inner(), "OpenPipeWireRemote", &(options)).await?;
Ok(fd.into_raw_fd())
}
#[doc(alias = "IsCameraPresent")]
#[doc(alias = "xdp_portal_is_camera_present")]
pub async fn is_camera_present(&self) -> Result<bool, Error> {
self.inner()
.get_property::<bool>("IsCameraPresent")
.await
.map_err(From::from)
}
}
#[cfg(feature = "feature_pipewire")]
pub async fn pipewire_node_id(fd: RawFd) -> Result<Option<u32>, pw::Error> {
let fd = unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 3) };
if fd == -1 {
return Err(pw::Error::CreationFailed);
}
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(fd, move |node_id| {
if let Ok(mut guard) = inner_sender.lock() {
if let Some(inner_sender) = guard.take() {
let _result = inner_sender.send(Ok(Some(node_id)));
}
}
}) {
#[cfg(feature = "log")]
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));
}
} else {
#[cfg(feature = "log")]
tracing::info!("Couldn't find any Node ID");
let mut guard = sender.lock().unwrap();
if let Some(sender) = guard.take() {
let _ = sender.send(Ok(None));
}
}
});
receiver.await.unwrap()
}
#[cfg(feature = "feature_pipewire")]
fn pipewire_node_id_inner<F: FnOnce(u32) + Clone + 'static>(
fd: RawFd,
callback: F,
) -> Result<(), pw::Error> {
use pw::prelude::*;
let mainloop = pw::MainLoop::new()?;
let context = pw::Context::new(&mainloop)?;
let core = context.connect_fd(fd, 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 {
#[cfg(feature = "log")]
tracing::info!("found properties: {:#?}", props);
if props.get("media.role") == Some("Camera") {
callback.clone()(global.id);
loop_clone.quit();
}
}
})
.register();
mainloop.run();
Ok(())
}