From 742ec8d73815a29cb461e2e617a56b655533888b Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 11 Feb 2024 13:09:33 -0700 Subject: [PATCH 01/25] Get swkbd input without max byte size parameter Is this much unsafe code worth such a small quality of life change? Maybe! --- ctru-rs/Cargo.toml | 1 + ctru-rs/examples/file-explorer.rs | 2 +- ctru-rs/examples/software-keyboard.rs | 2 +- ctru-rs/src/applets/swkbd.rs | 373 +++++++++++++++++++++----- 4 files changed, 311 insertions(+), 67 deletions(-) diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 4d6b363b..14084fc2 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -24,6 +24,7 @@ shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" } pthread-3ds = { git = "https://github.com/rust3ds/pthread-3ds.git" } libc = "0.2.121" bitflags = "2.3.3" +widestring = "1.0.2" [build-dependencies] toml = "0.5" diff --git a/ctru-rs/examples/file-explorer.rs b/ctru-rs/examples/file-explorer.rs index 5c158ac7..9647e41e 100644 --- a/ctru-rs/examples/file-explorer.rs +++ b/ctru-rs/examples/file-explorer.rs @@ -165,7 +165,7 @@ impl<'a> FileExplorer<'a> { fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) { let mut keyboard = SoftwareKeyboard::default(); - match keyboard.get_string(2048, self.apt, self.gfx) { + match keyboard.get_string(self.apt, self.gfx) { Ok((path, Button::Right)) => { // Clicked "OK". action(self, path); diff --git a/ctru-rs/examples/software-keyboard.rs b/ctru-rs/examples/software-keyboard.rs index 1d24a7d6..581874ac 100644 --- a/ctru-rs/examples/software-keyboard.rs +++ b/ctru-rs/examples/software-keyboard.rs @@ -46,7 +46,7 @@ fn main() { if hid.keys_down().contains(KeyPad::A) { // Raise the software keyboard. You can perform different actions depending on which // software button the user pressed. - match keyboard.get_string(2048, &apt, &gfx) { + match keyboard.get_string(&apt, &gfx) { Ok((text, Button::Right)) => println!("You entered: {text}"), Ok((_, Button::Left)) => println!("Cancelled"), Ok((_, Button::Middle)) => println!("How did you even press this?"), diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 9dd75d11..cf763b23 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -4,7 +4,12 @@ #![doc(alias = "keyboard")] use crate::services::{apt::Apt, gfx::Gfx}; -use ctru_sys::{self, SwkbdState}; +use ctru_sys::{ + self, aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcBreak, svcCloseHandle, + svcCreateMemoryBlock, APT_SendParameter, SwkbdButton, SwkbdDictWord, SwkbdExtra, + SwkbdLearningData, SwkbdState, SwkbdStatusData, APPID_SOFTWARE_KEYBOARD, APTCMD_MESSAGE, + NS_APPID, SWKBD_CALLBACK_OK, USERBREAK_PANIC, +}; use bitflags::bitflags; use libc; @@ -16,6 +21,10 @@ use std::str; type CallbackFunction = dyn Fn(&CStr) -> (CallbackResult, Option); +// I hate that we have to use this, but sometimes you gotta smuggle pointers into C callbacks +// and that's just how things are +static mut SWKBD_SHARED_MEM: *mut libc::c_void = std::ptr::null_mut(); + /// Configuration structure to setup the Software Keyboard applet. #[doc(alias = "SwkbdState")] pub struct SoftwareKeyboard { @@ -241,11 +250,6 @@ impl SoftwareKeyboard { /// Launches the applet based on the given configuration and returns a string containing the text input. /// - /// # Notes - /// - /// The text received from the keyboard will be truncated if it is longer than `max_bytes`. - /// Use [`SoftwareKeyboard::set_max_text_len()`] to make sure the buffer can contain the input text. - /// /// # Example /// /// ``` @@ -260,67 +264,15 @@ impl SoftwareKeyboard { /// use ctru::applets::swkbd::SoftwareKeyboard; /// let mut keyboard = SoftwareKeyboard::default(); /// - /// let (text, button) = keyboard.get_string(2048, &apt, &gfx)?; + /// let (text, button) = keyboard.get_string(&apt, &gfx)?; /// # /// # Ok(()) /// # } /// ``` #[doc(alias = "swkbdInputText")] - pub fn get_string( - &mut self, - max_bytes: usize, - apt: &Apt, - gfx: &Gfx, - ) -> Result<(String, 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. - let mut tmp = vec![0u8; max_bytes]; - let button = self.write_exact(&mut tmp, apt, gfx)?; - - // libctru does, however, seem to ensure that the buffer will always contain a properly - // terminated UTF-8 sequence even if the input has to be truncated, so these operations - // should be safe. - let len = unsafe { libc::strlen(tmp.as_ptr()) }; - tmp.truncate(len); - - let res = unsafe { String::from_utf8_unchecked(tmp) }; - - Ok((res, button)) - } + pub fn get_string(&mut self, _apt: &Apt, _gfx: &Gfx) -> Result<(String, Button), Error> { + let mut output = String::new(); - /// Fills the provided buffer with a UTF-8 encoded, NUL-terminated sequence of bytes from - /// this software keyboard. - /// - /// # Notes - /// - /// If the buffer is too small to contain the entire sequence received from the keyboard, - /// the output will be truncated. - /// - /// # Example - /// - /// ``` - /// # let _runner = test_runner::GdbRunner::default(); - /// # use std::error::Error; - /// # fn main() -> Result<(), Box> { - /// # use ctru::services::{apt::Apt, gfx::Gfx}; - /// # - /// # let gfx = Gfx::new().unwrap(); - /// # let apt = Apt::new().unwrap(); - /// # - /// use ctru::applets::swkbd::SoftwareKeyboard; - /// let mut keyboard = SoftwareKeyboard::default(); - /// - /// let mut buffer = vec![0; 100]; - /// - /// let button = keyboard.write_exact(&mut buffer, &apt, &gfx)?; - /// # - /// # Ok(()) - /// # } - /// ``` - #[doc(alias = "swkbdInputText")] - pub fn write_exact(&mut self, buf: &mut [u8], _apt: &Apt, _gfx: &Gfx) -> Result { unsafe { // The filter callback gets reset every time the SoftwareKeyboard is used. ctru_sys::swkbdSetFilterCallback( @@ -329,11 +281,11 @@ impl SoftwareKeyboard { (self as *mut Self).cast(), ); - match ctru_sys::swkbdInputText(self.state.as_mut(), buf.as_mut_ptr(), buf.len()) { + match Self::swkbd_input_text(self.state.as_mut(), output.as_mut_vec()) { ctru_sys::SWKBD_BUTTON_NONE => Err(self.state.result.into()), - ctru_sys::SWKBD_BUTTON_LEFT => Ok(Button::Left), - ctru_sys::SWKBD_BUTTON_MIDDLE => Ok(Button::Middle), - ctru_sys::SWKBD_BUTTON_RIGHT => Ok(Button::Right), + 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!(), } } @@ -675,6 +627,297 @@ impl SoftwareKeyboard { // Activate the specific validation rule for maximum length. 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 + unsafe fn swkbd_input_text(swkbd: *mut SwkbdState, buf: &mut Vec) -> 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, + SWKBD_D1_CLICK1, SWKBD_D2_CLICK0, SWKBD_D2_CLICK1, SWKBD_D2_CLICK2, + SWKBD_FILTER_CALLBACK, SWKBD_OUTOFMEM, + }; + + let mut extra = unsafe { (*swkbd).__bindgen_anon_1.extra }; + + // Calculate shared mem size + let mut shared_mem_size = 0; + + shared_mem_size += + (std::mem::size_of::() * ((*swkbd).max_text_len as usize + 1) + 3) & !3; + + let dict_off = shared_mem_size; + + shared_mem_size += + (std::mem::size_of::() * (*swkbd).dict_word_count as usize + 3) & !3; + + let status_off = shared_mem_size; + + shared_mem_size += if (*swkbd).initial_learning_offset >= 0 { + std::mem::size_of::() + } else { + 0 + }; + + let learning_off = shared_mem_size; + + shared_mem_size += if (*swkbd).initial_learning_offset >= 0 { + std::mem::size_of::() + } else { + 0 + }; + + if (*swkbd).save_state_flags & (1 << 0) != 0 { + (*swkbd).status_offset = shared_mem_size as _; + shared_mem_size += std::mem::size_of::(); + } + + if (*swkbd).save_state_flags & (1 << 1) != 0 { + (*swkbd).learning_offset = shared_mem_size as _; + shared_mem_size += std::mem::size_of::(); + } + + shared_mem_size = (shared_mem_size + 0xFFF) & !0xFFF; + + (*swkbd).shared_memory_size = shared_mem_size; + + // Allocate shared mem + unsafe { SWKBD_SHARED_MEM = libc::memalign(0x1000, shared_mem_size).cast() }; + + let mut swkbd_shared_mem_handle = 0; + + if unsafe { SWKBD_SHARED_MEM == std::ptr::null_mut() } { + (*swkbd).result = SWKBD_OUTOFMEM; + return SWKBD_BUTTON_NONE; + } + + let res = unsafe { + svcCreateMemoryBlock( + &mut swkbd_shared_mem_handle, + SWKBD_SHARED_MEM as _, + shared_mem_size as _, + MEMPERM_READ | MEMPERM_WRITE, + MEMPERM_READ | MEMPERM_WRITE, + ) + }; + + if R_FAILED(res) { + unsafe { + libc::free(SWKBD_SHARED_MEM); + (*swkbd).result = SWKBD_OUTOFMEM; + return SWKBD_BUTTON_NONE; + } + } + + // Copy stuff to shared mem + if extra.initial_text != std::ptr::null() { + (*swkbd).initial_text_offset = 0; + + let utf16_iter = CStr::from_ptr(extra.initial_text) + .to_str() + .unwrap() + .encode_utf16() + .chain(once(0)); + + let mut initial_text_cursor = SWKBD_SHARED_MEM.cast::(); + + for ch in utf16_iter { + *initial_text_cursor = ch; + unsafe { initial_text_cursor = initial_text_cursor.add(1) }; + } + } + + if extra.dict != std::ptr::null() { + (*swkbd).dict_offset = dict_off as _; + unsafe { + libc::memcpy( + SWKBD_SHARED_MEM.add(dict_off), + extra.dict.cast(), + std::mem::size_of::() * (*swkbd).dict_word_count as usize, + ) + }; + } + + if (*swkbd).initial_status_offset >= 0 { + (*swkbd).initial_status_offset = status_off as _; + unsafe { + libc::memcpy( + SWKBD_SHARED_MEM.add(status_off), + extra.status_data.cast(), + std::mem::size_of::(), + ) + }; + } + + if (*swkbd).initial_learning_offset >= 0 { + (*swkbd).initial_learning_offset = learning_off as _; + unsafe { + libc::memcpy( + SWKBD_SHARED_MEM.add(learning_off), + extra.learning_data.cast(), + std::mem::size_of::(), + ) + }; + } + + if extra.callback.is_some() { + (*swkbd).filter_flags |= SWKBD_FILTER_CALLBACK; + } else { + (*swkbd).filter_flags &= !SWKBD_FILTER_CALLBACK; + } + + // Launch swkbd + unsafe { + libc::memset( + std::ptr::addr_of_mut!((*swkbd).__bindgen_anon_1.reserved).cast(), + 0, + std::mem::size_of_val(&(*swkbd).__bindgen_anon_1.reserved), + ); + + if extra.callback.is_some() { + aptSetMessageCallback( + Some(Self::swkbd_message_callback), + std::ptr::addr_of_mut!(extra).cast(), + ); + } + + aptLaunchLibraryApplet( + APPID_SOFTWARE_KEYBOARD, + swkbd.cast(), + std::mem::size_of::(), + swkbd_shared_mem_handle, + ); + + if extra.callback.is_some() { + aptSetMessageCallback(None, std::ptr::null_mut()); + } + + let _ = svcCloseHandle(swkbd_shared_mem_handle); + } + + let button = match (*swkbd).result { + SWKBD_D1_CLICK0 | SWKBD_D2_CLICK0 => SWKBD_BUTTON_LEFT, + SWKBD_D2_CLICK1 => SWKBD_BUTTON_MIDDLE, + SWKBD_D0_CLICK | SWKBD_D1_CLICK1 | SWKBD_D2_CLICK2 => SWKBD_BUTTON_RIGHT, + _ => SWKBD_BUTTON_NONE, + }; + + let text16 = if (*swkbd).text_length > 0 { + unsafe { + widestring::Utf16Str::from_slice(std::slice::from_raw_parts( + SWKBD_SHARED_MEM + .add((*swkbd).text_offset as _) + .cast::(), + (*swkbd).text_length as usize + 1, + )) + .unwrap() + } + } else { + widestring::utf16str!("\0") + }; + + buf.extend(text16.encode_utf8()); + + if (*swkbd).save_state_flags & (1 << 0) != 0 { + unsafe { + libc::memcpy( + extra.status_data.cast(), + SWKBD_SHARED_MEM.add((*swkbd).status_offset as _), + std::mem::size_of::(), + ) + }; + } + + if (*swkbd).save_state_flags & (1 << 1) != 0 { + unsafe { + libc::memcpy( + extra.learning_data.cast(), + SWKBD_SHARED_MEM.add((*swkbd).learning_offset as _), + std::mem::size_of::(), + ) + }; + } + + unsafe { libc::free(SWKBD_SHARED_MEM) }; + + button + } + + // A reimplementation of `swkbdMessageCallback` from `libctru/source/applets/swkbd.c`. + // This is only needed because the original function is private to libctru, so we can't + // simply reuse their version + #[allow(non_snake_case)] + unsafe extern "C" fn swkbd_message_callback( + user: *mut libc::c_void, + sender: NS_APPID, + msg: *mut libc::c_void, + msg_size: libc::size_t, + ) { + let extra = user.cast::(); + let swkbd = msg.cast::(); + + if sender != ctru_sys::APPID_SOFTWARE_KEYBOARD + || msg_size != std::mem::size_of::() + { + return; + } + + let text16 = unsafe { + widestring::Utf16Str::from_slice(std::slice::from_raw_parts( + SWKBD_SHARED_MEM + .add((*swkbd).text_offset as _) + .cast::(), + (*swkbd).text_length as usize + 1, + )) + .unwrap() + }; + + let text8 = match String::from_utf8(text16.encode_utf8().collect()) { + Ok(s) => s, + Err(_) => { + svcBreak(USERBREAK_PANIC); + unreachable!(); + } + }; + + let mut retmsg = std::ptr::null(); + + (*swkbd).callback_result = (*extra).callback.unwrap()( + (*extra).callback_user, + &mut retmsg, + text8.as_ptr(), + text8.len(), + ) as _; + + let retmsg = if retmsg != std::ptr::null() { + unsafe { + let len = libc::strlen(retmsg); + std::str::from_utf8_unchecked(std::slice::from_raw_parts(retmsg, len + 1)) + } + } else { + "\0" + }; + + let callback_msg = &mut (*swkbd).callback_msg; + + if (*swkbd).callback_result > SWKBD_CALLBACK_OK as _ { + for (idx, ch) in retmsg.encode_utf16().take(callback_msg.len()).enumerate() { + callback_msg[idx] = ch; + } + } else { + callback_msg[0] = 0; + } + + let _ = APT_SendParameter( + envGetAptAppId(), + sender, + APTCMD_MESSAGE, + swkbd.cast(), + std::mem::size_of::() as _, + 0, + ); + } } impl ParentalLock { From 2864c06207ea3cd76e8a1d8980fb0a6a410a9804 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 11 Feb 2024 18:39:08 -0700 Subject: [PATCH 02/25] Don't include nul terminator when passing string data back to Rust --- ctru-rs/src/applets/swkbd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index cf763b23..c6ad7d85 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -809,12 +809,12 @@ impl SoftwareKeyboard { SWKBD_SHARED_MEM .add((*swkbd).text_offset as _) .cast::(), - (*swkbd).text_length as usize + 1, + (*swkbd).text_length as usize, )) .unwrap() } } else { - widestring::utf16str!("\0") + widestring::utf16str!("") }; buf.extend(text16.encode_utf8()); From 5413e77ec398ab5d7da1b400b7122ad93a6c8b25 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 11 Feb 2024 18:43:12 -0700 Subject: [PATCH 03/25] Use .is_null() instead of comparing to std::ptr::null() --- 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 c6ad7d85..04b83297 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -687,7 +687,7 @@ impl SoftwareKeyboard { let mut swkbd_shared_mem_handle = 0; - if unsafe { SWKBD_SHARED_MEM == std::ptr::null_mut() } { + if unsafe { SWKBD_SHARED_MEM.is_null() } { (*swkbd).result = SWKBD_OUTOFMEM; return SWKBD_BUTTON_NONE; } @@ -711,7 +711,7 @@ impl SoftwareKeyboard { } // Copy stuff to shared mem - if extra.initial_text != std::ptr::null() { + if !extra.initial_text.is_null() { (*swkbd).initial_text_offset = 0; let utf16_iter = CStr::from_ptr(extra.initial_text) @@ -728,7 +728,7 @@ impl SoftwareKeyboard { } } - if extra.dict != std::ptr::null() { + if !extra.dict.is_null() { (*swkbd).dict_offset = dict_off as _; unsafe { libc::memcpy( @@ -890,7 +890,7 @@ impl SoftwareKeyboard { text8.len(), ) as _; - let retmsg = if retmsg != std::ptr::null() { + let retmsg = if !retmsg.is_null() { unsafe { let len = libc::strlen(retmsg); std::str::from_utf8_unchecked(std::slice::from_raw_parts(retmsg, len + 1)) From 3a2000f6f59411c4212ea3f339cb3a9a3e9cee7c Mon Sep 17 00:00:00 2001 From: Fenrir Date: Mon, 12 Feb 2024 02:47:21 -0700 Subject: [PATCH 04/25] Use references and make unsafe blocks more fine-grained --- ctru-rs/src/applets/swkbd.rs | 108 +++++++++++++++++------------------ 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 04b83297..4e763d3d 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -631,7 +631,7 @@ impl SoftwareKeyboard { // 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 - unsafe fn swkbd_input_text(swkbd: *mut SwkbdState, buf: &mut Vec) -> SwkbdButton { + fn swkbd_input_text(swkbd: &mut SwkbdState, buf: &mut Vec) -> 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, @@ -639,22 +639,22 @@ impl SoftwareKeyboard { SWKBD_FILTER_CALLBACK, SWKBD_OUTOFMEM, }; - let mut extra = unsafe { (*swkbd).__bindgen_anon_1.extra }; + let mut extra = unsafe { swkbd.__bindgen_anon_1.extra }; // Calculate shared mem size let mut shared_mem_size = 0; shared_mem_size += - (std::mem::size_of::() * ((*swkbd).max_text_len as usize + 1) + 3) & !3; + (std::mem::size_of::() * (swkbd.max_text_len as usize + 1) + 3) & !3; let dict_off = shared_mem_size; shared_mem_size += - (std::mem::size_of::() * (*swkbd).dict_word_count as usize + 3) & !3; + (std::mem::size_of::() * swkbd.dict_word_count as usize + 3) & !3; let status_off = shared_mem_size; - shared_mem_size += if (*swkbd).initial_learning_offset >= 0 { + shared_mem_size += if swkbd.initial_learning_offset >= 0 { std::mem::size_of::() } else { 0 @@ -662,25 +662,25 @@ impl SoftwareKeyboard { let learning_off = shared_mem_size; - shared_mem_size += if (*swkbd).initial_learning_offset >= 0 { + shared_mem_size += if swkbd.initial_learning_offset >= 0 { std::mem::size_of::() } else { 0 }; - if (*swkbd).save_state_flags & (1 << 0) != 0 { - (*swkbd).status_offset = shared_mem_size as _; + if swkbd.save_state_flags & (1 << 0) != 0 { + swkbd.status_offset = shared_mem_size as _; shared_mem_size += std::mem::size_of::(); } - if (*swkbd).save_state_flags & (1 << 1) != 0 { - (*swkbd).learning_offset = shared_mem_size as _; + if swkbd.save_state_flags & (1 << 1) != 0 { + swkbd.learning_offset = shared_mem_size as _; shared_mem_size += std::mem::size_of::(); } shared_mem_size = (shared_mem_size + 0xFFF) & !0xFFF; - (*swkbd).shared_memory_size = shared_mem_size; + swkbd.shared_memory_size = shared_mem_size; // Allocate shared mem unsafe { SWKBD_SHARED_MEM = libc::memalign(0x1000, shared_mem_size).cast() }; @@ -688,7 +688,7 @@ impl SoftwareKeyboard { let mut swkbd_shared_mem_handle = 0; if unsafe { SWKBD_SHARED_MEM.is_null() } { - (*swkbd).result = SWKBD_OUTOFMEM; + swkbd.result = SWKBD_OUTOFMEM; return SWKBD_BUTTON_NONE; } @@ -705,42 +705,44 @@ impl SoftwareKeyboard { if R_FAILED(res) { unsafe { libc::free(SWKBD_SHARED_MEM); - (*swkbd).result = SWKBD_OUTOFMEM; + swkbd.result = SWKBD_OUTOFMEM; return SWKBD_BUTTON_NONE; } } // Copy stuff to shared mem if !extra.initial_text.is_null() { - (*swkbd).initial_text_offset = 0; + swkbd.initial_text_offset = 0; - let utf16_iter = CStr::from_ptr(extra.initial_text) - .to_str() - .unwrap() - .encode_utf16() - .chain(once(0)); + unsafe { + let utf16_iter = CStr::from_ptr(extra.initial_text) + .to_str() + .unwrap() + .encode_utf16() + .chain(once(0)); - let mut initial_text_cursor = SWKBD_SHARED_MEM.cast::(); + let mut initial_text_cursor = SWKBD_SHARED_MEM.cast::(); - for ch in utf16_iter { - *initial_text_cursor = ch; - unsafe { initial_text_cursor = initial_text_cursor.add(1) }; + for ch in utf16_iter { + *initial_text_cursor = ch; + initial_text_cursor = initial_text_cursor.add(1); + } } } if !extra.dict.is_null() { - (*swkbd).dict_offset = dict_off as _; + swkbd.dict_offset = dict_off as _; unsafe { libc::memcpy( SWKBD_SHARED_MEM.add(dict_off), extra.dict.cast(), - std::mem::size_of::() * (*swkbd).dict_word_count as usize, + std::mem::size_of::() * swkbd.dict_word_count as usize, ) }; } - if (*swkbd).initial_status_offset >= 0 { - (*swkbd).initial_status_offset = status_off as _; + if swkbd.initial_status_offset >= 0 { + swkbd.initial_status_offset = status_off as _; unsafe { libc::memcpy( SWKBD_SHARED_MEM.add(status_off), @@ -750,8 +752,8 @@ impl SoftwareKeyboard { }; } - if (*swkbd).initial_learning_offset >= 0 { - (*swkbd).initial_learning_offset = learning_off as _; + if swkbd.initial_learning_offset >= 0 { + swkbd.initial_learning_offset = learning_off as _; unsafe { libc::memcpy( SWKBD_SHARED_MEM.add(learning_off), @@ -762,17 +764,17 @@ impl SoftwareKeyboard { } if extra.callback.is_some() { - (*swkbd).filter_flags |= SWKBD_FILTER_CALLBACK; + swkbd.filter_flags |= SWKBD_FILTER_CALLBACK; } else { - (*swkbd).filter_flags &= !SWKBD_FILTER_CALLBACK; + swkbd.filter_flags &= !SWKBD_FILTER_CALLBACK; } // Launch swkbd unsafe { libc::memset( - std::ptr::addr_of_mut!((*swkbd).__bindgen_anon_1.reserved).cast(), + std::ptr::addr_of_mut!(swkbd.__bindgen_anon_1.reserved).cast(), 0, - std::mem::size_of_val(&(*swkbd).__bindgen_anon_1.reserved), + swkbd.__bindgen_anon_1.reserved.len(), ); if extra.callback.is_some() { @@ -784,7 +786,7 @@ impl SoftwareKeyboard { aptLaunchLibraryApplet( APPID_SOFTWARE_KEYBOARD, - swkbd.cast(), + swkbd as *mut _ as *mut _, std::mem::size_of::(), swkbd_shared_mem_handle, ); @@ -796,20 +798,18 @@ impl SoftwareKeyboard { let _ = svcCloseHandle(swkbd_shared_mem_handle); } - let button = match (*swkbd).result { + let button = match swkbd.result { SWKBD_D1_CLICK0 | SWKBD_D2_CLICK0 => SWKBD_BUTTON_LEFT, SWKBD_D2_CLICK1 => SWKBD_BUTTON_MIDDLE, SWKBD_D0_CLICK | SWKBD_D1_CLICK1 | SWKBD_D2_CLICK2 => SWKBD_BUTTON_RIGHT, _ => SWKBD_BUTTON_NONE, }; - let text16 = if (*swkbd).text_length > 0 { + let text16 = if swkbd.text_length > 0 { unsafe { widestring::Utf16Str::from_slice(std::slice::from_raw_parts( - SWKBD_SHARED_MEM - .add((*swkbd).text_offset as _) - .cast::(), - (*swkbd).text_length as usize, + SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), + swkbd.text_length as usize, )) .unwrap() } @@ -819,21 +819,21 @@ impl SoftwareKeyboard { buf.extend(text16.encode_utf8()); - if (*swkbd).save_state_flags & (1 << 0) != 0 { + if swkbd.save_state_flags & (1 << 0) != 0 { unsafe { libc::memcpy( extra.status_data.cast(), - SWKBD_SHARED_MEM.add((*swkbd).status_offset as _), + SWKBD_SHARED_MEM.add(swkbd.status_offset as _), std::mem::size_of::(), ) }; } - if (*swkbd).save_state_flags & (1 << 1) != 0 { + if swkbd.save_state_flags & (1 << 1) != 0 { unsafe { libc::memcpy( extra.learning_data.cast(), - SWKBD_SHARED_MEM.add((*swkbd).learning_offset as _), + SWKBD_SHARED_MEM.add(swkbd.learning_offset as _), std::mem::size_of::(), ) }; @@ -854,8 +854,8 @@ impl SoftwareKeyboard { msg: *mut libc::c_void, msg_size: libc::size_t, ) { - let extra = user.cast::(); - let swkbd = msg.cast::(); + let extra = &mut *user.cast::(); + let swkbd = &mut *msg.cast::(); if sender != ctru_sys::APPID_SOFTWARE_KEYBOARD || msg_size != std::mem::size_of::() @@ -865,10 +865,8 @@ impl SoftwareKeyboard { let text16 = unsafe { widestring::Utf16Str::from_slice(std::slice::from_raw_parts( - SWKBD_SHARED_MEM - .add((*swkbd).text_offset as _) - .cast::(), - (*swkbd).text_length as usize + 1, + SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), + swkbd.text_length as usize + 1, )) .unwrap() }; @@ -883,8 +881,8 @@ impl SoftwareKeyboard { let mut retmsg = std::ptr::null(); - (*swkbd).callback_result = (*extra).callback.unwrap()( - (*extra).callback_user, + swkbd.callback_result = extra.callback.unwrap()( + extra.callback_user, &mut retmsg, text8.as_ptr(), text8.len(), @@ -899,9 +897,9 @@ impl SoftwareKeyboard { "\0" }; - let callback_msg = &mut (*swkbd).callback_msg; + let callback_msg = &mut swkbd.callback_msg; - if (*swkbd).callback_result > SWKBD_CALLBACK_OK as _ { + if swkbd.callback_result > SWKBD_CALLBACK_OK as _ { for (idx, ch) in retmsg.encode_utf16().take(callback_msg.len()).enumerate() { callback_msg[idx] = ch; } @@ -913,7 +911,7 @@ impl SoftwareKeyboard { envGetAptAppId(), sender, APTCMD_MESSAGE, - swkbd.cast(), + swkbd as *mut _ as *mut _, std::mem::size_of::() as _, 0, ); From bd19f97050e8f183080ba90cf1bb2f867e112781 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 13 Feb 2024 22:37:41 -0700 Subject: [PATCH 05/25] Use rust functions instead of memcpy and memset --- ctru-rs/src/applets/swkbd.rs | 46 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 4e763d3d..4e419154 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -733,10 +733,10 @@ impl SoftwareKeyboard { if !extra.dict.is_null() { swkbd.dict_offset = dict_off as _; unsafe { - libc::memcpy( - SWKBD_SHARED_MEM.add(dict_off), - extra.dict.cast(), - std::mem::size_of::() * swkbd.dict_word_count as usize, + std::ptr::copy_nonoverlapping( + extra.dict, + SWKBD_SHARED_MEM.add(dict_off).cast(), + swkbd.dict_word_count as _, ) }; } @@ -744,10 +744,10 @@ impl SoftwareKeyboard { if swkbd.initial_status_offset >= 0 { swkbd.initial_status_offset = status_off as _; unsafe { - libc::memcpy( - SWKBD_SHARED_MEM.add(status_off), - extra.status_data.cast(), - std::mem::size_of::(), + std::ptr::copy_nonoverlapping( + extra.status_data, + SWKBD_SHARED_MEM.add(status_off).cast(), + 1, ) }; } @@ -755,10 +755,10 @@ impl SoftwareKeyboard { if swkbd.initial_learning_offset >= 0 { swkbd.initial_learning_offset = learning_off as _; unsafe { - libc::memcpy( - SWKBD_SHARED_MEM.add(learning_off), - extra.learning_data.cast(), - std::mem::size_of::(), + std::ptr::copy_nonoverlapping( + extra.learning_data, + SWKBD_SHARED_MEM.add(learning_off).cast(), + 1, ) }; } @@ -771,11 +771,7 @@ impl SoftwareKeyboard { // Launch swkbd unsafe { - libc::memset( - std::ptr::addr_of_mut!(swkbd.__bindgen_anon_1.reserved).cast(), - 0, - swkbd.__bindgen_anon_1.reserved.len(), - ); + swkbd.__bindgen_anon_1.reserved.fill(0); if extra.callback.is_some() { aptSetMessageCallback( @@ -821,20 +817,20 @@ impl SoftwareKeyboard { if swkbd.save_state_flags & (1 << 0) != 0 { unsafe { - libc::memcpy( - extra.status_data.cast(), - SWKBD_SHARED_MEM.add(swkbd.status_offset as _), - std::mem::size_of::(), + std::ptr::copy_nonoverlapping( + SWKBD_SHARED_MEM.add(swkbd.status_offset as _).cast(), + extra.status_data, + 1, ) }; } if swkbd.save_state_flags & (1 << 1) != 0 { unsafe { - libc::memcpy( - extra.learning_data.cast(), - SWKBD_SHARED_MEM.add(swkbd.learning_offset as _), - std::mem::size_of::(), + std::ptr::copy_nonoverlapping( + SWKBD_SHARED_MEM.add(swkbd.learning_offset as _).cast(), + extra.learning_data, + 1, ) }; } From fa6f98d62c520570444e98d85124216371a2c38f Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 13 Feb 2024 22:56:44 -0700 Subject: [PATCH 06/25] Simplify string conversion code --- ctru-rs/src/applets/swkbd.rs | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 4e419154..98291e0c 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -5,10 +5,10 @@ use crate::services::{apt::Apt, gfx::Gfx}; use ctru_sys::{ - self, aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcBreak, svcCloseHandle, + self, aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcCloseHandle, svcCreateMemoryBlock, APT_SendParameter, SwkbdButton, SwkbdDictWord, SwkbdExtra, SwkbdLearningData, SwkbdState, SwkbdStatusData, APPID_SOFTWARE_KEYBOARD, APTCMD_MESSAGE, - NS_APPID, SWKBD_CALLBACK_OK, USERBREAK_PANIC, + NS_APPID, SWKBD_CALLBACK_OK, }; use bitflags::bitflags; @@ -281,7 +281,7 @@ impl SoftwareKeyboard { (self as *mut Self).cast(), ); - match Self::swkbd_input_text(self.state.as_mut(), output.as_mut_vec()) { + match Self::swkbd_input_text(self.state.as_mut(), &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)), @@ -631,7 +631,7 @@ impl SoftwareKeyboard { // 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(swkbd: &mut SwkbdState, buf: &mut Vec) -> SwkbdButton { + fn swkbd_input_text(swkbd: &mut SwkbdState, 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, @@ -715,11 +715,11 @@ impl SoftwareKeyboard { swkbd.initial_text_offset = 0; unsafe { - let utf16_iter = CStr::from_ptr(extra.initial_text) - .to_str() - .unwrap() - .encode_utf16() - .chain(once(0)); + 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.cast::(); @@ -803,17 +803,16 @@ impl SoftwareKeyboard { let text16 = if swkbd.text_length > 0 { unsafe { - widestring::Utf16Str::from_slice(std::slice::from_raw_parts( + widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), swkbd.text_length as usize, )) - .unwrap() } } else { widestring::utf16str!("") }; - buf.extend(text16.encode_utf8()); + *output = text16.to_string(); if swkbd.save_state_flags & (1 << 0) != 0 { unsafe { @@ -860,20 +859,13 @@ impl SoftwareKeyboard { } let text16 = unsafe { - widestring::Utf16Str::from_slice(std::slice::from_raw_parts( + widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), swkbd.text_length as usize + 1, )) - .unwrap() }; - let text8 = match String::from_utf8(text16.encode_utf8().collect()) { - Ok(s) => s, - Err(_) => { - svcBreak(USERBREAK_PANIC); - unreachable!(); - } - }; + let text8 = text16.to_string(); let mut retmsg = std::ptr::null(); From f867d965f83f18c96974e95b45fe56cddb5a18ac Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 13 Feb 2024 23:53:14 -0700 Subject: [PATCH 07/25] Use if let instead of unwrap for nullable callback --- ctru-rs/src/applets/swkbd.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 98291e0c..fb4735bc 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -869,12 +869,14 @@ impl SoftwareKeyboard { let mut retmsg = std::ptr::null(); - swkbd.callback_result = extra.callback.unwrap()( - extra.callback_user, - &mut retmsg, - text8.as_ptr(), - text8.len(), - ) as _; + if let Some(cb) = extra.callback { + swkbd.callback_result = cb( + extra.callback_user, + &mut retmsg, + text8.as_ptr(), + text8.len(), + ) as _ + }; let retmsg = if !retmsg.is_null() { unsafe { From 61f91312d3c2de2a052ab074fe09e366224c06d2 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 12:21:52 -0700 Subject: [PATCH 08/25] Remove mention of deleted function --- 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 fb4735bc..32e293d9 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -606,7 +606,7 @@ impl SoftwareKeyboard { /// /// 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 [`SoftwareKeyboard::get_string()`] and [`SoftwareKeyboard::write_exact()`]. + /// receivable by [`SoftwareKeyboard::get_string()`]. /// /// # Example /// From 4de76522d327631b05fc75b6faa71f5380857992 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 12:25:53 -0700 Subject: [PATCH 09/25] swkbd_input_text is still an unsafe fn --- ctru-rs/src/applets/swkbd.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 32e293d9..3b61fdfc 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -281,7 +281,7 @@ impl SoftwareKeyboard { (self as *mut Self).cast(), ); - match Self::swkbd_input_text(self.state.as_mut(), &mut output) { + match Self::swkbd_input_text(&mut self.state, &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)), @@ -631,7 +631,9 @@ impl SoftwareKeyboard { // 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(swkbd: &mut SwkbdState, output: &mut String) -> SwkbdButton { + // + // SAFETY: `swkbd` must be initialized by `swkbdInit` before calling this function. + unsafe fn swkbd_input_text(swkbd: &mut SwkbdState, 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 2fcdd110d2e74d899669f722b5bd63a312b26b2b Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 12:33:48 -0700 Subject: [PATCH 10/25] Simplify string output logic --- ctru-rs/src/applets/swkbd.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 3b61fdfc..c1a67403 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -803,18 +803,16 @@ impl SoftwareKeyboard { _ => SWKBD_BUTTON_NONE, }; - let text16 = if swkbd.text_length > 0 { - unsafe { + if swkbd.text_length > 0 { + let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( - SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), - swkbd.text_length as usize, + SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), + swkbd.text_length as _, )) - } - } else { - widestring::utf16str!("") - }; + }; - *output = text16.to_string(); + *output = text16.to_string(); + } if swkbd.save_state_flags & (1 << 0) != 0 { unsafe { From f5276234050cfc4d546bfe4158185d265e21039a Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 13:47:11 -0700 Subject: [PATCH 11/25] Fix off-by-one error in swkbd_message_callback --- ctru-rs/src/applets/swkbd.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index c1a67403..fa0f45dc 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -890,7 +890,11 @@ impl SoftwareKeyboard { let callback_msg = &mut swkbd.callback_msg; if swkbd.callback_result > SWKBD_CALLBACK_OK as _ { - for (idx, ch) in retmsg.encode_utf16().take(callback_msg.len()).enumerate() { + for (idx, ch) in retmsg + .encode_utf16() + .take(callback_msg.len() - 1) + .enumerate() + { callback_msg[idx] = ch; } } else { From b6fc9a2596e779100398433affcca89235b41e54 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 16:28:43 -0700 Subject: [PATCH 12/25] Simplify some code + rename vars --- ctru-rs/src/applets/swkbd.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index fa0f45dc..feafdbd9 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -8,7 +8,7 @@ use ctru_sys::{ self, aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcCloseHandle, svcCreateMemoryBlock, APT_SendParameter, SwkbdButton, SwkbdDictWord, SwkbdExtra, SwkbdLearningData, SwkbdState, SwkbdStatusData, APPID_SOFTWARE_KEYBOARD, APTCMD_MESSAGE, - NS_APPID, SWKBD_CALLBACK_OK, + NS_APPID, }; use bitflags::bitflags; @@ -723,10 +723,10 @@ impl SoftwareKeyboard { .take(swkbd.max_text_len as _) .chain(once(0)); - let mut initial_text_cursor = SWKBD_SHARED_MEM.cast::(); + let mut initial_text_cursor = SWKBD_SHARED_MEM.cast(); - for ch in utf16_iter { - *initial_text_cursor = ch; + for code_point in utf16_iter { + *initial_text_cursor = code_point; initial_text_cursor = initial_text_cursor.add(1); } } @@ -860,7 +860,7 @@ impl SoftwareKeyboard { let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( - SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast::(), + SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), swkbd.text_length as usize + 1, )) }; @@ -880,8 +880,8 @@ impl SoftwareKeyboard { let retmsg = if !retmsg.is_null() { unsafe { - let len = libc::strlen(retmsg); - std::str::from_utf8_unchecked(std::slice::from_raw_parts(retmsg, len + 1)) + let len = libc::strlen(retmsg) + 1; + std::str::from_utf8_unchecked(std::slice::from_raw_parts(retmsg, len)) } } else { "\0" @@ -889,16 +889,12 @@ impl SoftwareKeyboard { let callback_msg = &mut swkbd.callback_msg; - if swkbd.callback_result > SWKBD_CALLBACK_OK as _ { - for (idx, ch) in retmsg - .encode_utf16() - .take(callback_msg.len() - 1) - .enumerate() - { - callback_msg[idx] = ch; - } - } else { - callback_msg[0] = 0; + for (idx, code_point) in retmsg + .encode_utf16() + .take(callback_msg.len() - 1) + .enumerate() + { + callback_msg[idx] = code_point; } let _ = APT_SendParameter( From bf3ca2a87d4be663bbfcb358981cdcb5d33dd4f4 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Wed, 14 Feb 2024 20:28:27 -0700 Subject: [PATCH 13/25] Force consistent use of unsafe blocks Isolating unsafe code is still important even in unsafe functions --- ctru-rs/src/applets/swkbd.rs | 39 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index feafdbd9..0f794ac2 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -633,6 +633,7 @@ impl SoftwareKeyboard { // an intermediate fixed-size buffer // // SAFETY: `swkbd` must be initialized by `swkbdInit` before calling this function. + #[deny(unsafe_op_in_unsafe_fn)] unsafe fn swkbd_input_text(swkbd: &mut SwkbdState, output: &mut String) -> SwkbdButton { use ctru_sys::{ MEMPERM_READ, MEMPERM_WRITE, R_FAILED, SWKBD_BUTTON_LEFT, SWKBD_BUTTON_MIDDLE, @@ -842,15 +843,15 @@ impl SoftwareKeyboard { // A reimplementation of `swkbdMessageCallback` from `libctru/source/applets/swkbd.c`. // This is only needed because the original function is private to libctru, so we can't // simply reuse their version - #[allow(non_snake_case)] + #[deny(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn swkbd_message_callback( user: *mut libc::c_void, sender: NS_APPID, msg: *mut libc::c_void, msg_size: libc::size_t, ) { - let extra = &mut *user.cast::(); - let swkbd = &mut *msg.cast::(); + let extra = unsafe { &mut *user.cast::() }; + let swkbd = unsafe { &mut *msg.cast::() }; if sender != ctru_sys::APPID_SOFTWARE_KEYBOARD || msg_size != std::mem::size_of::() @@ -870,12 +871,14 @@ impl SoftwareKeyboard { let mut retmsg = std::ptr::null(); if let Some(cb) = extra.callback { - swkbd.callback_result = cb( - extra.callback_user, - &mut retmsg, - text8.as_ptr(), - text8.len(), - ) as _ + swkbd.callback_result = unsafe { + cb( + extra.callback_user, + &mut retmsg, + text8.as_ptr(), + text8.len(), + ) + } as _ }; let retmsg = if !retmsg.is_null() { @@ -897,14 +900,16 @@ impl SoftwareKeyboard { callback_msg[idx] = code_point; } - let _ = APT_SendParameter( - envGetAptAppId(), - sender, - APTCMD_MESSAGE, - swkbd as *mut _ as *mut _, - std::mem::size_of::() as _, - 0, - ); + let _ = unsafe { + APT_SendParameter( + envGetAptAppId(), + sender, + APTCMD_MESSAGE, + swkbd as *mut _ as *mut _, + std::mem::size_of::() as _, + 0, + ) + }; } } From 271d1b598ab9a78b116a1a5c464f5a35119a800c Mon Sep 17 00:00:00 2001 From: Fenrir Date: Thu, 15 Feb 2024 11:49:33 -0700 Subject: [PATCH 14/25] Fix yet another off-by-one error --- 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 0f794ac2..a47a7bc9 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -862,7 +862,7 @@ impl SoftwareKeyboard { let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), - swkbd.text_length as usize + 1, + swkbd.text_length as _, )) }; From 757afe9fe6f195ffa8b9032a1ce1ee368839046a Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 16 Feb 2024 11:27:04 -0700 Subject: [PATCH 15/25] Make swkbd_input_text a method for SoftwareKeyboard This lets us mark the function as safe since it can only be called with an initialized `SwkbdState` --- ctru-rs/src/applets/swkbd.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index a47a7bc9..c7a2be11 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -281,7 +281,7 @@ impl SoftwareKeyboard { (self as *mut Self).cast(), ); - match Self::swkbd_input_text(&mut self.state, &mut output) { + 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)), @@ -631,10 +631,7 @@ impl SoftwareKeyboard { // 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 - // - // SAFETY: `swkbd` must be initialized by `swkbdInit` before calling this function. - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn swkbd_input_text(swkbd: &mut SwkbdState, output: &mut String) -> SwkbdButton { + 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, @@ -642,6 +639,7 @@ impl SoftwareKeyboard { SWKBD_FILTER_CALLBACK, SWKBD_OUTOFMEM, }; + let swkbd = self.state.as_mut(); let mut extra = unsafe { swkbd.__bindgen_anon_1.extra }; // Calculate shared mem size @@ -862,7 +860,7 @@ impl SoftwareKeyboard { let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), - swkbd.text_length as _, + swkbd.text_length as usize + 1, )) }; From cfd276c175032291af6f48e76b84e26246eb0179 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 16 Feb 2024 13:51:04 -0700 Subject: [PATCH 16/25] Get rid of static mut by using our own callback data --- ctru-rs/src/applets/swkbd.rs | 55 +++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index c7a2be11..935f15a8 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -21,15 +21,12 @@ use std::str; type CallbackFunction = dyn Fn(&CStr) -> (CallbackResult, Option); -// I hate that we have to use this, but sometimes you gotta smuggle pointers into C callbacks -// and that's just how things are -static mut SWKBD_SHARED_MEM: *mut libc::c_void = std::ptr::null_mut(); - /// Configuration structure to setup the Software Keyboard applet. #[doc(alias = "SwkbdState")] pub struct SoftwareKeyboard { state: Box, callback: Option>, + callback_data: MessageCallbackData, error_message: Option, initial_text: Option, } @@ -216,6 +213,21 @@ bitflags! { } } +// Internal book-keeping struct used to send data to `aptSetMessageCallback` when calling the software keyboard +struct MessageCallbackData { + extra: *mut SwkbdExtra, + swkbd_shared_mem_ptr: *mut libc::c_void, +} + +impl MessageCallbackData { + fn new() -> Self { + Self { + extra: std::ptr::null_mut(), + swkbd_shared_mem_ptr: std::ptr::null_mut(), + } + } +} + impl SoftwareKeyboard { /// Initialize a new configuration for the Software Keyboard applet depending on how many "exit" buttons are available to the user (1, 2 or 3). /// @@ -244,6 +256,7 @@ impl SoftwareKeyboard { callback: None, error_message: None, initial_text: None, + callback_data: MessageCallbackData::new(), } } } @@ -684,11 +697,11 @@ impl SoftwareKeyboard { swkbd.shared_memory_size = shared_mem_size; // Allocate shared mem - unsafe { SWKBD_SHARED_MEM = libc::memalign(0x1000, shared_mem_size).cast() }; + let swkbd_shared_mem_ptr = unsafe { libc::memalign(0x1000, shared_mem_size) }; let mut swkbd_shared_mem_handle = 0; - if unsafe { SWKBD_SHARED_MEM.is_null() } { + if swkbd_shared_mem_ptr.is_null() { swkbd.result = SWKBD_OUTOFMEM; return SWKBD_BUTTON_NONE; } @@ -696,7 +709,7 @@ impl SoftwareKeyboard { let res = unsafe { svcCreateMemoryBlock( &mut swkbd_shared_mem_handle, - SWKBD_SHARED_MEM as _, + swkbd_shared_mem_ptr as _, shared_mem_size as _, MEMPERM_READ | MEMPERM_WRITE, MEMPERM_READ | MEMPERM_WRITE, @@ -705,7 +718,7 @@ impl SoftwareKeyboard { if R_FAILED(res) { unsafe { - libc::free(SWKBD_SHARED_MEM); + libc::free(swkbd_shared_mem_ptr); swkbd.result = SWKBD_OUTOFMEM; return SWKBD_BUTTON_NONE; } @@ -722,7 +735,7 @@ impl SoftwareKeyboard { .take(swkbd.max_text_len as _) .chain(once(0)); - let mut initial_text_cursor = SWKBD_SHARED_MEM.cast(); + let mut initial_text_cursor = swkbd_shared_mem_ptr.cast(); for code_point in utf16_iter { *initial_text_cursor = code_point; @@ -736,7 +749,7 @@ impl SoftwareKeyboard { unsafe { std::ptr::copy_nonoverlapping( extra.dict, - SWKBD_SHARED_MEM.add(dict_off).cast(), + swkbd_shared_mem_ptr.add(dict_off).cast(), swkbd.dict_word_count as _, ) }; @@ -747,7 +760,7 @@ impl SoftwareKeyboard { unsafe { std::ptr::copy_nonoverlapping( extra.status_data, - SWKBD_SHARED_MEM.add(status_off).cast(), + swkbd_shared_mem_ptr.add(status_off).cast(), 1, ) }; @@ -758,7 +771,7 @@ impl SoftwareKeyboard { unsafe { std::ptr::copy_nonoverlapping( extra.learning_data, - SWKBD_SHARED_MEM.add(learning_off).cast(), + swkbd_shared_mem_ptr.add(learning_off).cast(), 1, ) }; @@ -775,9 +788,12 @@ impl SoftwareKeyboard { swkbd.__bindgen_anon_1.reserved.fill(0); if extra.callback.is_some() { + self.callback_data.extra = std::ptr::addr_of_mut!(extra); + self.callback_data.swkbd_shared_mem_ptr = swkbd_shared_mem_ptr; + aptSetMessageCallback( Some(Self::swkbd_message_callback), - std::ptr::addr_of_mut!(extra).cast(), + std::ptr::addr_of_mut!(self.callback_data).cast(), ); } @@ -805,7 +821,7 @@ impl SoftwareKeyboard { if swkbd.text_length > 0 { let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( - SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), + swkbd_shared_mem_ptr.add(swkbd.text_offset as _).cast(), swkbd.text_length as _, )) }; @@ -816,7 +832,7 @@ impl SoftwareKeyboard { if swkbd.save_state_flags & (1 << 0) != 0 { unsafe { std::ptr::copy_nonoverlapping( - SWKBD_SHARED_MEM.add(swkbd.status_offset as _).cast(), + swkbd_shared_mem_ptr.add(swkbd.status_offset as _).cast(), extra.status_data, 1, ) @@ -826,14 +842,14 @@ impl SoftwareKeyboard { if swkbd.save_state_flags & (1 << 1) != 0 { unsafe { std::ptr::copy_nonoverlapping( - SWKBD_SHARED_MEM.add(swkbd.learning_offset as _).cast(), + swkbd_shared_mem_ptr.add(swkbd.learning_offset as _).cast(), extra.learning_data, 1, ) }; } - unsafe { libc::free(SWKBD_SHARED_MEM) }; + unsafe { libc::free(swkbd_shared_mem_ptr) }; button } @@ -848,8 +864,9 @@ impl SoftwareKeyboard { msg: *mut libc::c_void, msg_size: libc::size_t, ) { - let extra = unsafe { &mut *user.cast::() }; + let data = unsafe { &mut *user.cast::() }; let swkbd = unsafe { &mut *msg.cast::() }; + let extra = unsafe { &mut *data.extra }; if sender != ctru_sys::APPID_SOFTWARE_KEYBOARD || msg_size != std::mem::size_of::() @@ -859,7 +876,7 @@ impl SoftwareKeyboard { let text16 = unsafe { widestring::Utf16Str::from_slice_unchecked(std::slice::from_raw_parts( - SWKBD_SHARED_MEM.add(swkbd.text_offset as _).cast(), + data.swkbd_shared_mem_ptr.add(swkbd.text_offset as _).cast(), swkbd.text_length as usize + 1, )) }; From 03b417dc9efa7f73554aee45b12926db0cbafe5e Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 16 Feb 2024 19:20:14 -0700 Subject: [PATCH 17/25] code_point -> code_unit --- 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 935f15a8..c8887435 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -737,8 +737,8 @@ impl SoftwareKeyboard { let mut initial_text_cursor = swkbd_shared_mem_ptr.cast(); - for code_point in utf16_iter { - *initial_text_cursor = code_point; + for code_unit in utf16_iter { + *initial_text_cursor = code_unit; initial_text_cursor = initial_text_cursor.add(1); } } @@ -907,12 +907,12 @@ impl SoftwareKeyboard { let callback_msg = &mut swkbd.callback_msg; - for (idx, code_point) in retmsg + for (idx, code_unit) in retmsg .encode_utf16() .take(callback_msg.len() - 1) .enumerate() { - callback_msg[idx] = code_point; + callback_msg[idx] = code_unit; } let _ = unsafe { From d6f22dc7977a3ee413f59b2dd21e0cb22dfc8948 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 16 Feb 2024 19:19:19 -0700 Subject: [PATCH 18/25] Use .cast() in more places --- ctru-rs/src/applets/swkbd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index c8887435..5098c890 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -799,7 +799,7 @@ impl SoftwareKeyboard { aptLaunchLibraryApplet( APPID_SOFTWARE_KEYBOARD, - swkbd as *mut _ as *mut _, + (swkbd as *mut SwkbdState).cast(), std::mem::size_of::(), swkbd_shared_mem_handle, ); @@ -920,7 +920,7 @@ impl SoftwareKeyboard { envGetAptAppId(), sender, APTCMD_MESSAGE, - swkbd as *mut _ as *mut _, + (swkbd as *mut SwkbdState).cast(), std::mem::size_of::() as _, 0, ) From f715fc6eb97ef76b2c86a72d427a568e023103a6 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 16 Feb 2024 19:30:54 -0700 Subject: [PATCH 19/25] Add some comments --- ctru-rs/src/applets/swkbd.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 5098c890..6290e07a 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -213,7 +213,8 @@ bitflags! { } } -// Internal book-keeping struct used to send data to `aptSetMessageCallback` when calling the software keyboard +// Internal book-keeping struct used to send data to `aptSetMessageCallback` when calling the software keyboard. +// We only need this because libctru doesn't keep a pointer to the shared memory block in `SwkbdExtra` for whatever reason struct MessageCallbackData { extra: *mut SwkbdExtra, swkbd_shared_mem_ptr: *mut libc::c_void, @@ -855,8 +856,7 @@ impl SoftwareKeyboard { } // A reimplementation of `swkbdMessageCallback` from `libctru/source/applets/swkbd.c`. - // This is only needed because the original function is private to libctru, so we can't - // simply reuse their version + // This function sets up and then calls the callback set by `swkbdSetFilterCallback` #[deny(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn swkbd_message_callback( user: *mut libc::c_void, From 144658345cbc612081cf954de88087e23f18b295 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sat, 17 Feb 2024 11:30:09 -0700 Subject: [PATCH 20/25] MessageCallbackData doesn't need to be a struct member --- ctru-rs/src/applets/swkbd.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 6290e07a..a1c5f7e5 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -26,7 +26,6 @@ type CallbackFunction = dyn Fn(&CStr) -> (CallbackResult, Option); pub struct SoftwareKeyboard { state: Box, callback: Option>, - callback_data: MessageCallbackData, error_message: Option, initial_text: Option, } @@ -220,15 +219,6 @@ struct MessageCallbackData { swkbd_shared_mem_ptr: *mut libc::c_void, } -impl MessageCallbackData { - fn new() -> Self { - Self { - extra: std::ptr::null_mut(), - swkbd_shared_mem_ptr: std::ptr::null_mut(), - } - } -} - impl SoftwareKeyboard { /// Initialize a new configuration for the Software Keyboard applet depending on how many "exit" buttons are available to the user (1, 2 or 3). /// @@ -257,7 +247,6 @@ impl SoftwareKeyboard { callback: None, error_message: None, initial_text: None, - callback_data: MessageCallbackData::new(), } } } @@ -788,13 +777,15 @@ impl SoftwareKeyboard { unsafe { swkbd.__bindgen_anon_1.reserved.fill(0); - if extra.callback.is_some() { - self.callback_data.extra = std::ptr::addr_of_mut!(extra); - self.callback_data.swkbd_shared_mem_ptr = swkbd_shared_mem_ptr; + let mut callback_data = MessageCallbackData { + extra: std::ptr::addr_of_mut!(extra), + swkbd_shared_mem_ptr, + }; + if extra.callback.is_some() { aptSetMessageCallback( Some(Self::swkbd_message_callback), - std::ptr::addr_of_mut!(self.callback_data).cast(), + std::ptr::addr_of_mut!(callback_data).cast(), ); } From 0576e34c3fffbbf9bae342e9679100fa32d9168a Mon Sep 17 00:00:00 2001 From: Fenrir Date: Mon, 19 Feb 2024 16:11:36 -0700 Subject: [PATCH 21/25] Remove redudant imports --- ctru-rs/src/applets/swkbd.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index a1c5f7e5..193e20db 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -5,14 +5,13 @@ use crate::services::{apt::Apt, gfx::Gfx}; use ctru_sys::{ - self, aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcCloseHandle, + aptLaunchLibraryApplet, aptSetMessageCallback, envGetAptAppId, svcCloseHandle, svcCreateMemoryBlock, APT_SendParameter, SwkbdButton, SwkbdDictWord, SwkbdExtra, SwkbdLearningData, SwkbdState, SwkbdStatusData, APPID_SOFTWARE_KEYBOARD, APTCMD_MESSAGE, NS_APPID, }; use bitflags::bitflags; -use libc; use std::ffi::{CStr, CString}; use std::fmt::Display; From cb67d92512492f3615ca58f8835d175edcd65250 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Mon, 19 Feb 2024 16:16:01 -0700 Subject: [PATCH 22/25] SoftwareKeyboard::get_string -> SoftwareKeyboard::launch --- ctru-rs/examples/file-explorer.rs | 2 +- ctru-rs/examples/software-keyboard.rs | 4 ++-- ctru-rs/src/applets/swkbd.rs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ctru-rs/examples/file-explorer.rs b/ctru-rs/examples/file-explorer.rs index 9647e41e..14f3474b 100644 --- a/ctru-rs/examples/file-explorer.rs +++ b/ctru-rs/examples/file-explorer.rs @@ -165,7 +165,7 @@ impl<'a> FileExplorer<'a> { fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) { let mut keyboard = SoftwareKeyboard::default(); - match keyboard.get_string(self.apt, self.gfx) { + match keyboard.launch(self.apt, self.gfx) { Ok((path, Button::Right)) => { // Clicked "OK". action(self, path); diff --git a/ctru-rs/examples/software-keyboard.rs b/ctru-rs/examples/software-keyboard.rs index 581874ac..1b58da58 100644 --- a/ctru-rs/examples/software-keyboard.rs +++ b/ctru-rs/examples/software-keyboard.rs @@ -44,9 +44,9 @@ fn main() { // Check if the user request to write some input. if hid.keys_down().contains(KeyPad::A) { - // Raise the software keyboard. You can perform different actions depending on which + // Launch the software keyboard. You can perform different actions depending on which // software button the user pressed. - match keyboard.get_string(&apt, &gfx) { + match keyboard.launch(&apt, &gfx) { Ok((text, Button::Right)) => println!("You entered: {text}"), Ok((_, Button::Left)) => println!("Cancelled"), Ok((_, Button::Middle)) => println!("How did you even press this?"), diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index 193e20db..a9e4a510 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -114,7 +114,7 @@ pub enum ButtonConfig { LeftMiddleRight = 3, } -/// Error returned by an unsuccessful [`SoftwareKeyboard::get_string()`]. +/// Error returned by an unsuccessful [`SoftwareKeyboard::launch()`]. #[doc(alias = "SwkbdResult")] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(i32)] @@ -266,13 +266,13 @@ impl SoftwareKeyboard { /// use ctru::applets::swkbd::SoftwareKeyboard; /// let mut keyboard = SoftwareKeyboard::default(); /// - /// let (text, button) = keyboard.get_string(&apt, &gfx)?; + /// let (text, button) = keyboard.launch(&apt, &gfx)?; /// # /// # Ok(()) /// # } /// ``` #[doc(alias = "swkbdInputText")] - pub fn get_string(&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 { @@ -608,7 +608,7 @@ impl SoftwareKeyboard { /// /// 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 [`SoftwareKeyboard::get_string()`]. + /// receivable by [`SoftwareKeyboard::launch()`]. /// /// # Example /// From 9c33e491f236e2664edbe92fb4f1c0130a349763 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 20 Feb 2024 10:38:59 -0700 Subject: [PATCH 23/25] Use next_multiple_of instead of bitshifts --- ctru-rs/src/applets/swkbd.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ctru-rs/src/applets/swkbd.rs b/ctru-rs/src/applets/swkbd.rs index a9e4a510..93f69ece 100644 --- a/ctru-rs/src/applets/swkbd.rs +++ b/ctru-rs/src/applets/swkbd.rs @@ -647,13 +647,13 @@ impl SoftwareKeyboard { // Calculate shared mem size let mut shared_mem_size = 0; - shared_mem_size += - (std::mem::size_of::() * (swkbd.max_text_len as usize + 1) + 3) & !3; + shared_mem_size += (std::mem::size_of::() * (swkbd.max_text_len as usize + 1)) + .next_multiple_of(std::mem::size_of::()); let dict_off = shared_mem_size; - shared_mem_size += - (std::mem::size_of::() * swkbd.dict_word_count as usize + 3) & !3; + shared_mem_size += (std::mem::size_of::() * swkbd.dict_word_count as usize) + .next_multiple_of(std::mem::size_of::()); let status_off = shared_mem_size; @@ -681,7 +681,7 @@ impl SoftwareKeyboard { shared_mem_size += std::mem::size_of::(); } - shared_mem_size = (shared_mem_size + 0xFFF) & !0xFFF; + shared_mem_size = shared_mem_size.next_multiple_of(0x1000); swkbd.shared_memory_size = shared_mem_size; From 6d6f9c0c18b3ad5d46474939f5ea58950a014f34 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 20 Feb 2024 10:57:23 -0700 Subject: [PATCH 24/25] Update CI nightly version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 358f2f78..f9c23883 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: matrix: toolchain: # Run against a "known good" nightly. Rustc version is 1 day behind the toolchain date - - nightly-2023-06-01 + - nightly-2024-02-18 # Check for breakage on latest nightly - nightly @@ -53,7 +53,7 @@ jobs: strategy: matrix: toolchain: - - nightly-2023-06-01 + - nightly-2024-02-18 - nightly continue-on-error: ${{ matrix.toolchain == 'nightly' }} runs-on: ubuntu-latest From 636c8d70280296937a9ec06ce9820136880847ca Mon Sep 17 00:00:00 2001 From: Fenrir Date: Tue, 20 Feb 2024 11:03:40 -0700 Subject: [PATCH 25/25] Update MSRV to 1.73 --- ctru-rs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 14084fc2..ddd15dba 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -10,7 +10,7 @@ categories = ["os", "api-bindings", "hardware-support"] exclude = ["examples"] license = "Zlib" edition = "2021" -rust-version = "1.70" +rust-version = "1.73" [lib] crate-type = ["rlib"]