ashpd/desktop/
screenshot.rs

1//! Take a screenshot or pick a color.
2//!
3//! Wrapper of the DBus interface: [`org.freedesktop.portal.Screenshot`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html).
4//!
5//! # Examples
6//!
7//! ## Taking a screenshot
8//!
9//! ```rust,no_run
10//! use ashpd::desktop::screenshot::Screenshot;
11//!
12//! async fn run() -> ashpd::Result<()> {
13//!     let response = Screenshot::request()
14//!         .interactive(true)
15//!         .modal(true)
16//!         .send()
17//!         .await?
18//!         .response()?;
19//!     println!("URI: {}", response.uri());
20//!     Ok(())
21//! }
22//! ```
23//!
24//! ## Picking a color
25//!
26//! ```rust,no_run
27//! use ashpd::desktop::Color;
28//!
29//! async fn run() -> ashpd::Result<()> {
30//!     let color = Color::pick().send().await?.response()?;
31//!     println!("({}, {}, {})", color.red(), color.green(), color.blue());
32//!
33//!     Ok(())
34//! }
35//! ```
36use std::fmt::Debug;
37
38use zbus::zvariant::{DeserializeDict, SerializeDict, Type};
39
40use super::{HandleToken, Request};
41use crate::{desktop::Color, proxy::Proxy, Error, WindowIdentifier};
42
43#[derive(SerializeDict, Type, Debug, Default)]
44#[zvariant(signature = "dict")]
45struct ScreenshotOptions {
46    handle_token: HandleToken,
47    modal: Option<bool>,
48    interactive: Option<bool>,
49}
50
51#[derive(SerializeDict, DeserializeDict, Type)]
52#[zvariant(signature = "dict")]
53/// The response of a [`ScreenshotRequest`] request.
54pub struct Screenshot {
55    uri: url::Url,
56}
57
58impl Screenshot {
59    #[cfg(feature = "backend")]
60    #[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
61    /// Create a new instance of the screenshot.
62    pub fn new(uri: url::Url) -> Self {
63        Self { uri }
64    }
65
66    /// Creates a new builder-pattern struct instance to construct
67    /// [`Screenshot`].
68    ///
69    /// This method returns an instance of [`ScreenshotRequest`].
70    pub fn request() -> ScreenshotRequest {
71        ScreenshotRequest::default()
72    }
73
74    /// The screenshot URI.
75    pub fn uri(&self) -> &url::Url {
76        &self.uri
77    }
78}
79
80impl Debug for Screenshot {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        f.write_str(self.uri.as_str())
83    }
84}
85
86#[derive(SerializeDict, Type, Debug, Default)]
87#[zvariant(signature = "dict")]
88struct ColorOptions {
89    handle_token: HandleToken,
90}
91
92#[derive(Debug)]
93#[doc(alias = "org.freedesktop.portal.Screenshot")]
94struct ScreenshotProxy<'a>(Proxy<'a>);
95
96impl<'a> ScreenshotProxy<'a> {
97    /// Create a new instance of [`ScreenshotProxy`].
98    pub async fn new() -> Result<ScreenshotProxy<'a>, Error> {
99        let proxy = Proxy::new_desktop("org.freedesktop.portal.Screenshot").await?;
100        Ok(Self(proxy))
101    }
102
103    /// Obtains the color of a single pixel.
104    ///
105    /// # Arguments
106    ///
107    /// * `identifier` - Identifier for the application window.
108    ///
109    /// # Specifications
110    ///
111    /// See also [`PickColor`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html#org-freedesktop-portal-screenshot-pickcolor).
112    #[doc(alias = "PickColor")]
113    #[doc(alias = "xdp_portal_pick_color")]
114    pub async fn pick_color(
115        &self,
116        identifier: Option<&WindowIdentifier>,
117        options: ColorOptions,
118    ) -> Result<Request<Color>, Error> {
119        let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
120        self.0
121            .request(&options.handle_token, "PickColor", &(&identifier, &options))
122            .await
123    }
124
125    /// Takes a screenshot.
126    ///
127    /// # Arguments
128    ///
129    /// * `identifier` - Identifier for the application window.
130    /// * `interactive` - Sets whether the dialog should offer customization
131    ///   before a screenshot or not.
132    /// * `modal` - Sets whether the dialog should be a modal.
133    ///
134    /// # Returns
135    ///
136    /// The screenshot URI.
137    ///
138    /// # Specifications
139    ///
140    /// See also [`Screenshot`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html#org-freedesktop-portal-screenshot-screenshot).
141    #[doc(alias = "Screenshot")]
142    #[doc(alias = "xdp_portal_take_screenshot")]
143    pub async fn screenshot(
144        &self,
145        identifier: Option<&WindowIdentifier>,
146        options: ScreenshotOptions,
147    ) -> Result<Request<Screenshot>, Error> {
148        let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
149        self.0
150            .request(
151                &options.handle_token,
152                "Screenshot",
153                &(&identifier, &options),
154            )
155            .await
156    }
157}
158
159impl<'a> std::ops::Deref for ScreenshotProxy<'a> {
160    type Target = zbus::Proxy<'a>;
161
162    fn deref(&self) -> &Self::Target {
163        &self.0
164    }
165}
166
167#[derive(Debug, Default)]
168#[doc(alias = "xdp_portal_pick_color")]
169/// A [builder-pattern] type to construct [`Color`].
170///
171/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
172pub struct ColorRequest {
173    identifier: Option<WindowIdentifier>,
174    options: ColorOptions,
175}
176
177impl ColorRequest {
178    #[must_use]
179    /// Sets a window identifier.
180    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
181        self.identifier = identifier.into();
182        self
183    }
184
185    /// Build the [`Color`].
186    pub async fn send(self) -> Result<Request<Color>, Error> {
187        let proxy = ScreenshotProxy::new().await?;
188        proxy
189            .pick_color(self.identifier.as_ref(), self.options)
190            .await
191    }
192}
193
194impl Color {
195    /// Creates a new builder-pattern struct instance to construct
196    /// [`Color`].
197    ///
198    /// This method returns an instance of [`ColorRequest`].
199    pub fn pick() -> ColorRequest {
200        ColorRequest::default()
201    }
202}
203
204#[derive(Debug, Default)]
205#[doc(alias = "xdp_portal_take_screenshot")]
206/// A [builder-pattern] type to construct a screenshot [`Screenshot`].
207///
208/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
209pub struct ScreenshotRequest {
210    options: ScreenshotOptions,
211    identifier: Option<WindowIdentifier>,
212}
213
214impl ScreenshotRequest {
215    #[must_use]
216    /// Sets a window identifier.
217    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
218        self.identifier = identifier.into();
219        self
220    }
221
222    /// Sets whether the dialog should be a modal.
223    #[must_use]
224    pub fn modal(mut self, modal: impl Into<Option<bool>>) -> Self {
225        self.options.modal = modal.into();
226        self
227    }
228
229    /// Sets whether the dialog should offer customization before a screenshot
230    /// or not.
231    #[must_use]
232    pub fn interactive(mut self, interactive: impl Into<Option<bool>>) -> Self {
233        self.options.interactive = interactive.into();
234        self
235    }
236
237    /// Build the [`Screenshot`].
238    pub async fn send(self) -> Result<Request<Screenshot>, Error> {
239        let proxy = ScreenshotProxy::new().await?;
240        proxy
241            .screenshot(self.identifier.as_ref(), self.options)
242            .await
243    }
244}