| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- #include "core/ReadLine.h"
- #include <ctype.h>
- #include <stdatomic.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
- #include "ErrorSimulator.h"
- #include "core/Logger.h"
- #include "core/Queue.h"
- #include "core/Thread.h"
- #include "core/Unicode.h"
- static constexpr size_t HISTORY_LENGTH = 10;
- static constexpr size_t CONSOLE_BUFFER_SIZE = 256;
- typedef struct ConsoleLine {
- char data[CONSOLE_BUFFER_SIZE];
- size_t length;
- } ConsoleLine;
- QUEUE(ConsoleLine, CL)
- QUEUE_SOURCE(ConsoleLine, CL)
- static atomic_bool running = true;
- static thrd_t readThread = {0};
- static QueueCL buffer = {0};
- static ConsoleLine currentBuffer = {0};
- static size_t move = 0;
- static size_t cursorMove = 0;
- static mtx_t bufferMutex = {0};
- static ConsoleLine history[HISTORY_LENGTH];
- static size_t historyOffset = 0;
- static size_t historyIndex = 0;
- static size_t historyLength = 0;
- static void addChar(u64 c) {
- UTF8 u = convertUnicodeToUTF8((u32)c);
- for(u32 k = 0; k < u.length; k++) {
- if(currentBuffer.length >= CONSOLE_BUFFER_SIZE - 1) {
- return;
- }
- for(size_t i = 0; i < move; i++) {
- currentBuffer.data[currentBuffer.length - i] =
- currentBuffer.data[currentBuffer.length - i - 1];
- }
- currentBuffer.length++;
- currentBuffer.data[currentBuffer.length - move - 1] = (char)u.data[k];
- currentBuffer.data[currentBuffer.length] = '\0';
- }
- }
- static void print(const char* s) {
- fputs(s, stdout);
- }
- static void refreshLine(const char* prefix) {
- print(prefix);
- print(currentBuffer.data);
- if(cursorMove > 0) {
- moveCursorLeft((int)cursorMove);
- }
- fflush(stdout);
- clearTerminalLine();
- }
- static bool clear() {
- move = 0;
- cursorMove = 0;
- currentBuffer.length = 0;
- currentBuffer.data[0] = '\0';
- historyOffset = 0;
- return false;
- }
- static void addToHistory() {
- if(historyLength < HISTORY_LENGTH) {
- historyLength++;
- }
- history[historyIndex] = currentBuffer;
- historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
- }
- static void lock() {
- if(mtx_lock(&bufferMutex) != thrd_success || MUTEX_LOCK_FAIL) {
- REPORT(LOG_WARNING, "could not lock buffer mutex");
- }
- }
- static void unlock() {
- if(mtx_unlock(&bufferMutex) != thrd_success || MUTEX_UNLOCK_FAIL) {
- REPORT(LOG_WARNING, "could not unlock buffer mutex");
- }
- }
- static void addLine() {
- addToHistory();
- lock();
- pushQueueDataCL(&buffer, currentBuffer);
- unlock();
- clear();
- }
- static bool removeChar() {
- size_t pos = currentBuffer.length - move;
- if(pos > 0) {
- size_t l = 1;
- while(pos - l >= 0) {
- if(!isUTF8Remainder((u8)currentBuffer.data[pos - l])) {
- break;
- }
- l++;
- }
- currentBuffer.length -= l;
- for(size_t i = pos - l; i <= currentBuffer.length; i++) {
- currentBuffer.data[i] = currentBuffer.data[i + l];
- }
- }
- return false;
- }
- static bool handleControlKey(u64 c) {
- switch(c) {
- case 3: return clear();
- case 10:
- case 13: return true;
- case 127: return removeChar();
- }
- return false;
- }
- static void copyHistory() {
- currentBuffer = history
- [(historyIndex - historyOffset + HISTORY_LENGTH) % HISTORY_LENGTH];
- move = 0;
- cursorMove = 0;
- }
- static void handleUpArrow() {
- if(historyOffset >= historyLength) {
- return;
- }
- historyOffset++;
- copyHistory();
- }
- static void handleDownArrow() {
- if(historyOffset <= 1) {
- return;
- }
- historyOffset--;
- copyHistory();
- }
- static char getMoved() {
- return currentBuffer.data[currentBuffer.length - move];
- }
- static void handleLeftArrow() {
- if(move < currentBuffer.length) {
- move++;
- while(move < currentBuffer.length && isUTF8Remainder((u8)getMoved())) {
- move++;
- }
- cursorMove++;
- }
- }
- static void handleRightArrow() {
- if(move > 0) {
- move--;
- while(move > 0 && isUTF8Remainder((u8)getMoved())) {
- move--;
- }
- cursorMove--;
- }
- }
- static void handleChars() {
- while(true) {
- u64 c = getRawChar();
- if(c == 0) {
- break;
- } else if(c == TERMINAL_KEY_ARROW_UP) {
- handleUpArrow();
- } else if(c == TERMINAL_KEY_ARROW_DOWN) {
- handleDownArrow();
- } else if(c == TERMINAL_KEY_ARROW_RIGHT) {
- handleRightArrow();
- } else if(c == TERMINAL_KEY_ARROW_LEFT) {
- handleLeftArrow();
- } else if(c == TERMINAL_KEY_DELETE) {
- if(move > 0) {
- handleRightArrow();
- removeChar();
- }
- } else if(iscntrl(c)) {
- if(handleControlKey(c)) {
- addLine();
- return;
- }
- } else {
- addChar(c);
- }
- }
- }
- static int loop(void* data) {
- (void)data;
- while(running) {
- handleChars();
- refreshLine("wusi> ");
- struct timespec t = {.tv_nsec = 1'000'000};
- thrd_sleep(&t, nullptr);
- }
- return 0;
- }
- bool startReadLine(void) {
- if(enterRawTerminal()) {
- REPORT(LOG_WARNING, "cannot set terminal attributes");
- }
- initQueueCL(&buffer, 10);
- atomic_store(&running, true);
- if(MUTEX_INIT_FAIL || mtx_init(&bufferMutex, mtx_plain) != thrd_success) {
- REPORT(LOG_ERROR, "cannot init buffer mutex");
- stopReadLine();
- return true;
- } else if(
- THREAD_INIT_FAIL ||
- thrd_create(&readThread, loop, nullptr) != thrd_success) {
- REPORT(LOG_ERROR, "cannot start read thread");
- stopReadLine();
- return true;
- }
- return false;
- }
- bool readLine(char* buffer_, size_t n) {
- if(buffer.length == 0) {
- return false;
- }
- lock();
- ConsoleLine* line = getQueueIndexCL(&buffer, 0);
- snprintf(buffer_, n, "%s", line->data);
- popQueueDataCL(&buffer);
- unlock();
- return true;
- }
- void stopReadLine() {
- atomic_store(&running, false);
- joinThreadSafe(&readThread);
- if(leaveRawTerminal()) {
- REPORT(LOG_WARNING, "cannot restore terminal attributes");
- }
- destroyQueueCL(&buffer);
- mtx_destroy(&bufferMutex);
- }
|