diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 16f4a3d..027a3cb 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -22,7 +22,7 @@ const-zero = "0.1.0" shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" } pthread-3ds = { git = "https://github.com/rust3ds/pthread-3ds.git" } libc = "0.2.121" -bitflags = "1.0.0" +bitflags = "2.3.3" widestring = "0.2.2" [build-dependencies] diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 7cd0f81..fa69034 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -1,11 +1,11 @@ //! Mii Selector applet. //! //! This applet opens a window on the console's bottom screen which lets the player/user choose a Mii from the ones present on their console. -//! The application can access the selected Mii's data via the use of the [`mii`](crate::mii) module. +//! The selected Mii is readable as a [`MiiData`](crate::mii::MiiData). use crate::mii::MiiData; use bitflags::bitflags; -use std::ffi::CString; +use std::{ffi::CString, error::Error, fmt}; /// Index of a Mii used to configure some parameters of the Mii Selector. #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -16,7 +16,7 @@ pub enum Index { All, } -/// The type of a Mii with their respective data +/// The type of a Mii. #[derive(Debug, Clone, Eq, PartialEq)] pub enum MiiType { /// Guest Mii. @@ -31,29 +31,56 @@ pub enum MiiType { } bitflags! { - /// Options for the Mii Selector + /// Options to configure the [MiiSelector]. + /// + ///

Example

+ /// + /// ```no_run + /// # use std::error::Error; + /// # fn main() -> Result<(), Box> { + /// # + /// use ctru::applets::mii_selector::{MiiSelector, Options}; + /// + /// // Setup a `MiiSelector` that can be cancelled and that makes Guest Miis available to select. + /// let opts = Options::ENABLE_CANCEL & Options::ENABLE_GUESTS; + /// + /// let mut mii_selector = MiiSelector::new(); + /// mii_selector.set_options(opts); + /// + /// let result = mii_selector.launch()?; + /// # + /// # Ok(()) + /// # } + /// ``` + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct Options: u32 { - /// Show the cancel button - const MII_SELECTOR_CANCEL = ctru_sys::MIISELECTOR_CANCEL; - /// Make guest Miis selectable - const MII_SELECTOR_GUESTS = ctru_sys::MIISELECTOR_GUESTS; - /// Show on the top screen - const MII_SELECTOR_TOP = ctru_sys::MIISELECTOR_TOP; - /// Start on the guest's page - const MII_SELECTOR_GUEST_START = ctru_sys::MIISELECTOR_GUESTSTART; + /// Show the cancel button. + const ENABLE_CANCEL = ctru_sys::MIISELECTOR_CANCEL; + /// Make guest Miis available to select. + const ENABLE_GUESTS = ctru_sys::MIISELECTOR_GUESTS; + /// Show on the top screen. + const USE_TOP_SCREEN = ctru_sys::MIISELECTOR_TOP; + /// Start on the guests' page. Requires [Options::ENABLE_GUESTS]. + const START_WITH_GUESTS = ctru_sys::MIISELECTOR_GUESTSTART; } } -/// An instance of the Mii Selector +/// Configuration object to setup the Mii Selector applet. /// /// # Example -/// ``` +/// ```no_run +/// # use std::error::Error; +/// # fn main() -> Result<(), Box> { +/// # /// use ctru::applets::mii_selector::MiiSelector; /// /// let mut mii_selector = MiiSelector::new(); -/// mii_selector.set_title("Example Mii selector"); +/// mii_selector.set_title("Example Mii Selector"); /// -/// let result = mii_selector.launch().unwrap(); +/// let result = mii_selector.launch()?; +/// # +/// # Ok(()) +/// # } /// ``` #[doc(alias = "MiiSelectorConf")] #[derive(Clone, Debug)] @@ -61,27 +88,27 @@ pub struct MiiSelector { config: Box, } -/// Return value from a MiiSelector's launch +/// Return value of a successful [MiiSelector::launch()]. #[non_exhaustive] #[derive(Clone, Debug)] -pub struct SelectionResult { - /// Data regarding the selected Mii. +pub struct Selection { + /// Data of the selected Mii. pub mii_data: MiiData, /// Type of the selected Mii. pub mii_type: MiiType, } -/// Error type for the Mii selector +/// Error returned by an unsuccessful [MiiSelector::launch()]. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum LaunchError { - /// The selected Mii's data is corrupt in some way + /// The selected Mii's data is corrupt in some way. InvalidChecksum, - /// Either the user cancelled the selection (see [Options::MII_SELECTOR_CANCEL]), or no valid Miis were available to select + /// Either the user cancelled the selection (see [Options::ENABLE_CANCEL]), or no valid Miis were available to select. NoMiiSelected, } impl MiiSelector { - /// Initializes a Mii Selector + /// Initialize a new configuration for the Mii Selector applet. #[doc(alias = "miiSelectorInit")] pub fn new() -> Self { let mut config = Box::::default(); @@ -91,9 +118,9 @@ impl MiiSelector { Self { config } } - /// Set the title of the Mii Selector. + /// Set the title of the Mii Selector window. /// - /// This function would panic if the given ``&str`` contains NUL bytes. + /// This function will panic if the given `&str` contains NUL bytes. #[doc(alias = "miiSelectorSetTitle")] pub fn set_title(&mut self, text: &str) { // This can only fail if the text contains NUL bytes in the string... which seems @@ -104,13 +131,15 @@ impl MiiSelector { } } - /// Set the options of the Mii Selector + /// Set the options of the Mii Selector. + /// + /// This will overwrite any previously saved options. #[doc(alias = "miiSelectorSetOptions")] pub fn set_options(&mut self, options: Options) { - unsafe { ctru_sys::miiSelectorSetOptions(self.config.as_mut(), options.bits) } + unsafe { ctru_sys::miiSelectorSetOptions(self.config.as_mut(), options.bits()) } } - /// Whitelist a guest Mii + /// Whitelist a guest Mii based on its index. #[doc(alias = "miiSelectorWhitelistGuestMii")] pub fn whitelist_guest_mii(&mut self, mii_index: Index) { let index = match mii_index { @@ -121,7 +150,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), index) } } - /// Blacklist a guest Mii + /// Blacklist a guest Mii based on its index. #[doc(alias = "miiSelectorBlacklistGuestMii")] pub fn blacklist_guest_mii(&mut self, mii_index: Index) { let index = match mii_index { @@ -132,7 +161,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) } } - /// Whitelist a user Mii + /// Whitelist a user Mii based on its index. #[doc(alias = "miiSelectorWhitelistUserMii")] pub fn whitelist_user_mii(&mut self, mii_index: Index) { let index = match mii_index { @@ -143,7 +172,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) } } - /// Blacklist a user Mii + /// Blacklist a user Mii based on its index. #[doc(alias = "miiSelectorBlacklistUserMii")] pub fn blacklist_user_mii(&mut self, mii_index: Index) { let index = match mii_index { @@ -154,8 +183,9 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } } - /// Set where the cursor will be. - /// If there's no Mii at that index, the cursor will start at the Mii with the index 0 + /// Set where the cursor will start at. + /// + /// If there's no Mii at that index, the cursor will start at the Mii with the index 0. pub fn set_initial_index(&mut self, index: usize) { // This function is static inline in libctru // https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155 @@ -163,9 +193,10 @@ impl MiiSelector { } /// Launch the Mii Selector. - /// Returns an error when the checksum of the Mii is invalid. + /// + /// Depending on the configuration, the Mii Selector window will appear either on the bottom screen (default behaviour) or the top screen (see [Options::USE_TOP_SCREEN]). #[doc(alias = "miiSelectorLaunch")] - pub fn launch(&mut self) -> Result { + pub fn launch(&mut self) -> Result { let mut return_val = Box::::default(); unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } @@ -187,12 +218,23 @@ impl Default for MiiSelector { } } -impl From for SelectionResult { +impl fmt::Display for LaunchError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidChecksum => write!(f, "selected mii has invalid checksum"), + Self::NoMiiSelected => write!(f, "no mii was selected"), + } + } +} + +impl Error for LaunchError {} + +impl From for Selection { fn from(ret: ctru_sys::MiiSelectorReturn) -> Self { let raw_mii_data = ret.mii; let mut guest_mii_name = ret.guest_mii_name; - SelectionResult { + Selection { mii_data: raw_mii_data.into(), mii_type: if ret.guest_mii_index != 0xFFFFFFFF { MiiType::Guest { diff --git a/ctru-rs/src/applets/mod.rs b/ctru-rs/src/applets/mod.rs index e8af7ca..15216fa 100644 --- a/ctru-rs/src/applets/mod.rs +++ b/ctru-rs/src/applets/mod.rs @@ -2,6 +2,9 @@ //! //! Applets are small integrated programs that the OS makes available to the developer to streamline commonly needed functionality. //! Thanks to these integrations the developer can avoid wasting time re-implementing common features and instead use a more reliable base for their application. +//! +//! Unlike [services](crate::services), applets aren't accessed via a system subprocess (which would require obtaining a special handle at runtime). +//! Instead, the user builds a configuration storing the various parameters which is then used to "launch" the applet. pub mod mii_selector; pub mod swkbd; diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 59c658d..86f6357 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -89,6 +89,7 @@ pub enum ValidInput { bitflags! { /// Keyboard feature flags. + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct Features: u32 { /// Parental PIN mode. const PARENTAL_PIN = ctru_sys::SWKBD_PARENTAL; @@ -111,6 +112,7 @@ bitflags! { } /// Keyboard input filtering flags + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct Filters: u32 { /// Disallows the usage of numerical digits. const DIGITS = ctru_sys::SWKBD_FILTER_DIGITS; @@ -183,13 +185,13 @@ impl Swkbd { /// Sets special features for this keyboard #[doc(alias = "swkbdSetFeatures")] pub fn set_features(&mut self, features: Features) { - unsafe { swkbdSetFeatures(self.state.as_mut(), features.bits) } + unsafe { swkbdSetFeatures(self.state.as_mut(), features.bits()) } } /// Configures input validation for this keyboard pub fn set_validation(&mut self, validation: ValidInput, filters: Filters) { self.state.valid_input = validation.into(); - self.state.filter_flags = filters.bits; + self.state.filter_flags = filters.bits(); } /// Configures the maximum number of digits that can be entered in the keyboard when the diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index 71c0f46..895bed1 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -21,7 +21,9 @@ pub type Result = ::std::result::Result; /// # Example /// /// ```no_run -/// pub fn hid_init() -> crate::Result<()> { +/// use ctru::error::{Result, ResultCode}; +/// +/// pub fn hid_init() -> Result<()> { /// // We run an unsafe function which returns a `ctru_sys::Result`. /// let result: ctru_sys::Result = unsafe { ctru_sys::hidInit() }; /// diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs index 7e29748..64ef0fa 100644 --- a/ctru-rs/src/lib.rs +++ b/ctru-rs/src/lib.rs @@ -14,10 +14,6 @@ //! Read thoroughly the official [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki) which guides you through the setup needed to install the required toolchain and helpful tools. //! After following the guide and understanding the many quirks of the Nintendo 3DS homebrew development environment, you can create a new project by including this crate as a dependency //! of your project in your `Cargo.toml` manifest and build your binaries either manually (for the `armv6k-nintendo-3ds` target) or via [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds). -//! -//! # Examples -//! -//! You can check out the examples provided with this crate which dive into most of the implemented functionality. #![crate_type = "rlib"] #![crate_name = "ctru"] diff --git a/ctru-rs/src/services/fs.rs b/ctru-rs/src/services/fs.rs index f008954..1f0fed7 100644 --- a/ctru-rs/src/services/fs.rs +++ b/ctru-rs/src/services/fs.rs @@ -17,20 +17,20 @@ use std::sync::Arc; use widestring::{WideCStr, WideCString}; bitflags! { - #[derive(Default)] + #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] struct FsOpen: u32 { const FS_OPEN_READ = 1; const FS_OPEN_WRITE = 2; const FS_OPEN_CREATE = 4; } - #[derive(Default)] + #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] struct FsWrite: u32 { const FS_WRITE_FLUSH = 1; const FS_WRITE_UPDATE_TIME = 256; } - #[derive(Default)] + #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] struct FsAttribute: u32 { const FS_ATTRIBUTE_DIRECTORY = 1; const FS_ATTRIBUTE_HIDDEN = 256; diff --git a/ctru-rs/src/services/gfx.rs b/ctru-rs/src/services/gfx.rs index 54ec061..b1432f2 100644 --- a/ctru-rs/src/services/gfx.rs +++ b/ctru-rs/src/services/gfx.rs @@ -222,8 +222,17 @@ impl Gfx { /// Creates a new [`Gfx`] instance with default init values /// It's the same as calling: /// - /// ``` - /// Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false) + /// ```no_run + /// # use std::error::Error; + /// # fn main() -> Result<(), Box> { + /// # + /// # use ctru::services::gfx::Gfx; + /// use ctru::services::gspgpu::FramebufferFormat; + /// + /// Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false); + /// # + /// # Ok(()) + /// # } /// ``` #[doc(alias = "gfxInit")] pub fn new() -> Result { diff --git a/ctru-rs/src/services/hid.rs b/ctru-rs/src/services/hid.rs index cb2550f..b592f6a 100644 --- a/ctru-rs/src/services/hid.rs +++ b/ctru-rs/src/services/hid.rs @@ -5,9 +5,12 @@ //! the accelerometer, and the gyroscope. use crate::error::ResultCode; -bitflags::bitflags! { +use bitflags::bitflags; + +bitflags! { /// A set of flags corresponding to the button and directional pad /// inputs on the 3DS + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct KeyPad: u32 { /// A button. const A = ctru_sys::KEY_A;