From 7bce39f49b711aef31ad4731c90e36038e8e5cb2 Mon Sep 17 00:00:00 2001 From: xolatile Date: Sat, 15 Mar 2025 23:20:06 +0100 Subject: [PATCH 1/1] For Anon... --- xerminal.h | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100755 xerminal.h diff --git a/xerminal.h b/xerminal.h new file mode 100755 index 0000000..73bcb96 --- /dev/null +++ b/xerminal.h @@ -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 +#include + +#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 -- 2.39.5