diff --git a/chapters/chapter_2.c b/chapters/chapter_2.c index c8a2300..93dae7d 100644 --- a/chapters/chapter_2.c +++ b/chapters/chapter_2.c @@ -98,23 +98,71 @@ void (* hello_world [4]) (void) = { }; /* -Now, we'll talk more about cursed functions! I broke code formatting rules for this, but it's worth it, time to learn. +Now, we'll talk more about cursed functions! I broke code formatting rules for this, but it's worth it, time to learn. Function or variable with 'static' instead of 'extern' +keyword before it is internal. That means it'll only be accessable in this file. That way, I can include file "chapter_2.h" in some other file, and know that I can't access or +modify some internal variable within that other file. They can only be accessed or modified in this file! Lets show some formatting examples below: + +@C +static int my_size = 0; +static char * my_name = "Name"; +static void * my_data = NULL; +@ + +Unlike when declaring external variables (seen in previous header files), you need to initialize internal variables to some value, like you see above. You shouldn't initialize +external variables, they have separate definition (initialization in this case). You can see them just below, they are initialized without 'extern' in front of them, and the same +goes for functions declared in "chapter_2.h", but there's another trick to this... You can declare internal functions before defining them, so you have 2 options for them: + +@C +// Option A: +static void * my_function (char * name, int size); + +int my_function (char * name, int size) { + int data = 0; + + // Do some work involving function arguments and modify data. + + return (data); +} + +// Option B: +static int my_function (char * name, int size) { + int data = 0; + + // Do some work involving function arguments and modify data. + + return (data); +} +@ + +Okay, external variables you see lower (without 'static') are all integers. Lets briefly see what types are the internal variables. + +- curses_format / curses_format: Array of characters (also known as string!), and their size is already known at the compile time, it's inside square braces. +- curses_screen: Pointer to character, we'll allocate memory for it later, so it'll be a dynamic array of characters, its' size can change. +- curses_activator: Pointer to integer, we'll also allocate memory for it, so we can't use square braces for those. Remember that for later. +- curses_action: Strictly speaking, pointer to another pointer to function of signature 'void SOMETHING (void);', but ignore it for now, don't get confused. */ -static char curses_format [CURSES_FORMAT + 1] = "\033[-;3-m-\033[0m"; -static char curses_cursor [CURSES_CURSOR + 1] = "\033[---;---H"; +static char curses_format [CURSES_FORMAT + 1] = "\033[-;3-m-\033[0m"; // Internal variable holding data for rendering single character, using ASCII escape sequences. +static char curses_cursor [CURSES_CURSOR + 1] = "\033[---;---H"; // Internal variable holding data for rendering cursor at some position. -static char * curses_screen = NULL; +static char * curses_screen = NULL; // We hold all terminal screen data here, you can think of it as an image, but better word is a framebuffer. -static int curses_action_count = 0; -static int * curses_activator = NULL; +static int curses_action_count = 0; // Count of defined actions, these are part of event handling system. +static int * curses_activator = NULL; // Array of action signals, when they are active, action is executed. -static void (* * curses_action) (void) = NULL; +static void (* * curses_action) (void) = NULL; // Array of function pointers, we use it to set event actions, so that user can interact with the terminal. -static struct termios curses_old_terminal; +static struct termios curses_old_terminal; // This is magic for making terminal enter and exit the raw mode. static struct termios curses_new_terminal; -static void curses_free (void) { +// We've explained these external variables in header file already. +int curses_character = 0; +int curses_signal = SIGNAL_NONE; +int curses_screen_width = 0; +int curses_screen_height = 0; +int curses_active = 1; + +static void curses_deinitialize (void) { curses_screen = deallocate (curses_screen); curses_activator = deallocate (curses_activator); curses_action = deallocate (curses_action); @@ -170,15 +218,6 @@ static char * curses_format_character (char character, int colour, int effect) { static void curses_idle (void) { return; } // If you have a lot of short functions that are intended to be in array of function pointers, you can align them like this. static void curses_exit (void) { curses_active = 0; } -int curses_realign_x = 0; -int curses_realign_y = 0; -int curses_tab_width = 8; -int curses_character = 0; -int curses_signal = SIGNAL_NONE; -int curses_screen_width = 0; -int curses_screen_height = 0; -int curses_active = 1; - /* External function definitions, those found in "chapter_2.h" header file. */ @@ -186,7 +225,7 @@ External function definitions, those found in "chapter_2.h" header file. void curses_configure (void) { struct winsize screen_dimension; - atexit (curses_free); + atexit (curses_deinitialize); fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, "ioctl: Failed to get terminal dimensions."); diff --git a/chapters/chapter_2.h b/chapters/chapter_2.h index 8edbc37..20b5207 100644 --- a/chapters/chapter_2.h +++ b/chapters/chapter_2.h @@ -86,21 +86,21 @@ you won't be able to see icons, you won't pass the Login text on your screen, yo #include "chapter_0.h" // We use functions declared in these two header files, so in order to make them visible, we included those files here. #include "chapter_1.h" -#define SIGNAL_ARROW_UP (0X415B1B) // Just hardcoding some arrow key ASCII escape sequences, we'll explain them in later chapters... +#define SIGNAL_ARROW_UP (0X415B1B) // Just hardcoding some arrow key ASCII escape sequences, we'll explain them in later chapters. #define SIGNAL_ARROW_DOWN (0X425B1B) #define SIGNAL_ARROW_RIGHT (0X435B1B) #define SIGNAL_ARROW_LEFT (0X445B1B) -#define SIGNAL_CONTROL (0X1000000) +#define SIGNAL_CONTROL (0X1000000) // We're also defining some signal masks, they'll be used later to mark which key combination is pressed. #define SIGNAL_SHIFT (0X2000000) #define SIGNAL_ALTERNATE (0X4000000) #define SIGNAL_SYSTEM (0X8000000) -#define CURSES_FORMAT ((int) sizeof ("\033[-;3-m-\033[0m") - 1) // We'll use these three macros through-out cursed functions, I'm following formatting rules, so it's here... +#define CURSES_FORMAT ((int) sizeof ("\033[-;3-m-\033[0m") - 1) // We'll use these three macros through-out cursed functions. #define CURSES_REVERT ((int) sizeof ("\033[H") - 1) #define CURSES_CURSOR ((int) sizeof ("\033[---;---H") - 1) -enum { +enum { // This enumeration will be used for signal processing. SIGNAL_NONE, SIGNAL_ANY, SIGNAL_A, SIGNAL_B, SIGNAL_C, SIGNAL_D, SIGNAL_E, SIGNAL_F, SIGNAL_G, SIGNAL_H, @@ -109,31 +109,40 @@ enum { SIGNAL_Y, SIGNAL_Z, SIGNAL_0, SIGNAL_1, SIGNAL_2, SIGNAL_3, SIGNAL_4, SIGNAL_5, SIGNAL_6, SIGNAL_7, SIGNAL_8, SIGNAL_9, SIGNAL_ESCAPE, SIGNAL_TABULATOR, SIGNAL_RETURN, SIGNAL_NEW_LINE, SIGNAL_SLASH, SIGNAL_BACKSLASH, SIGNAL_QUOTE, SIGNAL_BACKQUOTE, SIGNAL_SPACE, SIGNAL_BACKSPACE, SIGNAL_DOT, SIGNAL_COMMA, - SIGNAL_CITE, SIGNAL_CAPS_LOCK, SIGNAL_L_BRACKET, SIGNAL_R_BRACKET, SIGNAL_MINUS, SIGNAL_EQUAL, + SIGNAL_QUOTATION, SIGNAL_CAPS_LOCK, SIGNAL_L_BRACKET, SIGNAL_R_BRACKET, SIGNAL_MINUS, SIGNAL_EQUAL, SIGNAL_COUNT }; -extern int curses_character; -extern int curses_signal; -extern int curses_screen_width; -extern int curses_screen_height; -extern int curses_active; +/* +These below are external variables, they can be accessed and modified in any file where they're (re)declared. We're compiling C source files (.c) separately, so the compiler will +output object files (.o), and then we can link them together (most compilers are also linkers, this is nothing out of ordinary) into final executable. We can, for example, use +functions declared in "chapter_2.h" and defined in "chapter_2.c" in completely separate file "chapter_3.c", if we have included it with '#include "chapter_2.h". Keep in mind that +header files can be included recursively, that's why we use those header guards, '#ifndef SOMETHING', '#define SOMETHING' and '#endif' at the end. That way, they'll be included +only once, so compiler won't be confused and spit out errors. +*/ -extern void curses_configure (void); -extern void curses_synchronize (void); +extern int curses_character; // Literal character (integer) that user has pressed, some of them are more than single byte, like arrow keys defined above. +extern int curses_signal; // Converted value of 'curses_character' into values of macros and enumeration values named as 'SIGNAL_*'. +extern int curses_screen_width; // Width of terminal window in which we're rendering. +extern int curses_screen_height; // Height of terminal window in which we're rendering. +extern int curses_active; // As long as there's no termination signal, this variable will be different from zero. We use it to exit the program. -extern void curses_bind (int signal, void (* action) (void)); -extern void curses_unbind (int signal); +extern void curses_configure (void); // I like using the approach of "just do it" instead of "begin" and "end", due to that we'll see more internal functions and variables. +extern void curses_synchronize (void); // This function needs to be explained in more details, that'll be done in file "chapter_2.c". -extern void curses_render_cursor (int x, int y); +extern void curses_bind (int signal, void (* action) (void)); // Binding specific function pointer to some key, so each time key is pressed, function is executed. +extern void curses_unbind (int signal); // Unbinding any function that was bound to value of 'signal'. -extern void curses_render_character (char character, int colour, int effect, int x, int y); -extern void curses_render_background (char character, int colour, int effect); +extern void curses_render_cursor (int x, int y); // Render terminal cursor at position X and Y. + +extern void curses_render_character (char character, int colour, int effect, int x, int y); // Render single character at position X and Y. +extern void curses_render_background (char character, int colour, int effect); // Render entire buffer with the same character. // Remember that in chapter zero, I've separated 'string_*' and 'string_*_limit' functions. Now, there's always more ways to logically organize your code, for example, as below: extern int curses_render_string (char * string, int colour, int effect, int x, int y); extern int curses_render_number (int number, int colour, int effect, int x, int y); extern int curses_render_string_limit (char * string, int limit, int colour, int effect, int x, int y); extern int curses_render_number_limit (int number, int limit, int colour, int effect, int x, int y); +// I really hope that you already know what these functions do just by looking at the names of the arguments... #endif