Skip to content

Commit

Permalink
Improve SoftwareKeyboard::set_filter_callback
Browse files Browse the repository at this point in the history
  • Loading branch information
FenrirWolf committed Feb 15, 2024
1 parent 947c3f4 commit bf608bc
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 28 deletions.
11 changes: 4 additions & 7 deletions ctru-rs/examples/software-keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
use ctru::applets::swkbd::{Button, CallbackResult, SoftwareKeyboard};
use ctru::prelude::*;

use std::ffi::CString;

fn main() {
let apt = Apt::new().unwrap();
let mut hid = Hid::new().unwrap();
Expand All @@ -21,17 +19,16 @@ fn main() {
// Custom filter callback to handle the given input.
// Using this callback it's possible to integrate the applet
// with custom error messages when the input is incorrect.
keyboard.set_filter_callback(Some(Box::new(|str| {
// The string is guaranteed to contain valid Unicode text, so we can safely unwrap and use it as a normal `&str`.
if str.to_str().unwrap().contains("boo") {
keyboard.set_filter_callback(Some(|text| {
if text.contains("boo") {
return (
CallbackResult::Retry,
Some(CString::new("Ah, you scared me!").unwrap()),
Some(String::from("Ah, you scared me!")),
);
}

(CallbackResult::Ok, None)
})));
}));

println!("Press A to enter some text or press Start to exit.");

Expand Down
43 changes: 22 additions & 21 deletions ctru-rs/src/applets/swkbd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ use ctru_sys::{self, SwkbdState};
use bitflags::bitflags;
use libc;

use std::ffi::{CStr, CString};
use std::ffi::CString;
use std::fmt::Display;
use std::iter::once;
use std::str;

type CallbackFunction = dyn Fn(&CStr) -> (CallbackResult, Option<CString>);
type CallbackFunction = fn(&str) -> (CallbackResult, Option<String>);

/// Configuration structure to setup the Software Keyboard applet.
#[doc(alias = "SwkbdState")]
pub struct SoftwareKeyboard {
state: Box<SwkbdState>,
callback: Option<Box<CallbackFunction>>,
callback: Option<CallbackFunction>,
error_message: Option<CString>,
}

Expand Down Expand Up @@ -399,24 +399,23 @@ impl SoftwareKeyboard {
/// # fn main() {
/// #
/// use std::borrow::Cow;
/// use std::ffi::CString;
/// use ctru::applets::swkbd::{SoftwareKeyboard, CallbackResult};
///
/// let mut keyboard = SoftwareKeyboard::default();
///
/// keyboard.set_filter_callback(Some(Box::new(|str| {
/// if str.to_str().unwrap().contains("boo") {
/// keyboard.set_filter_callback(Some(|text| {
/// if text.contains("boo") {
/// return (
/// CallbackResult::Retry,
/// Some(CString::new("Ah, you scared me!").unwrap()),
/// Some(String::from("Ah, you scared me!")),
/// );
/// }
///
/// (CallbackResult::Ok, None)
/// })));
/// }));
/// #
/// # }
pub fn set_filter_callback(&mut self, callback: Option<Box<CallbackFunction>>) {
pub fn set_filter_callback(&mut self, callback: Option<CallbackFunction>) {
self.callback = callback;
}

Expand All @@ -425,30 +424,32 @@ impl SoftwareKeyboard {
user: *mut libc::c_void,
pp_message: *mut *const libc::c_char,
text: *const libc::c_char,
_text_size: libc::size_t,
text_size: libc::size_t,
) -> ctru_sys::SwkbdCallbackResult {
let this: *mut SoftwareKeyboard = user.cast();
let this = unsafe { &mut *user.cast::<SoftwareKeyboard>() };

unsafe {
// Reset any leftover error message.
(*this).error_message = None;
// Reset any leftover error message.
this.error_message = None;

let text = CStr::from_ptr(text);
unsafe {
let text = std::str::from_utf8_unchecked(std::slice::from_raw_parts(text, text_size));

let result = {
// Run the callback if still available.
if let Some(callback) = &mut (*this).callback {
let (res, cstr) = callback(text);
if let Some(callback) = this.callback {
let (result, error_message) = callback(text);

// Due to how `libctru` operates, the user is expected to keep the error message alive until
// the end of the Software Keyboard prompt. We ensure that happens by saving it within the configuration.
(*this).error_message = cstr;
if let Some(error_message) = error_message {
let error_message = CString::from_vec_unchecked(error_message.into_bytes());

*pp_message = error_message.as_ptr();

if let Some(newstr) = &(*this).error_message {
*pp_message = newstr.as_ptr();
this.error_message = Some(error_message);
}

res
result
} else {
CallbackResult::Ok
}
Expand Down

0 comments on commit bf608bc

Please sign in to comment.