279 lines
8.8 KiB
C
279 lines
8.8 KiB
C
/*
|
|
Copyright (c) 2023 : Ognjen 'xolatile' Milan Robovic
|
|
|
|
Xurses is free software! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation.
|
|
And when you do redistribute it or modify it, it will use either version 3 of the License, or (at yours truly opinion) any later version.
|
|
It is distributed in the hope that it will be useful or harmful, it really depends... But no warranty what so ever, seriously. See GNU/GPLv3.
|
|
*/
|
|
|
|
#ifndef XURSES_SOURCE
|
|
#define XURSES_SOURCE
|
|
|
|
#include <xolatile/xtandard.c>
|
|
|
|
#include <xolatile/xurses.h>
|
|
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* Internal constant definitions. */
|
|
|
|
#define CURSES_FORMAT ((int) sizeof ("\033[-;3-m-\033[0m") - 1)
|
|
#define CURSES_REVERT ((int) sizeof ("\033[H") - 1)
|
|
#define CURSES_CURSOR ((int) sizeof ("\033[---;---H") - 1)
|
|
|
|
/* Internal variable definitions. */
|
|
|
|
static char curses_format [CURSES_FORMAT + 1] = "\033[-;3-m-\033[0m";
|
|
static char curses_cursor [CURSES_CURSOR + 1] = "\033[---;---H";
|
|
|
|
static char * curses_screen = NULL;
|
|
|
|
static int curses_action_count = 0;
|
|
static int * curses_activator = NULL;
|
|
|
|
static void (* * curses_action) (void) = NULL;
|
|
|
|
static struct termios curses_old_terminal;
|
|
static struct termios curses_new_terminal;
|
|
|
|
/* Internal function definitions. */
|
|
|
|
static void curses_free (void) {
|
|
curses_screen = deallocate (curses_screen);
|
|
curses_activator = deallocate (curses_activator);
|
|
curses_action = deallocate (curses_action);
|
|
|
|
terminal_clear ();
|
|
|
|
fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
|
|
}
|
|
|
|
/* Return offset of variable 'curses_screen' according to X and Y coordinates. */
|
|
static char * curses_screen_offset (int x, int y) {
|
|
/*log_in (LOG_FAILURE, x <= -1, "curses_screen_offset: X position is below the lower bound.");
|
|
log_in (LOG_FAILURE, y <= -1, "curses_screen_offset: Y position is below the lower bound.");
|
|
log_in (LOG_FAILURE, x >= curses_screen_width, "curses_screen_offset: X position is above the upper bound.");
|
|
log_in (LOG_FAILURE, y >= curses_screen_height, "curses_screen_offset: Y position is above the upper bound.");*/
|
|
|
|
limit (& x, 0, curses_screen_width - 1);
|
|
limit (& y, 0, curses_screen_height - 1);
|
|
|
|
return (& curses_screen [CURSES_REVERT + CURSES_FORMAT * (y * curses_screen_width + x)]);
|
|
}
|
|
|
|
static char * curses_format_character (char character, int colour, int effect) {
|
|
/*log_in (LOG_WARNING, character_is_invisible (character), "curses_format_character: Can not format invisible characters.");
|
|
log_in (LOG_FAILURE, colour >= COLOUR_COUNT, "curses_format_character: Colour is invalid enumeration value.");
|
|
log_in (LOG_FAILURE, effect >= EFFECT_COUNT, "curses_format_character: Effect is invalid enumeration value.");*/
|
|
|
|
if (character_is_invisible (character) != 0) {
|
|
character = ' ';
|
|
}
|
|
|
|
colour %= COLOUR_COUNT;
|
|
effect %= EFFECT_COUNT;
|
|
|
|
switch (effect) {
|
|
case EFFECT_NORMAL: effect = 0; break;
|
|
case EFFECT_BOLD: effect = 1; break;
|
|
case EFFECT_ITALIC: effect = 3; break;
|
|
case EFFECT_UNDERLINE: effect = 4; break;
|
|
case EFFECT_BLINK: effect = 5; break;
|
|
case EFFECT_REVERSE: effect = 7; break;
|
|
default: effect = 0; break;
|
|
}
|
|
|
|
curses_format [2] = (char) effect + '0';
|
|
curses_format [5] = (char) colour + '0';
|
|
curses_format [7] = character;
|
|
|
|
/*log_out ("curses.log");*/
|
|
|
|
return (curses_format);
|
|
}
|
|
|
|
static void curses_idle (void) {
|
|
return;
|
|
}
|
|
|
|
static void curses_exit (void) {
|
|
curses_active = 0;
|
|
}
|
|
|
|
/* External variable definitions. */
|
|
|
|
int curses_realign_x = 0;
|
|
int curses_realign_y = 0;
|
|
int curses_tab_width = 8;
|
|
int curses_signal = SIGNAL_NONE;
|
|
int curses_screen_width = 0;
|
|
int curses_screen_height = 0;
|
|
int curses_active = 1;
|
|
|
|
/* External function definitions. */
|
|
|
|
void curses_configure (void) {
|
|
struct winsize screen_dimension;
|
|
|
|
atexit (curses_free);
|
|
|
|
fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions.");
|
|
|
|
curses_screen_width = (int) screen_dimension.ws_col;
|
|
curses_screen_height = (int) screen_dimension.ws_row;
|
|
|
|
fatal_failure (tcgetattr (STDIN_FILENO, & curses_old_terminal) == -1, "tcgetattr: Failed to get default terminal attributes.");
|
|
|
|
curses_new_terminal = curses_old_terminal;
|
|
|
|
curses_new_terminal.c_cc [VMIN] = (unsigned char) 0;
|
|
curses_new_terminal.c_cc [VTIME] = (unsigned char) 1;
|
|
|
|
curses_new_terminal.c_iflag &= (unsigned int) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
|
curses_new_terminal.c_oflag &= (unsigned int) ~(OPOST);
|
|
curses_new_terminal.c_cflag |= (unsigned int) (CS8);
|
|
curses_new_terminal.c_lflag &= (unsigned int) ~(ECHO | ICANON | IEXTEN | ISIG);
|
|
|
|
fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_new_terminal) == -1, "tcsetattr: Failed to set reverse terminal attributes.");
|
|
|
|
curses_screen = allocate (CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR + 1);
|
|
|
|
curses_bind (SIGNAL_ESCAPE, curses_exit);
|
|
|
|
terminal_clear ();
|
|
|
|
string_copy (& curses_screen [0], "\033[H");
|
|
}
|
|
|
|
void curses_synchronize (void) {
|
|
int signal;
|
|
|
|
curses_signal = signal = 0;
|
|
|
|
out (curses_screen, CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR);
|
|
|
|
in (& signal, 4);
|
|
|
|
if ((char) signal == '\033') {
|
|
curses_signal |= SIGNAL_ESCAPE;
|
|
} else if (character_is_digit ((char) signal) != 0) {
|
|
curses_signal |= SIGNAL_0 + (int) ((char) signal - '0');
|
|
} else if (character_is_lowercase ((char) signal) != 0) {
|
|
curses_signal |= SIGNAL_A + (int) ((char) signal - 'a');
|
|
} else if (character_is_uppercase ((char) signal) != 0) {
|
|
curses_signal |= SIGNAL_A + (int) ((char) signal - 'A');
|
|
curses_signal |= SIGNAL_SHIFT;
|
|
} else {
|
|
curses_signal = SIGNAL_NONE;
|
|
}
|
|
|
|
for (signal = 0; signal != curses_action_count; ++signal) {
|
|
if (curses_signal == curses_activator [signal]) {
|
|
curses_action [signal] ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void curses_bind (int signal, void (* action) (void)) {
|
|
++curses_action_count;
|
|
|
|
curses_activator = reallocate (curses_activator, curses_action_count * (int) sizeof (* curses_activator));
|
|
curses_action = reallocate (curses_action, curses_action_count * (int) sizeof (* curses_action));
|
|
|
|
curses_activator [curses_action_count - 1] = signal;
|
|
curses_action [curses_action_count - 1] = action;
|
|
}
|
|
|
|
void curses_unbind (int signal) {
|
|
(void) signal;
|
|
|
|
curses_activator [curses_action_count - 1] = SIGNAL_NONE;
|
|
curses_action [curses_action_count - 1] = curses_idle;
|
|
|
|
--curses_action_count;
|
|
}
|
|
|
|
void curses_render_cursor (int x, int y) {
|
|
x %= 1000;
|
|
y %= 1000;
|
|
|
|
string_copy_limit (curses_cursor + 2, string_realign (number_to_string (x), 3, '0'), 3);
|
|
string_copy_limit (curses_cursor + 6, string_realign (number_to_string (y), 3, '0'), 3);
|
|
|
|
string_copy_limit (& curses_screen [CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height], curses_cursor, CURSES_CURSOR);
|
|
}
|
|
|
|
void curses_render_character (char character, int colour, int effect, int x, int y) {
|
|
string_copy_limit (curses_screen_offset (x, y), curses_format_character (character, colour, effect), CURSES_FORMAT);
|
|
}
|
|
|
|
void curses_render_background (char character, int colour, int effect) {
|
|
int x, y;
|
|
|
|
for (y = 0; y != curses_screen_height; ++y) {
|
|
for (x = 0; x != curses_screen_width; ++x) {
|
|
curses_render_character (character, colour, effect, x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void curses_render_string_point (char * string, int limit, int colour, int effect, int * x, int * y) {
|
|
int offset;
|
|
|
|
for (offset = 0; offset != limit; ++offset) {
|
|
if (* x + offset < curses_screen_width) {
|
|
if (string [offset] == '\n') {
|
|
* x = curses_realign_x;
|
|
* y += 1;
|
|
} else if (string [offset] == '\t') {
|
|
* x += curses_tab_width;
|
|
} else {
|
|
curses_render_character (string [offset], colour, effect, * x, * y);
|
|
* x += 1;
|
|
}
|
|
} else {
|
|
curses_render_character ('+', COLOUR_GREY, EFFECT_BOLD, * x, * y);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void curses_render_number_point (int number, int limit, int colour, int effect, int * x, int * y) {
|
|
(void) number;
|
|
(void) limit;
|
|
(void) colour;
|
|
(void) effect;
|
|
(void) x;
|
|
(void) y;
|
|
return;
|
|
}
|
|
|
|
void curses_render_string_limit (char * string, int limit, int colour, int effect, int x, int y) {
|
|
int xx = x, yy = y;
|
|
|
|
curses_render_string_point (string, limit, colour, effect, & xx, & yy);
|
|
}
|
|
|
|
void curses_render_number_limit (int number, int limit, int colour, int effect, int x, int y) {
|
|
(void) number;
|
|
(void) limit;
|
|
(void) colour;
|
|
(void) effect;
|
|
(void) x;
|
|
(void) y;
|
|
return;
|
|
}
|
|
|
|
void curses_render_string (char * string, int colour, int effect, int x, int y) {
|
|
curses_render_string_limit (string, string_length (string), colour, effect, x, y);
|
|
}
|
|
|
|
void curses_render_number (int number, int colour, int effect, int x, int y) {
|
|
curses_render_number_limit (number, 4, colour, effect, x, y);
|
|
}
|
|
|
|
#endif
|