xurses/xurses.c

178 lines
5.9 KiB
C
Raw Normal View History

2023-08-28 18:52:28 -04:00
/*
* 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/xurses.h>
2023-10-04 19:34:38 -04:00
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
2023-08-28 18:52:28 -04:00
2023-10-04 19:34:38 -04:00
#define CURSES_LENGTH ((int) sizeof ("\033[-;3-m-\033[0m") - 1)
#define CURSES_OFFSET ((int) sizeof ("\033[H") - 1)
#define CURSES_RETURN ((int) sizeof ("\r\n") - 1)
2023-10-04 19:34:38 -04:00
static int curses_stop = SIGNAL_Q;
static int curses_signal = SIGNAL_NONE;
static int curses_screen_width = 0;
static int curses_screen_height = 0;
static int curses_screen_size = 0;
static char * curses_screen = NULL;
2023-08-28 18:52:28 -04:00
2023-10-04 19:34:38 -04:00
static char curses_format [CURSES_LENGTH + 1] = "\033[-;3-m-\033[0m";
2023-08-28 18:52:28 -04:00
2023-10-04 19:34:38 -04:00
static void (* curses_action [SIGNAL_COUNT]) (void) = { 0 };
static struct termios curses_old_terminal;
static struct termios curses_new_terminal;
int curses_active = 0;
static void curses_free (void) {
curses_screen = deallocate (curses_screen);
terminal_clear ();
fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
}
static void curses_screen_offset (void) {
string_copy (& curses_screen [0], "\033[H");
curses_screen_size = CURSES_OFFSET;
}
static char * curses_screen_position (int x, int y) {
fatal_failure (x <= -1, "curses_screen_position: X position is below the lower bound.");
fatal_failure (y <= -1, "curses_screen_position: Y position is below the lower bound.");
fatal_failure (x >= curses_screen_width, "curses_screen_position: X position is above the upper bound.");
fatal_failure (y >= curses_screen_height, "curses_screen_position: Y position is above the upper bound.");
return (& curses_screen [CURSES_LENGTH * (y * curses_screen_width + x) + y * CURSES_RETURN + CURSES_OFFSET]);
}
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.");
curses_format [2] = (char) (effect % EFFECT_COUNT) + '0';
curses_format [5] = (char) (colour % COLOUR_COUNT) + '0';
curses_format [7] = character;
log_out ("curses.log");
return (curses_format);
}
static void curses_idle (void) {
return;
}
void curses_configure (void) {
2023-08-28 18:52:28 -04:00
struct winsize screen_dimension;
2023-10-04 17:49:10 -04:00
char signal = 0;
char offset = 0;
2023-10-04 19:34:38 -04:00
atexit (curses_free);
fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions.");
2023-08-28 18:52:28 -04:00
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.");
2023-08-28 18:52:28 -04:00
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.");
2023-08-28 18:52:28 -04:00
curses_screen = allocate (CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height + (curses_screen_height - 1) * CURSES_RETURN + 1);
2023-08-28 18:52:28 -04:00
2023-10-04 17:49:10 -04:00
for (signal = SIGNAL_NONE; signal != SIGNAL_COUNT; ++signal) {
curses_unbind ((char) signal);
2023-08-28 18:52:28 -04:00
}
2023-09-20 06:28:31 -04:00
terminal_clear ();
curses_screen_offset ();
2023-10-04 17:49:10 -04:00
for (offset = 0; offset != curses_screen_height - 1; ++offset) {
string_copy (& curses_screen [CURSES_LENGTH * curses_screen_width * offset + CURSES_OFFSET], "\r\n");
}
2023-08-28 18:52:28 -04:00
2023-10-04 19:34:38 -04:00
curses_active = 1;
2023-08-28 18:52:28 -04:00
}
void curses_synchronize (void) {
2023-09-18 16:17:08 -04:00
curses_signal = '\0';
2023-10-04 19:34:38 -04:00
2023-10-04 17:49:10 -04:00
out (curses_screen, CURSES_OFFSET + CURSES_LENGTH * curses_screen_width * curses_screen_height/* + curses_screen_height * CURSES_RETURN*/);
2023-08-28 18:52:28 -04:00
in (& curses_signal, 1);
2023-10-04 17:49:10 -04:00
switch (curses_signal) {
case '0': curses_signal = SIGNAL_0; break;
case 'Q': curses_signal = SIGNAL_Q | SIGNAL_SHIFT; break;
case 'q': curses_signal = SIGNAL_Q; break;
default: curses_signal = SIGNAL_NONE; break;
}
2023-10-04 19:34:38 -04:00
if (curses_signal == curses_stop) {
curses_active = 0;
return;
}
2023-10-04 17:49:10 -04:00
if ((curses_signal > SIGNAL_ANY) && (curses_signal < SIGNAL_COUNT)) {
curses_action [curses_signal] ();
2023-08-28 18:52:28 -04:00
}
2023-09-18 16:17:08 -04:00
curses_screen_offset ();
2023-09-18 16:17:08 -04:00
}
2023-10-04 19:34:38 -04:00
void curses_render_character (char character, int colour, int effect, int x, int y) {
2023-10-04 15:12:51 -04:00
string_copy (curses_screen_position (x, y), curses_format_character (character, colour, effect));
curses_screen_size += CURSES_LENGTH;
}
2023-09-18 16:17:08 -04:00
2023-10-04 19:34:38 -04:00
void curses_render_background (char character, int colour, int effect) {
int x, y;
2023-09-18 16:17:08 -04:00
2023-10-04 19:34:38 -04:00
for (y = 0; y != curses_screen_height; ++y) {
for (x = 0; x != curses_screen_width; ++x) {
curses_render_character (character, colour, effect, x, y);
}
2023-08-28 18:52:28 -04:00
}
}
2023-10-04 17:49:10 -04:00
void curses_bind (int signal, void (* action) (void)) {
curses_action [signal] = action;
2023-08-28 18:52:28 -04:00
}
2023-10-04 17:49:10 -04:00
void curses_unbind (int signal) {
curses_action [signal] = curses_idle;
2023-08-28 18:52:28 -04:00
}
2023-10-04 19:34:38 -04:00
void curses_exit (int signal) {
curses_stop = signal;
2023-08-28 18:52:28 -04:00
}
#endif