mirror of
https://github.com/suchmememanyskill/CYD-Klipper.git
synced 2026-03-21 05:33:24 +00:00
serial_console: Add support to handle delete/arrow keys (#195)
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.
This commit is contained in:
@@ -5,11 +5,49 @@
|
|||||||
|
|
||||||
#define MAX_COMDLINE_SIZE 80
|
#define MAX_COMDLINE_SIZE 80
|
||||||
#define MAX_WORDS 6
|
#define MAX_WORDS 6
|
||||||
|
#define BACKSPACE_CHAR 0x08
|
||||||
|
#define ESCAPE_CHAR '\x1b'
|
||||||
|
#define CSI_CHAR '['
|
||||||
|
#define LEFT_ARROW_CHAR 'D'
|
||||||
|
#define RIGHT_ARROW_CHAR 'C'
|
||||||
|
#define DELETE_KEY_1_CHAR '3'
|
||||||
|
#define DELETE_KEY_2_CHAR '~'
|
||||||
|
#define PRINT_CHAR_START 32
|
||||||
|
#define PRINT_CHAR_END 126
|
||||||
|
|
||||||
namespace serial_console
|
namespace serial_console
|
||||||
{
|
{
|
||||||
bool global_disable_serial_console = false;
|
bool global_disable_serial_console = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Redraws the characters from the current cursor position to the end of the buffer on the serial console.
|
||||||
|
*
|
||||||
|
* This function is used to update the terminal display when characters are inserted or deleted
|
||||||
|
* in the middle of the input buffer. It:
|
||||||
|
* - Saves the current cursor position
|
||||||
|
* - Clears the line from the cursor to the end
|
||||||
|
* - Prints characters from 'cur' to 'len'
|
||||||
|
* - Restores the original cursor position
|
||||||
|
*
|
||||||
|
* @param cur Current cursor position within the buffer
|
||||||
|
* @param len Current length of the buffer (number of characters entered)
|
||||||
|
* @param buf Character buffer containing the input string
|
||||||
|
*/
|
||||||
|
static inline void redraw(const int cur, const int len, char *buf) {
|
||||||
|
if (!temporary_config.remote_echo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Serial.print("\x1b[s");
|
||||||
|
Serial.print("\x1b[K");
|
||||||
|
|
||||||
|
// Reprint characters from cur to end
|
||||||
|
for (int i = cur; i < len; i++) {
|
||||||
|
Serial.print((char)buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(" \x1b[u");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* read_string_until: Non-blocking replacement for Serial.readStringUntil()..
|
* read_string_until: Non-blocking replacement for Serial.readStringUntil()..
|
||||||
* With delimeter '\n' acts as 'read line'.
|
* With delimeter '\n' acts as 'read line'.
|
||||||
@@ -20,6 +58,7 @@ namespace serial_console
|
|||||||
bool read_string_until(char delimiter, char *result, int max_len)
|
bool read_string_until(char delimiter, char *result, int max_len)
|
||||||
{
|
{
|
||||||
static int index = 0;
|
static int index = 0;
|
||||||
|
static int cur = 0;
|
||||||
int c; // read character, -1 if none
|
int c; // read character, -1 if none
|
||||||
int cnt = 100; // limit on amount of iterations in one go; we're supposed to be non-blocking!
|
int cnt = 100; // limit on amount of iterations in one go; we're supposed to be non-blocking!
|
||||||
|
|
||||||
@@ -28,43 +67,75 @@ namespace serial_console
|
|||||||
--cnt;
|
--cnt;
|
||||||
|
|
||||||
// backspaceF
|
// backspaceF
|
||||||
if (c == 8)
|
if (c == BACKSPACE_CHAR && cur > 0) {
|
||||||
{
|
// shift characters left from cursor position
|
||||||
if (index > 0)
|
memmove(&result[cur - 1], &result[cur], index - cur);
|
||||||
{
|
|
||||||
if(temporary_config.remote_echo) Serial.print("\x08 \x08"); // overwrite last character with space and move cursor 1 back.
|
|
||||||
index--;
|
index--;
|
||||||
}
|
cur--;
|
||||||
|
// move cursor left on terminal and redraw updated string
|
||||||
|
if(temporary_config.remote_echo) Serial.print("\x1b[D");
|
||||||
|
redraw(cur, index, result);
|
||||||
|
// handle ANSI escape sequences (arrow keys, delete key)
|
||||||
|
} else if (c == ESCAPE_CHAR) {
|
||||||
|
|
||||||
|
if ((c = Serial.read()) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Expect '[' character
|
||||||
|
if (c != CSI_CHAR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if ((c = Serial.read()) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Left arrow key
|
||||||
|
if (c == LEFT_ARROW_CHAR && cur > 0) {
|
||||||
|
// move cursor left on terminal
|
||||||
|
if(temporary_config.remote_echo) Serial.print("\x1b[D");
|
||||||
|
cur--;
|
||||||
|
// Right arrow key
|
||||||
|
} else if(c == RIGHT_ARROW_CHAR && cur < index) {
|
||||||
|
// move cursor right on terminal
|
||||||
|
if(temporary_config.remote_echo) Serial.print("\x1b[C");
|
||||||
|
cur++;
|
||||||
|
// Delete key
|
||||||
|
} else if(c == DELETE_KEY_1_CHAR) {
|
||||||
|
if ((c = Serial.read()) == -1)
|
||||||
|
break;
|
||||||
|
if (c == DELETE_KEY_2_CHAR && cur < index) {
|
||||||
|
memmove(&result[cur], &result[cur + 1], index - cur - 1);
|
||||||
|
index--;
|
||||||
|
redraw(cur, index, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(temporary_config.remote_echo) Serial.print((char)c); // echo
|
// Handle printable characters
|
||||||
|
} else if (c >= PRINT_CHAR_START && c <= PRINT_CHAR_END) {
|
||||||
// Buffer overflow handling:
|
// Append character at the end
|
||||||
// start treating current buffer as invalid:
|
if (index < max_len - 1 && cur == index) {
|
||||||
// - stop collecting more data
|
if(temporary_config.remote_echo) Serial.print((char)c);
|
||||||
// - return false on delimeter, flushing everything collected,
|
result[index++] = c;
|
||||||
// - restart collection from scratch after delimeter,
|
cur++;
|
||||||
// - return control as normal.
|
// Insert character in the middl
|
||||||
|
} else if (index < max_len - 1) {
|
||||||
if (index >= max_len - 1)
|
memmove(&result[cur + 1], &result[cur], index - cur);
|
||||||
{
|
result[cur] = c;
|
||||||
if (c == delimiter) // got delimeter: flush buffer quietly, restart collection.
|
index++;
|
||||||
{
|
if(temporary_config.remote_echo) Serial.print((char)c);
|
||||||
|
cur++;
|
||||||
|
redraw(cur, index, result);
|
||||||
|
} else if (c == delimiter) { // got delimeter: flush buffer quietly, restart collection.
|
||||||
index = 0;
|
index = 0;
|
||||||
|
cur = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
continue; // discard any data past the end of the buffer, keep reading
|
|
||||||
}
|
|
||||||
|
|
||||||
result[index++] = c;
|
|
||||||
|
|
||||||
// delimiter was found
|
// delimiter was found
|
||||||
if (c == delimiter)
|
} else if (c == delimiter) {
|
||||||
{
|
|
||||||
|
if(temporary_config.remote_echo) Serial.println();
|
||||||
result[index] = '\0'; // Null-terminate the string
|
result[index] = '\0'; // Null-terminate the string
|
||||||
index = 0;
|
index = 0;
|
||||||
|
cur = 0;
|
||||||
return true; // Success: Delimiter found
|
return true; // Success: Delimiter found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user