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 {}