irox_winlocation_api/
lib.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5#[cfg(target_os = "windows")]
6pub use crate::windows::*;
7
8#[cfg(target_os = "windows")]
9mod windows {
10    use log::{error, info, trace, warn};
11    use windows::Devices::Geolocation::{
12        GeolocationAccessStatus, Geolocator, PositionChangedEventArgs, StatusChangedEventArgs,
13    };
14    use windows::Foundation::TypedEventHandler;
15    type EventRegistrationToken = i64;
16
17    pub use crate::data::*;
18    pub use crate::error::*;
19
20    pub struct WindowsLocationAPI {
21        locator: Geolocator,
22    }
23
24    impl WindowsLocationAPI {
25        pub fn connect() -> Result<WindowsLocationAPI, Error> {
26            let locator = Geolocator::new()?;
27            trace!("Geolocator created.");
28
29            if let Err(e) = locator.AllowFallbackToConsentlessPositions() {
30                warn!("Error requesting fallback to consentless positions: {e:?}")
31            }
32
33            let access = Geolocator::RequestAccessAsync()?;
34            let access_results = access.get()?;
35            match access_results {
36                GeolocationAccessStatus::Allowed => {
37                    info!("User granted geolocation access.");
38                }
39                GeolocationAccessStatus::Denied => {
40                    warn!("User denied geolocation access.");
41                }
42                _ => {
43                    warn!("Unknown result from geolocation access request.");
44                }
45            }
46
47            Ok(WindowsLocationAPI { locator })
48        }
49
50        pub fn get_location(&self) -> Result<WindowsCoordinate, Error> {
51            let res = self.locator.GetGeopositionAsync()?;
52            let res = res.get()?;
53            let res = &(res.Coordinate()?);
54            let out: WindowsCoordinate = res.into();
55            trace!("WindowsLocation: {out:?}");
56            Ok(out)
57        }
58
59        ///
60        /// Returns the current status of the connection
61        pub fn get_status(&self) -> Result<PositionStatus, Error> {
62            Ok(self.locator.LocationStatus()?.0.into())
63        }
64
65        ///
66        /// Registers a callback handler to receive updates when the location changes.
67        /// When the [`LocationHandler`] object gets dropped or goes out of scope, the callback
68        /// function is deregistered.
69        pub fn on_location_changed<T: FnMut(WindowsCoordinate) + Send + 'static>(
70            &self,
71            mut cb: T,
72        ) -> Result<LocationHandler, Error> {
73            let handler = TypedEventHandler::new(
74                move |_sender: &Option<Geolocator>, result: &Option<PositionChangedEventArgs>| {
75                    let Some(args) = result else {
76                        error!("No position changed args received.");
77                        return Ok(());
78                    };
79                    if let Ok(pos) = args.Position() {
80                        if let Ok(coord) = pos.Coordinate() {
81                            let out: WindowsCoordinate = (&coord).into();
82                            cb(out);
83                        }
84                    }
85
86                    Ok(())
87                },
88            );
89            let res = self.locator.PositionChanged(&handler)?;
90            trace!("Location handler registered.");
91            Ok(LocationHandler {
92                locator: &self.locator,
93                token: res,
94            })
95        }
96        pub fn on_status_changed<T: FnMut(PositionStatus) + Send + 'static>(
97            &self,
98            mut cb: T,
99        ) -> Result<StatusHandler, Error> {
100            let handler = TypedEventHandler::new(
101                move |_sender: &Option<Geolocator>, result: &Option<StatusChangedEventArgs>| {
102                    let Some(args) = result else {
103                        error!("No status changed args received.");
104                        return Ok(());
105                    };
106                    if let Ok(status) = args.Status() {
107                        let out: PositionStatus = status.0.into();
108                        cb(out);
109                    }
110                    Ok(())
111                },
112            );
113            let res = self.locator.StatusChanged(&handler)?;
114            Ok(StatusHandler {
115                locator: &self.locator,
116                token: res,
117            })
118        }
119    }
120
121    pub struct LocationHandler<'a> {
122        locator: &'a Geolocator,
123        token: EventRegistrationToken,
124    }
125
126    impl Drop for LocationHandler<'_> {
127        fn drop(&mut self) {
128            let _res = self.locator.RemovePositionChanged(self.token);
129            trace!("Dropped location handler.");
130        }
131    }
132
133    pub struct StatusHandler<'a> {
134        locator: &'a Geolocator,
135        token: EventRegistrationToken,
136    }
137
138    impl Drop for StatusHandler<'_> {
139        fn drop(&mut self) {
140            let _res = self.locator.RemoveStatusChanged(self.token);
141            trace!("Dropped location handler.");
142        }
143    }
144}
145
146#[cfg(target_os = "windows")]
147mod data;
148#[cfg(target_os = "windows")]
149mod error;