Skip to content

Commit 58741f5

Browse files
committed
serial_console: Add support to handle delete/arrow keys
This change adds support to serial console to handle delete left & right arrow and backspace key by changing result buffer based on the keypress and emulating the movement with espace characters in the terminal. This patch also takes care of buffer flow handling by checking the cur and index with the max available length for each operations.
1 parent 5b77702 commit 58741f5

File tree

1 file changed

+98
-27
lines changed

1 file changed

+98
-27
lines changed

CYD-Klipper/src/ui/serial/serial_console.cpp

Lines changed: 98 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,49 @@
55

66
#define MAX_COMDLINE_SIZE 80
77
#define MAX_WORDS 6
8+
#define BACKSPACE_CHAR 0x08
9+
#define ESCAPE_CHAR '\x1b'
10+
#define CSI_CHAR '['
11+
#define LEFT_ARROW_CHAR 'D'
12+
#define RIGHT_ARROW_CHAR 'C'
13+
#define DELETE_KEY_1_CHAR '3'
14+
#define DELETE_KEY_2_CHAR '~'
15+
#define PRINT_CHAR_START 32
16+
#define PRINT_CHAR_END 126
817

918
namespace serial_console
1019
{
1120
bool global_disable_serial_console = false;
1221

22+
/**
23+
* @brief Redraws the characters from the current cursor position to the end of the buffer on the serial console.
24+
*
25+
* This function is used to update the terminal display when characters are inserted or deleted
26+
* in the middle of the input buffer. It:
27+
* - Saves the current cursor position
28+
* - Clears the line from the cursor to the end
29+
* - Prints characters from 'cur' to 'len'
30+
* - Restores the original cursor position
31+
*
32+
* @param cur Current cursor position within the buffer
33+
* @param len Current length of the buffer (number of characters entered)
34+
* @param buf Character buffer containing the input string
35+
*/
36+
static inline void redraw(const int cur, const int len, char *buf) {
37+
if (!temporary_config.remote_echo)
38+
return;
39+
40+
Serial.print("\x1b[s");
41+
Serial.print("\x1b[K");
42+
43+
// Reprint characters from cur to end
44+
for (int i = cur; i < len; i++) {
45+
Serial.print((char)buf[i]);
46+
}
47+
48+
Serial.print(" \x1b[u");
49+
}
50+
1351
/*
1452
* read_string_until: Non-blocking replacement for Serial.readStringUntil()..
1553
* With delimeter '\n' acts as 'read line'.
@@ -20,6 +58,7 @@ namespace serial_console
2058
bool read_string_until(char delimiter, char *result, int max_len)
2159
{
2260
static int index = 0;
61+
static int cur = 0;
2362
int c; // read character, -1 if none
2463
int cnt = 100; // limit on amount of iterations in one go; we're supposed to be non-blocking!
2564

@@ -28,43 +67,75 @@ namespace serial_console
2867
--cnt;
2968

3069
// backspaceF
31-
if (c == 8)
32-
{
33-
if (index > 0)
34-
{
35-
if(temporary_config.remote_echo) Serial.print("\x08 \x08"); // overwrite last character with space and move cursor 1 back.
70+
if (c == BACKSPACE_CHAR && cur > 0) {
71+
// shift characters left from cursor position
72+
memmove(&result[cur - 1], &result[cur], index - cur);
3673
index--;
37-
}
38-
continue;
39-
}
74+
cur--;
75+
// move cursor left on terminal and redraw updated string
76+
if(temporary_config.remote_echo) Serial.print("\x1b[D");
77+
redraw(cur, index, result);
78+
// handle ANSI escape sequences (arrow keys, delete key)
79+
} else if (c == ESCAPE_CHAR) {
80+
81+
if ((c = Serial.read()) == -1)
82+
break;
4083

41-
if(temporary_config.remote_echo) Serial.print((char)c); // echo
84+
// Expect '[' character
85+
if (c != CSI_CHAR)
86+
continue;
4287

43-
// Buffer overflow handling:
44-
// start treating current buffer as invalid:
45-
// - stop collecting more data
46-
// - return false on delimeter, flushing everything collected,
47-
// - restart collection from scratch after delimeter,
48-
// - return control as normal.
88+
if ((c = Serial.read()) == -1)
89+
break;
4990

50-
if (index >= max_len - 1)
51-
{
52-
if (c == delimiter) // got delimeter: flush buffer quietly, restart collection.
53-
{
91+
// Left arrow key
92+
if (c == LEFT_ARROW_CHAR && cur > 0) {
93+
// move cursor left on terminal
94+
if(temporary_config.remote_echo) Serial.print("\x1b[D");
95+
cur--;
96+
// Right arrow key
97+
} else if(c == RIGHT_ARROW_CHAR && cur < index) {
98+
// move cursor right on terminal
99+
if(temporary_config.remote_echo) Serial.print("\x1b[C");
100+
cur++;
101+
// Delete key
102+
} else if(c == DELETE_KEY_1_CHAR) {
103+
if ((c = Serial.read()) == -1)
104+
break;
105+
if (c == DELETE_KEY_2_CHAR && cur < index) {
106+
memmove(&result[cur], &result[cur + 1], index - cur - 1);
107+
index--;
108+
redraw(cur, index, result);
109+
}
110+
}
111+
112+
// Handle printable characters
113+
} else if (c >= PRINT_CHAR_START && c <= PRINT_CHAR_END) {
114+
// Append character at the end
115+
if (index < max_len - 1 && cur == index) {
116+
if(temporary_config.remote_echo) Serial.print((char)c);
117+
result[index++] = c;
118+
cur++;
119+
// Insert character in the middl
120+
} else if (index < max_len - 1) {
121+
memmove(&result[cur + 1], &result[cur], index - cur);
122+
result[cur] = c;
123+
index++;
124+
if(temporary_config.remote_echo) Serial.print((char)c);
125+
cur++;
126+
redraw(cur, index, result);
127+
} else if (c == delimiter) { // got delimeter: flush buffer quietly, restart collection.
54128
index = 0;
129+
cur = 0;
55130
return false;
56131
}
57-
else
58-
continue; // discard any data past the end of the buffer, keep reading
59-
}
60-
61-
result[index++] = c;
62-
63132
// delimiter was found
64-
if (c == delimiter)
65-
{
133+
} else if (c == delimiter) {
134+
135+
if(temporary_config.remote_echo) Serial.println();
66136
result[index] = '\0'; // Null-terminate the string
67137
index = 0;
138+
cur = 0;
68139
return true; // Success: Delimiter found
69140
}
70141
}

0 commit comments

Comments
 (0)