Skip to content

Commit c48e55a

Browse files
FenrirWolf=
authored andcommitted
Add helper type for writing to UTF-16 buffers
1 parent 9787916 commit c48e55a

File tree

3 files changed

+60
-48
lines changed

3 files changed

+60
-48
lines changed

ctru-rs/src/applets/error.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
//!
33
//! This applet displays error text as a pop-up message on the lower screen.
44
5+
use crate::Utf16Writer;
56
use crate::services::{apt::Apt, gfx::Gfx};
67

78
use ctru_sys::{errorConf, errorDisp, errorInit};
89

10+
use std::fmt::Write;
11+
912
/// Configuration struct to set up the Error applet.
1013
#[doc(alias = "errorConf")]
1114
pub struct PopUp {
@@ -59,14 +62,9 @@ impl PopUp {
5962
/// 1900 UTF-16 code units in length after conversion.
6063
#[doc(alias = "errorText")]
6164
pub fn set_text(&mut self, text: &str) {
62-
for (idx, code_unit) in text
63-
.encode_utf16()
64-
.take(self.state.Text.len() - 1)
65-
.chain(std::iter::once(0))
66-
.enumerate()
67-
{
68-
self.state.Text[idx] = code_unit;
69-
}
65+
let mut writer = Utf16Writer::new(&mut self.state.Text);
66+
67+
let _ = writer.write_str(text);
7068
}
7169

7270
/// Launches the error applet.

ctru-rs/src/applets/swkbd.rs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
//! This applet opens a virtual keyboard on the console's bottom screen which lets the user write UTF-16 valid text.
44
#![doc(alias = "keyboard")]
55

6+
use crate::Utf16Writer;
67
use crate::services::{apt::Apt, gfx::Gfx};
8+
79
use ctru_sys::{
810
APPID_SOFTWARE_KEYBOARD, APT_SendParameter, APTCMD_MESSAGE, NS_APPID, SwkbdButton,
911
SwkbdDictWord, SwkbdLearningData, SwkbdState, SwkbdStatusData, aptLaunchLibraryApplet,
@@ -13,8 +15,7 @@ use ctru_sys::{
1315
use bitflags::bitflags;
1416

1517
use std::borrow::Cow;
16-
use std::fmt::Display;
17-
use std::iter::once;
18+
use std::fmt::{Display, Write};
1819
use std::str;
1920

2021
type CallbackFunction = dyn FnMut(&str) -> CallbackResult;
@@ -445,14 +446,9 @@ impl SoftwareKeyboard {
445446
#[doc(alias = "swkbdSetHintText")]
446447
pub fn set_hint_text(&mut self, text: Option<&str>) {
447448
if let Some(text) = text {
448-
for (idx, code_unit) in text
449-
.encode_utf16()
450-
.take(self.state.hint_text.len() - 1)
451-
.chain(once(0))
452-
.enumerate()
453-
{
454-
self.state.hint_text[idx] = code_unit;
455-
}
449+
let mut writer = Utf16Writer::new(&mut self.state.hint_text);
450+
451+
let _ = writer.write_str(text);
456452
} else {
457453
self.state.hint_text[0] = 0;
458454
}
@@ -551,14 +547,9 @@ impl SoftwareKeyboard {
551547
pub fn configure_button(&mut self, button: Button, text: &str, submit: bool) {
552548
let button_text = &mut self.state.button_text[button as usize];
553549

554-
for (idx, code_unit) in text
555-
.encode_utf16()
556-
.take(button_text.len() - 1)
557-
.chain(once(0))
558-
.enumerate()
559-
{
560-
button_text[idx] = code_unit;
561-
}
550+
let mut writer = Utf16Writer::new(button_text);
551+
552+
let _ = writer.write_str(text);
562553

563554
self.state.button_submits_text[button as usize] = submit;
564555
}
@@ -678,20 +669,13 @@ impl SoftwareKeyboard {
678669

679670
// Copy stuff to shared mem
680671
if let Some(initial_text) = self.initial_text.as_deref() {
681-
swkbd.initial_text_offset = 0;
682-
683-
let mut initial_text_cursor = swkbd_shared_mem_ptr.cast();
684-
685-
for code_unit in initial_text
686-
.encode_utf16()
687-
.take(swkbd.max_text_len as _)
688-
.chain(once(0))
689-
{
690-
unsafe {
691-
*initial_text_cursor = code_unit;
692-
initial_text_cursor = initial_text_cursor.add(1);
693-
}
694-
}
672+
let slice = unsafe {
673+
std::slice::from_raw_parts_mut(swkbd_shared_mem_ptr.cast(), initial_text.len())
674+
};
675+
676+
let mut writer = Utf16Writer::new(slice);
677+
678+
let _ = writer.write_str(initial_text);
695679
}
696680

697681
if !extra.dict.is_null() {
@@ -838,14 +822,9 @@ impl SoftwareKeyboard {
838822
swkbd.callback_result = result.discriminant().into();
839823

840824
if let CallbackResult::Retry(msg) | CallbackResult::Close(msg) = result {
841-
for (idx, code_unit) in msg
842-
.encode_utf16()
843-
.take(swkbd.callback_msg.len() - 1)
844-
.chain(once(0))
845-
.enumerate()
846-
{
847-
swkbd.callback_msg[idx] = code_unit;
848-
}
825+
let mut writer = Utf16Writer::new(&mut swkbd.callback_msg);
826+
827+
let _ = writer.write_str(&msg);
849828
}
850829

851830
let _ = unsafe {

ctru-rs/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,38 @@ pub use crate::error::{Error, Result};
8484
pub fn set_panic_hook(call_old_hook: bool) {
8585
crate::applets::error::set_panic_hook(call_old_hook);
8686
}
87+
88+
/// A helper type for writing string data into UTF-16 text buffers. Useful for interoperating with `libctru` APIs that expect UTF-16 as input.
89+
///
90+
/// The writer ensures that the text is written in-bounds and properly nul-terminated.
91+
pub struct Utf16Writer<'a> {
92+
buf: &'a mut [u16],
93+
index: usize,
94+
}
95+
96+
impl<'a> Utf16Writer<'a> {
97+
/// Creates a new [Utf16Writer] that writes its output into the provided buffer.
98+
pub fn new(buf: &'a mut [u16]) -> Self {
99+
Self { buf, index: 0 }
100+
}
101+
}
102+
103+
impl std::fmt::Write for Utf16Writer<'_> {
104+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
105+
let max = self.buf.len() - 1;
106+
107+
for code_unit in s.encode_utf16() {
108+
if self.index == max {
109+
self.buf[self.index] = 0;
110+
return Err(std::fmt::Error);
111+
} else {
112+
self.buf[self.index] = code_unit;
113+
self.index += 1;
114+
}
115+
}
116+
117+
self.buf[self.index] = 0;
118+
119+
Ok(())
120+
}
121+
}

0 commit comments

Comments
 (0)