123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- #ifndef RAWREADER_H
- #define RAWREADER_H
- #include <cstdio>
- #include <cstring>
- #include <termios.h>
- #include <unistd.h>
- template<int N, int H>
- class RawReader final {
- struct termios original;
-
- int index;
- char buffer[2][N];
- bool bufferIndex;
- bool noError;
- int move;
-
- char history[H][N];
- int historyOffset;
- int historyIndex;
- int historyLength;
-
- const char* prefix;
- public:
- typedef int DeciSeconds;
- RawReader(DeciSeconds timeout, const char* prefix) : index(0), bufferIndex(false), noError(true), move(0),
- historyOffset(0), historyIndex(0), historyLength(0), prefix(prefix) {
- // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
- if(tcgetattr(STDIN_FILENO, &original) == -1) {
- markError();
- return;
- }
- struct termios raw;
- if(tcgetattr(STDIN_FILENO, &raw) == -1) {
- markError();
- return;
- };
- raw.c_iflag &= ~(ICRNL | IXON);
- raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
- raw.c_cc[VMIN] = 0;
- raw.c_cc[VTIME] = timeout;
- if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw)) {
- markError();
- return;
- }
- buffer[0][0] = '\0';
- buffer[1][0] = '\0';
- }
- ~RawReader() {
- tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
- }
- RawReader(const RawReader&) = delete;
- RawReader(RawReader&&) = delete;
- RawReader& operator=(const RawReader&) = delete;
- RawReader& operator=(RawReader&&) = delete;
- bool canRead() const {
- return noError;
- }
- const char* readLine() {
- refreshLine();
- while(true) {
- char c = 0;
- int bytes = read(STDIN_FILENO, &c, 1);
- if(bytes < 0) {
- markError();
- break;
- } else if(bytes == 0 || c < 0) {
- break;
- }
- if(c == 27) {
- readEscapeSequence();
- } else if(iscntrl(c)) {
- if(handleControlKey(c)) {
- clearLine();
- return swapBuffers();
- }
- } else {
- addChar(c);
- }
- refreshLine();
- }
- clearLine();
- return nullptr;
- }
- void updateLine() {
- refreshLine();
- clearLine();
- }
- private:
- void print(const char* s) {
- fputs(s, stdout);
- }
- void clearLine() {
- print("\33[2K\r");
- }
- void markError() {
- noError = false;
- }
- const char* swapBuffers() {
- addToHistory();
- bufferIndex = !bufferIndex;
- clear();
- return buffer[!bufferIndex];
- }
- void refreshLine() {
- clearLine();
- print(prefix);
- print(buffer[bufferIndex]);
- if(move > 0) {
- printf("\33[%dD", move);
- }
- fflush(stdout);
- }
- bool handleControlKey(char c) {
- switch(c) {
- case 3: return clear();
- case 13: return true;
- case 127: return removeChar();
- }
- return false;
- }
- bool clear() {
- index = 0;
- move = 0;
- buffer[bufferIndex][0] = '\0';
- historyOffset = 0;
- return false;
- }
- bool removeChar() {
- int pos = index - move - 1;
- if(pos >= 0) {
- index--;
- while(buffer[bufferIndex][pos] != '\0') {
- buffer[bufferIndex][pos] = buffer[bufferIndex][pos + 1];
- pos++;
- }
- }
- if(index - move < 0) {
- move--;
- }
- return false;
- }
- void addChar(char c) {
- if(index + 1 < N) {
- index++;
- for(int i = 0; i < move + 1; i++) {
- buffer[bufferIndex][index - i] = buffer[bufferIndex][index - i - 1];
- }
- buffer[bufferIndex][index - move - 1] = c;
- }
- }
- char readChar() {
- char c;
- return read(STDIN_FILENO, &c, 1) == 1 ? c : -1;
- }
- void readEscapeSequence() {
- if(readChar() != '[') {
- return;
- }
- switch(readChar()) {
- case 'A':
- handleUpArrow();
- break;
- case 'B':
- handleDownArrow();
- break;
- case 'C':
- handleRightArrow();
- break;
- case 'D':
- handleLeftArrow();
- break;
- }
- }
- void handleUpArrow() {
- if(historyOffset >= historyLength) {
- return;
- }
- historyOffset++;
- copyHistory();
- }
- void handleDownArrow() {
- if(historyOffset <= 1) {
- return;
- }
- historyOffset--;
- copyHistory();
- }
- void handleLeftArrow() {
- if(move < index) {
- move++;
- }
- }
- void handleRightArrow() {
- if(move > 0) {
- move--;
- }
- }
- void addToHistory() {
- if(historyLength < H) {
- historyLength++;
- }
- strncpy(history[historyIndex], buffer[bufferIndex], N);
- historyIndex = (historyIndex + 1) % H;
- }
- void copyHistory() {
- strncpy(buffer[bufferIndex], history[(historyIndex - historyOffset + H) % H], N);
- move = 0;
- index = strnlen(buffer[bufferIndex], N);
- }
- };
- #endif
|