#include "tlibc/term.h" #if defined(_WIN64) || defined(_WIN32) #include #define try_win_bool(EXPR) if(!(EXPR)) { Return RESULT_ERROR_FMT(#EXPR " failed with WindowsError %lu", GetLastError()); } #else #include #include #include #define try_zero_errno(EXPR) if((EXPR) != 0) { \ char* errno_s = strerror_malloc(errno); \ ResultVar(void) err = RESULT_ERROR_FMT(#EXPR " failed with errno: %s", strerror(errno)); \ free(errno_s); \ Return err; \ } #endif Result(void) term_init(){ Deferral(8); #if defined(_WIN64) || defined(_WIN32) DWORD mode = 0; HANDLE console_handle; // configure stdout console_handle = GetStdHandle(STD_OUTPUT_HANDLE); try_assert(console_handle != INVALID_HANDLE_VALUE); GetConsoleMode(console_handle, &mode); // https://learn.microsoft.com/en-us/windows/console/setconsolemode mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; mode |= ENABLE_PROCESSED_OUTPUT; mode |= DISABLE_NEWLINE_AUTO_RETURN; SetConsoleMode(console_handle, mode); // configure stderr console_handle = GetStdHandle(STD_ERROR_HANDLE); try_assert(console_handle != INVALID_HANDLE_VALUE); GetConsoleMode(console_handle, &mode); mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; mode |= ENABLE_PROCESSED_OUTPUT; mode |= DISABLE_NEWLINE_AUTO_RETURN; SetConsoleMode(console_handle, mode); // configure stdin console_handle = GetStdHandle(STD_INPUT_HANDLE); try_assert(console_handle != INVALID_HANDLE_VALUE); GetConsoleMode(console_handle, &mode); mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; SetConsoleMode(console_handle, mode); #endif Return RESULT_VOID; } i64 getenv_int(const char* var_name){ char* s = getenv(var_name); if(s == NULL) return -1; return strtoll(s, NULL, 0); } Result(void) term_getSize(TerminalSize* out) { Deferral(4); #if defined(_WIN64) || defined(_WIN32) // helps when STD_OUT is redirected to a file HANDLE console_handle_stderr = GetStdHandle(STD_ERROR_HANDLE); try_assert(console_handle_stderr != INVALID_HANDLE_VALUE) CONSOLE_SCREEN_BUFFER_INFO consoleInfo; try_win_bool(GetConsoleScreenBufferInfo(console_handle_stderr, &consoleInfo)); out->cols = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1; out->rows = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1; #else struct winsize ws = {0}; // try to get terminal size from stdin, stdout, stderr if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == 0 ){ out->cols = ws.ws_col; out->rows = ws.ws_row; } // try to get size from environtent variables else { out->cols = getenv_int("COLUMNS"); out->rows = getenv_int("LINES"); } #endif try_assert(out->cols > 0); try_assert(out->rows > 0); Return RESULT_VOID; } Result(void) term_readLine(char* buf, u32 bufsize) { Deferral(1); if(fgets(buf, bufsize, stdin) == NULL){ try_stderrcode(ferror(stdin)); } Return RESULT_VOID; } Result(void) term_readLineHidden(char *buf, u32 bufsize) { Deferral(4); #if defined(_WIN64) || defined(_WIN32) HANDLE console_handle_stdin = GetStdHandle(STD_INPUT_HANDLE); try_assert(console_handle_stdin != INVALID_HANDLE_VALUE); DWORD old_mode; GetConsoleMode(console_handle_stdin, &old_mode); // turn off echo DWORD new_mode = old_mode & ~(ENABLE_ECHO_INPUT); SetConsoleMode(console_handle_stdin, new_mode); // restore echo Defer(SetConsoleMode(console_handle_stdin, old_mode)); #else struct termios old_mode, new_mode; try_zero_errno(tcgetattr(STDIN_FILENO, &old_mode)); new_mode = old_mode; // turn off echo new_mode.c_lflag &= ~(ECHO); try_zero_errno(tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_mode)); // restore echo Defer(tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_mode)); #endif // read line try_void(term_readLine(buf, bufsize)); fputc('\n', stdout); Return RESULT_VOID; } /* Most of escape sequences can be found there https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 */ #define ESC "\x1b" #define CSI ESC"[" void term_setFgColor16(Color16 c){ printf(CSI"%um", c); } void term_setBgColor16(Color16 c){ printf(CSI"%um", c + 10); } void term_bold(){ printf(CSI"1m"); } void term_italic(){ printf(CSI"3m"); } void term_underline(){ printf(CSI"4m"); } void term_strikethrough(){ printf(CSI"9m"); } void term_resetCursor() { printf(CSI"H"); } void term_resetColors() { printf(CSI"0m"); } void term_clear() { printf(CSI"0m" CSI"H" CSI"2J"); } void term_eraseRow(){ printf(CSI"2K\r"); } void term_cursorMove(u16 row, u16 column) { printf(CSI"%u;%uH", row, column); } void term_cursorHide() { printf(CSI"?25l"); } void term_cursorShow() { printf(CSI"?25h"); }