From 95d48ca3bc0134e5a143c4e73d7d9fd01d12ab01 Mon Sep 17 00:00:00 2001 From: xolatile Date: Fri, 10 Nov 2023 17:10:03 -0500 Subject: [PATCH] More on curses and program structure... --- chapters/chapter_2.c | 79 ++++++++++++++++++++++++++++++++++++---------------- xhartae.c | 11 +++++--- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/chapters/chapter_2.c b/chapters/chapter_2.c index 93dae7d..314d5a4 100644 --- a/chapters/chapter_2.c +++ b/chapters/chapter_2.c @@ -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); diff --git a/xhartae.c b/xhartae.c index 87f6686..1098ecd 100644 --- a/xhartae.c +++ b/xhartae.c @@ -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);