For Anon...

This commit is contained in:
Ognjen Milan Robovic 2025-03-15 23:20:06 +01:00
commit 7bce39f49b

389
xerminal.h Executable file
View File

@ -0,0 +1,389 @@
/// _ _
/// __ _____ _ __ _ __ ___ (_)_ __ __ _| |
/// \ \/ / _ \ '__| '_ ` _ \| | '_ \ / _` | |
/// > < __/ | | | | | | | | | | | (_| | |
/// /_/\_\___|_| |_| |_| |_|_|_| |_|\__,_|_|
///
/// Copyright (c) 1997 - Ognjen 'xolatile' Milan Robovic
///
/// xolatile@chud.cyou - xerminal - Library containing the full power of VT100 escape sequences or something for TUI programs.
///
/// This program is free software, free as in freedom and as in free beer, you can redistribute it and/or modify it under the terms of the GNU
/// General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version if you wish...
///
/// This program is distributed in the hope that it will be useful, but it is probably not, and without any warranty, without even the implied
/// warranty of merchantability or fitness for a particular purpose, because it is pointless. Please see the GNU (Geenoo) General Public License
/// for more details, if you dare, it is a lot of text that nobody wants to read...
#include <termios.h>
#include <sys/ioctl.h>
#define terminal_format_length (sizeof ("\033[-;3-m-\033[0m") - 1)
#define terminal_revert_length (sizeof ("\033[H") - 1)
#define terminal_cursor_length (sizeof ("\033[---;---H") - 1)
typedef struct {
char * screen;
uint screen_width;
uint screen_height;
char format [terminal_format_length + 1];
char cursor [terminal_cursor_length + 1];
bool active;
bool signal [signal_count];
uint character;
struct termios * old_terminal;
struct termios * new_terminal;
} terminal_structure;
static char * terminal_screen_offset (terminal_structure * terminal, uint x, uint y) {
return (& terminal->screen [terminal_revert_length + terminal_format_length * (y * terminal->screen_width + x) + 2 * y]);
}
static uint terminal_screen_length (terminal_structure * terminal) {
uint constant = terminal_revert_length + terminal_cursor_length + 1;
uint variable = terminal_format_length * terminal->screen_height * terminal->screen_width;
uint new_line = 2 * (terminal->screen_height - 1);
return (constant + variable + new_line);
}
static void terminal_screen_dimensions (terminal_structure * terminal) {
struct winsize screen_dimension = { 0 };
uint old_width = terminal->screen_width;
uint old_height = terminal->screen_height;
int status = ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension);
fatal_failure (status == -1, "ioctl: Failed to get dimensions.");
terminal->screen_width = screen_dimension.ws_col;
terminal->screen_height = screen_dimension.ws_row;
if ((old_width != terminal->screen_width) || (old_height != terminal->screen_height)) {
if (terminal->screen != null) {
terminal->screen = deallocate (terminal->screen);
}
terminal->screen = allocate (terminal_screen_length (terminal));
}
string_copy (& terminal->screen [0], "\033[H");
for (uint index = 0; index < terminal->screen_height - 1; ++index) {
string_copy (& terminal->screen [terminal_revert_length + index * terminal_format_length * terminal->screen_width], "\r\n");
}
}
static char * terminal_format_character (terminal_structure * terminal, char character, int colour, int effect) {
if (character_is_visible (character) == false) {
character = ' ';
}
colour %= colour_count;
effect %= effect_count;
terminal->format [2] = (char) effect + '0';
terminal->format [5] = (char) colour + '0';
terminal->format [7] = character;
return (terminal->format);
}
static terminal_structure * terminal_initialize (void) {
terminal_structure * terminal = allocate (sizeof (* terminal));
int status = -1;
string_copy_limit (terminal->format, "\033[-;3-m-\033[0m", terminal_format_length + 1);
string_copy_limit (terminal->cursor, "\033[---;---H", terminal_cursor_length + 1);
terminal->old_terminal = allocate (sizeof (* terminal->old_terminal));
terminal->new_terminal = allocate (sizeof (* terminal->new_terminal));
terminal_screen_dimensions (terminal);
status = tcgetattr (STDIN_FILENO, terminal->old_terminal);
fatal_failure (status == -1, "tcgetattr: Failed to get default attributes.");
memory_copy (terminal->new_terminal, terminal->old_terminal, sizeof (* terminal->old_terminal));
terminal->new_terminal->c_cc [VMIN] = (uchar) 0;
terminal->new_terminal->c_cc [VTIME] = (uchar) 1;
terminal->new_terminal->c_iflag &= (uint) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
terminal->new_terminal->c_oflag &= (uint) ~(OPOST);
terminal->new_terminal->c_cflag |= (uint) (CS8);
terminal->new_terminal->c_lflag &= (uint) ~(ECHO | ICANON | IEXTEN | ISIG);
status = tcsetattr (STDIN_FILENO, TCSAFLUSH, terminal->new_terminal);
fatal_failure (status == -1, "tcsetattr: Failed to set reverse attributes.");
terminal->active = true;
show_cursor (false);
echo_clear ();
return (terminal);
}
static terminal_structure * terminal_deinitialize (terminal_structure * terminal) {
int status = tcsetattr (STDIN_FILENO, TCSAFLUSH, terminal->old_terminal);
fatal_failure (status == -1, "tcsetattr: Failed to set default attributes.");
terminal->screen = deallocate (terminal->screen);
terminal->old_terminal = deallocate (terminal->old_terminal);
terminal->new_terminal = deallocate (terminal->new_terminal);
echo_clear ();
show_cursor (true);
return (deallocate (terminal));
}
static void terminal_synchronize (terminal_structure * terminal) {
uint character = 0;
output (terminal->screen, terminal_screen_length (terminal));
terminal_screen_dimensions (terminal);
for (uint index = 0; index < signal_count; ++index) {
terminal->signal [index] = false;
}
input (& character, sizeof (character));
terminal->character = (uint) character;
if (character == 0x0000001b) {
terminal->signal [signal_escape] = true;
} else if (character == 0x00415b1b) {
terminal->signal [signal_arrow_up] = true;
} else if (character == 0x00425b1b) {
terminal->signal [signal_arrow_down] = true;
} else if (character == 0x00435b1b) {
terminal->signal [signal_arrow_right] = true;
} else if (character == 0x00445b1b) {
terminal->signal [signal_arrow_left] = true;
} else if (character == 0x00000020) {
terminal->signal [signal_space] = true;
} else if (character == 0x0000007f) {
terminal->signal [signal_backspace] = true;
} else if (character == 0x0000000d) {
terminal->signal [signal_return] = true;
} else if (character_is_digit ((char) character) == true) {
terminal->signal [signal_0 + character - '0'] = true;
} else if (character_is_lowercase ((char) character) == true) {
terminal->signal [signal_a + character - 'a'] = true;
} else if (character_is_uppercase ((char) character) == true) {
terminal->signal [signal_a + character - 'A'] = true;
terminal->signal [signal_left_shift] = true;
terminal->signal [signal_right_shift] = true;
}
}
static void terminal_render_cursor (terminal_structure * terminal, uint x, uint y) { /* BROKE IT INTENTIONALLY */
string_copy_limit (terminal->cursor + 2, string_align_left (number_to_string (y % 1000 + 1), 3, '0'), 3);
string_copy_limit (terminal->cursor + 6, string_align_left (number_to_string (x % 1000 + 1), 3, '0'), 3);
string_copy_limit (& terminal->screen [terminal_screen_length (terminal) - terminal_cursor_length - 1], terminal->cursor, terminal_cursor_length);
}
static void terminal_render_character (terminal_structure * terminal, char character, uint colour, uint effect, uint x, uint y) {
if ((x >= terminal->screen_width) || (y >= terminal->screen_height)) {
return;
}
string_copy_limit (terminal_screen_offset (terminal, x, y), terminal_format_character (terminal, character, colour, effect), terminal_format_length);
}
static void terminal_render_toggle (terminal_structure * terminal, bool toggle, uint x, uint y) {
const char marker = (toggle == true) ? '+' : '-';
const char colour = (toggle == true) ? colour_green : colour_red;
terminal_render_character (terminal, '[', colour_grey, effect_bold, x + 0, y);
terminal_render_character (terminal, marker, colour, effect_bold, x + 1, y);
terminal_render_character (terminal, ']', colour_grey, effect_bold, x + 2, y);
}
static void terminal_render_fill_bar (terminal_structure * terminal, uint value, uint limit, char character, uint colour, uint effect, uint x, uint y) {
terminal_render_character (terminal, '[', colour_grey, effect_bold, x, y);
terminal_render_character (terminal, ']', colour_grey, effect_bold, x + limit + 1, y);
for (uint index = 0; index < limit; ++index) {
terminal_render_character (terminal, (index < value) ? character : ' ', colour, effect, x + index + 1, y);
}
}
static void terminal_render_string (terminal_structure * terminal, const char * string, uint colour, uint effect, uint x, uint y) {
for (uint index = 0; string [index] != '\0'; ++index) {
terminal_render_character (terminal, string [index], colour, effect, x + index, y);
}
}
static void terminal_render_number (terminal_structure * terminal, int number, uint colour, uint effect, uint x, uint y) {
terminal_render_string (terminal, number_to_string (number), colour, effect, x, y);
}
static void terminal_render_string_crop (terminal_structure * terminal, const char * string, uint colour, uint effect, uint x, uint y, uint crop) {
for (uint index = 0; (string [index] != '\0') && (index < crop); ++index) {
terminal_render_character (terminal, string [index], colour, effect, x + index, y);
}
}
static void terminal_render_number_crop (terminal_structure * terminal, int number, uint colour, uint effect, uint x, uint y, uint crop) {
terminal_render_string_crop (terminal, number_to_string (number), colour, effect, x, y, crop);
}
static void terminal_render_vertical_line (terminal_structure * terminal, char character, uint colour, uint effect, uint x, uint y, uint height) {
for (uint offset = 0; offset != height; ++offset) {
terminal_render_character (terminal, character, colour, effect, x, y + offset);
}
}
static void terminal_render_horizontal_line (terminal_structure * terminal, char character, uint colour, uint effect, uint x, uint y, uint width) {
for (uint offset = 0; offset != width; ++offset) {
terminal_render_character (terminal, character, colour, effect, x + offset, y);
}
}
static void terminal_render_rectangle_line (terminal_structure * terminal, char character, uint colour, uint effect, uint x, uint y, uint width, uint height) {
terminal_render_vertical_line (terminal, character, colour, effect, x + 0, y + 0, height + 0);
terminal_render_vertical_line (terminal, character, colour, effect, x + width - 1, y + 0, height + 0);
terminal_render_horizontal_line (terminal, character, colour, effect, x + 1, y + 0, width - 1);
terminal_render_horizontal_line (terminal, character, colour, effect, x + 1, y + height - 1, width - 1);
}
static void terminal_render_rectangle_fill (terminal_structure * terminal, char character, uint colour, uint effect, uint x, uint y, uint width, uint height) {
for (uint offset_y = 0; offset_y != height; ++offset_y) {
for (uint offset_x = 0; offset_x != width; ++offset_x) {
terminal_render_character (terminal, character, colour, effect, x + offset_x, y + offset_y);
}
}
}
static void terminal_render_background (terminal_structure * terminal, char character, uint colour, uint effect) {
for (uint y = 0; y != terminal->screen_height; ++y) {
for (uint x = 0; x != terminal->screen_width; ++x) {
terminal_render_character (terminal, character, colour, effect, x, y);
}
}
}
static void terminal_render_format (terminal_structure * terminal, const char * format, uint x, uint y, ...) {
va_list list;
uint offset_x = 0;
uint offset_y = 0;
colour_enumeration colour = colour_white;
effect_enumeration effect = effect_normal;
va_start (list, format);
for (; * format != character_null; ++format) {
switch (* format) {
case '\t': {
offset_x += 8;
} break;
case '\n': {
offset_x *= 0;
offset_y += 1;
} break;
case '\r': {
offset_x *= 0;
} break;
case '%': {
++format;
switch (* format) {
case '%': {
terminal_render_character (terminal, '%', colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
case 'i': {
char * number = number_to_string (va_arg (list, int));
terminal_render_string (terminal, number, colour, effect, x + offset_x, y + offset_y);
offset_x += string_length (number);
} break;
case 't': {
bool toggle = (bool) va_arg (list, int);
terminal_render_toggle (terminal, toggle, x + offset_x, y + offset_y);
offset_x += 3;
} break;
case 'b': {
bool boolean = (bool) va_arg (list, int);
terminal_render_string (terminal, (boolean == true) ? "true" : "false", colour, effect, x + offset_x, y + offset_y);
offset_x += (boolean == true) ? 4 : 5;
} break;
case 'c': {
char character = (char) va_arg (list, int);
terminal_render_character (terminal, character, colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
case 's': {
char * string = va_arg (list, char *);
terminal_render_string (terminal, string, colour, effect, x + offset_x, y + offset_y);
offset_x += string_length (string);
} break;
default: {
terminal_render_character (terminal, '?', colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
}
} break;
case '/': {
++format;
switch (* format) {
case '/': {
terminal_render_character (terminal, '/', colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
case 'A': effect = effect_normal; break;
case 'B': effect = effect_bold; break;
case 'C': effect = effect_italic; break;
case 'D': effect = effect_undefined_code; break;
case 'E': effect = effect_underline; break;
case 'F': effect = effect_blink; break;
case 'G': effect = effect_reverse; break;
case 'H': effect = effect_invisible_text; break;
case '0': colour = colour_grey; break;
case '1': colour = colour_red; break;
case '2': colour = colour_green; break;
case '3': colour = colour_yellow; break;
case '4': colour = colour_blue; break;
case '5': colour = colour_pink; break;
case '6': colour = colour_cyan; break;
case '7': colour = colour_white; break;
case '-': {
colour = colour_white;
effect = effect_normal;
} break;
default: {
terminal_render_character (terminal, '?', colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
}
} break;
default: {
terminal_render_character (terminal, * format, colour, effect, x + offset_x, y + offset_y);
++offset_x;
} break;
}
}
va_end (list);
}
#undef terminal_format_length
#undef terminal_revert_length
#undef terminal_cursor_length