Browse Source

Finalize applets

pull/134/head
Andrea Ciliberti 1 year ago
parent
commit
ab8a440e28
  1. 4
      ctru-rs/examples/file-explorer.rs
  2. 6
      ctru-rs/examples/mii-selector.rs
  3. 6
      ctru-rs/examples/software-keyboard.rs
  4. 187
      ctru-rs/src/applets/mii_selector.rs
  5. 4
      ctru-rs/src/applets/mod.rs
  6. 311
      ctru-rs/src/applets/swkbd.rs

4
ctru-rs/examples/file-explorer.rs

@ -1,7 +1,7 @@
//! A file explorer which shows off using standard library file system APIs to //! A file explorer which shows off using standard library file system APIs to
//! read the SD card. //! read the SD card.
use ctru::applets::swkbd::{Button, Swkbd}; use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*; use ctru::prelude::*;
use std::fs::DirEntry; use std::fs::DirEntry;
@ -159,7 +159,7 @@ impl<'a> FileExplorer<'a> {
} }
fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) { fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) {
let mut keyboard = Swkbd::default(); let mut keyboard = SoftwareKeyboard::default();
match keyboard.get_string(2048) { match keyboard.get_string(2048) {
Ok((path, Button::Right)) => { Ok((path, Button::Right)) => {

6
ctru-rs/examples/mii-selector.rs

@ -1,4 +1,4 @@
use ctru::applets::mii_selector::{LaunchError, MiiSelector, Options}; use ctru::applets::mii_selector::{Error, MiiSelector, Options};
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -25,8 +25,8 @@ fn main() {
result.mii_data.mole_details.is_enabled result.mii_data.mole_details.is_enabled
); );
} }
Err(LaunchError::InvalidChecksum) => println!("Corrupt Mii selected"), Err(Error::InvalidChecksum) => println!("Corrupt Mii selected"),
Err(LaunchError::NoMiiSelected) => println!("No Mii selected"), Err(Error::NoMiiSelected) => println!("No Mii selected"),
} }
// Main loop // Main loop

6
ctru-rs/examples/software-keyboard.rs

@ -1,4 +1,4 @@
use ctru::applets::swkbd::{Button, Swkbd}; use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -18,9 +18,9 @@ fn main() {
if hid.keys_down().contains(KeyPad::A) { if hid.keys_down().contains(KeyPad::A) {
// Prepares a software keyboard with two buttons: One to cancel input and one // Prepares a software keyboard with two buttons: One to cancel input and one
// to accept it. You can also use `Swkbd::new()` to launch the keyboard in different // to accept it. You can also use `SoftwareKeyboard::new()` to launch the keyboard in different
// configurations. // configurations.
let mut keyboard = Swkbd::default(); let mut keyboard = SoftwareKeyboard::default();
// Raise the software keyboard. You can perform different actions depending on which // Raise the software keyboard. You can perform different actions depending on which
// software button the user pressed // software button the user pressed

187
ctru-rs/src/applets/mii_selector.rs

@ -1,16 +1,22 @@
//! Mii Selector applet. //! 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. //! This applet opens a window which lets the player/user choose a Mii from the ones present on their console.
//! The selected Mii is readable as a [`MiiData`](crate::mii::MiiData). //! The selected Mii is readable as a [`MiiData`](crate::mii::MiiData).
use crate::mii::MiiData; use crate::mii::MiiData;
use bitflags::bitflags; use bitflags::bitflags;
use std::{error::Error, ffi::CString, fmt}; use std::{ffi::CString, fmt};
/// Index of a Mii on the Mii Selector interface. /// Index of a Mii on the [`MiiSelector`] interface.
///
/// See [`MiiSelector::whitelist_user_mii()`] and related functions for more information.
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Index { pub enum Index {
/// Specific Mii index. /// Specific Mii index.
///
/// # Notes
///
/// Indexes start at 0.
Index(u32), Index(u32),
/// All Miis. /// All Miis.
All, All,
@ -31,64 +37,30 @@ pub enum MiiType {
} }
bitflags! { bitflags! {
/// Options to configure the [MiiSelector]. /// Options to configure the [`MiiSelector`].
///
/// <h1>Example</h1>
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::mii_selector::{MiiSelector, Options};
/// ///
/// // Setup a `MiiSelector` that can be cancelled and that makes Guest Miis available to select. /// See [`MiiSelector::set_options()`] to learn how to use them.
/// 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)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct Options: u32 { pub struct Options: u32 {
/// Show the cancel button. /// Show the cancel button.
const ENABLE_CANCEL = ctru_sys::MIISELECTOR_CANCEL; const ENABLE_CANCEL = ctru_sys::MIISELECTOR_CANCEL;
/// Make guest Miis available to select. /// Make guest Miis available to select.
const ENABLE_GUESTS = ctru_sys::MIISELECTOR_GUESTS; const ENABLE_GUESTS = ctru_sys::MIISELECTOR_GUESTS;
/// Show the Mii Selector window on the top screen. /// Show the [`MiiSelector`] window on the top screen.
const USE_TOP_SCREEN = ctru_sys::MIISELECTOR_TOP; const USE_TOP_SCREEN = ctru_sys::MIISELECTOR_TOP;
/// Start the Mii Selector on the guests' page. Requires [Options::ENABLE_GUESTS]. /// Start the [`MiiSelector`] on the guests' page. Requires [`Options::ENABLE_GUESTS`].
const START_WITH_GUESTS = ctru_sys::MIISELECTOR_GUESTSTART; const START_WITH_GUESTS = ctru_sys::MIISELECTOR_GUESTSTART;
} }
} }
/// Configuration structure to setup the Mii Selector applet. /// Configuration structure to setup the Mii Selector applet.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::mii_selector::MiiSelector;
///
/// let mut mii_selector = MiiSelector::new();
/// mii_selector.set_title("Example Mii Selector");
///
/// let result = mii_selector.launch()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "MiiSelectorConf")] #[doc(alias = "MiiSelectorConf")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MiiSelector { pub struct MiiSelector {
config: Box<ctru_sys::MiiSelectorConf>, config: Box<ctru_sys::MiiSelectorConf>,
} }
/// Return value of a successful [MiiSelector::launch()]. /// Return value of a successful [`MiiSelector::launch()`].
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Selection { pub struct Selection {
@ -98,12 +70,12 @@ pub struct Selection {
pub mii_type: MiiType, pub mii_type: MiiType,
} }
/// Error returned by an unsuccessful [MiiSelector::launch()]. /// Error returned by an unsuccessful [`MiiSelector::launch()`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum LaunchError { pub enum Error {
/// The selected Mii's data is corrupt in some way. /// The selected Mii's data is corrupt.
InvalidChecksum, InvalidChecksum,
/// Either the user cancelled the selection (see [Options::ENABLE_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, NoMiiSelected,
} }
@ -120,7 +92,18 @@ impl MiiSelector {
/// Set the title of the Mii Selector window. /// Set the title of the Mii Selector window.
/// ///
/// # Panics
/// This function will panic if the given `&str` contains NUL bytes. /// This function will panic if the given `&str` contains NUL bytes.
///
/// # Example
/// ```no_run
/// # fn main() {
/// use ctru::applets::mii_selector::MiiSelector;
///
/// let mut mii_selector = MiiSelector::new();
/// mii_selector.set_title("Select a Mii!");
/// # }
/// ```
#[doc(alias = "miiSelectorSetTitle")] #[doc(alias = "miiSelectorSetTitle")]
pub fn set_title(&mut self, text: &str) { pub fn set_title(&mut self, text: &str) {
// This can only fail if the text contains NUL bytes in the string... which seems // This can only fail if the text contains NUL bytes in the string... which seems
@ -133,13 +116,42 @@ impl MiiSelector {
/// Set the options of the Mii Selector. /// Set the options of the Mii Selector.
/// ///
/// This will overwrite any previously saved options. /// This will overwrite any previously saved options. Use bitwise operations to set all your wanted options at once.
///
/// # Example
/// ```no_run
/// # fn main() {
/// use ctru::applets::mii_selector::{MiiSelector, Options};
/// let mut mii_selector = MiiSelector::new();
///
/// // Setup a `MiiSelector` that can be cancelled and that makes Guest Miis available to select.
/// let opts = Options::ENABLE_CANCEL & Options::ENABLE_GUESTS;
/// mii_selector.set_options(opts);
/// # }
/// ```
#[doc(alias = "miiSelectorSetOptions")] #[doc(alias = "miiSelectorSetOptions")]
pub fn set_options(&mut self, options: Options) { 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 based on its index. /// Whitelist a guest Mii based on its index.
///
/// # Notes
///
/// Guest Mii's won't be available regardless of their whitelist/blacklist state if the [`MiiSelector`] is run without setting [`Options::ENABLE_GUESTS`].
/// Look into [`MiiSelector::set_options()`] to see how to work with options.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// let mut mii_selector = MiiSelector::new();
///
/// // Whitelist the guest Mii at index 2.
/// mii_selector.whitelist_guest_mii(Index::Index(2));
/// # }
/// ```
#[doc(alias = "miiSelectorWhitelistGuestMii")] #[doc(alias = "miiSelectorWhitelistGuestMii")]
pub fn whitelist_guest_mii(&mut self, mii_index: Index) { pub fn whitelist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
@ -151,6 +163,23 @@ impl MiiSelector {
} }
/// Blacklist a guest Mii based on its index. /// Blacklist a guest Mii based on its index.
///
/// # Notes
///
/// Guest Mii's won't be available regardless of their whitelist/blacklist state if the [`MiiSelector`] is run without setting [`Options::ENABLE_GUESTS`].
/// Look into [`MiiSelector::set_options()`] to see how to work with options.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// let mut mii_selector = MiiSelector::new();
///
/// // Blacklist the guest Mii at index 1 so that it cannot be selected.
/// mii_selector.blacklist_guest_mii(Index::Index(1));
/// # }
/// ```
#[doc(alias = "miiSelectorBlacklistGuestMii")] #[doc(alias = "miiSelectorBlacklistGuestMii")]
pub fn blacklist_guest_mii(&mut self, mii_index: Index) { pub fn blacklist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
@ -161,7 +190,19 @@ impl MiiSelector {
unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) }
} }
/// Whitelist a user Mii based on its index. /// Whitelist a user-created Mii based on its index.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// let mut mii_selector = MiiSelector::new();
///
/// // Whitelist the user-created Mii at index 0.
/// mii_selector.whitelist_user_mii(Index::Index(0));
/// # }
/// ```
#[doc(alias = "miiSelectorWhitelistUserMii")] #[doc(alias = "miiSelectorWhitelistUserMii")]
pub fn whitelist_user_mii(&mut self, mii_index: Index) { pub fn whitelist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
@ -172,7 +213,19 @@ impl MiiSelector {
unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) }
} }
/// Blacklist a user Mii based on its index. /// Blacklist a user-created Mii based on its index.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// let mut mii_selector = MiiSelector::new();
///
/// // Blacklist all user-created Miis so that they cannot be selected.
/// mii_selector.blacklist_user_mii(Index::All);
/// # }
/// ```
#[doc(alias = "miiSelectorBlacklistUserMii")] #[doc(alias = "miiSelectorBlacklistUserMii")]
pub fn blacklist_user_mii(&mut self, mii_index: Index) { pub fn blacklist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index { let index = match mii_index {
@ -183,9 +236,10 @@ impl MiiSelector {
unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) }
} }
/// Set where the cursor will start at. /// Set where the GUI cursor will start at.
/// ///
/// If there's no Mii at that index, the cursor will start at the Mii with the index 0. /// If there's no Mii at that index, the cursor will start at the Mii with the index 0.
#[doc(alias = "miiSelectorSetInitialIndex")]
pub fn set_initial_index(&mut self, index: usize) { pub fn set_initial_index(&mut self, index: usize) {
// This function is static inline in libctru // This function is static inline in libctru
// https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155 // https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155
@ -194,20 +248,41 @@ impl MiiSelector {
/// Launch the Mii Selector. /// Launch the Mii Selector.
/// ///
/// 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]). /// 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`]).
///
/// TODO: UNSAFE OPERATION, LAUNCHING APPLETS REQUIRES GRAPHICS, WITHOUT AN ACTIVE GFX THIS WILL CAUSE A SEGMENTATION FAULT.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::mii_selector::{MiiSelector, Options};
///
/// let mut mii_selector = MiiSelector::new();
/// mii_selector.set_title("Select a Mii!");
///
/// let opts = Options::ENABLE_CANCEL & Options::ENABLE_GUESTS;
/// mii_selector.set_options(opts);
///
/// let result = mii_selector.launch()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "miiSelectorLaunch")] #[doc(alias = "miiSelectorLaunch")]
pub fn launch(&mut self) -> Result<Selection, LaunchError> { pub fn launch(&mut self) -> Result<Selection, Error> {
let mut return_val = Box::<ctru_sys::MiiSelectorReturn>::default(); let mut return_val = Box::<ctru_sys::MiiSelectorReturn>::default();
unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) }
if return_val.no_mii_selected != 0 { if return_val.no_mii_selected != 0 {
return Err(LaunchError::NoMiiSelected); return Err(Error::NoMiiSelected);
} }
if unsafe { ctru_sys::miiSelectorChecksumIsValid(return_val.as_mut()) } { if unsafe { ctru_sys::miiSelectorChecksumIsValid(return_val.as_mut()) } {
Ok((*return_val).into()) Ok((*return_val).into())
} else { } else {
Err(LaunchError::InvalidChecksum) Err(Error::InvalidChecksum)
} }
} }
} }
@ -218,7 +293,7 @@ impl Default for MiiSelector {
} }
} }
impl fmt::Display for LaunchError { impl fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::InvalidChecksum => write!(f, "selected mii has invalid checksum"), Self::InvalidChecksum => write!(f, "selected mii has invalid checksum"),
@ -227,7 +302,7 @@ impl fmt::Display for LaunchError {
} }
} }
impl Error for LaunchError {} impl std::error::Error for Error {}
impl From<ctru_sys::MiiSelectorReturn> for Selection { impl From<ctru_sys::MiiSelectorReturn> for Selection {
fn from(ret: ctru_sys::MiiSelectorReturn) -> Self { fn from(ret: ctru_sys::MiiSelectorReturn) -> Self {

4
ctru-rs/src/applets/mod.rs

@ -4,7 +4,9 @@
//! Thanks to these integrations the developer can avoid wasting time re-implementing common features and instead use a more reliable base for their application. //! 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). //! 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. //! Instead, the application builds a configuration storing the various parameters which is then used to "launch" the applet.
//!
//! Applets block execution of the thread that launches them as long as the user doesn't close the applet.
pub mod mii_selector; pub mod mii_selector;
pub mod swkbd; pub mod swkbd;

311
ctru-rs/src/applets/swkbd.rs

@ -1,23 +1,28 @@
//! Software Keyboard applet. //! Software Keyboard applet.
//! //!
//! This applet opens a virtual keyboard on the console's bottom screen which lets the player/user write UTF-16 valid text. //! This applet opens a virtual keyboard on the console's bottom screen which lets the user write UTF-16 valid text.
// TODO: Implement remaining functionality (password mode, filter callbacks, etc.). Also improve "max text length" API. Improve `number of buttons` API when creating a new SoftwareKeyboard.
// TODO: Split the Parental PIN lock operations into a different type.
use bitflags::bitflags; use bitflags::bitflags;
use ctru_sys::{ use ctru_sys::{
self, swkbdInit, swkbdInputText, swkbdSetButton, swkbdSetFeatures, swkbdSetHintText, SwkbdState, self, swkbdInit, swkbdInputText, swkbdSetButton, swkbdSetFeatures, swkbdSetHintText, SwkbdState,
}; };
use libc; use libc;
use std::fmt::Display;
use std::iter::once; use std::iter::once;
use std::str; use std::str;
/// An instance of the software keyboard. /// Configuration structure to setup the Software Keyboard applet.
#[doc(alias = "SwkbdState")] #[doc(alias = "SwkbdState")]
#[derive(Clone)] #[derive(Clone)]
pub struct Swkbd { pub struct SoftwareKeyboard {
state: Box<SwkbdState>, state: Box<SwkbdState>,
} }
/// The kind of keyboard to be initialized. /// The type of keyboard used by the [`SoftwareKeyboard`].
///
/// Can be set with [`SoftwareKeyboard::new()`]
#[doc(alias = "SwkbdType")] #[doc(alias = "SwkbdType")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
@ -28,13 +33,15 @@ pub enum Kind {
Qwerty = ctru_sys::SWKBD_TYPE_QWERTY, Qwerty = ctru_sys::SWKBD_TYPE_QWERTY,
/// Only number pad. /// Only number pad.
Numpad = ctru_sys::SWKBD_TYPE_NUMPAD, Numpad = ctru_sys::SWKBD_TYPE_NUMPAD,
/// On JPN systems: a keyboard without japanese input capablities. /// On JPN systems: a keyboard without japanese input capabilities.
/// ///
/// On any other region: same as [`Normal`](Kind::Normal). /// On any other region: same as [`Normal`](Kind::Normal).
Western = ctru_sys::SWKBD_TYPE_WESTERN, Western = ctru_sys::SWKBD_TYPE_WESTERN,
} }
/// Represents which button the user pressed to close the software keyboard. /// Represents which button the user pressed to close the [`SoftwareKeyboard`].
///
/// Button text and behaviour can be customized with [`SoftwareKeyboard::configure_button()`].
#[doc(alias = "SwkbdButton")] #[doc(alias = "SwkbdButton")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
@ -47,30 +54,37 @@ pub enum Button {
Right = ctru_sys::SWKBD_BUTTON_RIGHT, Right = ctru_sys::SWKBD_BUTTON_RIGHT,
} }
/// Error type for the software keyboard. /// Error returned by an unsuccessful [`SoftwareKeyboard::get_string()`].
#[doc(alias = "SwkbdResult")] #[doc(alias = "SwkbdResult")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)] #[repr(i32)]
pub enum Error { pub enum Error {
/// Invalid parameters inputted in the Software Keyboard. /// Invalid parameters given to the [`SoftwareKeyboard`] configuration.
InvalidInput = ctru_sys::SWKBD_INVALID_INPUT, InvalidParameters = ctru_sys::SWKBD_INVALID_INPUT,
/// Out of memory. /// [`SoftwareKeyboard`] ran out of memory.
OutOfMem = ctru_sys::SWKBD_OUTOFMEM, OutOfMem = ctru_sys::SWKBD_OUTOFMEM,
/// Home button was pressed during execution. /// Home button was pressed while [`SoftwareKeyboard`] was running.
HomePressed = ctru_sys::SWKBD_HOMEPRESSED, HomePressed = ctru_sys::SWKBD_HOMEPRESSED,
/// Reset button was pressed during execution. /// Reset button was pressed while [`SoftwareKeyboard`] was running.
ResetPressed = ctru_sys::SWKBD_RESETPRESSED, ResetPressed = ctru_sys::SWKBD_RESETPRESSED,
/// Power button was pressed during execution. /// Power button was pressed while [`SoftwareKeyboard`] was running.
PowerPressed = ctru_sys::SWKBD_POWERPRESSED, PowerPressed = ctru_sys::SWKBD_POWERPRESSED,
/// The parental PIN was correct. /// The parental lock PIN was correct.
///
/// While this variant isn't *technically* considerable an error
/// the result of a Parental PIN operation won't return a string to the program, thus it's still exceptional behaviour.
ParentalOk = ctru_sys::SWKBD_PARENTAL_OK, ParentalOk = ctru_sys::SWKBD_PARENTAL_OK,
/// The parental PIN was incorrect. /// The parental lock PIN was incorrect.
ParentalFail = ctru_sys::SWKBD_PARENTAL_FAIL, ParentalFail = ctru_sys::SWKBD_PARENTAL_FAIL,
/// Input triggered the filter. /// Input triggered the filter.
///
/// You can have a look at [`Filters`] to activate custom filters.
BannedInput = ctru_sys::SWKBD_BANNED_INPUT, BannedInput = ctru_sys::SWKBD_BANNED_INPUT,
} }
/// Restrictions on keyboard input. /// Restrictions to enforce rules on the keyboard input.
///
/// See [`SoftwareKeyboard::set_validation()`]
#[doc(alias = "SwkbdValidInput")] #[doc(alias = "SwkbdValidInput")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
@ -79,71 +93,113 @@ pub enum ValidInput {
Anything = ctru_sys::SWKBD_ANYTHING, Anything = ctru_sys::SWKBD_ANYTHING,
/// Empty inputs are not accepted. /// Empty inputs are not accepted.
NotEmpty = ctru_sys::SWKBD_NOTEMPTY, NotEmpty = ctru_sys::SWKBD_NOTEMPTY,
/// Blank (consisting only of whitespaces) inputs are not accepted. /// Blank inputs (consisting only of whitespaces) are not accepted.
NotBlank = ctru_sys::SWKBD_NOTBLANK, NotBlank = ctru_sys::SWKBD_NOTBLANK,
/// Neither empty inputs nor blank inputs are accepted. /// Neither empty inputs nor blank inputs are accepted.
NotEmptyNotBlank = ctru_sys::SWKBD_NOTEMPTY_NOTBLANK, NotEmptyNotBlank = ctru_sys::SWKBD_NOTEMPTY_NOTBLANK,
/// Input must have a fixed length. Maximum length can be specified with [`Swkbd::set_max_text_len`]; /// Input must have a fixed length. Maximum length can be specified with [`SoftwareKeyboard::set_max_text_len()`];
FixedLen = ctru_sys::SWKBD_FIXEDLEN, FixedLen = ctru_sys::SWKBD_FIXEDLEN,
} }
bitflags! { bitflags! {
/// Keyboard feature flags. /// Special features that can be activated via [`SoftwareKeyboard::set_features()`].
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct Features: u32 { pub struct Features: u32 {
/// Parental PIN mode. /// Parental PIN mode.
///
/// # Notes
///
/// Refer to [`Error::ParentalOk`] and [`Error::ParentalFail`] to check whether the Parental PIN lock was successfully opened.
const PARENTAL_PIN = ctru_sys::SWKBD_PARENTAL; const PARENTAL_PIN = ctru_sys::SWKBD_PARENTAL;
/// Darken top screen while the Software Keyboard is active. /// Darken top screen while the [`SoftwareKeyboard`] is active.
const DARKEN_TOP_SCREEN = ctru_sys::SWKBD_DARKEN_TOP_SCREEN; const DARKEN_TOP_SCREEN = ctru_sys::SWKBD_DARKEN_TOP_SCREEN;
/// Enable predictive input (necessary for Kanji on JPN consoles). /// Enable predictive input (necessary for Kanji on JPN consoles).
const PREDICTIVE_INPUT = ctru_sys::SWKBD_PREDICTIVE_INPUT; const PREDICTIVE_INPUT = ctru_sys::SWKBD_PREDICTIVE_INPUT;
/// Enable multiline input. /// Enable multiline input.
const MULTILINE = ctru_sys::SWKBD_MULTILINE; const MULTILINE = ctru_sys::SWKBD_MULTILINE;
/// Enable fixed-width mode. /// Enable fixed-width mode.
const FIXED_WIDTH = ctru_sys::SWKBD_FIXED_WIDTH; const FIXED_WIDTH = ctru_sys::SWKBD_FIXED_WIDTH;
/// Allow the usage of the Home Button while the Software Keyboard is active. /// Allow the usage of the Home Button while the [`SoftwareKeyboard`] is running.
const ALLOW_HOME = ctru_sys::SWKBD_ALLOW_HOME; const ALLOW_HOME = ctru_sys::SWKBD_ALLOW_HOME;
/// Allow the usage of the Reset Button while the Software Keyboard is active. /// Allow the usage of the Reset Button while the [`SoftwareKeyboard`] is running.
const ALLOW_RESET = ctru_sys::SWKBD_ALLOW_RESET; const ALLOW_RESET = ctru_sys::SWKBD_ALLOW_RESET;
/// Allow the usage of the Power Button while the Software Keyboard is active. /// Allow the usage of the Power Button while the [`SoftwareKeyboard`] is running.
const ALLOW_POWER = ctru_sys::SWKBD_ALLOW_POWER; const ALLOW_POWER = ctru_sys::SWKBD_ALLOW_POWER;
/// Default to the QWERTY page when the Software Keyboard is shown. /// Default to the QWERTY page when the [`SoftwareKeyboard`] is shown.
const DEFAULT_QWERTY = ctru_sys::SWKBD_DEFAULT_QWERTY; const DEFAULT_QWERTY = ctru_sys::SWKBD_DEFAULT_QWERTY;
} }
/// Keyboard input filtering flags /// Availble filters to disallow some types of input for the [`SoftwareKeyboard`].
///
/// See [`SoftwareKeyboard::set_validation()`]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct Filters: u32 { pub struct Filters: u32 {
/// Disallows the usage of numerical digits. /// Disallow the usage of numerical digits.
///
/// The maximum number of digits that are allowed to be used while this filter is active
/// can be configured with [`SoftwareKeyboard::set_max_digits()`] (default is 0).
const DIGITS = ctru_sys::SWKBD_FILTER_DIGITS; const DIGITS = ctru_sys::SWKBD_FILTER_DIGITS;
/// Disallows the usage of the "at" (@) sign. /// Disallow the usage of the "at" (@) sign.
const AT = ctru_sys::SWKBD_FILTER_AT; const AT = ctru_sys::SWKBD_FILTER_AT;
/// Disallows the usage of the "percent" (%) sign. /// Disallow the usage of the "percent" (%) sign.
const PERCENT = ctru_sys::SWKBD_FILTER_PERCENT; const PERCENT = ctru_sys::SWKBD_FILTER_PERCENT;
/// Disallows the usage of the "backslash" (\) sign. /// Disallow the usage of the "backslash" (\) sign.
const BACKSLASH = ctru_sys::SWKBD_FILTER_BACKSLASH; const BACKSLASH = ctru_sys::SWKBD_FILTER_BACKSLASH;
/// Disallows the use of profanity via Nintendo's profanity filter. /// Disallow the use of profanity via Nintendo's profanity filter.
const PROFANITY = ctru_sys::SWKBD_FILTER_PROFANITY; const PROFANITY = ctru_sys::SWKBD_FILTER_PROFANITY;
/// Use a custom callback in order to filter the input. /// Use a custom callback in order to filter the input.
///
/// TODO: It's currently impossible to setup a custom filter callback.
const CALLBACK = ctru_sys::SWKBD_FILTER_CALLBACK; const CALLBACK = ctru_sys::SWKBD_FILTER_CALLBACK;
} }
} }
impl Swkbd { impl SoftwareKeyboard {
/// Initializes a software keyboard of the specified type and the chosen number of buttons /// Initialize a new configuration for the Software Keyboard applet depending on how many "exit" buttons are available to the user (1, 2 or 3).
/// (from 1-3). ///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Kind};
///
/// // Standard keyboard.
/// let keyboard = SoftwareKeyboard::new(Kind::Normal, 2);
///
/// // Numpad (with only the "confirm" button).
/// let keyboard = SoftwareKeyboard::new(Kind::Numpad, 1);
/// #
/// # }
#[doc(alias = "swkbdInit")] #[doc(alias = "swkbdInit")]
pub fn new(keyboard_type: Kind, num_buttons: i32) -> Self { pub fn new(keyboard_type: Kind, num_buttons: i32) -> Self {
unsafe { unsafe {
let mut state = Box::<SwkbdState>::default(); let mut state = Box::<SwkbdState>::default();
swkbdInit(state.as_mut(), keyboard_type.into(), num_buttons, -1); swkbdInit(state.as_mut(), keyboard_type.into(), num_buttons, -1);
Swkbd { state } SoftwareKeyboard { state }
} }
} }
/// Gets input from this keyboard and appends it to the provided string. /// Launches the applet based on the given configuration and returns a string containing the text input.
///
/// # Notes
/// ///
/// The text received from the keyboard will be truncated if it is longer than `max_bytes`. /// The text received from the keyboard will be truncated if it is longer than `max_bytes`.
///
/// TODO: UNSAFE OPERATION, LAUNCHING APPLETS REQUIRES GRAPHICS, WITHOUT AN ACTIVE GFX THIS WILL CAUSE A SEGMENTATION FAULT.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::swkbd::SoftwareKeyboard;
/// let mut keyboard = SoftwareKeyboard::default();
///
/// let (text, button) = keyboard.get_string(2048)?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "swkbdInputText")] #[doc(alias = "swkbdInputText")]
pub fn get_string(&mut self, max_bytes: usize) -> Result<(String, Button), Error> { pub fn get_string(&mut self, max_bytes: usize) -> Result<(String, Button), Error> {
// Unfortunately the libctru API doesn't really provide a way to get the exact length // Unfortunately the libctru API doesn't really provide a way to get the exact length
@ -167,8 +223,28 @@ impl Swkbd {
/// Fills the provided buffer with a UTF-8 encoded, NUL-terminated sequence of bytes from /// Fills the provided buffer with a UTF-8 encoded, NUL-terminated sequence of bytes from
/// this software keyboard. /// this software keyboard.
/// ///
/// # Notes
///
/// If the buffer is too small to contain the entire sequence received from the keyboard, /// If the buffer is too small to contain the entire sequence received from the keyboard,
/// the output will be truncated but should still be well-formed UTF-8. /// the output will be truncated.
///
/// TODO: UNSAFE OPERATION, LAUNCHING APPLETS REQUIRES GRAPHICS, WITHOUT AN ACTIVE GFX THIS WILL CAUSE A SEGMENTATION FAULT.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::swkbd::SoftwareKeyboard;
/// let mut keyboard = SoftwareKeyboard::default();
///
/// let mut buffer = vec![0; 100];
///
/// let button = keyboard.write_exact(&mut buffer)?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "swkbdInputText")] #[doc(alias = "swkbdInputText")]
pub fn write_exact(&mut self, buf: &mut [u8]) -> Result<Button, Error> { pub fn write_exact(&mut self, buf: &mut [u8]) -> Result<Button, Error> {
unsafe { unsafe {
@ -182,26 +258,85 @@ impl Swkbd {
} }
} }
/// Sets special features for this keyboard /// Set special features for this keyboard.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Features};
///
/// let mut keyboard = SoftwareKeyboard::default();
///
/// let features = Features::DARKEN_TOP_SCREEN & Features::MULTILINE;
/// keyboard.set_features(features);
/// #
/// # }
#[doc(alias = "swkbdSetFeatures")] #[doc(alias = "swkbdSetFeatures")]
pub fn set_features(&mut self, features: Features) { 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 /// Configure input validation for this keyboard.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters};
/// let mut keyboard = SoftwareKeyboard::default();
///
/// // Disallow empty or blank input.
/// let validation = ValidInput::NotEmptyNotBlank;
///
/// // Disallow the use of numerical digits and profanity.
/// let filters = Filters::DIGITS & Filters::PROFANITY;
/// keyboard.set_validation(validation, filters);
/// #
/// # }
pub fn set_validation(&mut self, validation: ValidInput, filters: Filters) { pub fn set_validation(&mut self, validation: ValidInput, filters: Filters) {
self.state.valid_input = validation.into(); 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 /// Configure the maximum number of digits that can be entered in the keyboard when the [`Filters::DIGITS`] flag is enabled.
/// `Filters::DIGITS` flag is enabled ///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters};
/// let mut keyboard = SoftwareKeyboard::default();
///
/// // Disallow empty or blank input.
/// let validation = ValidInput::NotEmptyNotBlank;
///
/// // Disallow the use of numerical digits. This filter is customizable thanks to `set_max_digits`.
/// let filters = Filters::DIGITS;
/// keyboard.set_validation(validation, filters);
///
/// // No more than 3 numbers are accepted.
/// keyboard.set_max_digits(3);
/// #
/// # }
pub fn set_max_digits(&mut self, digits: u16) { pub fn set_max_digits(&mut self, digits: u16) {
self.state.max_digits = digits; self.state.max_digits = digits;
} }
/// Sets the hint text for this software keyboard (that is, the help text that is displayed /// Set the hint text for this software keyboard.
/// when the textbox is empty) ///
/// The hint text is the text shown in gray before any text gets written in the input box.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::SoftwareKeyboard;
/// let mut keyboard = SoftwareKeyboard::default();
///
/// keyboard.set_hint_text("Write here what you like!");
/// #
/// # }
#[doc(alias = "swkbdSetHintText")] #[doc(alias = "swkbdSetHintText")]
pub fn set_hint_text(&mut self, text: &str) { pub fn set_hint_text(&mut self, text: &str) {
unsafe { unsafe {
@ -210,12 +345,30 @@ impl Swkbd {
} }
} }
/// Configures the look and behavior of a button for this keyboard. /// Configure the look and behavior of a button for this keyboard.
///
/// # Arguments
///
/// - `button` - the [`Button`] to be configured based on the position.
/// - `text` - the text displayed in the button.
/// - `submit` - whether pressing the button will accept the keyboard's input or discard it.
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind};
///
/// // We create a `SoftwareKeyboard` with left and right buttons.
/// let mut keyboard = SoftwareKeyboard::new(Kind::Normal, 2);
/// ///
/// `button` is the `Button` to be configured /// // Set the left button text to "Cancel" and pressing it will NOT return the user's input.
/// `text` configures the display text for the button /// keyboard.configure_button(Button::Left, "Cancel", false);
/// `submit` configures whether pressing the button will accept the keyboard's input or ///
/// discard it. /// // Set the right button text to "Ok" and pressing it will return the user's input.
/// keyboard.configure_button(Button::Right, "Ok", true);
/// #
/// # }
#[doc(alias = "swkbdSetButton")] #[doc(alias = "swkbdSetButton")]
pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) { pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) {
unsafe { unsafe {
@ -229,19 +382,33 @@ impl Swkbd {
} }
} }
/// Configures the maximum number of UTF-16 code units that can be entered into the software /// Configure the maximum number of UTF-16 code units that can be entered into the software
/// keyboard. By default the limit is 0xFDE8 code units. /// keyboard. By default the limit is `0xFDE8` code units.
///
/// # Notes
/// ///
/// Note that keyboard input is converted from UTF-16 to UTF-8 before being handed to Rust, /// Keyboard input is converted from UTF-16 to UTF-8 before being handed to Rust,
/// so this code point limit does not necessarily equal the max number of UTF-8 code points /// so this code point limit does not necessarily equal the max number of UTF-8 code points
/// receivable by the `get_utf8` and `get_bytes` functions. /// receivable by [`SoftwareKeyboard::get_string()`] and [`SoftwareKeyboard::write_exact()`].
///
/// # Example
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind};
/// let mut keyboard = SoftwareKeyboard::default();
///
/// // Set the maximum text length to 18 UTF-16 code units.
/// keyboard.set_max_text_len(18);
/// #
/// # }
pub fn set_max_text_len(&mut self, len: u16) { pub fn set_max_text_len(&mut self, len: u16) {
self.state.max_text_len = len; self.state.max_text_len = len;
} }
fn parse_swkbd_error(&self) -> Error { fn parse_swkbd_error(&self) -> Error {
match self.state.result { match self.state.result {
ctru_sys::SWKBD_INVALID_INPUT => Error::InvalidInput, ctru_sys::SWKBD_INVALID_INPUT => Error::InvalidParameters,
ctru_sys::SWKBD_OUTOFMEM => Error::OutOfMem, ctru_sys::SWKBD_OUTOFMEM => Error::OutOfMem,
ctru_sys::SWKBD_HOMEPRESSED => Error::HomePressed, ctru_sys::SWKBD_HOMEPRESSED => Error::HomePressed,
ctru_sys::SWKBD_RESETPRESSED => Error::ResetPressed, ctru_sys::SWKBD_RESETPRESSED => Error::ResetPressed,
@ -254,12 +421,44 @@ impl Swkbd {
} }
} }
impl Default for Swkbd { /// Creates a new [`SoftwareKeyboard`] configuration set to using a [`Kind::Normal`] keyboard and 2 [`Button`]s.
impl Default for SoftwareKeyboard {
fn default() -> Self { fn default() -> Self {
Swkbd::new(Kind::Normal, 2) SoftwareKeyboard::new(Kind::Normal, 2)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidParameters => write!(
f,
"software keyboard was configured with invalid parameters"
),
Self::OutOfMem => write!(f, "software keyboard ran out of memory"),
Self::HomePressed => {
write!(f, "home button pressed while software keyboard was running")
}
Self::ResetPressed => write!(
f,
"reset button pressed while software keyboard was running"
),
Self::PowerPressed => write!(
f,
"power button pressed while software keyboard was running"
),
Self::ParentalOk => write!(f, "parental lock pin was correct"),
Self::ParentalFail => write!(f, "parental lock pin was incorrect"),
Self::BannedInput => write!(
f,
"input given to the software keyboard triggered the active filters"
),
}
} }
} }
impl std::error::Error for Error {}
from_impl!(Kind, ctru_sys::SwkbdType); from_impl!(Kind, ctru_sys::SwkbdType);
from_impl!(Button, ctru_sys::SwkbdButton); from_impl!(Button, ctru_sys::SwkbdButton);
from_impl!(Error, ctru_sys::SwkbdResult); from_impl!(Error, ctru_sys::SwkbdResult);

Loading…
Cancel
Save