Browse Source

Explained more about curses...

master
Ognjen Milan Robovic 7 months ago
parent
commit
59fd9b711b
2 changed files with 84 additions and 36 deletions
  1. +58
    -19
      chapters/chapter_2.c
  2. +26
    -17
      chapters/chapter_2.h

+ 58
- 19
chapters/chapter_2.c View File

@@ -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 <termios.h> 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.");



+ 26
- 17
chapters/chapter_2.h View File

@@ -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 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_configure (void);
extern void curses_synchronize (void);
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_bind (int signal, void (* action) (void));
extern void curses_unbind (int signal);
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_cursor (int x, int y);
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);
extern void curses_render_background (char character, int colour, int effect);
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

Loading…
Cancel
Save