ctru/services/hid.rs
1//! Human Interface Device service.
2//!
3//! The HID service provides read access to user input such as [button presses](Hid::keys_down), [touch screen presses](Hid::touch_position),
4//! and [circle pad information](Hid::circlepad_position). It also provides information from the [volume slider](Hid::volume_slider()),
5//! the [accelerometer](Hid::accelerometer_vector()), and the [gyroscope](Hid::gyroscope_rate()).
6#![doc(alias = "input")]
7#![doc(alias = "controller")]
8#![doc(alias = "gamepad")]
9
10use std::sync::Mutex;
11
12use crate::error::ResultCode;
13use crate::services::ServiceReference;
14
15use bitflags::bitflags;
16
17static HID_ACTIVE: Mutex<()> = Mutex::new(());
18
19bitflags! {
20 /// A set of flags corresponding to the button and directional pad inputs present on the 3DS.
21 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
22 pub struct KeyPad: u32 {
23 /// A button.
24 const A = ctru_sys::KEY_A;
25 /// B button.
26 const B = ctru_sys::KEY_B;
27 /// Select button.
28 const SELECT = ctru_sys::KEY_SELECT;
29 /// Start button.
30 const START = ctru_sys::KEY_START;
31 /// D-Pad Right.
32 const DPAD_RIGHT = ctru_sys::KEY_DRIGHT;
33 /// D-Pad Left.
34 const DPAD_LEFT = ctru_sys::KEY_DLEFT;
35 /// D-Pad Up.
36 const DPAD_UP = ctru_sys::KEY_DUP;
37 /// D-Pad Down.
38 const DPAD_DOWN = ctru_sys::KEY_DDOWN;
39 /// R button.
40 const R = ctru_sys::KEY_R;
41 /// L button.
42 const L = ctru_sys::KEY_L;
43 /// X button.
44 const X = ctru_sys::KEY_X;
45 /// Y button.
46 const Y = ctru_sys::KEY_Y;
47 /// ZL button.
48 const ZL = ctru_sys::KEY_ZL;
49 /// ZR button.
50 const ZR = ctru_sys::KEY_ZR;
51 /// Touchscreen.
52 const TOUCH = ctru_sys::KEY_TOUCH;
53 /// C-Stick Right.
54 const CSTICK_RIGHT = ctru_sys::KEY_CSTICK_RIGHT;
55 /// C-Stick Left.
56 const CSTICK_LEFT = ctru_sys::KEY_CSTICK_LEFT;
57 /// C-Stick Up.
58 const CSTICK_UP = ctru_sys::KEY_CSTICK_UP;
59 /// C-Stick Down.
60 const CSTICK_DOWN = ctru_sys::KEY_CSTICK_DOWN;
61 /// CirclePad Right.
62 const CPAD_RIGHT = ctru_sys::KEY_CPAD_RIGHT;
63 /// CirclePad Left.
64 const CPAD_LEFT = ctru_sys::KEY_CPAD_LEFT;
65 /// CirclePad Up.
66 const CPAD_UP = ctru_sys::KEY_CPAD_UP;
67 /// CirclePad Down.
68 const CPAD_DOWN = ctru_sys::KEY_CPAD_DOWN;
69
70 // Convenience catch-all for the D-Pad and the CirclePad
71
72 /// Direction Up (either D-Pad or CirclePad).
73 const UP = KeyPad::DPAD_UP.bits() | KeyPad::CPAD_UP.bits();
74 /// Direction Down (either D-Pad or CirclePad).
75 const DOWN = KeyPad::DPAD_DOWN.bits() | KeyPad::CPAD_DOWN.bits();
76 /// Direction Left (either D-Pad or CirclePad).
77 const LEFT = KeyPad::DPAD_LEFT.bits() | KeyPad::CPAD_LEFT.bits();
78 /// Direction Right (either D-Pad or CirclePad).
79 const RIGHT = KeyPad::DPAD_RIGHT.bits() | KeyPad::CPAD_RIGHT.bits();
80 }
81}
82
83/// Error enum for generic errors within the [`Hid`] service.
84#[non_exhaustive]
85#[derive(Copy, Clone, Debug, PartialEq, Eq)]
86pub enum Error {
87 /// An attempt was made to access the accelerometer while disabled.
88 UnavailableAccelerometer,
89 /// An attempt was made to access the gyroscope while disabled.
90 UnavailableGyroscope,
91}
92
93/// Representation of the acceleration vector read by the accelerometer.
94///
95/// Have a look at [`Hid::set_accelerometer()`] for more information.
96#[allow(missing_docs)]
97#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
98pub struct Acceleration {
99 x: i16,
100 y: i16,
101 z: i16,
102}
103
104/// Representation of the angular rate read by the gyroscope.
105///
106/// Have a look at [`Hid::set_gyroscope()`] for more information.
107#[allow(missing_docs)]
108#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
109pub struct AngularRate {
110 roll: i16,
111 pitch: i16,
112 yaw: i16,
113}
114
115/// Handle to the HID service.
116pub struct Hid {
117 active_accelerometer: bool,
118 active_gyroscope: bool,
119 _service_handler: ServiceReference,
120}
121
122impl Hid {
123 /// Initialize a new service handle.
124 ///
125 /// # Errors
126 ///
127 /// This function will return an error if the service was unable to be initialized.
128 /// Since this service requires no special or elevated permissions, errors are rare in practice.
129 ///
130 /// # Example
131 ///
132 /// ```
133 /// # let _runner = test_runner::GdbRunner::default();
134 /// # use std::error::Error;
135 /// # fn main() -> Result<(), Box<dyn Error>> {
136 /// #
137 /// use ctru::services::hid::Hid;
138 ///
139 /// let hid = Hid::new()?;
140 /// #
141 /// # Ok(())
142 /// # }
143 /// ```
144 #[doc(alias = "hidInit")]
145 pub fn new() -> crate::Result<Hid> {
146 let handler = ServiceReference::new(
147 &HID_ACTIVE,
148 || {
149 ResultCode(unsafe { ctru_sys::hidInit() })?;
150
151 Ok(())
152 },
153 || unsafe {
154 let _ = ctru_sys::HIDUSER_DisableGyroscope();
155 let _ = ctru_sys::HIDUSER_DisableAccelerometer();
156
157 ctru_sys::hidExit();
158 },
159 )?;
160
161 Ok(Self {
162 active_accelerometer: false,
163 active_gyroscope: false,
164 _service_handler: handler,
165 })
166 }
167
168 /// Scan the HID service for all user input occurring on the current frame.
169 ///
170 /// This function should be called on every frame when polling
171 /// for user input.
172 ///
173 /// # Example
174 ///
175 /// ```
176 /// # let _runner = test_runner::GdbRunner::default();
177 /// # use std::error::Error;
178 /// # fn main() -> Result<(), Box<dyn Error>> {
179 /// #
180 /// use ctru::services::hid::Hid;
181 /// let mut hid = Hid::new()?;
182 ///
183 /// hid.scan_input();
184 /// #
185 /// # Ok(())
186 /// # }
187 /// ```
188 #[doc(alias = "hidScanInput")]
189 pub fn scan_input(&mut self) {
190 unsafe { ctru_sys::hidScanInput() };
191 }
192
193 /// Returns a bitflag struct representing which buttons have just been pressed
194 /// on the current frame (and were not pressed on the previous frame).
195 ///
196 /// # Example
197 ///
198 /// ```
199 /// # let _runner = test_runner::GdbRunner::default();
200 /// # use std::error::Error;
201 /// # fn main() -> Result<(), Box<dyn Error>> {
202 /// #
203 /// use ctru::services::hid::{Hid, KeyPad};
204 /// let mut hid = Hid::new()?;
205 ///
206 /// hid.scan_input();
207 ///
208 /// if hid.keys_down().contains(KeyPad::A) {
209 /// println!("You have pressed the A button!")
210 /// }
211 /// #
212 /// # Ok(())
213 /// # }
214 /// ```
215 #[doc(alias = "hidKeysDown")]
216 pub fn keys_down(&self) -> KeyPad {
217 unsafe {
218 let keys = ctru_sys::hidKeysDown();
219 KeyPad::from_bits_truncate(keys)
220 }
221 }
222
223 /// Returns a bitflag struct representing which buttons have been held down
224 /// during the current frame.
225 ///
226 /// # Example
227 ///
228 /// ```
229 /// # let _runner = test_runner::GdbRunner::default();
230 /// # use std::error::Error;
231 /// # fn main() -> Result<(), Box<dyn Error>> {
232 /// #
233 /// use ctru::services::hid::{Hid, KeyPad};
234 /// let mut hid = Hid::new()?;
235 ///
236 /// hid.scan_input();
237 ///
238 /// if hid.keys_held().contains(KeyPad::START) {
239 /// println!("You are holding the START button!")
240 /// }
241 /// #
242 /// # Ok(())
243 /// # }
244 /// ```
245 #[doc(alias = "hidKeysHeld")]
246 pub fn keys_held(&self) -> KeyPad {
247 unsafe {
248 let keys = ctru_sys::hidKeysHeld();
249 KeyPad::from_bits_truncate(keys)
250 }
251 }
252
253 /// Returns a bitflag struct representing which buttons have just been released on
254 /// the current frame.
255 ///
256 /// # Example
257 ///
258 /// ```
259 /// # let _runner = test_runner::GdbRunner::default();
260 /// # use std::error::Error;
261 /// # fn main() -> Result<(), Box<dyn Error>> {
262 /// #
263 /// use ctru::services::hid::{Hid, KeyPad};
264 /// let mut hid = Hid::new()?;
265 ///
266 /// hid.scan_input();
267 ///
268 /// if hid.keys_held().contains(KeyPad::B) {
269 /// println!("You have released the B button!")
270 /// }
271 /// #
272 /// # Ok(())
273 /// # }
274 /// ```
275 #[doc(alias = "hidKeysUp")]
276 pub fn keys_up(&self) -> KeyPad {
277 unsafe {
278 let keys = ctru_sys::hidKeysUp();
279 KeyPad::from_bits_truncate(keys)
280 }
281 }
282
283 /// Returns the current touch position in pixels (x, y).
284 ///
285 /// # Notes
286 ///
287 /// (0, 0) represents the top left corner of the screen.
288 ///
289 /// # Example
290 ///
291 /// ```
292 /// # let _runner = test_runner::GdbRunner::default();
293 /// # use std::error::Error;
294 /// # fn main() -> Result<(), Box<dyn Error>> {
295 /// #
296 /// use ctru::services::hid::Hid;
297 /// let mut hid = Hid::new()?;
298 ///
299 /// hid.scan_input();
300 ///
301 /// let (touch_x, touch_y) = hid.touch_position();
302 /// #
303 /// # Ok(())
304 /// # }
305 /// ```
306 #[doc(alias = "hidTouchRead")]
307 pub fn touch_position(&self) -> (u16, u16) {
308 let mut res = ctru_sys::touchPosition { px: 0, py: 0 };
309
310 unsafe {
311 ctru_sys::hidTouchRead(&mut res);
312 }
313
314 (res.px, res.py)
315 }
316
317 /// Returns the current circle pad position in relative (x, y).
318 ///
319 /// # Notes
320 ///
321 /// (0, 0) represents the center of the circle pad.
322 ///
323 /// # Example
324 ///
325 /// ```
326 /// # let _runner = test_runner::GdbRunner::default();
327 /// # use std::error::Error;
328 /// # fn main() -> Result<(), Box<dyn Error>> {
329 /// #
330 /// use ctru::services::hid::Hid;
331 /// let mut hid = Hid::new()?;
332 ///
333 /// hid.scan_input();
334 ///
335 /// let (pad_x, pad_y) = hid.circlepad_position();
336 /// #
337 /// # Ok(())
338 /// # }
339 /// ```
340 #[doc(alias = "hidCircleRead")]
341 pub fn circlepad_position(&self) -> (i16, i16) {
342 let mut res = ctru_sys::circlePosition { dx: 0, dy: 0 };
343
344 unsafe {
345 ctru_sys::hidCircleRead(&mut res);
346 }
347
348 (res.dx, res.dy)
349 }
350
351 /// Returns the current volume slider position (between 0 and 1).
352 ///
353 /// # Notes
354 ///
355 /// The [`ndsp`](crate::services::ndsp) service automatically uses the volume slider's position to handle audio mixing.
356 /// As such this method should not be used to programmatically change the volume.
357 ///
358 /// Its purpose is only to inform the program of the volume slider's position (e.g. checking if the user has muted the audio).
359 ///
360 /// # Example
361 ///
362 /// ```
363 /// # use std::error::Error;
364 /// # fn main() -> Result<(), Box<dyn Error>> {
365 /// #
366 /// use ctru::services::hid::Hid;
367 /// let mut hid = Hid::new()?;
368 ///
369 /// hid.scan_input();
370 ///
371 /// let volume = hid.volume_slider();
372 /// #
373 /// # Ok(())
374 /// # }
375 /// ```
376 #[doc(alias = "HIDUSER_GetSoundVolume")]
377 pub fn volume_slider(&self) -> f32 {
378 let mut slider = 0;
379
380 unsafe {
381 let _ = ctru_sys::HIDUSER_GetSoundVolume(&mut slider);
382 }
383
384 (slider as f32) / 63.
385 }
386
387 /// Activate/deactivate the console's acceleration sensor.
388 ///
389 /// # Example
390 ///
391 /// ```
392 /// # use std::error::Error;
393 /// # fn main() -> Result<(), Box<dyn Error>> {
394 /// #
395 /// use ctru::services::hid::Hid;
396 /// let mut hid = Hid::new()?;
397 ///
398 /// // The accelerometer will start to register movements.
399 /// hid.set_accelerometer(true).unwrap();
400 /// #
401 /// # Ok(())
402 /// # }
403 /// ```
404 #[doc(alias = "HIDUSER_EnableAccelerometer")]
405 #[doc(alias = "HIDUSER_DisableAccelerometer")]
406 pub fn set_accelerometer(&mut self, enabled: bool) -> crate::Result<()> {
407 if enabled {
408 ResultCode(unsafe { ctru_sys::HIDUSER_EnableAccelerometer() })?;
409 } else {
410 ResultCode(unsafe { ctru_sys::HIDUSER_DisableAccelerometer() })?;
411 }
412
413 self.active_accelerometer = enabled;
414
415 Ok(())
416 }
417
418 /// Activate/deactivate the console's gyroscopic sensor.
419 ///
420 /// # Example
421 ///
422 /// ```
423 /// # use std::error::Error;
424 /// # fn main() -> Result<(), Box<dyn Error>> {
425 /// #
426 /// use ctru::services::hid::Hid;
427 /// let mut hid = Hid::new()?;
428 ///
429 /// // The gyroscope will start to register positions.
430 /// hid.set_gyroscope(true).unwrap();
431 /// #
432 /// # Ok(())
433 /// # }
434 /// ```
435 #[doc(alias = "HIDUSER_EnableGyroscope")]
436 #[doc(alias = "HIDUSER_DisableGyroscope")]
437 pub fn set_gyroscope(&mut self, enabled: bool) -> crate::Result<()> {
438 if enabled {
439 ResultCode(unsafe { ctru_sys::HIDUSER_EnableGyroscope() })?;
440 } else {
441 ResultCode(unsafe { ctru_sys::HIDUSER_DisableGyroscope() })?;
442 }
443
444 self.active_gyroscope = enabled;
445
446 Ok(())
447 }
448
449 /// Returns the acceleration vector (x, y, z) registered by the accelerometer.
450 ///
451 /// # Errors
452 ///
453 /// This function will return an error if the accelerometer was not previously enabled.
454 /// Have a look at [`Hid::set_accelerometer()`] to enable the accelerometer.
455 ///
456 /// # Example
457 ///
458 /// ```
459 /// # use std::error::Error;
460 /// # fn main() -> Result<(), Box<dyn Error>> {
461 /// #
462 /// use ctru::services::hid::Hid;
463 /// let mut hid = Hid::new()?;
464 ///
465 /// // The accelerometer will start to register movements.
466 /// hid.set_accelerometer(true).unwrap();
467 ///
468 /// // It's necessary to run `scan_input()` to update the accelerometer's readings.
469 /// hid.scan_input();
470 ///
471 /// // This call fails if the accelerometer was not previously enabled.
472 /// let acceleration = hid.accelerometer_vector()?;
473 /// #
474 /// # Ok(())
475 /// # }
476 /// ```
477 #[doc(alias = "hidAccelRead")]
478 pub fn accelerometer_vector(&self) -> Result<Acceleration, Error> {
479 if !self.active_accelerometer {
480 return Err(Error::UnavailableAccelerometer);
481 }
482
483 let mut res = ctru_sys::accelVector { x: 0, y: 0, z: 0 };
484
485 unsafe {
486 ctru_sys::hidAccelRead(&mut res);
487 }
488
489 Ok(Acceleration {
490 x: res.x,
491 y: res.y,
492 z: res.z,
493 })
494 }
495
496 /// Returns the angular rate registered by the gyroscope.
497 ///
498 /// # Errors
499 ///
500 /// This function returns an error if the gyroscope was not previously enabled.
501 /// Have a look at [`Hid::set_gyroscope()`].
502 ///
503 /// # Example
504 ///
505 /// ```
506 /// # use std::error::Error;
507 /// # fn main() -> Result<(), Box<dyn Error>> {
508 /// #
509 /// use ctru::services::hid::Hid;
510 /// let mut hid = Hid::new()?;
511 ///
512 /// // The gyroscope will start to register positions.
513 /// hid.set_gyroscope(true).unwrap();
514 ///
515 /// // It's necessary to run `scan_input()` to update the gyroscope's readings.
516 /// hid.scan_input();
517 ///
518 /// // This call fails if the gyroscope was not previously enabled.
519 /// let angular_rate = hid.gyroscope_rate()?;
520 /// #
521 /// # Ok(())
522 /// # }
523 /// ```
524 #[doc(alias = "hidGyroRead")]
525 pub fn gyroscope_rate(&self) -> Result<AngularRate, Error> {
526 if !self.active_gyroscope {
527 return Err(Error::UnavailableGyroscope);
528 }
529
530 let mut res = ctru_sys::angularRate { x: 0, y: 0, z: 0 };
531
532 unsafe {
533 ctru_sys::hidGyroRead(&mut res);
534 }
535
536 Ok(AngularRate {
537 roll: res.x,
538 pitch: res.y,
539 yaw: res.z,
540 })
541 }
542}
543
544impl From<Acceleration> for (i16, i16, i16) {
545 fn from(value: Acceleration) -> (i16, i16, i16) {
546 (value.x, value.y, value.z)
547 }
548}
549
550impl From<AngularRate> for (i16, i16, i16) {
551 fn from(value: AngularRate) -> (i16, i16, i16) {
552 (value.roll, value.pitch, value.yaw)
553 }
554}
555
556impl std::fmt::Display for Error {
557 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558 match self {
559 Self::UnavailableAccelerometer => write!(f, "tried using accelerometer while disabled"),
560 Self::UnavailableGyroscope => write!(f, "tried using gyroscope while disabled"),
561 }
562 }
563}
564
565impl std::error::Error for Error {}