2023-11-07 10:14:07 -05:00
/*
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 .
*/
2023-11-09 09:41:53 -05:00
# 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.
2023-11-07 10:14:07 -05:00
/*
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 .
2023-11-09 09:41:53 -05:00
@ 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
2023-11-07 10:14:07 -05:00
extern unsigned long int * PLEASE_NO ( const signed short int , void * , const char const * * , long double [ ] ) ;
2023-11-09 09:41:53 -05:00
@
2023-11-07 10:14:07 -05:00
So , lets go through those 6 normal functions , and then lets see what ' s wrong with ' PLEASE_NO ' function .
2023-11-09 09:41:53 -05:00
2023-11-07 10:14:07 -05:00
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 .
2023-11-07 10:14:07 -05:00
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 .
2023-11-09 09:41:53 -05:00
2023-11-07 10:14:07 -05:00
Now , lets talk very briefly about what ' s wrong with ' PLEASE_NO ' :
2023-11-09 09:41:53 -05:00
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 .
2023-11-07 10:14:07 -05:00
- 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 .
2023-11-07 10:14:07 -05:00
*/
2023-11-09 09:41:53 -05:00
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.
2023-11-07 10:14:07 -05:00
extern void hello_world_1 ( void ) ;
extern void hello_world_2 ( void ) ;
extern void hello_world_3 ( void ) ;
2023-11-09 09:41:53 -05:00
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-07 10:14:07 -05:00
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"
2023-11-10 16:41:24 -05:00
# define SIGNAL_ARROW_UP (0X415B1B) // Just hardcoding some arrow key ASCII escape sequences, we'll explain them in later chapters.
2023-11-10 13:30:15 -05:00
# define SIGNAL_ARROW_DOWN (0X425B1B)
# define SIGNAL_ARROW_RIGHT (0X435B1B)
# define SIGNAL_ARROW_LEFT (0X445B1B)
2023-11-10 16:41:24 -05:00
# define SIGNAL_CONTROL (0X1000000) // We're also defining some signal masks, they'll be used later to mark which key combination is pressed.
2023-11-10 13:30:15 -05:00
# define SIGNAL_SHIFT (0X2000000)
# define SIGNAL_ALTERNATE (0X4000000)
# define SIGNAL_SYSTEM (0X8000000)
2023-11-10 16:41:24 -05:00
# define CURSES_FORMAT ((int) sizeof ("\033[-;3-m-\033[0m") - 1) // We'll use these three macros through-out cursed functions.
2023-11-10 13:30:15 -05:00
# define CURSES_REVERT ((int) sizeof ("\033[H") - 1)
# define CURSES_CURSOR ((int) sizeof ("\033[---;---H") - 1)
2023-11-10 16:41:24 -05:00
enum { // This enumeration will be used for signal processing.
2023-11-10 13:30:15 -05:00
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 ,
2023-11-10 16:41:24 -05:00
SIGNAL_QUOTATION , SIGNAL_CAPS_LOCK , SIGNAL_L_BRACKET , SIGNAL_R_BRACKET , SIGNAL_MINUS , SIGNAL_EQUAL ,
2023-11-10 13:30:15 -05:00
SIGNAL_COUNT
} ;
2023-11-10 16:41:24 -05:00
/*
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.
2023-11-10 13:30:15 -05:00
2023-11-10 16:41:24 -05:00
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".
2023-11-10 13:30:15 -05:00
2023-11-10 16:41:24 -05:00
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'.
2023-11-10 13:30:15 -05:00
2023-11-10 16:41:24 -05:00
extern void curses_render_cursor ( int x , int y ) ; // Render terminal cursor at position X and Y.
2023-11-10 13:30:15 -05:00
2023-11-10 16:41:24 -05:00
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.
2023-11-10 13:30:15 -05:00
// 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 ) ;
2023-11-10 16:41:24 -05:00
// I really hope that you already know what these functions do just by looking at the names of the arguments...
2023-11-10 13:30:15 -05:00
2023-11-07 10:14:07 -05:00
# endif