From a025219a21254c8970e19a90bf31148d351f6579 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Mon, 21 Sep 2026 11:55:10 +0000 Subject: add raw input echo --- include/line/mode.h | 53 ++++--------------- module-test/line_test.cc | 14 ++--- src/line/mode.cc | 130 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 51 deletions(-) create mode 100644 src/line/mode.cc diff --git a/include/line/mode.h b/include/line/mode.h index 8811e66..2baad81 100644 --- a/include/line/mode.h +++ b/include/line/mode.h @@ -1,59 +1,28 @@ #pragma once -#include #include -#include +#include using namespace std; // TODO: remove namespace ov4 { -inline termios attr; - -void disable_raw() -{ - tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr); -} - -void enable_raw() +struct T_position { - termios attr_raw; - tcgetattr(STDIN_FILENO, &attr); - tcgetattr(STDIN_FILENO, &attr_raw); - - // LOCAL + int row; + int col; +}; - // disable - // echo - // canonical - // signal - // disable post-input processing, aka control-V - attr_raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); - - // INPUT - - // disable - // software flow control, aka control-S/Q - // \n and \r conversion - attr_raw.c_iflag &= ~(IXON | ICRNL); - - // OUTPUT +inline termios attr; - // disable - // post-output processing - attr_raw.c_oflag &= ~(OPOST); +void disable_raw(); +void enable_raw(); - // misc - attr_raw.c_iflag &= ~(BRKINT | INPCK | ISTRIP); - attr_raw.c_cflag |= ~(CS8); +void clear_below(); - atexit(disable_raw); +std::string readline(); - // TCSAFLUSH - // the change shall occur after all output written to fildes is transmitted - // and all input so far received but not read shall be discarded before the change is made - tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr_raw); -} +T_position get_cursor_position(); } diff --git a/module-test/line_test.cc b/module-test/line_test.cc index 7bc16df..d71d088 100644 --- a/module-test/line_test.cc +++ b/module-test/line_test.cc @@ -2,6 +2,7 @@ * test line editor $ g++ \ module-test/line_test.cc \ + src/line/mode.cc \ -std=c++26 -Iinclude -Ithird_party/magic_enum/include \ -O0 -ggdb3 -fno-omit-frame-pointer -fno-inline -D_GLIBCXX_ASSERTIONS \ -o module-test/line_test.out @@ -11,6 +12,7 @@ #include #include +#include #include "line/mode.h" @@ -20,14 +22,8 @@ using namespace ov4; int main() { enable_raw(); - char c; - while (read(STDIN_FILENO, &c, 1) == 1) - { - if (c == 'q') break; - if (iscntrl(c)) - print("control: {}\r\n", static_cast(c)); - else - print("entered: {}\r\n", c); - } + string s = readline(); + disable_raw(); + cout << s << endl; return 0; } diff --git a/src/line/mode.cc b/src/line/mode.cc new file mode 100644 index 0000000..83eda67 --- /dev/null +++ b/src/line/mode.cc @@ -0,0 +1,130 @@ +#include "line/mode.h" + +#include +#include +#include +#include + +using namespace std; + +namespace ov4 +{ + +void disable_raw() +{ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr); +} + +void enable_raw() +{ + termios attr_raw; + tcgetattr(STDIN_FILENO, &attr); + tcgetattr(STDIN_FILENO, &attr_raw); + + // LOCAL + + // disable + // echo + // canonical + // signal + // disable post-input processing, aka control-V + attr_raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); + + // INPUT + + // disable + // software flow control, aka control-S/Q + // \n and \r conversion + attr_raw.c_iflag &= ~(IXON | ICRNL); + + // OUTPUT + + // disable + // post-output processing + attr_raw.c_oflag &= ~(OPOST); + + // misc + attr_raw.c_iflag &= ~(BRKINT | INPCK | ISTRIP); + attr_raw.c_cflag |= ~(CS8); + + atexit(disable_raw); + + // TCSAFLUSH + // the change shall occur after all output written to fildes is transmitted + // and all input so far received but not read shall be discarded before the change is made + tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr_raw); +} + +void clear_below() +{ + print("\x1b[0J"); +} + +string readline() +{ + string buffer; + char c; + while (read(STDIN_FILENO, &c, 1) == 1) + { + if (c == 'Q') break; + else if (c == 'C') + { + clear_below(); + } + else if (c == 'R') + { + get_cursor_position(); + } + else if (iscntrl(c)) + { + print("control: {}\r\n", static_cast(c)); + fflush(stdout); + if (c == 13) // \n + { + //print("line: {}\r\n", buffer); + return buffer; + } + } + else + { + print("{}", c); + buffer += c; + fflush(stdout); + } + } + return buffer; +} + +T_position get_cursor_position() +{ + char c; + string buffer; + T_position pos; + int row, col; + tcflush(STDIN_FILENO, TCIFLUSH); + cout << "\x1b[6n" << flush; + while (read(STDIN_FILENO, &c, 1) == 1) + { + if (c == 'R') break; + buffer += c; + } + if (buffer.size() <= 2 || buffer[0] != '\x1b' || buffer[1] != '[') + { + pos.row = -1; + pos.col = -1; + return pos; + } + if ((sscanf(buffer.c_str(), "\x1b[%d;%d", &row, &col)) != 2) + { + pos.row = -1; + pos.col = -1; + return pos; + } + pos.row = row; + pos.col = col; + print("row {}, col {}\r\n", row, col); + fflush(stdout); + return pos; +} + +} -- cgit v1.3.1-4-g156e