From 5266872c2b1d41ca8b8278e07f0573d55ff9cc19 Mon Sep 17 00:00:00 2001 From: Timerix Date: Sun, 16 Nov 2025 00:45:08 +0500 Subject: [PATCH] added a lot of terminal control functions --- src/client/client.c | 21 +-- src/server/request_handlers/send_error.c | 2 +- src/term.c | 181 +++++++++++++++++------ src/term.h | 39 ++++- 4 files changed, 175 insertions(+), 68 deletions(-) diff --git a/src/client/client.c b/src/client/client.c index 1437bad..8352c8b 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -49,9 +49,7 @@ static Result(void) askUserNameAndPassword(ClientCredentials* cred){ str username = str_null; while(true) { printf("username: "); - if(fgets(username_buf, sizeof(username_buf), stdin) == NULL){ - Return RESULT_ERROR("STDIN is closed", false); - } + try_void(term_readLine(username_buf, sizeof(username_buf))); username = str_from_cstr(username_buf); str_trim(&username, true); if(username.size < USERNAME_SIZE_MIN || username.size > USERNAME_SIZE_MAX){ @@ -66,9 +64,7 @@ static Result(void) askUserNameAndPassword(ClientCredentials* cred){ while(true) { printf("password: "); // TODO: hide password - if(fgets(password_buf, sizeof(password_buf), stdin) == NULL){ - Return RESULT_ERROR("STDIN is closed", false); - } + try_void(term_readLineHidden(password_buf, sizeof(password_buf))); password = str_from_cstr(password_buf); str_trim(&password, true); if(password.size < PASSWORD_SIZE_MIN || password.size > PASSWORD_SIZE_MAX){ @@ -84,9 +80,7 @@ static Result(void) askUserNameAndPassword(ClientCredentials* cred){ Result(void) Client_run(Client* client) { Deferral(16); - if(!term_init()){ - Return RESULT_ERROR("can't init terminal", false); - } + try_void(term_init()); fputs(greeting_art.data, stdout); try_void(askUserNameAndPassword(&client->cred)); @@ -98,9 +92,7 @@ Result(void) Client_run(Client* client) { while(!stop){ sleepMsec(50); fputs("> ", stdout); - if(fgets(input_buf.data, input_buf.size, stdin) == NULL){ - Return RESULT_ERROR("STDIN is closed", false); - } + try_void(term_readLine(input_buf.data, input_buf.size)); command_input = str_from_cstr(input_buf.data); str_trim(&command_input, true); @@ -124,7 +116,6 @@ Result(void) Client_run(Client* client) { static Result(void) commandExec(Client* client, str command, bool* stop){ Deferral(64); char answer_buf[10000]; - const u32 answer_buf_size = sizeof(answer_buf); if(is_alias("q") || is_alias("quit") || is_alias("exit")){ fputs(farewell_art.data, stdout); *stop = true; @@ -146,9 +137,7 @@ static Result(void) commandExec(Client* client, str command, bool* stop){ ServerConnection_close(client->server_connection); puts("Enter server address (ip:port:public_key): "); - if(fgets(answer_buf, answer_buf_size, stdin) == NULL){ - Return RESULT_ERROR("STDIN is closed", false); - } + try_void(term_readLine(answer_buf, sizeof(answer_buf))); str new_server_link = str_from_cstr(answer_buf); str_trim(&new_server_link, true); diff --git a/src/server/request_handlers/send_error.c b/src/server/request_handlers/send_error.c index 2c02083..a9027f0 100644 --- a/src/server/request_handlers/send_error.c +++ b/src/server/request_handlers/send_error.c @@ -50,5 +50,5 @@ Result(void) sendErrorMessage_f(cstr log_ctx, bool logAsError, Defer(va_end(argv)); try_void(__sendErrorMessage_fv(log_ctx, logAsError, conn, res_head, format, argv)); - return RESULT_VOID; + Return RESULT_VOID; } diff --git a/src/term.c b/src/term.c index 9da8345..9fd96aa 100644 --- a/src/term.c +++ b/src/term.c @@ -1,95 +1,182 @@ #include "term.h" -#include -#include IFWIN("windows.h", "sys/ioctl.h") - -bool term_init(){ #if defined(_WIN64) || defined(_WIN32) - DWORD mode=0; - HANDLE h; - - // configure stdout - h = GetStdHandle(STD_OUTPUT_HANDLE); - if(h == INVALID_HANDLE_VALUE) - return false; - GetConsoleMode(h, &mode); - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - mode |= ENABLE_PROCESSED_OUTPUT; - SetConsoleMode(h, mode); - - // configure stdin - h = GetStdHandle(STD_INPUT_HANDLE); - if(h == INVALID_HANDLE_VALUE) - return false; - GetConsoleMode(h, &mode); - mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; - mode |= ENABLE_PROCESSED_INPUT; - SetConsoleMode(h, mode); + #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) { Return RESULT_ERROR_FMT(#EXPR " failed with errno: %s", strerror(errno)); } #endif - return true; +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; } -int getenv_int(const char* var_name){ - char* str=getenv(var_name); - if(str==NULL) +i64 getenv_int(const char* var_name){ + char* s = getenv(var_name); + if(s == NULL) return -1; - return strtol(str, NULL, 0); + return strtoll(s, NULL, 0); } -bool term_getSize(TerminalSize* out) { +Result(void) term_getSize(TerminalSize* out) { + Deferral(4); + #if defined(_WIN64) || defined(_WIN32) // helps when STD_OUT is redirected to a file - HANDLE hConsoleErr = GetStdHandle(STD_ERROR_HANDLE); + HANDLE console_handle_stderr = GetStdHandle(STD_ERROR_HANDLE); + try_assert(console_handle_stderr != INVALID_HANDLE_VALUE) CONSOLE_SCREEN_BUFFER_INFO consoleInfo; - if(!GetConsoleScreenBufferInfo(hConsoleErr, &consoleInfo)) - return false; + 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; + 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"); + out->cols = getenv_int("COLUMNS"); + out->rows = getenv_int("LINES"); } #endif - return out->cols > 0 && out->rows > 0; + try_assert(out->cols > 0); + try_assert(out->rows > 0); + Return RESULT_VOID; } +Result(void) term_readLine(char* buf, u32 bufsize) { + Deferral(1); + try_assert(fgets(buf, bufsize, stdin) != NULL); + 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)); + fputchar('\n'); + + 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("\e[H"); + printf(CSI"H"); } void term_resetColors() { - printf("\e[0m"); + printf(CSI"0m"); } void term_clear() { - printf("\e[0m\e[H\e[2J"); + printf(CSI"0m" CSI"H" CSI"2J"); +} + +void term_eraseRow(){ + printf(CSI"2K\r"); } void term_cursorMove(u16 row, u16 column) { - printf("\e[%u;%uH",row,column); + printf(CSI"%u;%uH", row, column); } void term_cursorHide() { - printf("\e[?25l"); + printf(CSI"?25l"); } void term_cursorShow() { - printf("\e[?25h"); + printf(CSI"?25h"); } diff --git a/src/term.h b/src/term.h index 3b7b971..a405408 100644 --- a/src/term.h +++ b/src/term.h @@ -1,16 +1,47 @@ #pragma once -#include "tlibc/std.h" +#include "tlibc/errors.h" typedef struct TerminalSize { i16 cols; i16 rows; } TerminalSize; -bool term_init(); -bool term_getSize(TerminalSize* out); -void term_resetCursor(); +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();