Fenrir
7 years ago
3 changed files with 195 additions and 0 deletions
@ -0,0 +1,193 @@ |
|||||||
|
use std::mem; |
||||||
|
use std::str; |
||||||
|
|
||||||
|
use libctru::{self, SwkbdState, swkbdInit, swkbdSetFeatures, swkbdInputText}; |
||||||
|
|
||||||
|
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 can be up to 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 = unsafe { self.get_bytes(&mut tmp)? }; |
||||||
|
|
||||||
|
// Make sure we haven't overflowed our buffer. libctru might already check this,
|
||||||
|
// but we'll do it here too just in case
|
||||||
|
let len = unsafe { libc::strlen(tmp.as_ptr()) }; |
||||||
|
assert!(len <= MAX_BYTES); |
||||||
|
|
||||||
|
// Not sure if this is falliable or not in this stage of the process,
|
||||||
|
// but catch any decoding errors to be sure
|
||||||
|
let utf8 = match str::from_utf8(&tmp[..len]) { |
||||||
|
Ok(parsed) => parsed, |
||||||
|
Err(_) => return Err(Error::InvalidInput), |
||||||
|
}; |
||||||
|
|
||||||
|
// Finally, copy the validated input into the user's `String`
|
||||||
|
*buf += utf8; |
||||||
|
Ok(button) |
||||||
|
} |
||||||
|
|
||||||
|
/// Fills the provided buffer with a NUL-terminated sequence of bytes from the software
|
||||||
|
/// keyboard
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// The received bytes are nominally UTF-8 formatted, but the provided buffer must be large
|
||||||
|
/// enough to receive both the text from the software keyboard along with a NUL-terminator.
|
||||||
|
/// Otherwise undefined behavior can result.
|
||||||
|
pub unsafe fn get_bytes(&mut self, buf: &mut [u8]) -> Result<Button, Error> { |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/// 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) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue