ashpd/desktop/
location.rs1use std::fmt::Debug;
28
29use futures_util::{Stream, TryFutureExt};
30use serde::Deserialize;
31use serde_repr::Serialize_repr;
32use zbus::zvariant::{DeserializeDict, ObjectPath, OwnedObjectPath, SerializeDict, Type};
33
34use super::{session::SessionPortal, HandleToken, Request, Session};
35use crate::{proxy::Proxy, Error, WindowIdentifier};
36
37#[cfg_attr(feature = "glib", derive(glib::Enum))]
38#[cfg_attr(feature = "glib", enum_type(name = "AshpdLocationAccuracy"))]
39#[derive(Serialize_repr, PartialEq, Eq, Clone, Copy, Debug, Type)]
40#[doc(alias = "XdpLocationAccuracy")]
41#[repr(u32)]
42pub enum Accuracy {
44 #[doc(alias = "XDP_LOCATION_ACCURACY_NONE")]
45 None = 0,
47 #[doc(alias = "XDP_LOCATION_ACCURACY_COUNTRY")]
48 Country = 1,
50 #[doc(alias = "XDP_LOCATION_ACCURACY_CITY")]
51 City = 2,
53 #[doc(alias = "XDP_LOCATION_ACCURACY_NEIGHBORHOOD")]
54 Neighborhood = 3,
56 #[doc(alias = "XDP_LOCATION_ACCURACY_STREET")]
57 Street = 4,
59 #[doc(alias = "XDP_LOCATION_ACCURACY_EXACT")]
60 Exact = 5,
62}
63
64#[derive(SerializeDict, Type, Debug, Default)]
65#[zvariant(signature = "dict")]
67struct CreateSessionOptions {
68 session_handle_token: HandleToken,
70 #[zvariant(rename = "distance-threshold")]
72 distance_threshold: Option<u32>,
73 #[zvariant(rename = "time-threshold")]
75 time_threshold: Option<u32>,
76 accuracy: Option<Accuracy>,
78}
79
80#[derive(SerializeDict, Type, Debug, Default)]
81#[zvariant(signature = "dict")]
83struct SessionStartOptions {
84 handle_token: HandleToken,
86}
87
88#[derive(Deserialize, Type)]
89pub struct Location(OwnedObjectPath, LocationInner);
91
92impl Location {
93 pub fn session_handle(&self) -> ObjectPath<'_> {
95 self.0.as_ref()
96 }
97
98 pub fn accuracy(&self) -> f64 {
100 self.1.accuracy
101 }
102
103 pub fn altitude(&self) -> Option<f64> {
105 if self.1.altitude == -f64::MAX {
106 None
107 } else {
108 Some(self.1.altitude)
109 }
110 }
111
112 pub fn speed(&self) -> Option<f64> {
114 if self.1.speed == -1f64 {
115 None
116 } else {
117 Some(self.1.speed)
118 }
119 }
120
121 pub fn heading(&self) -> Option<f64> {
124 if self.1.heading == -1f64 {
125 None
126 } else {
127 Some(self.1.heading)
128 }
129 }
130
131 pub fn description(&self) -> Option<&str> {
133 if self.1.description.is_empty() {
134 None
135 } else {
136 Some(&self.1.description)
137 }
138 }
139
140 pub fn latitude(&self) -> f64 {
142 self.1.latitude
143 }
144
145 pub fn longitude(&self) -> f64 {
147 self.1.longitude
148 }
149
150 pub fn timestamp(&self) -> std::time::Duration {
152 std::time::Duration::from_secs(self.1.timestamp.0)
153 }
154}
155
156impl Debug for Location {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 f.debug_struct("Location")
159 .field("accuracy", &self.accuracy())
160 .field("altitude", &self.altitude())
161 .field("speed", &self.speed())
162 .field("heading", &self.heading())
163 .field("description", &self.description())
164 .field("latitude", &self.latitude())
165 .field("longitude", &self.longitude())
166 .field("timestamp", &self.timestamp())
167 .finish()
168 }
169}
170
171#[derive(Debug, SerializeDict, DeserializeDict, Type)]
172#[zvariant(signature = "dict")]
173struct LocationInner {
174 #[zvariant(rename = "Accuracy")]
175 accuracy: f64,
176 #[zvariant(rename = "Altitude")]
177 altitude: f64,
178 #[zvariant(rename = "Speed")]
179 speed: f64,
180 #[zvariant(rename = "Heading")]
181 heading: f64,
182 #[zvariant(rename = "Description")]
183 description: String,
184 #[zvariant(rename = "Latitude")]
185 latitude: f64,
186 #[zvariant(rename = "Longitude")]
187 longitude: f64,
188 #[zvariant(rename = "Timestamp")]
189 timestamp: (u64, u64),
190}
191
192#[derive(Debug)]
197#[doc(alias = "org.freedesktop.portal.Location")]
198pub struct LocationProxy<'a>(Proxy<'a>);
199
200impl<'a> LocationProxy<'a> {
201 pub async fn new() -> Result<LocationProxy<'a>, Error> {
203 let proxy = Proxy::new_desktop("org.freedesktop.portal.Location").await?;
204 Ok(Self(proxy))
205 }
206
207 #[doc(alias = "LocationUpdated")]
213 #[doc(alias = "XdpPortal::location-updated")]
214 pub async fn receive_location_updated(&self) -> Result<impl Stream<Item = Location>, Error> {
215 self.0.signal("LocationUpdated").await
216 }
217
218 #[doc(alias = "CreateSession")]
232 pub async fn create_session(
233 &self,
234 distance_threshold: Option<u32>,
235 time_threshold: Option<u32>,
236 accuracy: Option<Accuracy>,
237 ) -> Result<Session<'a, Self>, Error> {
238 let options = CreateSessionOptions {
239 distance_threshold,
240 time_threshold,
241 accuracy,
242 ..Default::default()
243 };
244 let (path, proxy) = futures_util::try_join!(
245 self.0
246 .call::<OwnedObjectPath>("CreateSession", &(options))
247 .into_future(),
248 Session::from_unique_name(&options.session_handle_token).into_future(),
249 )?;
250 assert_eq!(proxy.path(), &path.into_inner());
251 Ok(proxy)
252 }
253
254 #[doc(alias = "Start")]
267 #[doc(alias = "xdp_portal_location_monitor_start")]
268 pub async fn start(
269 &self,
270 session: &Session<'_, Self>,
271 identifier: Option<&WindowIdentifier>,
272 ) -> Result<Request<()>, Error> {
273 let options = SessionStartOptions::default();
274 let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
275 self.0
276 .empty_request(
277 &options.handle_token,
278 "Start",
279 &(session, &identifier, &options),
280 )
281 .await
282 }
283}
284
285impl crate::Sealed for LocationProxy<'_> {}
286impl SessionPortal for LocationProxy<'_> {}
287
288impl<'a> std::ops::Deref for LocationProxy<'a> {
289 type Target = zbus::Proxy<'a>;
290
291 fn deref(&self) -> &Self::Target {
292 &self.0
293 }
294}