ctru/services/ptm/
user.rs

1//! PTM User service.
2//!
3//! This sub-service of the Power-Time service family includes getters for various hardware/console states, such as whether
4//! the console [shell is open](PTMUser::shell_state) or the current [battery level](PTMUser::battery_level).
5#[doc(alias = "battery")]
6#[doc(alias = "shell")]
7use std::sync::Mutex;
8
9use crate::error::{Error, Result, ResultCode};
10use crate::services::ServiceReference;
11
12static PTMU_ACTIVE: Mutex<()> = Mutex::new(());
13
14/// Whether the console's shell is open or closed.
15#[repr(u8)]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17pub enum ShellState {
18    /// Clam shell is currently closed.
19    Closed = 0,
20    /// Clam shell is currently open.
21    Open = 1,
22}
23
24/// Representation of the console's battery charge level.
25///
26/// These values correspond to the various states the battery is shown to be in the Home Menu UI.
27#[repr(u8)]
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
29pub enum BatteryLevel {
30    /// Battery charge at 0%. System shutdown is imminent.
31    Drained = 0,
32    /// Battery charge between 5-1%.
33    Critical = 1,
34    /// Battery charge between 10-6%.
35    VeryLow = 2,
36    /// Battery charge between 30-11%.
37    Low = 3,
38    /// Battery charge between 60-31%.
39    Medium = 4,
40    /// Battery charge between 100-61%.
41    High = 5,
42}
43
44/// Handle to the PTM:User service.
45pub struct PTMUser {
46    _service_handler: ServiceReference,
47}
48
49impl PTMUser {
50    /// Initialize a new service handle.
51    ///
52    /// # Errors
53    ///
54    /// This function will return an error if the service was unable to be initialized.
55    /// Since this service requires no special or elevated permissions, errors are rare in practice.
56    ///
57    /// # Example
58    ///
59    /// ```
60    /// # let _runner = test_runner::GdbRunner::default();
61    /// # use std::error::Error;
62    /// # fn main() -> Result<(), Box<dyn Error>> {
63    /// #
64    /// use ctru::services::ptm::user::PTMUser;
65    ///
66    /// let ptmu = PTMUser::new()?;
67    /// #
68    /// # Ok(())
69    /// # }
70    /// ```
71    #[doc(alias = "ptmuInit")]
72    pub fn new() -> Result<Self> {
73        let handler = ServiceReference::new(
74            &PTMU_ACTIVE,
75            || {
76                ResultCode(unsafe { ctru_sys::ptmuInit() })?;
77
78                Ok(())
79            },
80            || unsafe {
81                ctru_sys::ptmuExit();
82            },
83        )?;
84
85        Ok(Self {
86            _service_handler: handler,
87        })
88    }
89
90    /// Returns whether the console's clamshell is closed or open.
91    ///
92    /// # Example
93    ///
94    /// ```
95    /// # let _runner = test_runner::GdbRunner::default();
96    /// # use std::error::Error;
97    /// # fn main() -> Result<(), Box<dyn Error>> {
98    /// #
99    /// use ctru::services::ptm::user::{PTMUser, ShellState};
100    ///
101    /// let ptmu = PTMUser::new()?;
102    ///
103    /// let state = ptmu.shell_state()?;
104    ///
105    /// match state {
106    ///     ShellState::Closed => println!("The shell is closed! How are you able to read this?"),
107    ///     ShellState::Open => println!("The shell is open! That might seem obvious to you."),
108    /// }
109    /// #
110    /// # Ok(())
111    /// # }
112    /// ```
113    #[doc(alias = "PTMU_GetShellState")]
114    pub fn shell_state(&self) -> Result<ShellState> {
115        let mut state: u8 = 0;
116
117        ResultCode(unsafe { ctru_sys::PTMU_GetShellState(&mut state) })?;
118
119        state.try_into()
120    }
121
122    /// Returns the console's current battery charge level.
123    ///
124    /// # Example
125    ///
126    /// ```
127    /// # let _runner = test_runner::GdbRunner::default();
128    /// # use std::error::Error;
129    /// # fn main() -> Result<(), Box<dyn Error>> {
130    /// #
131    /// use ctru::services::ptm::user::{PTMUser, BatteryLevel};
132    ///
133    /// let ptmu = PTMUser::new()?;
134    ///
135    /// let charge = ptmu.battery_level()?;
136    ///
137    /// if charge <= BatteryLevel::Low {
138    ///     println!("You should put the console to charge!");
139    /// }
140    /// #
141    /// # Ok(())
142    /// # }
143    /// ```
144    #[doc(alias = "PTMU_GetBatteryLevel")]
145    pub fn battery_level(&self) -> Result<BatteryLevel> {
146        let mut level: u8 = 0;
147
148        ResultCode(unsafe { ctru_sys::PTMU_GetBatteryLevel(&mut level) })?;
149
150        level.try_into()
151    }
152
153    /// Returns whether the console is currently charging its battery.
154    ///
155    /// # Example
156    ///
157    /// ```
158    /// # let _runner = test_runner::GdbRunner::default();
159    /// # use std::error::Error;
160    /// # fn main() -> Result<(), Box<dyn Error>> {
161    /// #
162    /// use ctru::services::ptm::user::PTMUser;
163    ///
164    /// let ptmu = PTMUser::new()?;
165    ///
166    /// let is_charging = ptmu.is_charging()?;
167    ///
168    /// if is_charging {
169    ///     println!("That is one juicy power line.");
170    /// }
171    /// #
172    /// # Ok(())
173    /// # }
174    /// ```
175    #[doc(alias = "PTMU_GetBatteryChargeState")]
176    pub fn is_charging(&self) -> Result<bool> {
177        let mut charging: u8 = 0;
178
179        ResultCode(unsafe { ctru_sys::PTMU_GetBatteryChargeState(&mut charging) })?;
180
181        match charging {
182            0 => Ok(false),
183            1 => Ok(true),
184            v => Err(Error::Other(format!(
185                "unexpected charging state value: {v}",
186            ))),
187        }
188    }
189
190    /// Returns the console's total step count.
191    ///
192    /// # Example
193    ///
194    /// ```
195    /// # let _runner = test_runner::GdbRunner::default();
196    /// # use std::error::Error;
197    /// # fn main() -> Result<(), Box<dyn Error>> {
198    /// #
199    /// use ctru::services::ptm::user::PTMUser;
200    ///
201    /// let ptmu = PTMUser::new()?;
202    ///
203    /// let steps = ptmu.step_count()?;
204    ///
205    /// println!("You accumulated {steps} steps. Don't stop moving!");
206    /// #
207    /// # Ok(())
208    /// # }
209    /// ```
210    #[doc(alias = "PTMU_GetTotalStepCount")]
211    pub fn step_count(&self) -> Result<u32> {
212        let mut steps: u32 = 0;
213
214        ResultCode(unsafe { ctru_sys::PTMU_GetTotalStepCount(&mut steps) })?;
215
216        Ok(steps)
217    }
218}
219
220impl TryFrom<u8> for ShellState {
221    type Error = Error;
222
223    fn try_from(value: u8) -> Result<Self> {
224        match value {
225            0 => Ok(Self::Closed),
226            1 => Ok(Self::Open),
227            v => Err(Error::Other(format!("unexpected shell state value: {v}",))),
228        }
229    }
230}
231
232impl TryFrom<u8> for BatteryLevel {
233    type Error = Error;
234
235    fn try_from(value: u8) -> Result<Self> {
236        match value {
237            0 => Ok(Self::Drained),
238            1 => Ok(Self::Critical),
239            2 => Ok(Self::VeryLow),
240            3 => Ok(Self::Low),
241            4 => Ok(Self::Medium),
242            5 => Ok(Self::High),
243            v => Err(Error::Other(
244                format!("unexpected battery level value: {v}",),
245            )),
246        }
247    }
248}
249
250from_impl!(ShellState, u8);
251from_impl!(BatteryLevel, u8);