From 4c97787c27af5b7867c8a1262a3383a84a1d4957 Mon Sep 17 00:00:00 2001 From: Timerix Date: Tue, 25 Nov 2025 18:22:14 +0500 Subject: [PATCH] moved term.h to tlibc --- include/tlibc/term.h | 47 +++++++++++ src/term.c | 189 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 include/tlibc/term.h create mode 100644 src/term.c diff --git a/include/tlibc/term.h b/include/tlibc/term.h new file mode 100644 index 0000000..a405408 --- /dev/null +++ b/include/tlibc/term.h @@ -0,0 +1,47 @@ +#pragma once +#include "tlibc/errors.h" + +typedef struct TerminalSize { + i16 cols; + i16 rows; +} TerminalSize; + +typedef enum Color16 { + Color16_Black = 30, + Color16_DarkRed = 31, + Color16_DarkGreen = 32, + Color16_DarkYellow = 33, + Color16_DarkBlue = 34, + Color16_DarkMagenta = 35, + Color16_DarkCyan = 36, + Color16_Gray = 37, + Color16_DarkGray = 90, + Color16_Red = 91, + Color16_Green = 92, + Color16_Yellow = 93, + Color16_Blue = 94, + Color16_Magenta = 95, + Color16_Cyan = 96, + Color16_White = 97 +} Color16; + +Result(void) term_init(); +Result(void) term_getSize(TerminalSize* out); + +Result(void) term_readLine(char* buf, u32 bufsize); +Result(void) term_readLineHidden(char *buf, u32 bufsize); + +void term_setFgColor16(Color16 c); +void term_setBgColor16(Color16 c); +void term_bold(); +void term_italic(); +void term_underline(); +void term_strikethrough(); +void term_resetColors(); + +void term_clear(); +void term_eraseRow(); +void term_resetCursor(); +void term_cursorMove(u16 row, u16 column); +void term_cursorHide(); +void term_cursorShow(); diff --git a/src/term.c b/src/term.c new file mode 100644 index 0000000..746b022 --- /dev/null +++ b/src/term.c @@ -0,0 +1,189 @@ +#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"); +}