From 83ebecbb1f2a6cb4c930bc4d8a96eba474d79daa Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 20 Feb 2024 16:43:07 -0700 Subject: [PATCH 1/7] Use Rust strings in set_initial_text --- ctru-rs/src/applets/swkbd.rs | 45 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index ce2ab69..fd00b31 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -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; @@ -25,7 +24,7 @@ type CallbackFunction = dyn Fn(&str) -> (CallbackResult, Option, filter_callback: Option>, - initial_text: Option, + initial_text: Option>, } /// Configuration structure to setup the Parental Lock applet. @@ -391,33 +390,25 @@ 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 /// /// ``` - /// # let _runner = test_runner::GdbRunner::default(); + /// # let _runner = test_runner::GdbRunner::default()>; /// # fn main() { /// # /// 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(String::from("Write here what you like!"))); /// # /// # } #[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>) { + self.initial_text = text; } /// Set the hint text for this software keyboard. @@ -661,19 +652,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); } From 01731cac0eea8c7c7821d9294e56dc9056eca981 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Thu, 7 Mar 2024 01:45:50 -0700 Subject: [PATCH 2/7] Set hint text directly instead of using libctru fn --- ctru-rs/src/applets/swkbd.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index fd00b31..5ebeb93 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -397,13 +397,13 @@ impl SoftwareKeyboard { /// # Example /// /// ``` - /// # let _runner = test_runner::GdbRunner::default()>; + /// # let _runner = test_runner::GdbRunner::default(); /// # fn main() { /// # /// use ctru::applets::swkbd::SoftwareKeyboard; /// let mut keyboard = SoftwareKeyboard::default(); /// - /// keyboard.set_initial_text(Some(String::from("Write here what you like!"))); + /// keyboard.set_initial_text(Some("Write here what you like!".into())); /// # /// # } #[doc(alias = "swkbdSetInitialText")] @@ -415,6 +415,13 @@ impl SoftwareKeyboard { /// /// 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 /// /// ``` @@ -424,14 +431,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; } } From e5581b46e75829ed055b93da4b5aa4b3b02716c4 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Thu, 7 Mar 2024 02:09:28 -0700 Subject: [PATCH 3/7] swkbd_input_text should still be an unsafe fn --- ctru-rs/src/applets/swkbd.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 5ebeb93..80e9f78 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -272,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!(), + } } } @@ -583,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. + 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, From efaccdd2e5d9717993e8cf8c2d493b34440f4262 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 10 Mar 2024 18:28:00 -0600 Subject: [PATCH 4/7] swkbd_input_text is no longer an unsafe fn --- ctru-rs/src/applets/swkbd.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 80e9f78..09f37bd 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -269,17 +269,15 @@ impl SoftwareKeyboard { /// # } /// ``` #[doc(alias = "swkbdInputText")] - pub fn launch(&mut self, _apt: &Apt, _gfx: &Gfx) -> Result<(String, Button), Error> { + pub fn launch(&mut self, apt: &Apt, gfx: &Gfx) -> Result<(String, Button), Error> { let mut output = String::new(); - 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!(), - } + match self.swkbd_input_text(&mut output, apt, gfx) { + 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!(), } } @@ -585,13 +583,9 @@ impl SoftwareKeyboard { self.state.valid_input = ValidInput::FixedLen.into(); } - /// 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. - unsafe 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. + fn swkbd_input_text(&mut self, output: &mut String, _apt: &Apt, _gfx: &Gfx) -> 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, From d8cffcff611b28c17d4df81fae04d7e5dc07ca1a Mon Sep 17 00:00:00 2001 From: Fenrir Date: Thu, 21 Mar 2024 02:15:04 -0600 Subject: [PATCH 5/7] Remove allocation in Swkbd::configure_button --- ctru-rs/src/applets/swkbd.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 09f37bd..df3b5e4 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -541,15 +541,18 @@ impl SoftwareKeyboard { /// # } #[doc(alias = "swkbdSetButton")] pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) { - unsafe { - let nul_terminated: String = text.chars().chain(once('\0')).collect(); - ctru_sys::swkbdSetButton( - self.state.as_mut(), - button.into(), - nul_terminated.as_ptr(), - submit, - ); + let button_text = &mut self.state.button_text[button as usize]; + + for (idx, code_unit) in text + .encode_utf16() + .take(ctru_sys::SWKBD_MAX_BUTTON_TEXT_LEN as _) + .chain(once(0)) + .enumerate() + { + button_text[idx] = code_unit; } + + self.state.button_submits_text[button as usize] = submit; } /// Configure the maximum number of UTF-16 code units that can be entered into the software From 476a8120eac7ce950b84accd963d77e6f855a77e Mon Sep 17 00:00:00 2001 From: Fenrir Date: Thu, 21 Mar 2024 02:18:53 -0600 Subject: [PATCH 6/7] Variable name and style fixes --- ctru-rs/src/applets/swkbd.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index df3b5e4..6a42ebd 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -437,13 +437,13 @@ impl SoftwareKeyboard { #[doc(alias = "swkbdSetHintText")] pub fn set_hint_text(&mut self, text: Option<&str>) { if let Some(text) = text { - for (idx, code_point) in text + for (idx, code_unit) in text .encode_utf16() .take(self.state.hint_text.len() - 1) .chain(once(0)) .enumerate() { - self.state.hint_text[idx] = code_point; + self.state.hint_text[idx] = code_unit; } } else { self.state.hint_text[0] = 0; @@ -732,7 +732,7 @@ impl SoftwareKeyboard { // We need to pass a thin pointer to the boxed closure over FFI. Since we know that the message callback will finish before // `self` is allowed to be moved again, we can safely use a pointer to the local value contained in `self.filter_callback` // The cast here is also sound since the pointer will only be read from if `self.filter_callback.is_some()` returns true. - let mut message_callback_data = MessageCallbackData { + let mut data = MessageCallbackData { filter_callback: std::ptr::addr_of!(self.filter_callback).cast(), swkbd_shared_mem_ptr, }; @@ -740,7 +740,7 @@ impl SoftwareKeyboard { if self.filter_callback.is_some() { aptSetMessageCallback( Some(Self::swkbd_message_callback), - std::ptr::addr_of_mut!(message_callback_data).cast(), + std::ptr::addr_of_mut!(data).cast(), ) } From ad8b328387458843682ba766eff7b171ef467658 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 26 Mar 2024 18:12:58 -0600 Subject: [PATCH 7/7] Use array len instead of libctru constant --- ctru-rs/src/applets/swkbd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 6a42ebd..c3b49fc 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -545,7 +545,7 @@ impl SoftwareKeyboard { for (idx, code_unit) in text .encode_utf16() - .take(ctru_sys::SWKBD_MAX_BUTTON_TEXT_LEN as _) + .take(button_text.len() - 1) .chain(once(0)) .enumerate() {