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);