Skip to content

Commit cb77ad2

Browse files
authored
Revise swkbd filter callback API (#216)
* Revise swkbd filter callback API * Use type alias for CallbackFunction (again)
1 parent 4d9587d commit cb77ad2

File tree

2 files changed

+36
-24
lines changed

2 files changed

+36
-24
lines changed

ctru-rs/examples/software-keyboard.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ fn main() {
1919
// Custom filter callback to handle the given input.
2020
// Using this callback it's possible to integrate the applet
2121
// with custom error messages when the input is incorrect.
22-
keyboard.set_filter_callback(Some(Box::new(move |str| {
23-
if str.contains("boo") {
24-
return (CallbackResult::Retry, Some("Ah, you scared me!".into()));
22+
let mut did_it_again = false;
23+
24+
keyboard.set_filter_callback(Some(Box::new(move |input| {
25+
if input.contains("boo") && !did_it_again {
26+
did_it_again = true;
27+
CallbackResult::Retry("Aaaah, you scared me! Don't use that word again!".into())
28+
} else if input.contains("boo") && did_it_again {
29+
CallbackResult::Close("Hey, I told you to stop that!".into())
30+
} else {
31+
did_it_again = false;
32+
CallbackResult::Ok
2533
}
26-
27-
(CallbackResult::Ok, None)
2834
})));
2935

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

ctru-rs/src/applets/swkbd.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::fmt::Display;
1717
use std::iter::once;
1818
use std::str;
1919

20-
type CallbackFunction = dyn Fn(&str) -> (CallbackResult, Option<Cow<'static, str>>);
20+
type CallbackFunction = dyn FnMut(&str) -> CallbackResult;
2121

2222
/// Configuration structure to setup the Software Keyboard applet.
2323
#[doc(alias = "SwkbdState")]
@@ -59,15 +59,24 @@ pub enum Kind {
5959
///
6060
/// The custom callback can be set using [`SoftwareKeyboard::set_filter_callback()`].
6161
#[doc(alias = "SwkbdCallbackResult")]
62-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
62+
#[derive(Clone, Debug, PartialEq, Eq)]
6363
#[repr(u8)]
6464
pub enum CallbackResult {
6565
/// The callback yields a positive result.
6666
Ok = ctru_sys::SWKBD_CALLBACK_OK,
6767
/// The callback finds the input invalid, but lets the user try again.
68-
Retry = ctru_sys::SWKBD_CALLBACK_CONTINUE,
68+
Retry(Cow<'static, str>) = ctru_sys::SWKBD_CALLBACK_CONTINUE,
6969
/// The callback finds the input invalid and closes the Software Keyboard view.
70-
Close = ctru_sys::SWKBD_CALLBACK_CLOSE,
70+
Close(Cow<'static, str>) = ctru_sys::SWKBD_CALLBACK_CLOSE,
71+
}
72+
73+
impl CallbackResult {
74+
fn discriminant(&self) -> u8 {
75+
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
76+
// between `repr(C)` structs, each of which has the `u8` discriminant as its first
77+
// field, so we can read the discriminant without offsetting the pointer.
78+
unsafe { *(self as *const Self).cast() }
79+
}
7180
}
7281

7382
/// Represents which button the user pressed to close the [`SoftwareKeyboard`].
@@ -210,9 +219,8 @@ bitflags! {
210219
}
211220

212221
// Internal book-keeping struct used to send data to `aptSetMessageCallback` when calling the software keyboard.
213-
#[derive(Copy, Clone)]
214222
struct MessageCallbackData {
215-
filter_callback: *const Box<CallbackFunction>,
223+
filter_callback: *mut Box<CallbackFunction>,
216224
swkbd_shared_mem_ptr: *mut libc::c_void,
217225
}
218226

@@ -347,12 +355,12 @@ impl SoftwareKeyboard {
347355
///
348356
/// let mut keyboard = SoftwareKeyboard::default();
349357
///
350-
/// keyboard.set_filter_callback(Some(Box::new(move |str| {
351-
/// if str.contains("boo") {
352-
/// return (CallbackResult::Retry, Some("Ah, you scared me!".into()));
358+
/// keyboard.set_filter_callback(Some(Box::new(move |input| {
359+
/// if input.contains("boo") {
360+
/// CallbackResult::Retry("Aaaah, you scared me!".into())
361+
/// } else {
362+
/// CallbackResult::Ok
353363
/// }
354-
///
355-
/// (CallbackResult::Ok, None)
356364
/// })));
357365
/// #
358366
/// # }
@@ -733,7 +741,7 @@ impl SoftwareKeyboard {
733741
// `self` is allowed to be moved again, we can safely use a pointer to the local value contained in `self.filter_callback`
734742
// The cast here is also sound since the pointer will only be read from if `self.filter_callback.is_some()` returns true.
735743
let mut data = MessageCallbackData {
736-
filter_callback: (&raw const self.filter_callback).cast(),
744+
filter_callback: (&raw mut self.filter_callback).cast(),
737745
swkbd_shared_mem_ptr,
738746
};
739747

@@ -813,7 +821,8 @@ impl SoftwareKeyboard {
813821
}
814822

815823
let swkbd = unsafe { &mut *msg.cast::<SwkbdState>() };
816-
let data = unsafe { *user.cast::<MessageCallbackData>() };
824+
825+
let data = unsafe { &*user.cast::<MessageCallbackData>() };
817826

818827
let text16 = unsafe {
819828
widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts(
@@ -824,13 +833,11 @@ impl SoftwareKeyboard {
824833

825834
let text8 = text16.to_string();
826835

827-
let filter_callback = unsafe { &**data.filter_callback };
828-
829-
let (result, retmsg) = filter_callback(&text8);
836+
let result = unsafe { &mut **data.filter_callback }(&text8);
830837

831-
swkbd.callback_result = result as _;
838+
swkbd.callback_result = result.discriminant().into();
832839

833-
if let Some(msg) = retmsg.as_deref() {
840+
if let CallbackResult::Retry(msg) | CallbackResult::Close(msg) = result {
834841
for (idx, code_unit) in msg
835842
.encode_utf16()
836843
.take(swkbd.callback_msg.len() - 1)
@@ -989,4 +996,3 @@ from_impl!(ValidInput, i32);
989996
from_impl!(ValidInput, u32);
990997
from_impl!(ButtonConfig, i32);
991998
from_impl!(PasswordMode, u32);
992-
from_impl!(CallbackResult, u32);

0 commit comments

Comments
 (0)