1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Interact with the clipboard.
//!
//! The portal is mostly meant to be used along with
//! [`RemoteDesktop`](crate::desktop::remote_desktop::RemoteDesktop)

use std::collections::HashMap;

use futures_util::{Stream, StreamExt};
use zbus::zvariant::{DeserializeDict, OwnedFd, OwnedObjectPath, SerializeDict, Type, Value};

use super::Session;
use crate::{proxy::Proxy, Result};

#[derive(Debug, Type, SerializeDict)]
#[zvariant(signature = "dict")]
struct SetSelectionOptions<'a> {
    mime_types: &'a [&'a str],
}

#[derive(Debug, Type, DeserializeDict)]
#[zvariant(signature = "dict")]
/// The details of a new clipboard selection.
pub struct SelectionOwnerChanged {
    mime_types: Option<Vec<String>>,
    session_is_owner: Option<bool>,
}

impl SelectionOwnerChanged {
    /// Whether the session is the owner of the clipboard selection or not.
    pub fn session_is_owner(&self) -> Option<bool> {
        self.session_is_owner
    }

    /// A list of mime types the new clipboard has content for.
    pub fn mime_types(&self) -> Vec<String> {
        self.mime_types.clone().unwrap_or_default()
    }
}

#[doc(alias = "org.freedesktop.portal.Clipboard")]
/// Wrapper of the DBus interface: [`org.freedesktop.portal.Clipboard`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html).
pub struct Clipboard<'a>(Proxy<'a>);

impl<'a> Clipboard<'a> {
    /// Create a new instance of [`Clipboard`].
    pub async fn new() -> Result<Clipboard<'a>> {
        Ok(Self(
            Proxy::new_desktop("org.freedesktop.portal.Clipboard").await?,
        ))
    }

    /// # Specifications
    ///
    /// See also [`RequestClipboard`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-requestclipboard).
    #[doc(alias = "RequestClipboard")]
    pub async fn request(&self, session: &Session<'_>) -> Result<()> {
        let options: HashMap<&str, Value<'_>> = HashMap::default();
        self.0
            .call_method("RequestClipboard", &(session, options))
            .await?;
        Ok(())
    }

    /// # Specifications
    ///
    /// See also [`SetSelection`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-setselection).
    #[doc(alias = "SetSelection")]
    pub async fn set_selection(&self, session: &Session<'_>, mime_types: &[&str]) -> Result<()> {
        let options = SetSelectionOptions { mime_types };
        self.0.call("SetSelection", &(session, options)).await?;

        Ok(())
    }

    /// # Specifications
    ///
    /// See also [`SelectionWrite`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-selectionwrite).
    #[doc(alias = "SelectionWrite")]
    pub async fn selection_write(&self, session: &Session<'_>, serial: u32) -> Result<OwnedFd> {
        let fd = self
            .0
            .call::<OwnedFd>("SelectionWrite", &(session, serial))
            .await?;
        Ok(fd)
    }

    /// # Specifications
    ///
    /// See also [`SelectionWriteDone`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-selectionwritedone).
    #[doc(alias = "SelectionWriteDone")]
    pub async fn selection_write_done(
        &self,
        session: &Session<'_>,
        serial: u32,
        success: bool,
    ) -> Result<()> {
        self.0
            .call("SelectionWriteDone", &(session, serial, success))
            .await
    }

    /// # Specifications
    ///
    /// See also [`SelectionRead`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-selectionread).
    #[doc(alias = "SelectionRead")]
    pub async fn selection_read(&self, session: &Session<'_>, mime_type: &str) -> Result<OwnedFd> {
        let fd = self
            .0
            .call::<OwnedFd>("SelectionRead", &(session, mime_type))
            .await?;
        Ok(fd)
    }

    /// Notifies the session that the clipboard selection has changed.
    /// # Specifications
    ///
    /// See also [`SelectionOwnerChanged`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-selectionownerchanged).
    #[doc(alias = "SelectionOwnerChanged")]
    pub async fn receive_selection_owner_changed(
        &self,
    ) -> Result<impl Stream<Item = (Session, SelectionOwnerChanged)>> {
        Ok(self
            .0
            .signal::<(OwnedObjectPath, SelectionOwnerChanged)>("SelectionOwnerChanged")
            .await?
            .filter_map(|(p, o)| async move { Session::new(p).await.map(|s| (s, o)).ok() }))
    }

    /// # Specifications
    ///
    /// See also [`SelectionTransfer`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Clipboard.html#org-freedesktop-portal-clipboard-selectiontransfer).
    #[doc(alias = "SelectionTransfer")]
    pub async fn receive_selection_transfer(
        &self,
    ) -> Result<impl Stream<Item = (Session, String, u32)>> {
        Ok(self
            .0
            .signal::<(OwnedObjectPath, String, u32)>("SelectionTransfer")
            .await?
            .filter_map(|(p, mime_type, serial)| async move {
                Session::new(p)
                    .await
                    .map(|session| (session, mime_type, serial))
                    .ok()
            }))
    }
}

impl<'a> std::ops::Deref for Clipboard<'a> {
    type Target = zbus::Proxy<'a>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}