]> git.xolatile.top Git - emil-xolatilization.git/commitdiff
For Anon...
authorxolatile <xolatile@proton.me>
Sat, 15 Mar 2025 22:20:06 +0000 (23:20 +0100)
committerxolatile <xolatile@proton.me>
Sat, 15 Mar 2025 22:20:06 +0000 (23:20 +0100)
xerminal.h [new file with mode: 0755]

diff --git a/xerminal.h b/xerminal.h
new file mode 100755 (executable)
index 0000000..73bcb96
--- /dev/null
@@ -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