|
|
@@ -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); |
|
|
|
|
|
|
|