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. 307
      ctru-rs/src/applets/swkbd.rs

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

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
//! A file explorer which shows off using standard library file system APIs to
//! read the SD card.
use ctru::applets::swkbd::{Button, Swkbd};
use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*;
use std::fs::DirEntry;
@ -159,7 +159,7 @@ impl<'a> FileExplorer<'a> { @@ -159,7 +159,7 @@ impl<'a> FileExplorer<'a> {
}
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) {
Ok((path, Button::Right)) => {

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

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

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
use ctru::applets::swkbd::{Button, Swkbd};
use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*;
fn main() {
@ -18,9 +18,9 @@ fn main() { @@ -18,9 +18,9 @@ fn main() {
if hid.keys_down().contains(KeyPad::A) {
// 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.
let mut keyboard = Swkbd::default();
let mut keyboard = SoftwareKeyboard::default();
// Raise the software keyboard. You can perform different actions depending on which
// software button the user pressed

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

@ -1,16 +1,22 @@ @@ -1,16 +1,22 @@
//! 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).
use crate::mii::MiiData;
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)]
pub enum Index {
/// Specific Mii index.
///
/// # Notes
///
/// Indexes start at 0.
Index(u32),
/// All Miis.
All,
@ -31,64 +37,30 @@ pub enum MiiType { @@ -31,64 +37,30 @@ pub enum MiiType {
}
bitflags! {
/// 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.
/// let opts = Options::ENABLE_CANCEL & Options::ENABLE_GUESTS;
///
/// let mut mii_selector = MiiSelector::new();
/// mii_selector.set_options(opts);
/// Options to configure the [`MiiSelector`].
///
/// let result = mii_selector.launch()?;
/// #
/// # Ok(())
/// # }
/// ```
/// See [`MiiSelector::set_options()`] to learn how to use them.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct Options: u32 {
/// 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 the Mii Selector window on the top screen.
/// Show the [`MiiSelector`] window on the top screen.
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;
}
}
/// 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")]
#[derive(Clone, Debug)]
pub struct MiiSelector {
config: Box<ctru_sys::MiiSelectorConf>,
}
/// Return value of a successful [MiiSelector::launch()].
/// Return value of a successful [`MiiSelector::launch()`].
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct Selection {
@ -98,12 +70,12 @@ pub struct Selection { @@ -98,12 +70,12 @@ pub struct Selection {
pub mii_type: MiiType,
}
/// Error returned by an unsuccessful [MiiSelector::launch()].
/// 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.
pub enum Error {
/// The selected Mii's data is corrupt.
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,
}
@ -120,7 +92,18 @@ impl MiiSelector { @@ -120,7 +92,18 @@ impl MiiSelector {
/// Set the title of the Mii Selector window.
///
/// # Panics
/// 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")]
pub fn set_title(&mut self, text: &str) {
// This can only fail if the text contains NUL bytes in the string... which seems
@ -133,13 +116,42 @@ impl MiiSelector { @@ -133,13 +116,42 @@ impl MiiSelector {
/// 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")]
pub fn set_options(&mut self, options: Options) {
unsafe { ctru_sys::miiSelectorSetOptions(self.config.as_mut(), options.bits()) }
}
/// 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")]
pub fn whitelist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index {
@ -151,6 +163,23 @@ impl MiiSelector { @@ -151,6 +163,23 @@ impl MiiSelector {
}
/// 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")]
pub fn blacklist_guest_mii(&mut self, mii_index: Index) {
let index = match mii_index {
@ -161,7 +190,19 @@ impl MiiSelector { @@ -161,7 +190,19 @@ impl MiiSelector {
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")]
pub fn whitelist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index {
@ -172,7 +213,19 @@ impl MiiSelector { @@ -172,7 +213,19 @@ impl MiiSelector {
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")]
pub fn blacklist_user_mii(&mut self, mii_index: Index) {
let index = match mii_index {
@ -183,9 +236,10 @@ impl MiiSelector { @@ -183,9 +236,10 @@ impl MiiSelector {
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.
#[doc(alias = "miiSelectorSetInitialIndex")]
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
@ -194,20 +248,41 @@ impl MiiSelector { @@ -194,20 +248,41 @@ impl MiiSelector {
/// 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")]
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();
unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) }
if return_val.no_mii_selected != 0 {
return Err(LaunchError::NoMiiSelected);
return Err(Error::NoMiiSelected);
}
if unsafe { ctru_sys::miiSelectorChecksumIsValid(return_val.as_mut()) } {
Ok((*return_val).into())
} else {
Err(LaunchError::InvalidChecksum)
Err(Error::InvalidChecksum)
}
}
}
@ -218,7 +293,7 @@ impl Default for MiiSelector { @@ -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 {
match self {
Self::InvalidChecksum => write!(f, "selected mii has invalid checksum"),
@ -227,7 +302,7 @@ impl fmt::Display for LaunchError { @@ -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 {
fn from(ret: ctru_sys::MiiSelectorReturn) -> Self {

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

@ -4,7 +4,9 @@ @@ -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.
//!
//! 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 swkbd;

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

@ -1,23 +1,28 @@ @@ -1,23 +1,28 @@
//! 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 ctru_sys::{
self, swkbdInit, swkbdInputText, swkbdSetButton, swkbdSetFeatures, swkbdSetHintText, SwkbdState,
};
use libc;
use std::fmt::Display;
use std::iter::once;
use std::str;
/// An instance of the software keyboard.
/// Configuration structure to setup the Software Keyboard applet.
#[doc(alias = "SwkbdState")]
#[derive(Clone)]
pub struct Swkbd {
pub struct SoftwareKeyboard {
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")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
@ -28,13 +33,15 @@ pub enum Kind { @@ -28,13 +33,15 @@ pub enum Kind {
Qwerty = ctru_sys::SWKBD_TYPE_QWERTY,
/// Only number pad.
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).
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")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
@ -47,30 +54,37 @@ pub enum Button { @@ -47,30 +54,37 @@ pub enum Button {
Right = ctru_sys::SWKBD_BUTTON_RIGHT,
}
/// Error type for the software keyboard.
/// Error returned by an unsuccessful [`SoftwareKeyboard::get_string()`].
#[doc(alias = "SwkbdResult")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum Error {
/// Invalid parameters inputted in the Software Keyboard.
InvalidInput = ctru_sys::SWKBD_INVALID_INPUT,
/// Out of memory.
/// Invalid parameters given to the [`SoftwareKeyboard`] configuration.
InvalidParameters = ctru_sys::SWKBD_INVALID_INPUT,
/// [`SoftwareKeyboard`] ran out of memory.
OutOfMem = ctru_sys::SWKBD_OUTOFMEM,
/// Home button was pressed during execution.
/// Home button was pressed while [`SoftwareKeyboard`] was running.
HomePressed = ctru_sys::SWKBD_HOMEPRESSED,
/// Reset button was pressed during execution.
/// Reset button was pressed while [`SoftwareKeyboard`] was running.
ResetPressed = ctru_sys::SWKBD_RESETPRESSED,
/// Power button was pressed during execution.
/// Power button was pressed while [`SoftwareKeyboard`] was running.
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,
/// The parental PIN was incorrect.
/// The parental lock PIN was incorrect.
ParentalFail = ctru_sys::SWKBD_PARENTAL_FAIL,
/// Input triggered the filter.
///
/// You can have a look at [`Filters`] to activate custom filters.
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")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
@ -79,21 +93,25 @@ pub enum ValidInput { @@ -79,21 +93,25 @@ pub enum ValidInput {
Anything = ctru_sys::SWKBD_ANYTHING,
/// Empty inputs are not accepted.
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,
/// Neither empty inputs nor blank inputs are accepted.
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,
}
bitflags! {
/// Keyboard feature flags.
/// Special features that can be activated via [`SoftwareKeyboard::set_features()`].
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct Features: u32 {
/// 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;
/// 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;
/// Enable predictive input (necessary for Kanji on JPN consoles).
const PREDICTIVE_INPUT = ctru_sys::SWKBD_PREDICTIVE_INPUT;
@ -101,49 +119,87 @@ bitflags! { @@ -101,49 +119,87 @@ bitflags! {
const MULTILINE = ctru_sys::SWKBD_MULTILINE;
/// Enable fixed-width mode.
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;
/// 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;
/// 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;
/// 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;
}
/// 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)]
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;
/// Disallows the usage of the "at" (@) sign.
/// Disallow the usage of the "at" (@) sign.
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;
/// Disallows the usage of the "backslash" (\) sign.
/// Disallow the usage of the "backslash" (\) sign.
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;
/// 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;
}
}
impl Swkbd {
/// Initializes a software keyboard of the specified type and the chosen number of buttons
/// (from 1-3).
impl SoftwareKeyboard {
/// Initialize a new configuration for the Software Keyboard applet depending on how many "exit" buttons are available to the user (1, 2 or 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")]
pub fn new(keyboard_type: Kind, num_buttons: i32) -> Self {
unsafe {
let mut state = Box::<SwkbdState>::default();
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`.
///
/// 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")]
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
@ -167,8 +223,28 @@ impl Swkbd { @@ -167,8 +223,28 @@ impl Swkbd {
/// Fills the provided buffer with a UTF-8 encoded, NUL-terminated sequence of bytes from
/// this software keyboard.
///
/// # Notes
///
/// 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")]
pub fn write_exact(&mut self, buf: &mut [u8]) -> Result<Button, Error> {
unsafe {
@ -182,26 +258,85 @@ impl Swkbd { @@ -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")]
pub fn set_features(&mut self, features: Features) {
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) {
self.state.valid_input = validation.into();
self.state.filter_flags = filters.bits();
}
/// Configures the maximum number of digits that can be entered in the keyboard when the
/// `Filters::DIGITS` flag is enabled
/// Configure the maximum number of digits that can be entered in the keyboard when the [`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) {
self.state.max_digits = digits;
}
/// Sets the hint text for this software keyboard (that is, the help text that is displayed
/// when the textbox is empty)
/// Set the hint text for this software keyboard.
///
/// 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")]
pub fn set_hint_text(&mut self, text: &str) {
unsafe {
@ -210,12 +345,30 @@ impl Swkbd { @@ -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.
///
/// `button` is the `Button` to be configured
/// `text` configures the display text for the button
/// `submit` configures 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);
///
/// // Set the left button text to "Cancel" and pressing it will NOT return the user's input.
/// keyboard.configure_button(Button::Left, "Cancel", false);
///
/// // 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")]
pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) {
unsafe {
@ -229,19 +382,33 @@ impl Swkbd { @@ -229,19 +382,33 @@ impl Swkbd {
}
}
/// Configures the maximum number of UTF-16 code units that can be entered into the software
/// keyboard. By default the limit is 0xFDE8 code units.
/// 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.
///
/// Note that keyboard input is converted from UTF-16 to UTF-8 before being handed to Rust,
/// # Notes
///
/// 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
/// 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) {
self.state.max_text_len = len;
}
fn parse_swkbd_error(&self) -> Error {
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_HOMEPRESSED => Error::HomePressed,
ctru_sys::SWKBD_RESETPRESSED => Error::ResetPressed,
@ -254,12 +421,44 @@ impl Swkbd { @@ -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 {
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!(Button, ctru_sys::SwkbdButton);
from_impl!(Error, ctru_sys::SwkbdResult);

Loading…
Cancel
Save