Files
tlibc/src/term.c
2025-11-25 18:22:14 +05:00

190 lines
4.9 KiB
C

#include "tlibc/term.h"
#if defined(_WIN64) || defined(_WIN32)
#include <windows.h>
#define try_win_bool(EXPR) if(!(EXPR)) { Return RESULT_ERROR_FMT(#EXPR " failed with WindowsError %lu", GetLastError()); }
#else
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#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");
}