Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More swkbd API improvements #172

Merged
merged 7 commits into from
Mar 28, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 51 additions & 42 deletions ctru-rs/src/applets/swkbd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use ctru_sys::{
use bitflags::bitflags;

use std::borrow::Cow;
use std::ffi::{CStr, CString};
use std::fmt::Display;
use std::iter::once;
use std::str;
Expand All @@ -25,7 +24,7 @@ type CallbackFunction = dyn Fn(&str) -> (CallbackResult, Option<Cow<'static, str
pub struct SoftwareKeyboard {
state: Box<SwkbdState>,
filter_callback: Option<Box<CallbackFunction>>,
initial_text: Option<CString>,
initial_text: Option<Cow<'static, str>>,
}

/// Configuration structure to setup the Parental Lock applet.
Expand Down Expand Up @@ -273,12 +272,14 @@ impl SoftwareKeyboard {
pub fn launch(&mut self, _apt: &Apt, _gfx: &Gfx) -> Result<(String, Button), Error> {
let mut output = String::new();

match self.swkbd_input_text(&mut output) {
ctru_sys::SWKBD_BUTTON_NONE => Err(self.state.result.into()),
ctru_sys::SWKBD_BUTTON_LEFT => Ok((output, Button::Left)),
ctru_sys::SWKBD_BUTTON_MIDDLE => Ok((output, Button::Middle)),
ctru_sys::SWKBD_BUTTON_RIGHT => Ok((output, Button::Right)),
_ => unreachable!(),
unsafe {
match self.swkbd_input_text(&mut output) {
ctru_sys::SWKBD_BUTTON_NONE => Err(self.state.result.into()),
ctru_sys::SWKBD_BUTTON_LEFT => Ok((output, Button::Left)),
ctru_sys::SWKBD_BUTTON_MIDDLE => Ok((output, Button::Middle)),
ctru_sys::SWKBD_BUTTON_RIGHT => Ok((output, Button::Right)),
_ => unreachable!(),
}
}
}

Expand Down Expand Up @@ -391,6 +392,10 @@ impl SoftwareKeyboard {
///
/// The initial text is the text already written when you open the software keyboard.
///
/// # Notes
///
/// Passing [`None`] will clear the initial text.
///
/// # Example
///
/// ```
Expand All @@ -400,30 +405,25 @@ impl SoftwareKeyboard {
/// use ctru::applets::swkbd::SoftwareKeyboard;
/// let mut keyboard = SoftwareKeyboard::default();
///
/// keyboard.set_initial_text(Some("Write here what you like!"));
/// keyboard.set_initial_text(Some("Write here what you like!".into()));
/// #
/// # }
#[doc(alias = "swkbdSetInitialText")]
pub fn set_initial_text(&mut self, text: Option<&str>) {
if let Some(text) = text {
let initial_text = CString::new(text).unwrap();

unsafe {
ctru_sys::swkbdSetInitialText(self.state.as_mut(), initial_text.as_ptr());
}

self.initial_text = Some(initial_text);
} else {
unsafe { ctru_sys::swkbdSetInitialText(self.state.as_mut(), std::ptr::null()) };

self.initial_text = None;
}
pub fn set_initial_text(&mut self, text: Option<Cow<'static, str>>) {
self.initial_text = text;
}

/// 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.
///
/// # Notes
///
/// Passing [`None`] will clear the hint text.
///
/// The hint text will be converted to UTF-16 when passed to the software keyboard, and the text will be truncated
/// if the length exceeds 64 code units after conversion.
///
/// # Example
///
/// ```
Expand All @@ -433,14 +433,22 @@ impl SoftwareKeyboard {
/// use ctru::applets::swkbd::SoftwareKeyboard;
/// let mut keyboard = SoftwareKeyboard::default();
///
/// keyboard.set_hint_text("Write here what you like!");
/// keyboard.set_hint_text(Some("Write here what you like!"));
/// #
/// # }
#[doc(alias = "swkbdSetHintText")]
pub fn set_hint_text(&mut self, text: &str) {
unsafe {
let nul_terminated: String = text.chars().chain(once('\0')).collect();
ctru_sys::swkbdSetHintText(self.state.as_mut(), nul_terminated.as_ptr());
pub fn set_hint_text(&mut self, text: Option<&str>) {
if let Some(text) = text {
for (idx, code_point) in text
.encode_utf16()
.take(self.state.hint_text.len() - 1)
.chain(once(0))
.enumerate()
{
self.state.hint_text[idx] = code_point;
}
} else {
self.state.hint_text[0] = 0;
}
}

Expand Down Expand Up @@ -577,10 +585,13 @@ impl SoftwareKeyboard {
self.state.valid_input = ValidInput::FixedLen.into();
}

// A reimplementation of `swkbdInputText` from `libctru/source/applets/swkbd.c`. Allows us to
// get text from the software keyboard and put it directly into a `String` without requiring
// an intermediate fixed-size buffer
fn swkbd_input_text(&mut self, output: &mut String) -> SwkbdButton {
/// A reimplementation of `swkbdInputText` from `libctru/source/applets/swkbd.c`. Allows us to fix various
/// API nits and get rid of awkward type conversions when interacting with the Software Keyboard.
///
/// # Safety
///
/// The [`Apt`] and [`Gfx`] services must be active when this function is called.
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved
unsafe fn swkbd_input_text(&mut self, output: &mut String) -> SwkbdButton {
use ctru_sys::{
MEMPERM_READ, MEMPERM_WRITE, R_FAILED, SWKBD_BUTTON_LEFT, SWKBD_BUTTON_MIDDLE,
SWKBD_BUTTON_NONE, SWKBD_BUTTON_RIGHT, SWKBD_D0_CLICK, SWKBD_D1_CLICK0,
Expand Down Expand Up @@ -661,19 +672,17 @@ impl SoftwareKeyboard {
}

// Copy stuff to shared mem
if !extra.initial_text.is_null() {
if let Some(initial_text) = self.initial_text.as_deref() {
swkbd.initial_text_offset = 0;

unsafe {
let utf16_iter =
str::from_utf8_unchecked(CStr::from_ptr(extra.initial_text).to_bytes())
.encode_utf16()
.take(swkbd.max_text_len as _)
.chain(once(0));

let mut initial_text_cursor = swkbd_shared_mem_ptr.cast();
let mut initial_text_cursor = swkbd_shared_mem_ptr.cast();

for code_unit in utf16_iter {
for code_unit in initial_text
.encode_utf16()
.take(swkbd.max_text_len as _)
.chain(once(0))
{
unsafe {
*initial_text_cursor = code_unit;
initial_text_cursor = initial_text_cursor.add(1);
}
Expand Down
Loading