pub(crate) const DESTINATION: &str = "org.freedesktop.portal.Flatpak";
pub(crate) const PATH: &str = "/org/freedesktop/portal/Flatpak";
use std::{
collections::HashMap,
ffi::CString,
fmt::Debug,
os::unix::{
ffi::OsStrExt,
prelude::{AsRawFd, RawFd},
},
path::Path,
};
use enumflags2::{bitflags, BitFlags};
use serde::Serialize;
use serde_repr::{Deserialize_repr, Serialize_repr};
use zbus::zvariant::{DeserializeDict, Fd, OwnedObjectPath, SerializeDict, Type};
use crate::{
helpers::{call_method, receive_signal},
Error,
};
#[bitflags]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Copy, Clone, Debug, Type)]
#[repr(u32)]
pub enum SandboxFlags {
DisplayAccess,
SoundAccess,
GpuAccess,
SessionBusAccess,
AccessibilityBusAccess,
}
#[bitflags]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Copy, Clone, Debug, Type)]
#[repr(u32)]
#[doc(alias = "XdpSpawnFlags")]
pub enum SpawnFlags {
#[doc(alias = "XDP_SPAWN_FLAG_CLEARENV")]
ClearEnv,
#[doc(alias = "XDP_SPAWN_FLAG_LATEST")]
LatestVersion,
#[doc(alias = "XDP_SPAWN_FLAG_SANDBOX")]
Sandbox,
#[doc(alias = "XDP_SPAWN_FLAG_NO_NETWORK")]
NoNetwork,
#[doc(alias = "XDP_SPAWN_FLAG_WATCH")]
WatchBus,
ExposePids,
NotifyStart,
SharePids,
EmptyApp,
}
#[bitflags]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Copy, Clone, Debug, Type)]
#[repr(u32)]
pub enum SupportsFlags {
ExposePids,
}
#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct SpawnOptions {
#[zvariant(rename = "sandbox-expose")]
sandbox_expose: Option<Vec<String>>,
#[zvariant(rename = "sandbox-expose-ro")]
sandbox_expose_ro: Option<Vec<String>>,
#[zvariant(rename = "sandbox-expose-fd")]
sandbox_expose_fd: Option<Vec<Fd>>,
#[zvariant(rename = "sandbox-expose-fd-ro")]
sandbox_expose_fd_ro: Option<Vec<Fd>>,
#[zvariant(rename = "sandbox-flags")]
sandbox_flags: Option<BitFlags<SandboxFlags>>,
#[zvariant(rename = "unset-env")]
unset_env: Option<Vec<String>>,
#[zvariant(rename = "usr-fd")]
usr_fd: Option<RawFd>,
#[zvariant(rename = "app-fd")]
app_fd: Option<RawFd>,
}
impl SpawnOptions {
#[must_use]
pub fn sandbox_expose(mut self, sandbox_expose: &[impl AsRef<str> + Type + Serialize]) -> Self {
self.sandbox_expose = Some(
sandbox_expose
.iter()
.map(|s| s.as_ref().to_owned())
.collect(),
);
self
}
#[must_use]
pub fn sandbox_expose_ro(
mut self,
sandbox_expose_ro: &[impl AsRef<str> + Type + Serialize],
) -> Self {
self.sandbox_expose_ro = Some(
sandbox_expose_ro
.iter()
.map(|s| s.as_ref().to_owned())
.collect(),
);
self
}
#[must_use]
pub fn sandbox_expose_fd(mut self, sandbox_expose_fd: &[&impl AsRawFd]) -> Self {
self.sandbox_expose_fd = Some(
sandbox_expose_fd
.iter()
.map(|f| Fd::from(f.as_raw_fd()))
.collect(),
);
self
}
#[must_use]
pub fn sandbox_expose_fd_ro(mut self, sandbox_expose_fd_ro: &[&impl AsRawFd]) -> Self {
self.sandbox_expose_fd_ro = Some(
sandbox_expose_fd_ro
.iter()
.map(|f| Fd::from(f.as_raw_fd()))
.collect(),
);
self
}
#[must_use]
pub fn sandbox_flags(mut self, sandbox_flags: BitFlags<SandboxFlags>) -> Self {
self.sandbox_flags = Some(sandbox_flags);
self
}
#[must_use]
pub fn unset_env(mut self, env: &[&str]) -> Self {
self.unset_env = Some(env.iter().map(|e| e.to_owned().to_owned()).collect());
self
}
#[must_use]
pub fn usr_fd(mut self, fd: impl AsRawFd) -> Self {
self.usr_fd = Some(fd.as_raw_fd());
self
}
#[must_use]
pub fn app_fd(mut self, fd: impl AsRawFd) -> Self {
self.app_fd = Some(fd.as_raw_fd());
self
}
}
#[derive(SerializeDict, DeserializeDict, Type, Debug, Default)]
#[zvariant(signature = "dict")]
struct CreateMonitorOptions {}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Flatpak")]
pub struct FlatpakProxy<'a>(zbus::Proxy<'a>);
impl<'a> FlatpakProxy<'a> {
pub async fn new(connection: &zbus::Connection) -> Result<FlatpakProxy<'a>, Error> {
let proxy = zbus::ProxyBuilder::new_bare(connection)
.interface("org.freedesktop.portal.Flatpak")?
.path(PATH)?
.destination(DESTINATION)?
.build()
.await?;
Ok(Self(proxy))
}
pub fn inner(&self) -> &zbus::Proxy<'_> {
&self.0
}
#[doc(alias = "CreateUpdateMonitor")]
#[doc(alias = "xdp_portal_update_monitor_start")]
pub async fn create_update_monitor(&self) -> Result<UpdateMonitorProxy<'a>, Error> {
let options = CreateMonitorOptions::default();
let path: OwnedObjectPath =
call_method(self.inner(), "CreateUpdateMonitor", &(options)).await?;
UpdateMonitorProxy::new(self.inner().connection(), path.into_inner()).await
}
#[doc(alias = "SpawnStarted")]
pub async fn receive_spawn_started(&self) -> Result<(u32, u32), Error> {
receive_signal(self.inner(), "SpawnStarted").await
}
#[doc(alias = "SpawnExited")]
#[doc(alias = "XdpPortal::spawn-exited")]
pub async fn receive_spawn_existed(&self) -> Result<(u32, u32), Error> {
receive_signal(self.inner(), "SpawnExited").await
}
#[doc(alias = "Spawn")]
#[doc(alias = "xdp_portal_spawn")]
pub async fn spawn(
&self,
cwd_path: impl AsRef<Path>,
argv: &[impl AsRef<Path>],
fds: HashMap<u32, Fd>,
envs: HashMap<&str, &str>,
flags: BitFlags<SpawnFlags>,
options: SpawnOptions,
) -> Result<u32, Error> {
let cwd_path = CString::new(cwd_path.as_ref().as_os_str().as_bytes())
.expect("The `cwd_path` should not contain a trailing 0 bytes");
let argv = argv
.iter()
.map(|s| {
CString::new(s.as_ref().as_os_str().as_bytes())
.expect("The `argv` should not contain a trailing 0 bytes")
})
.collect::<Vec<_>>();
call_method(
self.inner(),
"Spawn",
&(
cwd_path.as_bytes_with_nul(),
argv.iter()
.map(|c| c.as_bytes_with_nul())
.collect::<Vec<_>>(),
fds,
envs,
flags,
options,
),
)
.await
}
#[doc(alias = "SpawnSignal")]
#[doc(alias = "xdp_portal_spawn_signal")]
pub async fn spawn_signal(
&self,
pid: u32,
signal: u32,
to_process_group: bool,
) -> Result<(), Error> {
call_method(
self.inner(),
"SpawnSignal",
&(pid, signal, to_process_group),
)
.await
}
pub async fn supports(&self) -> Result<BitFlags<SupportsFlags>, Error> {
self.inner()
.get_property::<BitFlags<SupportsFlags>>("supports")
.await
.map_err(From::from)
}
}
mod update_monitor;
pub use update_monitor::{UpdateInfo, UpdateMonitorProxy, UpdateProgress, UpdateStatus};