Fix USB terminal backspace handling

This commit is contained in:
mikecarper
2026-08-10 23:47:09 -07:00
parent 48a5d5b4cd
commit 71619934fc
3 changed files with 64 additions and 2 deletions
+32 -2
View File
@@ -139,6 +139,35 @@ static void clearUsbTerminalLine() {
usb_terminal_line_len = 0;
}
static void printUsbTerminalInputEcho() {
const char* password = nullptr;
size_t visible_len = usb_terminal_line_len;
if (mesh::cli::parseTerminalArgumentCommand(
usb_terminal_line, "login", password)
== mesh::cli::TerminalArgumentCommandMatch::Valid) {
visible_len = static_cast<size_t>(password - usb_terminal_line);
}
if (visible_len > 0) {
Serial.write(reinterpret_cast<const uint8_t*>(usb_terminal_line),
visible_len);
}
for (size_t i = visible_len; i < usb_terminal_line_len; i++) {
Serial.print('*');
}
}
static void redrawUsbTerminalInput() {
// The documented picocom `--imap spchex` converts an echoed BS to "[08]".
// It deliberately leaves CR untouched, so redraw the edited line with CR
// and printable bytes only. Padding clears a removed tab or wide glyph.
Serial.print("\r> ");
printUsbTerminalInputEcho();
Serial.print(" ");
Serial.print("\r> ");
printUsbTerminalInputEcho();
}
static bool isUsbTerminalDataConnected() {
#if defined(RP2040_PLATFORM)
return (bool)Serial;
@@ -285,8 +314,9 @@ static void serviceUsbTerminal() {
if (c == '\b' || c == 0x7F) {
if (usb_terminal_line_len > 0) {
usb_terminal_line[--usb_terminal_line_len] = 0;
Serial.print("\b \b");
usb_terminal_line_len = mesh::cli::eraseLastTerminalInput(
usb_terminal_line, usb_terminal_line_len);
redrawUsbTerminalInput();
}
continue;
}
+14
View File
@@ -118,6 +118,20 @@ inline bool shouldMaskTerminalInput(const char* line) {
== TerminalArgumentCommandMatch::Valid;
}
// Remove one complete UTF-8 code point from a terminal input buffer. Invalid
// trailing bytes are still removed safely, and the result remains terminated.
inline size_t eraseLastTerminalInput(char* line, size_t line_length) {
if (line == nullptr || line_length == 0) return 0;
size_t new_length = line_length - 1;
while (new_length > 0
&& (static_cast<uint8_t>(line[new_length]) & 0xC0) == 0x80) {
new_length--;
}
line[new_length] = 0;
return new_length;
}
inline TerminalChannelCommandMatch parseTerminalChannelMessage(
const char* command, TerminalChannelMessage& message) {
message.selector = nullptr;
@@ -198,6 +198,24 @@ TEST(CLICommandUtils, MasksOnlyTerminalLoginPasswordInput) {
EXPECT_FALSE(mesh::cli::shouldMaskTerminalInput("login-status"));
}
TEST(CLICommandUtils, BackspaceErasesAsciiAndUtf8Characters) {
char ascii[] = "trace path 2 7773";
size_t length = strlen(ascii);
length = mesh::cli::eraseLastTerminalInput(ascii, length);
EXPECT_STREQ("trace path 2 777", ascii);
EXPECT_EQ(strlen(ascii), length);
char utf8[] = "channel Public hi 👋";
length = strlen(utf8);
length = mesh::cli::eraseLastTerminalInput(utf8, length);
EXPECT_STREQ("channel Public hi ", utf8);
EXPECT_EQ(strlen(utf8), length);
EXPECT_EQ(0u, mesh::cli::eraseLastTerminalInput(nullptr, 0));
EXPECT_EQ(0u, mesh::cli::eraseLastTerminalInput(utf8, 0));
}
TEST(CLICommandUtils, ParsesStrictDecimalValues) {
float value = 0.0f;