Skip to main content

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 serde::{Deserialize, Serialize};
39use zbus::zvariant::{
40    Optional, Type,
41    as_value::{self, optional},
42};
43
44use super::{HandleToken, Request};
45use crate::{Error, Uri, WindowIdentifier, desktop::Color, proxy::Proxy};
46
47/// Options for taking a screenshot.
48#[derive(Serialize, Deserialize, Type, Debug, Default)]
49#[zvariant(signature = "dict")]
50pub struct ScreenshotOptions {
51    #[serde(with = "as_value", skip_deserializing)]
52    handle_token: HandleToken,
53    #[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
54    modal: Option<bool>,
55    #[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
56    interactive: Option<bool>,
57    #[serde(default, with = "optional", skip_serializing)]
58    #[cfg_attr(not(feature = "backend"), allow(dead_code))]
59    permission_store_checked: Option<bool>,
60}
61
62impl ScreenshotOptions {
63    /// Sets whether the dialog should be modal.
64    #[must_use]
65    pub fn set_modal(mut self, modal: impl Into<Option<bool>>) -> Self {
66        self.modal = modal.into();
67        self
68    }
69
70    /// Gets whether the dialog should be modal.
71    #[cfg(feature = "backend")]
72    pub fn modal(&self) -> Option<bool> {
73        self.modal
74    }
75
76    /// Sets whether the dialog should offer customization.
77    #[must_use]
78    pub fn set_interactive(mut self, interactive: impl Into<Option<bool>>) -> Self {
79        self.interactive = interactive.into();
80        self
81    }
82
83    /// Gets whether the dialog should offer customization.
84    #[cfg(feature = "backend")]
85    pub fn interactive(&self) -> Option<bool> {
86        self.interactive
87    }
88
89    /// Gets whether the permission store has been checked.
90    #[cfg(feature = "backend")]
91    pub fn permission_store_checked(&self) -> Option<bool> {
92        self.permission_store_checked
93    }
94}
95
96#[derive(Serialize, Deserialize, Type)]
97#[zvariant(signature = "dict")]
98/// The response of a [`ScreenshotRequest`] request.
99pub struct Screenshot {
100    #[serde(with = "as_value")]
101    uri: Uri,
102}
103
104impl Screenshot {
105    #[cfg(feature = "backend")]
106    #[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
107    /// Create a new instance of the screenshot.
108    pub fn new(uri: Uri) -> Self {
109        Self { uri }
110    }
111
112    /// Creates a new builder-pattern struct instance to construct
113    /// [`Screenshot`].
114    ///
115    /// This method returns an instance of [`ScreenshotRequest`].
116    pub fn request() -> ScreenshotRequest {
117        ScreenshotRequest::default()
118    }
119
120    /// The screenshot URI.
121    pub fn uri(&self) -> &Uri {
122        &self.uri
123    }
124}
125
126impl Debug for Screenshot {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        f.write_str(self.uri.as_str())
129    }
130}
131
132/// Options for picking a color.
133#[derive(Serialize, Deserialize, Type, Debug, Default)]
134#[zvariant(signature = "dict")]
135pub struct ColorOptions {
136    #[serde(with = "as_value", skip_deserializing)]
137    handle_token: HandleToken,
138}
139
140/// Wrapper of the DBus interface: [`org.freedesktop.portal.Screenshot`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html).
141#[derive(Debug)]
142#[doc(alias = "org.freedesktop.portal.Screenshot")]
143pub struct ScreenshotProxy(Proxy<'static>);
144
145impl ScreenshotProxy {
146    /// Create a new instance of [`ScreenshotProxy`].
147    pub async fn new() -> Result<Self, Error> {
148        let proxy = Proxy::new_desktop("org.freedesktop.portal.Screenshot").await?;
149        Ok(Self(proxy))
150    }
151
152    /// Create a new instance of [`ScreenshotProxy`].
153    pub async fn with_connection(connection: zbus::Connection) -> Result<Self, Error> {
154        let proxy =
155            Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.Screenshot")
156                .await?;
157        Ok(Self(proxy))
158    }
159
160    /// Returns the portal interface version.
161    pub fn version(&self) -> u32 {
162        self.0.version()
163    }
164
165    /// Obtains the color of a single pixel.
166    ///
167    /// # Arguments
168    ///
169    /// * `identifier` - Identifier for the application window.
170    ///
171    /// # Specifications
172    ///
173    /// See also [`PickColor`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html#org-freedesktop-portal-screenshot-pickcolor).
174    #[doc(alias = "PickColor")]
175    #[doc(alias = "xdp_portal_pick_color")]
176    pub async fn pick_color(
177        &self,
178        identifier: Option<&WindowIdentifier>,
179        options: ColorOptions,
180    ) -> Result<Request<Color>, Error> {
181        let identifier = Optional::from(identifier);
182        self.0
183            .request(&options.handle_token, "PickColor", &(identifier, &options))
184            .await
185    }
186
187    /// Takes a screenshot.
188    ///
189    /// # Arguments
190    ///
191    /// * `identifier` - Identifier for the application window.
192    /// * `interactive` - Sets whether the dialog should offer customization
193    ///   before a screenshot or not.
194    /// * `modal` - Sets whether the dialog should be a modal.
195    ///
196    /// # Returns
197    ///
198    /// The screenshot URI.
199    ///
200    /// # Specifications
201    ///
202    /// See also [`Screenshot`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html#org-freedesktop-portal-screenshot-screenshot).
203    #[doc(alias = "Screenshot")]
204    #[doc(alias = "xdp_portal_take_screenshot")]
205    pub async fn screenshot(
206        &self,
207        identifier: Option<&WindowIdentifier>,
208        options: ScreenshotOptions,
209    ) -> Result<Request<Screenshot>, Error> {
210        let identifier = Optional::from(identifier);
211        self.0
212            .request(&options.handle_token, "Screenshot", &(identifier, &options))
213            .await
214    }
215}
216
217impl std::ops::Deref for ScreenshotProxy {
218    type Target = zbus::Proxy<'static>;
219
220    fn deref(&self) -> &Self::Target {
221        &self.0
222    }
223}
224
225#[derive(Debug, Default)]
226#[doc(alias = "xdp_portal_pick_color")]
227/// A [builder-pattern] type to construct [`Color`].
228///
229/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
230pub struct ColorRequest {
231    identifier: Option<WindowIdentifier>,
232    options: ColorOptions,
233    connection: Option<zbus::Connection>,
234}
235
236impl ColorRequest {
237    #[must_use]
238    /// Sets a window identifier.
239    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
240        self.identifier = identifier.into();
241        self
242    }
243
244    #[must_use]
245    /// Sets a connection to use other than the internal one.
246    pub fn connection(mut self, connection: Option<zbus::Connection>) -> Self {
247        self.connection = connection;
248        self
249    }
250
251    /// Build the [`Color`].
252    pub async fn send(self) -> Result<Request<Color>, Error> {
253        let proxy = if let Some(connection) = self.connection {
254            ScreenshotProxy::with_connection(connection).await?
255        } else {
256            ScreenshotProxy::new().await?
257        };
258        proxy
259            .pick_color(self.identifier.as_ref(), self.options)
260            .await
261    }
262}
263
264impl Color {
265    /// Creates a new builder-pattern struct instance to construct
266    /// [`Color`].
267    ///
268    /// This method returns an instance of [`ColorRequest`].
269    pub fn pick() -> ColorRequest {
270        ColorRequest::default()
271    }
272}
273
274#[derive(Debug, Default)]
275#[doc(alias = "xdp_portal_take_screenshot")]
276/// A [builder-pattern] type to construct a screenshot [`Screenshot`].
277///
278/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
279pub struct ScreenshotRequest {
280    options: ScreenshotOptions,
281    identifier: Option<WindowIdentifier>,
282    connection: Option<zbus::Connection>,
283}
284
285impl ScreenshotRequest {
286    #[must_use]
287    /// Sets a window identifier.
288    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
289        self.identifier = identifier.into();
290        self
291    }
292
293    /// Sets whether the dialog should be a modal.
294    #[must_use]
295    pub fn modal(mut self, modal: impl Into<Option<bool>>) -> Self {
296        self.options.modal = modal.into();
297        self
298    }
299
300    /// Sets whether the dialog should offer customization before a screenshot
301    /// or not.
302    #[must_use]
303    pub fn interactive(mut self, interactive: impl Into<Option<bool>>) -> Self {
304        self.options.interactive = interactive.into();
305        self
306    }
307
308    #[must_use]
309    /// Sets a connection to use other than the internal one.
310    pub fn connection(mut self, connection: Option<zbus::Connection>) -> Self {
311        self.connection = connection;
312        self
313    }
314
315    /// Build the [`Screenshot`].
316    pub async fn send(self) -> Result<Request<Screenshot>, Error> {
317        let proxy = if let Some(connection) = self.connection {
318            ScreenshotProxy::with_connection(connection).await?
319        } else {
320            ScreenshotProxy::new().await?
321        };
322        proxy
323            .screenshot(self.identifier.as_ref(), self.options)
324            .await
325    }
326}