More on curses and program structure...

This commit is contained in:
Ognjen Milan Robovic 2023-11-10 17:10:03 -05:00
parent 59fd9b711b
commit 95d48ca3bc
2 changed files with 62 additions and 28 deletions

View File

@ -162,14 +162,64 @@ int curses_screen_width = 0;
int curses_screen_height = 0;
int curses_active = 1;
static void curses_deinitialize (void) {
curses_screen = deallocate (curses_screen);
/*
I need to quickly explain how I'm structuring this subprogram. You can think of functions and variables starting with 'curses_*' as tiny standalone library if it's easier. They
deal with terminal input and output. User only needs to call function 'curses_configure' only once, before doing any rendering, then make an "infinite" loop that'll stop when
external variable 'curses_active' is equal to zero. In that loop, user can call any 'curses_render_*' function, and at the end call function 'curses_synchronize' only once. So the
following program structure would look something like this:
@C
// ...
int main (int argc, char * * argv) {
// ...
curses_configure ();
while (curses_active != 0) { // Notice the negative condition, the loop will stop then the condition is 0, so when 'curses_active' is 0.
// Calling functions 'curses_render_*' and doing other work...
curses_synchronize ();
}
// ...
return (0);
}
@
*/
static void curses_initialize (void) { // This function will be called when 'curses_configure' is called, automatically.
struct winsize screen_dimension;
fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, // If function 'ioctl' failed, we immediately aborting the entire program.
"ioctl: Failed to get terminal dimensions."); // I split those error messages, you can find your own formatting style.
curses_screen_width = (int) screen_dimension.ws_col; // We get the dimensions of terminal window by calling that 'ioctl' function.
curses_screen_height = (int) screen_dimension.ws_row;
fatal_failure (tcgetattr (STDIN_FILENO, & curses_old_terminal) == -1, // Now we need to obtain data for current non-raw terminal to restore it later.
"tcgetattr: Failed to get default terminal attributes.");
curses_new_terminal = curses_old_terminal; // Here we set our raw terminal to be the same as the non-raw one.
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); // Now it's time to modify it to be raw, this essentially means no-echo.
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, // Finally, we're passing intormations to our terminal, and it becomes raw.
"tcsetattr: Failed to set reverse terminal attributes.");
}
static void curses_deinitialize (void) { // This function will be only called once, automatically, at the program exit.
curses_screen = deallocate (curses_screen); // It's important to deallocate all previously allocated memory.
curses_activator = deallocate (curses_activator);
curses_action = deallocate (curses_action);
terminal_clear ();
terminal_clear (); // This only make things look prettier, we don't have mess when we exit the program.
fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, "tcsetattr: Failed to set default terminal attributes.");
fatal_failure (tcsetattr (STDIN_FILENO, TCSAFLUSH, & curses_old_terminal) == -1, // Again, if this fails, we're doomed, we're aborting.
"tcsetattr: Failed to set default terminal attributes.");
}
static char * curses_screen_offset (int x, int y) {
@ -223,28 +273,9 @@ External function definitions, those found in "chapter_2.h" header file.
*/
void curses_configure (void) {
struct winsize screen_dimension;
atexit (curses_deinitialize);
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_initialize ();
curses_screen = allocate (CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR + 1);

View File

@ -93,13 +93,16 @@ One sane C program should have the following structure (please keep in mind that
*/
int main (int argc, char * * argv) {
int i;
(void) argc;
(void) argv;
for (i = 0; i != (int) (sizeof (hello_world) / sizeof (* hello_world)); ++i) {
hello_world [i] ();
curses_configure ();
while (curses_active != 0) {
curses_render_background ('.', COLOUR_GREY, EFFECT_BOLD);
curses_render_character ('@', COLOUR_RED, EFFECT_BOLD, 1, 1);
curses_synchronize ();
}
return (EXIT_SUCCESS);