Browse Source

Restructure ServiceReference and wip Hid

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
1a704b0006
  1. 9
      ctru-rs/examples/buttons.rs
  2. 46
      ctru-rs/examples/movement.rs
  3. 15
      ctru-rs/src/console.rs
  4. 23
      ctru-rs/src/lib.rs
  5. 3
      ctru-rs/src/services/gfx.rs
  6. 159
      ctru-rs/src/services/hid.rs
  7. 3
      ctru-rs/src/services/ndsp/mod.rs
  8. 43
      ctru-rs/src/services/reference.rs
  9. 3
      ctru-rs/src/services/romfs.rs
  10. 3
      ctru-rs/src/services/soc.rs

9
ctru-rs/examples/buttons.rs

@ -25,6 +25,13 @@ fn main() { @@ -25,6 +25,13 @@ fn main() {
// Get information about which keys were held down on this frame.
let keys = hid.keys_held();
// Print the status of the 2 sliders.
println!(
"\x1b[20;0HVolume slider: {} ",
hid.slider_volume()
);
println!("\x1b[21;0H3D slider: {} ", hid.slider_3d());
// We only want to print when the keys we're holding now are different
// from what they were on the previous frame.
if keys != old_keys {
@ -44,7 +51,7 @@ fn main() { @@ -44,7 +51,7 @@ fn main() {
// and the `.intersects()` method checks for any of the provided keys.
//
// You can also use the `.bits()` method to do direct comparisons on
// the underlying bits
// the underlying bits.
if keys.contains(KeyPad::A) {
println!("You held A!");

46
ctru-rs/examples/movement.rs

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
//! Movement example.
//!
//! Simple application to showcase the use of the accellerometer and gyroscope.
use ctru::prelude::*;
fn main() {
ctru::use_panic_handler();
let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut());
println!("Move the console around!");
println!("\x1b[29;16HPress Start to exit");
// Activate the accelerometer and the gyroscope.
// Because of the complex nature of the movement sensors, they aren't activated by default with the `Hid` service.
// However, they can simply be turned on and off whenever necessary.
hid.enable_accellerometer();
hid.enable_gyroscope();
while apt.main_loop() {
// Scan all the controller inputs.
// Accellerometer and gyroscope require this step to update the readings.
hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
// Be careful: reading without activating the sensors (as done before this loop) will result in a panic.
println!(
"\x1b[3;0HAccelleration: {:?} ",
hid.accellerometer_vector()
);
println!(
"\x1b[4;0HGyroscope angular rate: {:?} ",
hid.gyroscope_rate()
);
gfx.wait_for_vblank();
}
}

15
ctru-rs/src/console.rs

@ -76,10 +76,7 @@ impl<'screen> Console<'screen> { @@ -76,10 +76,7 @@ impl<'screen> Console<'screen> {
unsafe { consoleInit(screen.as_raw(), context.as_mut()) };
Console {
context,
screen,
}
Console { context, screen }
}
/// Returns `true` if a valid [`Console`] to print on is currently selected.
@ -219,7 +216,15 @@ impl<'screen> Console<'screen> { @@ -219,7 +216,15 @@ impl<'screen> Console<'screen> {
panic!("height of new console window out of bounds");
}
unsafe { consoleSetWindow(self.context.as_mut(), x.into(), y.into(), width.into(), height.into()) };
unsafe {
consoleSetWindow(
self.context.as_mut(),
x.into(),
y.into(),
width.into(),
height.into(),
)
};
}
/// Reset the window's size to default parameters.

23
ctru-rs/src/lib.rs

@ -87,15 +87,24 @@ fn panic_hook_setup() { @@ -87,15 +87,24 @@ fn panic_hook_setup() {
if main_thread == std::thread::current().id() && console::Console::exists() {
println!("\nPress SELECT to exit the software");
match Hid::new() {
Ok(mut hid) => loop {
hid.scan_input();
let keys = hid.keys_down();
if keys.contains(KeyPad::SELECT) {
// Due to how the Hid service operates, we can't safely use 2 handles to it at the same time.
// Furthermore, the panic hook runs before the panic cleanup is done, which means that any other handles
// to the service will still be alive during this process.
// Regardless, we can "unsafely" spin up a new instance, since the module won't be used any further from the main process,
// which is going to get cleaned up right after this loop.
unsafe {
let _ = ctru_sys::hidInit();
loop {
ctru_sys::hidScanInput();
let keys = ctru_sys::hidKeysDown();
if KeyPad::from_bits_truncate(keys).contains(KeyPad::SELECT) {
break;
}
},
Err(e) => println!("Error while intializing Hid controller during panic: {e}"),
}
ctru_sys::hidExit();
}
}
});

3
ctru-rs/src/services/gfx.rs

@ -244,7 +244,7 @@ pub struct Gfx { @@ -244,7 +244,7 @@ pub struct Gfx {
_service_handler: ServiceReference,
}
static GFX_ACTIVE: Mutex<usize> = Mutex::new(0);
static GFX_ACTIVE: Mutex<()> = Mutex::new(());
impl Gfx {
/// Initialize a new default service handle.
@ -311,7 +311,6 @@ impl Gfx { @@ -311,7 +311,6 @@ impl Gfx {
) -> Result<Self> {
let handler = ServiceReference::new(
&GFX_ACTIVE,
false,
|| unsafe {
ctru_sys::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), use_vram_buffers);

159
ctru-rs/src/services/hid.rs

@ -1,15 +1,21 @@ @@ -1,15 +1,21 @@
//! Human Interface Device service.
//!
//! The HID service provides read access to user input such as [button presses](Hid::keys_down), [touch screen presses](Hid::touch_position),
//! and [circle pad information](Hid::circlepad_position). It also provides information from the sound volume slider, the accelerometer, and the gyroscope.
// TODO: Implement volume slider, accelerometer and gyroscope + any other missing functionality.
//! and [circle pad information](Hid::circlepad_position). It also provides information from the [3D slider](Hid::slider_3d()), the [volume slider](Hid::slider_volume()),
//! the [accelerometer](Hid::accellerometer_vector()), and the [gyroscope](Hid::gyroscope_rate()).
#![doc(alias = "input")]
#![doc(alias = "controller")]
#![doc(alias = "gamepad")]
use std::sync::Mutex;
use crate::error::ResultCode;
use crate::services::ServiceReference;
use bitflags::bitflags;
static HID_ACTIVE: Mutex<()> = Mutex::new(());
bitflags! {
/// A set of flags corresponding to the button and directional pad inputs present on the 3DS.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
@ -75,7 +81,11 @@ bitflags! { @@ -75,7 +81,11 @@ bitflags! {
}
/// Handle to the HID service.
pub struct Hid(());
pub struct Hid {
active_accellerometer: bool,
active_gyroscope: bool,
_service_handler: ServiceReference,
}
impl Hid {
/// Initialize a new service handle.
@ -100,10 +110,26 @@ impl Hid { @@ -100,10 +110,26 @@ impl Hid {
/// ```
#[doc(alias = "hidInit")]
pub fn new() -> crate::Result<Hid> {
unsafe {
ResultCode(ctru_sys::hidInit())?;
Ok(Hid(()))
}
let handler = ServiceReference::new(
&HID_ACTIVE,
|| {
ResultCode(unsafe { ctru_sys::hidInit() })?;
Ok(())
},
|| unsafe {
let _ = ctru_sys::HIDUSER_DisableGyroscope();
let _ = ctru_sys::HIDUSER_DisableAccelerometer();
ctru_sys::hidExit();
},
)?;
Ok(Self {
active_accellerometer: false,
active_gyroscope: false,
_service_handler: handler,
})
}
/// Scan the HID service for all user input occurring on the current frame.
@ -282,11 +308,122 @@ impl Hid { @@ -282,11 +308,122 @@ impl Hid {
(res.dx, res.dy)
}
/// Returns the current volume slider position (between 0 and 1).
///
/// # Notes
///
/// The [`ndsp`](crate::services::ndsp) service automatically uses the volume slider's position to handle audio mixing.
/// As such this method should not be used to programmatically change the volume.
///
/// Its purpose is only to inform the program of the volume slider's position (e.g. checking if the user has muted the audio).
///
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::hid::Hid;
/// let mut hid = Hid::new()?;
///
/// hid.scan_input();
///
/// let volume = hid.slider_volume();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "HIDUSER_GetSoundVolume")]
pub fn slider_volume(&self) -> f32 {
let mut slider = 0;
unsafe {
let _ = ctru_sys::HIDUSER_GetSoundVolume(&mut slider);
}
(slider as f32) / 63.
}
/// Returns the current 3D slider position (between 0 and 1).
///
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::hid::Hid;
/// let mut hid = Hid::new()?;
///
/// hid.scan_input();
///
/// let volume = hid.volume_slider();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "osGet3DSliderState")]
pub fn slider_3d(&self) -> f32 {
// TODO: Replace with the static inline function `osGet3DSliderState`, which works the exact same way.
unsafe { (*(ctru_sys::OS_SHAREDCFG_VADDR as *mut ctru_sys::osSharedConfig_s)).slider_3d }
}
#[doc(alias = "HIDUSER_EnableAccelerometer")]
pub fn enable_accellerometer(&mut self) {
let _ = unsafe { ctru_sys::HIDUSER_EnableAccelerometer() };
self.active_accellerometer = true;
}
#[doc(alias = "HIDUSER_EnableGyroscope")]
pub fn enable_gyroscope(&mut self) {
let _ = unsafe { ctru_sys::HIDUSER_EnableGyroscope() };
self.active_gyroscope = true;
}
#[doc(alias = "HIDUSER_DisableAccelerometer")]
pub fn disable_accellerometer(&mut self) {
let _ = unsafe { ctru_sys::HIDUSER_DisableAccelerometer() };
self.active_accellerometer = false;
}
#[doc(alias = "HIDUSER_DisableGyroscope")]
pub fn disable_gyroscope(&mut self) {
let _ = unsafe { ctru_sys::HIDUSER_DisableGyroscope() };
self.active_gyroscope = false;
}
#[doc(alias = "hidAccelRead")]
pub fn accellerometer_vector(&self) -> (i16, i16, i16) {
if !self.active_accellerometer {
panic!("tried to read accellerometer while disabled")
}
let mut res = ctru_sys::accelVector { x: 0, y: 0, z: 0 };
unsafe {
ctru_sys::hidAccelRead(&mut res);
}
(res.x, res.y, res.z)
}
#[doc(alias = "hidGyroRead")]
pub fn gyroscope_rate(&self) -> (i16, i16, i16) {
if !self.active_gyroscope {
panic!("tried to read accellerometer while disabled")
}
let mut res = ctru_sys::angularRate { x: 0, y: 0, z: 0 };
unsafe {
ctru_sys::hidGyroRead(&mut res);
}
impl Drop for Hid {
#[doc(alias = "hidExit")]
fn drop(&mut self) {
unsafe { ctru_sys::hidExit() };
(res.x, res.y, res.z)
}
}

3
ctru-rs/src/services/ndsp/mod.rs

@ -98,7 +98,7 @@ pub struct Channel<'ndsp> { @@ -98,7 +98,7 @@ pub struct Channel<'ndsp> {
_rf: RefMut<'ndsp, ()>, // we don't need to hold any data
}
static NDSP_ACTIVE: Mutex<usize> = Mutex::new(0);
static NDSP_ACTIVE: Mutex<()> = Mutex::new(());
/// Handle to the DSP service.
///
@ -133,7 +133,6 @@ impl Ndsp { @@ -133,7 +133,6 @@ impl Ndsp {
pub fn new() -> crate::Result<Self> {
let _service_handler = ServiceReference::new(
&NDSP_ACTIVE,
false,
|| {
ResultCode(unsafe { ctru_sys::ndspInit() })?;

43
ctru-rs/src/services/reference.rs

@ -1,35 +1,37 @@ @@ -1,35 +1,37 @@
use crate::Error;
use std::sync::Mutex;
use std::sync::{Mutex, MutexGuard, TryLockError};
pub(crate) struct ServiceReference {
counter: &'static Mutex<usize>,
_guard: MutexGuard<'static, ()>,
close: Box<dyn Fn() + Send + Sync>,
}
impl ServiceReference {
pub fn new<S, E>(
counter: &'static Mutex<usize>,
allow_multiple: bool,
start: S,
close: E,
) -> crate::Result<Self>
pub fn new<S, E>(counter: &'static Mutex<()>, start: S, close: E) -> crate::Result<Self>
where
S: FnOnce() -> crate::Result<()>,
E: Fn() + Send + Sync + 'static,
{
let mut value = counter
.lock()
.expect("Mutex Counter for ServiceReference is poisoned"); // todo: handle poisoning
let _guard = match counter.try_lock() {
Ok(lock) => lock,
Err(e) => match e {
TryLockError::Poisoned(guard) => {
// If the MutexGuard is poisoned that means that the "other" service instance (of which the thread panicked)
// was NOT properly closed. To avoid any weird behaviour, we try closing the service now, to then re-open a fresh instance.
//
// It's up to our `close()` implementations to avoid panicking/doing weird stuff again.
close();
if *value == 0 {
start()?;
} else if !allow_multiple {
return Err(Error::ServiceAlreadyActive);
guard.into_inner()
}
TryLockError::WouldBlock => return Err(Error::ServiceAlreadyActive),
},
};
*value += 1;
start()?;
Ok(Self {
counter,
_guard,
close: Box::new(close),
})
}
@ -37,13 +39,6 @@ impl ServiceReference { @@ -37,13 +39,6 @@ impl ServiceReference {
impl Drop for ServiceReference {
fn drop(&mut self) {
let mut value = self
.counter
.lock()
.expect("Mutex Counter for ServiceReference is poisoned"); // todo: handle poisoning
*value -= 1;
if *value == 0 {
(self.close)();
}
}
}

3
ctru-rs/src/services/romfs.rs

@ -38,7 +38,7 @@ pub struct RomFS { @@ -38,7 +38,7 @@ pub struct RomFS {
_service_handler: ServiceReference,
}
static ROMFS_ACTIVE: Mutex<usize> = Mutex::new(0);
static ROMFS_ACTIVE: Mutex<()> = Mutex::new(());
impl RomFS {
/// Mount the bundled RomFS archive as a virtual drive.
@ -63,7 +63,6 @@ impl RomFS { @@ -63,7 +63,6 @@ impl RomFS {
pub fn new() -> crate::Result<Self> {
let _service_handler = ServiceReference::new(
&ROMFS_ACTIVE,
true,
|| {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
ResultCode(unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) })?;

3
ctru-rs/src/services/soc.rs

@ -19,7 +19,7 @@ pub struct Soc { @@ -19,7 +19,7 @@ pub struct Soc {
sock_3dslink: libc::c_int,
}
static SOC_ACTIVE: Mutex<usize> = Mutex::new(0);
static SOC_ACTIVE: Mutex<()> = Mutex::new(());
impl Soc {
/// Initialize a new service handle using a socket buffer size of `0x100000` bytes.
@ -71,7 +71,6 @@ impl Soc { @@ -71,7 +71,6 @@ impl Soc {
pub fn init_with_buffer_size(num_bytes: usize) -> crate::Result<Self> {
let _service_handler = ServiceReference::new(
&SOC_ACTIVE,
false,
|| {
let soc_mem = unsafe { memalign(0x1000, num_bytes) } as *mut u32;
ResultCode(unsafe { ctru_sys::socInit(soc_mem, num_bytes as u32) })?;

Loading…
Cancel
Save