use std::{collections::HashMap, num::NonZeroU32, os::fd::OwnedFd};
use enumflags2::{BitFlags, bitflags};
use futures_util::Stream;
use serde::{Deserialize, Serialize, de::Visitor};
use serde_repr::{Deserialize_repr, Serialize_repr};
use zbus::zvariant::{
self, ObjectPath, Optional, OwnedObjectPath, OwnedValue, Type,
as_value::{self, optional},
};
use super::{HandleToken, PersistMode, Request, Session, session::SessionPortal};
use crate::{Error, WindowIdentifier, proxy::Proxy};
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug, Copy, Clone, Type)]
#[bitflags]
#[repr(u32)]
pub enum Capabilities {
Keyboard,
Pointer,
Touchscreen,
}
#[derive(Debug, Serialize, Type, Default)]
#[zvariant(signature = "dict")]
pub struct CreateSessionOptions {
#[serde(with = "as_value")]
handle_token: HandleToken,
#[serde(with = "as_value")]
session_handle_token: HandleToken,
#[serde(with = "as_value")]
capabilities: BitFlags<Capabilities>,
}
impl CreateSessionOptions {
pub fn set_capabilities(mut self, capabilities: BitFlags<Capabilities>) -> Self {
self.capabilities = capabilities;
self
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
struct CreateSessionResponse {
#[serde(with = "as_value")]
session_handle: OwnedObjectPath,
#[serde(with = "as_value")]
capabilities: BitFlags<Capabilities>,
}
#[derive(Debug, Serialize, Type, Default)]
#[zvariant(signature = "dict")]
pub struct CreateSession2Options {
#[serde(with = "as_value")]
session_handle_token: HandleToken,
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
struct CreateSession2Results {
#[serde(with = "as_value")]
session_handle: OwnedObjectPath,
}
#[derive(Debug, Serialize, Type, Default)]
#[zvariant(signature = "dict")]
pub struct StartOptions {
#[serde(with = "as_value")]
handle_token: HandleToken,
#[serde(with = "as_value")]
capabilities: BitFlags<Capabilities>,
#[serde(with = "optional", skip_serializing_if = "Option::is_none")]
restore_token: Option<String>,
#[serde(with = "optional", skip_serializing_if = "Option::is_none")]
persist_mode: Option<PersistMode>,
}
impl StartOptions {
pub fn set_capabilities(mut self, capabilities: BitFlags<Capabilities>) -> Self {
self.capabilities = capabilities;
self
}
pub fn set_restore_token(mut self, restore_token: impl Into<Option<String>>) -> Self {
self.restore_token = restore_token.into();
self
}
pub fn set_persist_mode(mut self, persist_mode: impl Into<Option<PersistMode>>) -> Self {
self.persist_mode = persist_mode.into();
self
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
pub struct StartResponse {
#[serde(with = "as_value")]
capabilities: BitFlags<Capabilities>,
#[serde(default, with = "optional")]
clipboard_enabled: Option<bool>,
#[serde(default, with = "optional")]
restore_token: Option<String>,
}
impl StartResponse {
pub fn capabilities(&self) -> BitFlags<Capabilities> {
self.capabilities
}
pub fn is_clipboard_enabled(&self) -> bool {
self.clipboard_enabled.unwrap_or(false)
}
pub fn restore_token(&self) -> Option<&str> {
self.restore_token.as_deref()
}
}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct GetZonesOptions {
#[serde(with = "as_value")]
handle_token: HandleToken,
}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct SetPointerBarriersOptions {
#[serde(with = "as_value")]
handle_token: HandleToken,
}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct EnableOptions {}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct DisableOptions {}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct ReleaseOptions {
#[serde(with = "optional", skip_serializing_if = "Option::is_none")]
activation_id: Option<u32>,
#[serde(with = "optional", skip_serializing_if = "Option::is_none")]
cursor_position: Option<(f64, f64)>,
}
impl ReleaseOptions {
pub fn set_activation_id(mut self, activation_id: impl Into<Option<u32>>) -> Self {
self.activation_id = activation_id.into();
self
}
pub fn set_cursor_position(mut self, cursor_position: impl Into<Option<(f64, f64)>>) -> Self {
self.cursor_position = cursor_position.into();
self
}
}
#[derive(Default, Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct ConnectToEISOptions {}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "(oa{sv})")]
pub struct Disabled(OwnedObjectPath, HashMap<String, OwnedValue>);
impl Disabled {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn options(&self) -> &HashMap<String, OwnedValue> {
&self.1
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
struct DeactivatedOptions {
#[serde(default, with = "optional")]
activation_id: Option<u32>,
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "(oa{sv})")]
pub struct Deactivated(OwnedObjectPath, DeactivatedOptions);
impl Deactivated {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn activation_id(&self) -> Option<u32> {
self.1.activation_id
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
struct ActivatedOptions {
#[serde(default, with = "optional")]
activation_id: Option<u32>,
#[serde(default, with = "optional")]
cursor_position: Option<(f32, f32)>,
#[serde(default, with = "optional")]
barrier_id: Option<ActivatedBarrier>,
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "(oa{sv})")]
pub struct Activated(OwnedObjectPath, ActivatedOptions);
impl Activated {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn activation_id(&self) -> Option<u32> {
self.1.activation_id
}
pub fn cursor_position(&self) -> Option<(f32, f32)> {
self.1.cursor_position
}
pub fn barrier_id(&self) -> Option<ActivatedBarrier> {
self.1.barrier_id
}
}
#[derive(Clone, Copy, Debug, Type)]
#[zvariant(signature = "u")]
pub enum ActivatedBarrier {
Barrier(BarrierID),
UnknownBarrier,
}
impl<'de> Deserialize<'de> for ActivatedBarrier {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let visitor = ActivatedBarrierVisitor {};
deserializer.deserialize_u32(visitor)
}
}
struct ActivatedBarrierVisitor {}
impl Visitor<'_> for ActivatedBarrierVisitor {
type Value = ActivatedBarrier;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "an unsigned 32bit integer (u32)")
}
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match BarrierID::new(v) {
Some(v) => Ok(ActivatedBarrier::Barrier(v)),
None => Ok(ActivatedBarrier::UnknownBarrier),
}
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
struct ZonesChangedOptions {
#[serde(default, with = "optional")]
zone_set: Option<u32>,
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "(oa{sv})")]
pub struct ZonesChanged(OwnedObjectPath, ZonesChangedOptions);
impl ZonesChanged {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn zone_set(&self) -> Option<u32> {
self.1.zone_set
}
}
#[derive(Debug, Clone, Copy, Deserialize, Type)]
#[zvariant(signature = "(uuii)")]
pub struct Region(u32, u32, i32, i32);
impl Region {
pub fn width(self) -> u32 {
self.0
}
pub fn height(self) -> u32 {
self.1
}
pub fn x_offset(self) -> i32 {
self.2
}
pub fn y_offset(self) -> i32 {
self.3
}
}
#[derive(Debug, Type, Deserialize)]
#[zvariant(signature = "dict")]
pub struct Zones {
#[serde(default, with = "as_value")]
zones: Vec<Region>,
#[serde(default, with = "as_value")]
zone_set: u32,
}
impl Zones {
pub fn regions(&self) -> &[Region] {
&self.zones
}
pub fn zone_set(&self) -> u32 {
self.zone_set
}
}
pub type BarrierID = NonZeroU32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Type)]
#[zvariant(signature = "(iiii)")]
pub struct BarrierPosition {
x1: i32,
y1: i32,
x2: i32,
y2: i32,
}
impl BarrierPosition {
pub fn new(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
Self { x1, y1, x2, y2 }
}
pub fn as_tuple(&self) -> (i32, i32, i32, i32) {
(self.x1, self.y1, self.x2, self.y2)
}
pub fn x1(&self) -> i32 {
self.x1
}
pub fn y1(&self) -> i32 {
self.y1
}
pub fn x2(&self) -> i32 {
self.x2
}
pub fn y2(&self) -> i32 {
self.y2
}
}
impl From<(i32, i32, i32, i32)> for BarrierPosition {
fn from(pos: (i32, i32, i32, i32)) -> Self {
Self {
x1: pos.0,
y1: pos.1,
x2: pos.2,
y2: pos.3,
}
}
}
impl From<BarrierPosition> for (i32, i32, i32, i32) {
fn from(pos: BarrierPosition) -> Self {
pos.as_tuple()
}
}
impl Serialize for BarrierPosition {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_tuple().serialize(serializer)
}
}
#[derive(Debug, Serialize, Type)]
#[zvariant(signature = "dict")]
pub struct Barrier {
#[serde(with = "as_value")]
barrier_id: BarrierID,
#[serde(with = "as_value")]
position: BarrierPosition,
}
impl Barrier {
pub fn new(barrier_id: BarrierID, position: impl Into<BarrierPosition>) -> Self {
Self {
barrier_id,
position: position.into(),
}
}
pub fn barrier_id(&self) -> BarrierID {
self.barrier_id
}
pub fn position(&self) -> BarrierPosition {
self.position
}
}
#[derive(Debug, Deserialize, Type)]
#[zvariant(signature = "dict")]
pub struct SetPointerBarriersResponse {
#[serde(default, with = "as_value")]
failed_barriers: Vec<BarrierID>,
}
impl SetPointerBarriersResponse {
pub fn failed_barriers(&self) -> &[BarrierID] {
&self.failed_barriers
}
}
#[doc(alias = "org.freedesktop.portal.InputCapture")]
pub struct InputCapture(Proxy<'static>);
impl InputCapture {
pub async fn new() -> Result<InputCapture, Error> {
let proxy = Proxy::new_desktop("org.freedesktop.portal.InputCapture").await?;
Ok(Self(proxy))
}
pub async fn with_connection(connection: zbus::Connection) -> Result<InputCapture, Error> {
let proxy =
Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.InputCapture")
.await?;
Ok(Self(proxy))
}
pub fn version(&self) -> u32 {
self.0.version()
}
#[doc(alias = "CreateSession")]
pub async fn create_session(
&self,
identifier: Option<&WindowIdentifier>,
options: CreateSessionOptions,
) -> Result<(Session<Self>, BitFlags<Capabilities>), Error> {
let identifier = Optional::from(identifier);
let (request, proxy) = futures_util::try_join!(
self.0.request::<CreateSessionResponse>(
&options.handle_token,
"CreateSession",
(identifier, &options)
),
Session::from_unique_name(self.0.connection().clone(), &options.session_handle_token),
)?;
let response = request.response()?;
assert_eq!(proxy.path(), &response.session_handle.as_ref());
Ok((proxy, response.capabilities))
}
#[doc(alias = "CreateSession2")]
pub async fn create_session2(
&self,
options: CreateSession2Options,
) -> Result<Session<Self>, Error> {
let proxy =
Session::from_unique_name(self.0.connection().clone(), &options.session_handle_token)
.await?;
let response = self
.0
.call_versioned::<CreateSession2Results>("CreateSession2", &options, 2)
.await?;
assert_eq!(proxy.path(), &response.session_handle.as_ref());
Ok(proxy)
}
#[doc(alias = "Start")]
pub async fn start(
&self,
session: &Session<Self>,
identifier: Option<&WindowIdentifier>,
options: StartOptions,
) -> Result<Request<StartResponse>, Error> {
let identifier = Optional::from(identifier);
self.0
.request_versioned(
&options.handle_token,
"Start",
(session, identifier, &options),
2,
)
.await
}
#[doc(alias = "GetZones")]
pub async fn zones(
&self,
session: &Session<Self>,
options: GetZonesOptions,
) -> Result<Request<Zones>, Error> {
self.0
.request(&options.handle_token, "GetZones", (session, &options))
.await
}
#[doc(alias = "SetPointerBarriers")]
pub async fn set_pointer_barriers(
&self,
session: &Session<Self>,
barriers: &[Barrier],
zone_set: u32,
options: SetPointerBarriersOptions,
) -> Result<Request<SetPointerBarriersResponse>, Error> {
self.0
.request(
&options.handle_token,
"SetPointerBarriers",
&(session, &options, barriers, zone_set),
)
.await
}
#[doc(alias = "Enable")]
pub async fn enable(
&self,
session: &Session<Self>,
options: EnableOptions,
) -> Result<(), Error> {
self.0.call("Enable", &(session, &options)).await
}
#[doc(alias = "Disable")]
pub async fn disable(
&self,
session: &Session<Self>,
options: DisableOptions,
) -> Result<(), Error> {
self.0.call("Disable", &(session, &options)).await
}
#[doc(alias = "Release")]
pub async fn release(
&self,
session: &Session<Self>,
options: ReleaseOptions,
) -> Result<(), Error> {
self.0.call("Release", &(session, &options)).await
}
#[doc(alias = "ConnectToEIS")]
pub async fn connect_to_eis(
&self,
session: &Session<Self>,
options: ConnectToEISOptions,
) -> Result<OwnedFd, Error> {
let fd = self
.0
.call::<zvariant::OwnedFd>("ConnectToEIS", &(session, options))
.await?;
Ok(fd.into())
}
#[doc(alias = "Disabled")]
pub async fn receive_disabled(&self) -> Result<impl Stream<Item = Disabled>, Error> {
self.0.signal("Disabled").await
}
#[doc(alias = "Activated")]
pub async fn receive_activated(&self) -> Result<impl Stream<Item = Activated>, Error> {
self.0.signal("Activated").await
}
#[doc(alias = "Deactivated")]
pub async fn receive_deactivated(&self) -> Result<impl Stream<Item = Deactivated>, Error> {
self.0.signal("Deactivated").await
}
#[doc(alias = "ZonesChanged")]
pub async fn receive_zones_changed(&self) -> Result<impl Stream<Item = ZonesChanged>, Error> {
self.0.signal("ZonesChanged").await
}
#[doc(alias = "SupportedCapabilities")]
pub async fn supported_capabilities(&self) -> Result<BitFlags<Capabilities>, Error> {
self.0.property("SupportedCapabilities").await
}
}
impl std::ops::Deref for InputCapture {
type Target = zbus::Proxy<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl crate::Sealed for InputCapture {}
impl SessionPortal for InputCapture {}
#[cfg(feature = "clipboard")]
impl crate::desktop::clipboard::IsClipboardSession for InputCapture {}
#[cfg(test)]
mod tests {
use super::BarrierPosition;
#[test]
fn test_barrier_position() {
let pos = BarrierPosition::new(1, 2, 3, 4);
assert_eq!(pos.as_tuple(), (1, 2, 3, 4));
assert_eq!(pos.x1(), 1);
assert_eq!(pos.y1(), 2);
assert_eq!(pos.x2(), 3);
assert_eq!(pos.y2(), 4);
let string = serde_json::to_string(&pos).unwrap();
assert_eq!(string, "[1,2,3,4]");
let pos2 = BarrierPosition::from((1, 2, 3, 4));
assert_eq!(pos, pos2);
}
}