aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ctru-rs/src/applets/mod.rs1
-rw-r--r--ctru-rs/src/applets/swkbd.rs211
-rw-r--r--ctru-rs/src/lib.rs1
-rw-r--r--examples/src/bin/software-keyboard.rs47
4 files changed, 260 insertions, 0 deletions
diff --git a/ctru-rs/src/applets/mod.rs b/ctru-rs/src/applets/mod.rs
new file mode 100644
index 0000000..73469a3
--- /dev/null
+++ b/ctru-rs/src/applets/mod.rs
@@ -0,0 +1 @@
+pub mod swkbd;
diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs
new file mode 100644
index 0000000..515dd92
--- /dev/null
+++ b/ctru-rs/src/applets/swkbd.rs
@@ -0,0 +1,211 @@
+use std::iter::once;
+use std::mem;
+use std::str;
+
+use libctru::{self, SwkbdState, swkbdInit, swkbdSetFeatures, swkbdSetHintText, swkbdInputText,
+ swkbdSetButton};
+
+use libc;
+
+/// An instance of the software keyboard.
+pub struct Swkbd {
+ state: Box<SwkbdState>,
+}
+
+/// The kind of keyboard to be initialized.
+///
+/// Normal is the full keyboard with several pages (QUERTY/accents/symbol/mobile)
+/// Querty is a QUERTY-only keyboard.
+/// Numpad is a number pad.
+/// Western is a text keyboard without japanese symbols (only applies to JPN systems). For other
+/// systems it's the same as a Normal keyboard.
+#[derive(Copy, Clone, Debug)]
+pub enum Kind {
+ Normal,
+ Querty,
+ Numpad,
+ Western,
+}
+
+/// Represents which button the user pressed to close the software keyboard.
+#[derive(Copy, Clone, Debug)]
+pub enum Button {
+ Left,
+ Middle,
+ Right,
+}
+
+/// Error type for the software keyboard.
+#[derive(Copy, Clone, Debug)]
+pub enum Error {
+ InvalidInput,
+ OutOfMem,
+ HomePressed,
+ ResetPressed,
+ PowerPressed,
+ ParentalOk,
+ ParentalFail,
+ BannedInput,
+}
+
+/// Restrictions on keyboard input
+#[derive(Copy, Clone, Debug)]
+pub enum ValidInput {
+ Anything,
+ NotEmpty,
+ NotEmptyNotBlank,
+ NotBlank,
+ FixedLen,
+}
+
+/// Keyboard feature flags
+bitflags! {
+ pub struct Features: u32 {
+ const PARENTAL_PIN = 1 << 0;
+ const DARKEN_TOP_SCREEN = 1 << 1;
+ const PREDICTIVE_INPUT = 1 << 2;
+ const MULTILINE = 1 << 3;
+ const FIXED_WIDTH = 1 << 4;
+ const ALLOW_HOME = 1 << 5;
+ const ALLOW_RESET = 1 << 6;
+ const ALLOW_POWER = 1 << 7;
+ const DEFAULT_QUERTY = 1 << 8;
+ }
+}
+
+/// Keyboard input filtering flags
+bitflags! {
+ pub struct Filters: u32 {
+ const DIGITS = 1 << 0;
+ const AT = 1 << 1;
+ const PERCENT = 1 << 2;
+ const BACKSLASH = 1 << 3;
+ const PROFANITY = 1 << 4;
+ const CALLBACK = 1 << 5;
+ }
+}
+
+impl Swkbd {
+ /// Initializes a software keyboard of the specified type and the chosen number of buttons
+ /// (from 1-3).
+ pub fn init(keyboard_type: Kind, num_buttons: i32) -> Self {
+ unsafe {
+ let mut state = Box::new(mem::uninitialized::<SwkbdState>());
+ swkbdInit(state.as_mut(), keyboard_type as u32, num_buttons, -1);
+ Swkbd { state }
+ }
+ }
+
+ /// Gets input from this keyboard and appends it to the provided string.
+ ///
+ /// The text received from the keyboard will be truncated if it is greater than 2048 bytes
+ /// in length.
+ pub fn get_utf8(&mut self, buf: &mut String) -> Result<Button, Error> {
+ // Unfortunately the libctru API doesn't really provide a way to get the exact length
+ // of the string that it receieves from the software keyboard. Instead it expects you
+ // to pass in a buffer and hope that it's big enough to fit the entire string, so
+ // you have to set some upper limit on the potential size of the user's input.
+ const MAX_BYTES: usize = 2048;
+ let mut tmp = [0u8; MAX_BYTES];
+ let button = self.get_bytes(&mut tmp)?;
+
+ // libctru does, however, seem to ensure that the buffer will always contain a properly
+ // terminated UTF-8 sequence even if the input has to be truncated, so these operations
+ // should be safe.
+ let len = unsafe { libc::strlen(tmp.as_ptr()) };
+ let utf8 = unsafe { str::from_utf8_unchecked(&tmp[..len]) };
+
+ // Copy the input into the user's `String`
+ *buf += utf8;
+ Ok(button)
+ }
+
+ /// Fills the provided buffer with a UTF-8 encoded, NUL-terminated sequence of bytes from
+ /// this software 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
+ pub fn get_bytes(&mut self, buf: &mut [u8]) -> Result<Button, Error> {
+ unsafe {
+ match swkbdInputText(self.state.as_mut(), buf.as_mut_ptr(), buf.len()) {
+ libctru::SWKBD_BUTTON_NONE => Err(self.parse_swkbd_error()),
+ libctru::SWKBD_BUTTON_LEFT => Ok(Button::Left),
+ libctru::SWKBD_BUTTON_MIDDLE => Ok(Button::Middle),
+ libctru::SWKBD_BUTTON_RIGHT => Ok(Button::Right),
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ /// Sets special features for this keyboard
+ pub fn set_features(&mut self, features: Features) {
+ 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 as i32;
+ 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
+ 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)
+ pub fn set_hint_text(&mut self, text: &str) {
+ unsafe {
+ let nul_terminated: String = text.chars().chain(once('\0')).collect();
+ swkbdSetHintText(self.state.as_mut(), nul_terminated.as_ptr());
+ }
+ }
+
+ /// Configures the look and behavior of a button for this keyboard.
+ ///
+ /// `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.
+ pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) {
+ unsafe {
+ let nul_terminated: String = text.chars().chain(once('\0')).collect();
+ swkbdSetButton(self.state.as_mut(), button as u32, nul_terminated.as_ptr(), submit);
+ }
+ }
+
+ /// 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.
+ ///
+ /// Note that 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.
+ 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 {
+ libctru::SWKBD_INVALID_INPUT => Error::InvalidInput,
+ libctru::SWKBD_OUTOFMEM => Error::OutOfMem,
+ libctru::SWKBD_HOMEPRESSED => Error::HomePressed,
+ libctru::SWKBD_RESETPRESSED => Error::ResetPressed,
+ libctru::SWKBD_POWERPRESSED => Error::PowerPressed,
+ libctru::SWKBD_PARENTAL_OK => Error::ParentalOk,
+ libctru::SWKBD_PARENTAL_FAIL => Error::ParentalFail,
+ libctru::SWKBD_BANNED_INPUT => Error::BannedInput,
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl Default for Swkbd {
+ fn default() -> Self {
+ Swkbd::init(Kind::Normal, 2)
+ }
+}
diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs
index 56ac370..c3caf62 100644
--- a/ctru-rs/src/lib.rs
+++ b/ctru-rs/src/lib.rs
@@ -11,6 +11,7 @@ extern crate widestring;
extern crate ctru_sys as libctru;
+pub mod applets;
pub mod console;
pub mod error;
pub mod srv;
diff --git a/examples/src/bin/software-keyboard.rs b/examples/src/bin/software-keyboard.rs
new file mode 100644
index 0000000..cb1b058
--- /dev/null
+++ b/examples/src/bin/software-keyboard.rs
@@ -0,0 +1,47 @@
+extern crate ctru;
+
+use ctru::gfx::Gfx;
+use ctru::console::Console;
+use ctru::services::apt::Apt;
+use ctru::services::hid::{Hid, KeyPad};
+use ctru::applets::swkbd::{Swkbd, Button};
+
+fn main() {
+ let apt = Apt::init().unwrap();
+ let hid = Hid::init().unwrap();
+ let gfx = Gfx::default();
+ let _console = Console::default();
+
+ println!("Press A to enter some text or press Start to quit");
+
+ while apt.main_loop() {
+ gfx.flush_buffers();
+ gfx.swap_buffers();
+ gfx.wait_for_vblank();
+
+ hid.scan_input();
+
+ if hid.keys_down().contains(KeyPad::KEY_A) {
+ // Prepares a software keyboard with two buttons: One to cancel input and one
+ // to accept it. You can also use `Swkbd::init()` to launch the keyboard in different
+ // configurations.
+ let mut keyboard = Swkbd::default();
+
+ // String used to store text received from the keyboard
+ let mut text = String::new();
+
+ // Raise the software keyboard. You can perform different actions depending on which
+ // software button the user pressed
+ match keyboard.get_utf8(&mut text) {
+ Ok(Button::Right) => println!("You entered: {}", text),
+ Ok(Button::Left) => println!("Cancelled"),
+ Ok(Button::Middle) => println!("How did you even press this?"),
+ Err(_) => println!("Oh noes, an error happened!"),
+ }
+ }
+
+ if hid.keys_down().contains(KeyPad::KEY_START) {
+ break;
+ }
+ }
+}