Browse Source

everything moved into a class, history, left / right moving, full line
clear

Kajetan Johannes Hammerle 3 years ago
parent
commit
44867b22f4
4 changed files with 251 additions and 109 deletions
  1. 1 0
      .gitignore
  2. 15 108
      Main.cpp
  3. 3 1
      Makefile
  4. 232 0
      RawReader.h

+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 raw_terminal
+nbproject

+ 15 - 108
Main.cpp

@@ -1,116 +1,23 @@
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <termios.h>
-#include <unistd.h>
+#include <iostream>
+#include <cstring>
 
-// https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
-
-struct termios orig_termios;
-
-void disableRawMode() {
-    tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
-}
-
-void enableRawMode() {
-    tcgetattr(STDIN_FILENO, &orig_termios);
-    atexit(disableRawMode);
-
-    struct termios raw;
-    tcgetattr(STDIN_FILENO, &raw);
-    raw.c_iflag &= ~(ICRNL | IXON);
-    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN /*| ISIG*/);
-    raw.c_cc[VMIN] = 0;
-    raw.c_cc[VTIME] = 0;
-
-    tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
-}
-
-char buffer[256];
-int move = 0;
-
-void refreshLine() {
-    putchar('\r');
-    printf("> %s", buffer);
-    if(move > 0) {
-        printf("\33[%dD", move);
-    }
-    fflush(STDIN_FILENO);
-}
+#include "RawReader.h"
 
 int main() {
-    enableRawMode();
-
-    int index = 0;
-    buffer[0] = '\0';
-
-    while(1) {
-        int c = 0;
-        int bytes = read(STDIN_FILENO, &c, 1);
-
-        printf("\33[2K\r");
-
-        if(bytes < 0) {
-            break;
-        } else if(bytes == 0) {
-            //printf("timeout\n");
-            refreshLine();
-            continue;
-        } else if(c == 'q') {
-            break;
-        }
-
-        if(c == 27) {
-            char escape[10];
-            int escapeBytes = read(STDIN_FILENO, escape, 10);
-            printf("Escape sequence of length: %d\n", escapeBytes);
-            if(escapeBytes == 2) {
-                if(escape[0] == '[' && escape[1] == 'D' && move < index) {
-                    move++;
-                }
-                if(escape[0] == '[' && escape[1] == 'C' && move > 0) {
-                    move--;
-                }
-            }
-
-            continue;
-        }
-
-        if(iscntrl(c)) {
-            switch(c) {
-                case 127:
-                    if(index > 0) {
-                        if(move == 0) {
-                            index--;
-                            buffer[index] = '\0';
-                        } else {
-                            int pos = index - move - 1;
-                            if(pos >= 0) {
-                                while(buffer[pos] != '\0') {
-                                    buffer[pos] = buffer[pos + 1];
-                                    pos++;
-                                }
-                                index--;
-                            }
-                        }
-                    }
-                    break;
-                case 13:
-                    puts(buffer);
-                    index = 0;
-                    buffer[0] = '\0';
-                    move = 0;
-                    break;
-            }
-            printf("%d\n", c);
-        } else {
-            printf("%d ('%c')\n", c, c);
-            if(move == 0) {
-                buffer[index++] = c;
-                buffer[index] = '\0';
+    RawReader<10, 5> reader(1);
+
+    while(reader.canRead()) {
+        // simulate work between reads
+        usleep(50000);
+        //std::cout << "Work\n";
+
+        const char* s = reader.readLine("> ");
+        if(s != nullptr) {
+            std::cout << s << "\n";
+            if(strcmp(s, "q") == 0) {
+                break;
             }
         }
-        refreshLine();
     }
     return 0;
 }

+ 3 - 1
Makefile

@@ -1,7 +1,9 @@
 all: raw_terminal
+
+run: all
 	./raw_terminal
 
-raw_terminal: Main.cpp
+raw_terminal: Main.cpp RawReader.h
 	g++ -o $@ Main.cpp -Wall -Wextra -Werror -pedantic
 
 clean:

+ 232 - 0
RawReader.h

@@ -0,0 +1,232 @@
+#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;
+
+public:
+    typedef int DeciSeconds;
+
+    RawReader(DeciSeconds timeout) : index(0), bufferIndex(false), noError(true), move(0),
+    historyOffset(0), historyIndex(0), historyLength(0) {
+        // 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(const char* prefix) {
+        refreshLine(prefix);
+        while(true) {
+            char c = 0;
+            int bytes = read(STDIN_FILENO, &c, 1);
+            if(bytes < 0) {
+                markError();
+                break;
+            } else if(bytes == 0) {
+                break;
+            }
+
+            if(c == 27) {
+                readEscapeSequence();
+            } else if(iscntrl(c)) {
+                if(handleControlKey(c)) {
+                    clearLine();
+                    return swapBuffers();
+                }
+            } else {
+                addChar(c);
+            }
+            refreshLine(prefix);
+        }
+        clearLine();
+        return nullptr;
+    }
+
+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(const char* prefix) {
+        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) {
+            buffer[bufferIndex][index++] = c;
+            buffer[bufferIndex][index] = '\0';
+        }
+    }
+
+    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