diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 83ebcf9..69b2db1 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -4,7 +4,7 @@ description = "A safe wrapper around smealum's ctrulib." license = "https://en.wikipedia.org/wiki/Zlib_License" name = "ctru-rs" links = "ctru" -version = "0.5.1" +version = "0.6.0" [lib] crate-type = ["rlib"] @@ -15,7 +15,7 @@ path = "../ctru-sys" version = "0.4" [dependencies.bitflags] -version = "0.7.0" +version = "0.9.0" [dependencies.widestring] version = "0.2.2" diff --git a/ctru-rs/src/services/fs.rs b/ctru-rs/src/services/fs.rs index 1db0674..0a0e2e4 100644 --- a/ctru-rs/src/services/fs.rs +++ b/ctru-rs/src/services/fs.rs @@ -18,26 +18,29 @@ use std::sync::Arc; use widestring::{WideCString, WideCStr}; bitflags! { - flags FsOpen: u32 { - const FS_OPEN_READ = 1, - const FS_OPEN_WRITE = 2, - const FS_OPEN_CREATE = 4, + #[derive(Default)] + struct FsOpen: u32 { + const FS_OPEN_READ = 1; + const FS_OPEN_WRITE = 2; + const FS_OPEN_CREATE = 4; } } bitflags! { - flags FsWrite: u32 { - const FS_WRITE_FLUSH = 1, - const FS_WRITE_UPDATE_TIME = 256, + #[derive(Default)] + struct FsWrite: u32 { + const FS_WRITE_FLUSH = 1; + const FS_WRITE_UPDATE_TIME = 256; } } bitflags! { - flags FsAttribute: u32 { - const FS_ATTRIBUTE_DIRECTORY = 1, - const FS_ATTRIBUTE_HIDDEN = 256, - const FS_ATTRIBUTE_ARCHIVE = 65536, - const FS_ATTRIBUTE_READ_ONLY = 16777216, + #[derive(Default)] + struct FsAttribute: u32 { + const FS_ATTRIBUTE_DIRECTORY = 1; + const FS_ATTRIBUTE_HIDDEN = 256; + const FS_ATTRIBUTE_ARCHIVE = 65536; + const FS_ATTRIBUTE_READ_ONLY = 16777216; } } @@ -467,7 +470,7 @@ impl File { impl Metadata { /// Returns whether this metadata is for a directory. pub fn is_dir(&self) -> bool { - self.attributes == self.attributes | FS_ATTRIBUTE_DIRECTORY.bits + self.attributes == self.attributes | FS_ATTRIBUTE_DIRECTORY.bits() } /// Returns whether this metadata is for a regular file. @@ -586,7 +589,7 @@ impl OpenOptions { let path = to_utf16(path); let fs_path = ::libctru::fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = ::libctru::FSUSER_OpenFile(&mut file_handle, self.arch_handle, - fs_path, flags.bits, 0); + fs_path, flags.bits(), 0); if r < 0 { return Err(IoError::new(IoErrorKind::Other, ::Error::from(r))); } @@ -685,7 +688,7 @@ pub fn create_dir>(arch: &Archive, path: P) -> IoResult<()> { let path = to_utf16(path.as_ref()); let fs_path = ::libctru::fsMakePath(PathType::UTF16.into(), path.as_ptr() as _); let r = ::libctru::FSUSER_CreateDirectory(arch.handle, fs_path, - FS_ATTRIBUTE_DIRECTORY.bits); + FS_ATTRIBUTE_DIRECTORY.bits()); if r < 0 { Err(IoError::new(IoErrorKind::Other, ::Error::from(r))) } else { @@ -722,7 +725,7 @@ pub fn metadata>(arch: &Archive, path: P) -> IoResult { let maybe_dir = read_dir(&arch, path.as_ref()); match (maybe_file, maybe_dir) { (Ok(file), _) => file.metadata(), - (_, Ok(_dir)) => Ok(Metadata { attributes: FS_ATTRIBUTE_DIRECTORY.bits, size: 0 }), + (_, Ok(_dir)) => Ok(Metadata { attributes: FS_ATTRIBUTE_DIRECTORY.bits(), size: 0 }), (Err(e), _) => Err(e), } } diff --git a/ctru-rs/src/services/hid.rs b/ctru-rs/src/services/hid.rs index bcf6d1f..b70d890 100644 --- a/ctru-rs/src/services/hid.rs +++ b/ctru-rs/src/services/hid.rs @@ -1,76 +1,65 @@ -use std::convert::Into; +//! HID service +//! +//! The HID service provides access to user input such as button presses, touch screen presses, +//! and circle pad information. It also provides information from the sound volume slider, +//! the accelerometer, and the gyroscope. -pub enum PadKey { - A, - B, - Select, - Start, - DPadRight, - DPadLeft, - DPadUp, - DPadDown, - R, - L, - X, - Y, - ZL, - ZR, - Touch, - CSRight, - CSLeft, - CSUp, - CSDown, - CRight, - CLeft, - CUp, - CDown, - // convenience catch-all for dpad and cpad - Up, - Down, - Left, - Right, -} - -impl From for u32 { - fn from(p: PadKey) -> u32 { - use self::PadKey::*; - use ::libctru::_bindgen_ty_18::*; - match p { - Up => KEY_DUP as u32 | KEY_CPAD_UP as u32, - Down => KEY_DDOWN as u32 | KEY_CPAD_DOWN as u32, - Left => KEY_DLEFT as u32 | KEY_CPAD_LEFT as u32, - Right => KEY_DRIGHT as u32 | KEY_CPAD_RIGHT as u32, - - A => KEY_A as u32, - B => KEY_B as u32, - X => KEY_X as u32, - Y => KEY_Y as u32, - L => KEY_L as u32, - R => KEY_R as u32, - ZL => KEY_ZL as u32, - ZR => KEY_ZR as u32, - Start => KEY_START as u32, - Select => KEY_SELECT as u32, - Touch => KEY_TOUCH as u32, - CSRight => KEY_CSTICK_RIGHT as u32, - CSLeft => KEY_CSTICK_LEFT as u32, - CSUp => KEY_CSTICK_UP as u32, - CSDown => KEY_CSTICK_DOWN as u32, - CRight => KEY_CPAD_RIGHT as u32, - CLeft => KEY_CPAD_LEFT as u32, - CDown => KEY_CPAD_DOWN as u32, - CUp => KEY_CPAD_UP as u32, - DPadLeft => KEY_DLEFT as u32, - DPadRight => KEY_DRIGHT as u32, - DPadUp => KEY_DUP as u32, - DPadDown => KEY_DDOWN as u32, - } +/// A set of flags corresponding to the button and directional pad +/// inputs on the 3DS +bitflags! { + #[derive(Default)] + pub struct KeyPad: u32 { + const KEY_A = 1u32 << 0; + const KEY_B = 1u32 << 1; + const KEY_SELECT = 1u32 << 2; + const KEY_START = 1u32 << 3; + const KEY_DRIGHT = 1u32 << 4; + const KEY_DLEFT = 1u32 << 5; + const KEY_DUP = 1u32 << 6; + const KEY_DDOWN = 1u32 << 7; + const KEY_R = 1u32 << 8; + const KEY_L = 1u32 << 9; + const KEY_X = 1u32 << 10; + const KEY_Y = 1u32 << 11; + const KEY_ZL = 1u32 << 14; + const KEY_ZR = 1u32 << 15; + const KEY_TOUCH = 1u32 << 20; + const KEY_CSTICK_RIGHT = 1u32 << 24; + const KEY_CSTICK_LEFT = 1u32 << 25; + const KEY_CSTICK_UP = 1u32 << 26; + const KEY_CSTICK_DOWN = 1u32 << 27; + const KEY_CPAD_RIGHT = 1u32 << 28; + const KEY_CPAD_LEFT = 1u32 << 29; + const KEY_CPAD_UP = 1u32 << 30; + const KEY_CPAD_DOWN = 1u32 << 31; + // convenience catch-all for the dpad and cpad + const KEY_UP = KEY_DUP.bits | KEY_CPAD_UP.bits; + const KEY_DOWN = KEY_DDOWN.bits | KEY_CPAD_DOWN.bits; + const KEY_LEFT = KEY_DLEFT.bits | KEY_CPAD_LEFT.bits; + const KEY_RIGHT = KEY_DRIGHT.bits | KEY_CPAD_RIGHT.bits; } } +/// A reference-counted handle to the HID service. The service is closed +/// when all instances of this struct fall out of scope. +/// +/// This service requires no special permissions to use. pub struct Hid(()); +/// Represents user input to the touchscreen. +pub struct TouchPosition(::libctru::touchPosition); + +/// Represents the current position of the 3DS circle pad. +pub struct CirclePosition(::libctru::circlePosition); + +/// Initializes the HID service. +/// +/// # Errors +/// +/// This function will return an error if the service was unable to be initialized. +/// Since this service requires no special or elevated permissions, errors are +/// rare in practice. impl Hid { pub fn init() -> ::Result { unsafe { @@ -83,44 +72,67 @@ impl Hid { } } + /// Scans the HID service for all user input occurring on the current + /// frame. This function should be called on every frame when polling + /// for user input. pub fn scan_input(&self) { unsafe { ::libctru::hidScanInput() }; } - pub fn key_down(&self, key: PadKey) -> bool { - let k: u32 = key.into(); + /// Returns a bitflag struct representing which buttons have just been pressed + /// on the current frame (and were not pressed on the previous frame). + pub fn keys_down(&self) -> KeyPad { unsafe { - if ::libctru::hidKeysDown() & k != 0 { - true - } else { - false - } + let keys = ::libctru::hidKeysDown(); + KeyPad::from_bits_truncate(keys) } } - pub fn key_held(&self, key: PadKey) -> bool { - let k: u32 = key.into(); + /// Returns a bitflag struct representing which buttons have been held down + /// during the current frame. + pub fn keys_held(&self) -> KeyPad { unsafe { - if ::libctru::hidKeysHeld() & k != 0 { - true - } else { - false - } + let keys = ::libctru::hidKeysHeld(); + KeyPad::from_bits_truncate(keys) } } - pub fn key_up(&self, key: PadKey) -> bool { - let k: u32 = key.into(); + /// Returns a bitflag struct representing which buttons have just been released on + /// the current frame. + pub fn keys_up(&self) -> KeyPad { unsafe { - if ::libctru::hidKeysUp() & k != 0 { - return true; - } else { - return false; - } + let keys = ::libctru::hidKeysUp(); + KeyPad::from_bits_truncate(keys) } } } +impl TouchPosition { + /// Create a new TouchPosition instance. + pub fn new() -> Self { + TouchPosition(::libctru::touchPosition { px: 0, py: 0 }) + } + + /// Returns the current touch position in pixels. + pub fn get(&mut self) -> (u16, u16) { + unsafe { ::libctru::hidTouchRead(&mut self.0); } + (self.0.px, self.0.py) + } +} + +impl CirclePosition { + /// Create a new CirclePosition instance. + pub fn new() -> Self { + CirclePosition(::libctru::circlePosition { dx: 0, dy: 0 }) + } + + /// Returns the current circle pad position in (x, y) form. + pub fn get(&mut self) -> (i16, i16) { + unsafe { ::libctru::hidCircleRead(&mut self.0); } + (self.0.dx, self.0.dy) + } +} + impl Drop for Hid { fn drop(&mut self) { unsafe { ::libctru::hidExit() }; diff --git a/examples/src/bin/buttons.rs b/examples/src/bin/buttons.rs new file mode 100644 index 0000000..00dd33e --- /dev/null +++ b/examples/src/bin/buttons.rs @@ -0,0 +1,75 @@ +extern crate ctru; + +use ctru::gfx::Gfx; +use ctru::console::Console; +use ctru::services::apt::Apt; +use ctru::services::hid::{self, Hid, KeyPad}; + +fn main() { + // Setup services + let apt = Apt::init().unwrap(); + let hid = Hid::init().unwrap(); + let mut gfx = Gfx::default(); + let mut console = Console::default(); + + println!("Hi there! Try pressing a button"); + println!("\x1b[29;16HPress Start to exit"); + + // This struct will contain the keys that we held on the previous frame + let mut old_keys = KeyPad::empty(); + + while apt.main_loop() { + // Scan for user input on the current frame. + hid.scan_input(); + + // Get information about which keys were held down on this frame + let keys = hid.keys_held(); + + // 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 { + + // Clear the screen + console.clear(); + + // We print these again because we just cleared the screen above + println!("Hi there! Try pressing a button"); + println!("\x1b[29;16HPress Start to exit"); + + // Move the cursor back to the top of the screen + println!("\x1b[3;0H"); + + // Print to the screen depending on which keys were held. + // + // The .contains() method checks for all of the provided keys, + // 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 + + if keys.contains(hid::KEY_A) { + println!("You held A!"); + } + if keys.bits() & hid::KEY_B.bits() != 0 { + println!("You held B!"); + } + if keys.contains(hid::KEY_X | hid::KEY_Y) { + println!("You held X and Y!"); + } + if keys.intersects(hid::KEY_L | hid::KEY_R | hid::KEY_ZL | hid::KEY_ZR) { + println!("You held a shoulder button!"); + } + if keys.intersects(hid::KEY_START) { + println!("See ya!"); + break + } + } + + // Save our current key presses for the next frame + old_keys = keys; + + // Flush and swap framebuffers + gfx.flush_buffers(); + gfx.swap_buffers(); + } +} diff --git a/examples/src/bin/hello-both-screens.rs b/examples/src/bin/hello-both-screens.rs index ade33d4..627d9b0 100644 --- a/examples/src/bin/hello-both-screens.rs +++ b/examples/src/bin/hello-both-screens.rs @@ -3,7 +3,7 @@ extern crate ctru; use ctru::gfx::{Gfx, Screen}; use ctru::console::Console; use ctru::services::apt::Apt; -use ctru::services::hid::{Hid, PadKey}; +use ctru::services::hid::{self, Hid}; fn main() { // Initialize services @@ -35,7 +35,7 @@ fn main() { gfx.swap_buffers(); hid.scan_input(); - if hid.key_down(PadKey::Start) { + if hid.keys_down().contains(hid::KEY_START) { break; } } diff --git a/examples/src/bin/hello-world.rs b/examples/src/bin/hello-world.rs index cb63510..6daee98 100644 --- a/examples/src/bin/hello-world.rs +++ b/examples/src/bin/hello-world.rs @@ -3,7 +3,7 @@ extern crate ctru; use ctru::gfx::Gfx; use ctru::console::Console; use ctru::services::apt::Apt; -use ctru::services::hid::{Hid, PadKey}; +use ctru::services::hid::{self, Hid}; fn main() { // Initialize ctrulib service handles. @@ -45,7 +45,7 @@ fn main() { hid.scan_input(); // Check if the user has pressed the given button on this frame. // If so, break out of the loop. - if hid.key_down(PadKey::Start) { + if hid.keys_down().contains(hid::KEY_START) { break; } }