xhartae/chapters/chapter_2.h

140 lines
10 KiB
C
Raw Normal View History

/*
Copyright (c) 2023 : Ognjen 'xolatile' Milan Robovic
Xhartae is free software! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation.
And when you do redistribute it or modify it, it will use either version 3 of the License, or (at yours truly opinion) any later version.
It is distributed in the hope that it will be useful or harmful, it really depends... But no warranty what so ever, seriously. See GNU/GPLv3.
*/
#ifndef CHAPTER_2_HEADER
#define CHAPTER_2_HEADER
/*
This is probably the first program new programmers write in language they're learning. It simply prints text to standard output. As C is very old programming language, you have a
lot of ways to do it, so we'll simply put all of them into array of function pointers, and call them sequentially in loop in our main function.
*/
#include <stdio.h> // We need this header file for functions 'puts' and 'printf'.
#include <unistd.h> // And in this header file we have write system call.
/*
Lets talk about function declaration. In C, sometimes you need to declare functions that you'll use before they're called. I prefer to always declare functions except when I'm
quickly prototyping something out. Function declarations are property of old programming languages, you don't have to use them if you're calling every function after it's defined
(but not declared, there's a difference), but if that function is only used in one file, you should use 'static' keyword like in the example below. Keyword 'extern' tells the
compiler that your function will be used in other files, or if you're compiling your files separately, it won't add the function address (in some cases). In any case, function
declarations should be in C header file, and function definitions should be in C source file.
@C
2023-11-09 13:22:59 -05:00
// Function declaration: // # // Output: // Input:
extern void function_0 (void); // 0 // undefined (none) // undefined (none)
extern float function_1 (double a); // 1 // ieee754_32b // ieee754_64b
extern int function_2 (int a, int b); // 2 // integer_32b // integer_32b, integer_32b
static unsigned int function_3 (char * a, int b, char c); // 3 // natural_32b // integer_8b_pointer, integer_32b, integer_8b
static char * function_4 (char * a); // 4 // integer_8b_pointer // integer_8b_pointer
static void * function_5 (struct s * a, void * b); // 5 // undefined_pointer // [structure]_pointer, undefined_pointer
extern unsigned long int * PLEASE_NO (const signed short int, void *, const char const * *, long double []);
@
So, lets go through those 6 normal functions, and then lets see what's wrong with 'PLEASE_NO' function.
0) This is the simplest form of function, which can be used to make the code more clear (sometimes) by breaking down long functions into smaller ones, and calling them in certain
2023-11-09 13:22:59 -05:00
order. However, don't abuse it for code refactoring, because most of the time, procedural code is the easiest to read, write and debug.
1) In C (and in non-embedded environment, also known as your normal post-2012 laptop or computer), keyword 'float' is (almost) real number encoded in IEEE754, 32 bits or 4 bytes
long, while keyword 'double' is same as float, but enlarged to 64 bits or 8 bytes. They are pain to use in some cases because of limited precision.
2023-11-09 13:22:59 -05:00
2) Functions can have multiple arguments (input variables), but only one return value (output "variable"). In C, you can name the arguments, but not the return value, also keep in
mind that you should choose descriptive names (in most cases) for function arguments, not name them a, b, c, d like in these examples.
3) If you really want the compiler to verify that some return value is of type 'unsigned int' aka 'uint32_t', you can do so, I prefer 'int' since all number literals in C are
integers by default ('int'). We'll use terms like 'int / integer/ int32_t' interchangably, so keep that in mind in future chapters.
4) Also, type 'char *' can be pointer to (address of) of some 'char' typed variable or array of type 'char'. We'll talk more about arrays in future chapters, but know that we
could use 'char a []' in functions 3 and 4 as well, it's same. You could use them both in order to know which is array and which is pointer.
5) Lastly, this function returns pointer to any type, hence compiler won't do type-checking, it's necesary to this sometimes. It also accepts pointer to (previously defined)
structure 's', and if you used 'typedef' with it, you could just say 's * a', we'll talk more about those.
Now, lets talk very briefly about what's wrong with 'PLEASE_NO':
2023-11-09 13:22:59 -05:00
- Don't use long types 'unsigned long int', if you really type with those characteristics, use 'size_t : stdlib', 'uint64_t : stdint' or 'unsigned long' in worst case.
- Don't use 'const' keyword at all, that's my honest advice, it won't catch that many potential bugs in C, it'll just cause useless compiler warnings. Live unsafe, think more.
- Don't use 'signed', since it's default for any integer type, as well as you shouldn't use keywords 'register', 'volatile', 'auto', but more on that later...
2023-11-09 13:22:59 -05:00
- You can use '[]' in function declarations, but it doesn't mean much since array is passed as pointer to first element in C (array decay), so '*' is enough.
- Keep in mind that newer (non-ANSI) C standards allow some of the things that I'm not using here, but I don't personally like newer C standards, I'll mention that a lot.
- Last one is tricky, you should name function agruments in function declarations, but some linters will warn you not to do it, since some compiler don't check them.
2023-11-10 08:31:22 -05:00
Very soon, you'll be able to write your own small C programs, so prepare for it.
*/
extern void hello_world_0 (void); // All 4 functions take no agruments, and return nothing. They just execute the code that's defined in them.
extern void hello_world_1 (void);
extern void hello_world_2 (void);
extern void hello_world_3 (void);
extern void (* hello_world [4]) (void); // External (global) variable with name 'hello_world' of type 'array of 4 function pointers with no input/output'.
2023-11-10 13:30:15 -05:00
/*
Alright, we'll never use those 4 functions, but now you know how to declare any function, it's time for more serious examples. Below you'll see some scary looking cursed functions
that interact with the terminal in which we're executing this program. We'll change the terminal from default (sane) mode to raw (insane) mode. That means the characters we press
on our keyboard won't be printed in terminal, like usual. That'll allow us to make cursed C program that'll scare away all GUI fanatics. In the ancient times, when people used
teletypewriters, terminal was obscure magic. Nowdays, parents scare their children into obedience by showing them terminals. Like, if you don't behave, I'll edit X11 scripts and
you won't be able to see icons, you won't pass the Login text on your screen, you'll never open your web browser again (lynx rules).
*/
#include <termios.h> // Terminal input, output and configuration.
#include <sys/ioctl.h> // Rest is some Linux related cancer...
#include <sys/types.h>
#include <sys/stat.h>
#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_DOWN (0X425B1B)
#define SIGNAL_ARROW_RIGHT (0X435B1B)
#define SIGNAL_ARROW_LEFT (0X445B1B)
#define SIGNAL_CONTROL (0X1000000)
#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_REVERT ((int) sizeof ("\033[H") - 1)
#define CURSES_CURSOR ((int) sizeof ("\033[---;---H") - 1)
enum {
SIGNAL_NONE,
SIGNAL_ANY,
SIGNAL_A, SIGNAL_B, SIGNAL_C, SIGNAL_D, SIGNAL_E, SIGNAL_F, SIGNAL_G, SIGNAL_H,
SIGNAL_I, SIGNAL_J, SIGNAL_K, SIGNAL_L, SIGNAL_M, SIGNAL_N, SIGNAL_O, SIGNAL_P,
SIGNAL_Q, SIGNAL_R, SIGNAL_S, SIGNAL_T, SIGNAL_U, SIGNAL_V, SIGNAL_W, SIGNAL_X,
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_COUNT
};
extern int curses_character;
extern int curses_signal;
extern int curses_screen_width;
extern int curses_screen_height;
extern int curses_active;
extern void curses_configure (void);
extern void curses_synchronize (void);
extern void curses_bind (int signal, void (* action) (void));
extern void curses_unbind (int signal);
extern void curses_render_cursor (int x, int 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);
// 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);
#endif