use std::{fmt::Debug, os::fd::OwnedFd};
use enumflags2::{BitFlags, bitflags};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use zbus::zvariant::{
self, Optional, OwnedValue, Type,
as_value::{self, optional},
};
use super::{HandleToken, PersistMode, Request, Session, session::SessionPortal};
use crate::{
Error, WindowIdentifier,
desktop::session::{CreateSessionOptions, CreateSessionResponse},
proxy::Proxy,
};
#[bitflags]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Copy, Clone, Debug, Type)]
#[repr(u32)]
#[doc(alias = "XdpOutputType")]
pub enum SourceType {
#[doc(alias = "XDP_OUTPUT_MONITOR")]
Monitor,
#[doc(alias = "XDP_OUTPUT_WINDOW")]
Window,
#[doc(alias = "XDP_OUTPUT_VIRTUAL")]
Virtual,
}
#[bitflags]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug, Copy, Clone, Type)]
#[repr(u32)]
#[doc(alias = "XdpCursorMode")]
pub enum CursorMode {
#[doc(alias = "XDP_CURSOR_MODE_HIDDEN")]
Hidden,
#[doc(alias = "XDP_CURSOR_MODE_EMBEDDED")]
Embedded,
#[doc(alias = "XDP_CURSOR_MODE_METADATA")]
Metadata,
}
#[derive(Serialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct OpenPipeWireRemoteOptions {}
#[derive(Serialize, Deserialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct SelectSourcesOptions {
#[serde(with = "as_value", skip_deserializing)]
handle_token: HandleToken,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
types: Option<BitFlags<SourceType>>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
multiple: Option<bool>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
cursor_mode: Option<CursorMode>,
#[serde(
with = "optional",
skip_serializing_if = "Option::is_none",
skip_deserializing
)]
restore_token: Option<String>,
#[serde(default, with = "optional", skip_serializing)]
#[cfg_attr(not(feature = "backend"), allow(dead_code))]
restore_data: Option<(String, u32, OwnedValue)>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
persist_mode: Option<PersistMode>,
}
impl SelectSourcesOptions {
#[must_use]
pub fn set_multiple(mut self, multiple: impl Into<Option<bool>>) -> Self {
self.multiple = multiple.into();
self
}
#[cfg(feature = "backend")]
pub fn is_multiple(&self) -> Option<bool> {
self.multiple
}
#[must_use]
pub fn set_cursor_mode(mut self, cursor_mode: impl Into<Option<CursorMode>>) -> Self {
self.cursor_mode = cursor_mode.into();
self
}
#[cfg(feature = "backend")]
pub fn cursor_mode(&self) -> Option<CursorMode> {
self.cursor_mode
}
#[must_use]
pub fn set_sources(mut self, types: impl Into<Option<BitFlags<SourceType>>>) -> Self {
self.types = types.into();
self
}
#[cfg(feature = "backend")]
pub fn sources(&self) -> Option<BitFlags<SourceType>> {
self.types
}
#[must_use]
pub fn set_persist_mode(mut self, persist_mode: impl Into<Option<PersistMode>>) -> Self {
self.persist_mode = persist_mode.into();
self
}
#[cfg(feature = "backend")]
pub fn persist_mode(&self) -> Option<PersistMode> {
self.persist_mode
}
#[must_use]
pub fn set_restore_token<'a>(mut self, token: impl Into<Option<&'a str>>) -> Self {
self.restore_token = token.into().map(ToOwned::to_owned);
self
}
#[cfg(feature = "backend")]
pub fn restore_data<'a>(&'a self) -> Option<(&'a str, u32, &'a zbus::zvariant::Value<'a>)> {
use std::borrow::Borrow;
match &self.restore_data {
Some((key, version, data)) => Some((key.as_str(), *version, data.borrow())),
None => None,
}
}
}
#[derive(Serialize, Deserialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct StartCastOptions {
#[serde(with = "as_value", skip_deserializing)]
handle_token: HandleToken,
}
#[derive(Default, Serialize, Deserialize, Type)]
#[zvariant(signature = "dict")]
pub struct Streams {
#[serde(default, with = "as_value", skip_serializing_if = "Vec::is_empty")]
streams: Vec<Stream>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
restore_token: Option<String>,
#[serde(default, with = "optional", skip_serializing)]
#[cfg_attr(not(feature = "backend"), allow(dead_code))]
restore_data: Option<(String, u32, OwnedValue)>,
}
impl Streams {
pub fn restore_token(&self) -> Option<&str> {
self.restore_token.as_deref()
}
pub fn streams(&self) -> &[Stream] {
&self.streams
}
#[cfg(feature = "backend")]
pub fn restore_data(&self) -> Option<&(String, u32, OwnedValue)> {
self.restore_data.as_ref()
}
}
impl Debug for Streams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Streams")
.field(&self.restore_token)
.field(&self.streams)
.finish()
}
}
#[cfg(feature = "backend")]
#[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
pub struct StreamsBuilder {
streams: Streams,
}
#[cfg(feature = "backend")]
#[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
impl StreamsBuilder {
pub fn new(streams: Vec<Stream>) -> Self {
Self {
streams: Streams {
streams,
restore_token: None,
restore_data: None,
},
}
}
pub fn restore_data(mut self, data: Option<(String, u32, impl Into<OwnedValue>)>) -> Self {
self.streams.restore_data = data.map(|(s, u, d)| (s, u, d.into()));
self
}
pub fn build(self) -> Streams {
self.streams
}
}
#[derive(Clone, Serialize, Deserialize, Type)]
pub struct Stream(u32, StreamProperties);
impl Stream {
pub fn pipe_wire_node_id(&self) -> u32 {
self.0
}
pub fn position(&self) -> Option<(i32, i32)> {
self.1.position
}
pub fn size(&self) -> Option<(i32, i32)> {
self.1.size
}
pub fn source_type(&self) -> Option<SourceType> {
self.1.source_type
}
pub fn id(&self) -> Option<&str> {
self.1.id.as_deref()
}
pub fn mapping_id(&self) -> Option<&str> {
self.1.mapping_id.as_deref()
}
}
impl Debug for Stream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Stream")
.field("pipewire_node_id", &self.pipe_wire_node_id())
.field("position", &self.position())
.field("size", &self.size())
.field("source_type", &self.source_type())
.field("id", &self.id())
.finish()
}
}
#[derive(Clone, Serialize, Deserialize, Type, Debug)]
#[zvariant(signature = "dict")]
struct StreamProperties {
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
position: Option<(i32, i32)>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
size: Option<(i32, i32)>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
source_type: Option<SourceType>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
mapping_id: Option<String>,
}
#[cfg(feature = "backend")]
#[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
pub struct StreamBuilder {
stream: Stream,
}
#[cfg(feature = "backend")]
#[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
impl StreamBuilder {
pub fn new(pipe_wire_node_id: u32) -> Self {
Self {
stream: Stream(
pipe_wire_node_id,
StreamProperties {
id: None,
position: None,
size: None,
source_type: None,
mapping_id: None,
},
),
}
}
pub fn id(mut self, id: impl Into<Option<String>>) -> Self {
self.stream.1.id = id.into();
self
}
pub fn position(mut self, position: impl Into<Option<(i32, i32)>>) -> Self {
self.stream.1.position = position.into();
self
}
pub fn size(mut self, size: impl Into<Option<(i32, i32)>>) -> Self {
self.stream.1.size = size.into();
self
}
pub fn source_type(mut self, source_type: impl Into<Option<SourceType>>) -> Self {
self.stream.1.source_type = source_type.into();
self
}
pub fn mapping_id(mut self, mapping_id: impl Into<Option<String>>) -> Self {
self.stream.1.mapping_id = mapping_id.into();
self
}
pub fn build(self) -> Stream {
self.stream
}
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.ScreenCast")]
pub struct Screencast(Proxy<'static>);
impl Screencast {
pub async fn new() -> Result<Self, Error> {
let proxy = Proxy::new_desktop("org.freedesktop.portal.ScreenCast").await?;
Ok(Self(proxy))
}
pub async fn with_connection(connection: zbus::Connection) -> Result<Self, Error> {
let proxy =
Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.ScreenCast")
.await?;
Ok(Self(proxy))
}
pub fn version(&self) -> u32 {
self.0.version()
}
#[doc(alias = "CreateSession")]
#[doc(alias = "xdp_portal_create_screencast_session")]
pub async fn create_session(
&self,
options: CreateSessionOptions,
) -> Result<Session<Self>, Error> {
let (request, proxy) = futures_util::try_join!(
self.0.request::<CreateSessionResponse>(
&options.handle_token,
"CreateSession",
&options
),
Session::from_unique_name(self.0.connection().clone(), &options.session_handle_token),
)?;
assert_eq!(proxy.path(), &request.response()?.session_handle.as_ref());
Ok(proxy)
}
#[doc(alias = "OpenPipeWireRemote")]
pub async fn open_pipe_wire_remote(
&self,
session: &Session<impl IsScreencastSession>,
options: OpenPipeWireRemoteOptions,
) -> Result<OwnedFd, Error> {
let fd = self
.0
.call::<zvariant::OwnedFd>("OpenPipeWireRemote", &(session, options))
.await?;
Ok(fd.into())
}
#[doc(alias = "SelectSources")]
pub async fn select_sources(
&self,
session: &Session<impl IsScreencastSession>,
options: SelectSourcesOptions,
) -> Result<Request<()>, Error> {
self.0
.empty_request(&options.handle_token, "SelectSources", &(session, &options))
.await
}
#[doc(alias = "Start")]
pub async fn start(
&self,
session: &Session<impl IsScreencastSession>,
identifier: Option<&WindowIdentifier>,
options: StartCastOptions,
) -> Result<Request<Streams>, Error> {
let identifier = Optional::from(identifier);
self.0
.request(
&options.handle_token,
"Start",
&(session, identifier, &options),
)
.await
}
#[doc(alias = "AvailableCursorModes")]
pub async fn available_cursor_modes(&self) -> Result<BitFlags<CursorMode>, Error> {
self.0.property_versioned("AvailableCursorModes", 2).await
}
#[doc(alias = "AvailableSourceTypes")]
pub async fn available_source_types(&self) -> Result<BitFlags<SourceType>, Error> {
self.0.property("AvailableSourceTypes").await
}
}
impl std::ops::Deref for Screencast {
type Target = zbus::Proxy<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl crate::Sealed for Screencast {}
impl SessionPortal for Screencast {}
pub trait IsScreencastSession: SessionPortal {}
impl IsScreencastSession for Screencast {}