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_SOURCE
# define CHAPTER_2_SOURCE
2023-11-09 09:41:53 -05:00
# include "chapter_2.h" // We're copying function and variable declarations from this file here. This shouldn't be copied twice, more on that later...
2023-11-07 10:14:07 -05:00
/*
Function ' hello_world_0 ' uses ( rougly said ) a system call instead of a function , but I don ' t even know what will different compilers do to that line of code . What ' s a system call ?
Well , when you ' re writing assembly , you have some general purpose registers , on mine and your machine , there ' s 16 of them , and you move some data in them , then use ' syscall '
instruction , which magically does something , like open or close a file descriptor , read or write to it , but more on that later ! A lot more . . . Important thing to know is that when
you use keyword ' sizeof ' on a string , it ' ll count null terminator , so it ' ll have value 14 , and type ' size_t ' . It ' s provided in < unistd . h > header file .
2023-11-09 09:41:53 -05:00
@ C
2023-11-10 13:30:15 -05:00
write ( // mov rax 1 ; Literal of Linux write system call, defined internally.
STDOUT_FILENO , // mov rdi 1 ; Literal of standard output file descriptor, defined as 'STDOUT_FILENO'.
2023-11-07 10:14:07 -05:00
" Hello world! \n " , // mov rsi X ; Address of our string.
sizeof ( " Hello world! \n " ) // mov rdx [Y] ; Literal of size of our string.
) ; // syscall ; Ask kernel to do some work.
2023-11-09 09:41:53 -05:00
@
2023-11-07 10:14:07 -05:00
We ' ll talk about assembly a lot in future chapters , but you should filter that out of your brain until you learn C well . . .
*/
void hello_world_0 ( void ) {
write ( STDOUT_FILENO , " Hello world! \n " , sizeof ( " Hello world! \n " ) ) ;
}
/*
In function ' hello_world_1 ' we ' re using function ' puts ' this time , which is considered unsafe because it ' ll fail when input string isn ' t null terminated . Keep in mind that it also
prints line feed ( new line ) on the end . Why I call it line feed ? Some terminals and text editors distinguish carriage return ( ' \r ' / 13 / CR ) and line feed ( ' \n ' / 10 / LF ) , and
some of them use both , like old teleprinters used to do , where the name ' tty ' on UNIX - based operating systems comes from .
*/
void hello_world_1 ( void ) {
puts ( " Hello world! " ) ;
}
/*
Now , in function ' hello_world_2 ' and function ' hello_world_3 ' , we ' re using ' printf ' function , which is in my opinion important for debugging . It has many variations , so we ' ll have
a separate chapter only about it . Know that it ' s a variadic function , which means that it can take more than one input argument , and later we ' ll learn to make our own variadic
functions as well . It ' s also a bit unsafe , if you ' re not careful how you use it , but nothing drastic can happen , most compilers catch those kinds of errors .
*/
void hello_world_2 ( void ) {
printf ( " Hello world! \n " ) ;
}
/*
It ' s also worth noting that we ' re using it ' s arguments in function ' hello_world_3 ' , function ' printf ' will print characters one by one from its ' format , until it encounters ' % ' ,
if the next character is ' % ' , it prints one ' % ' , if it ' s ' s ' , then it ' ll print a string , which we provide in the next argument . When compiler optimizations are turned on , it will
change all sorts of things here , since it ' s obvious that we ' re using simple string in this example .
*/
void hello_world_3 ( void ) {
printf ( " %s %s! \n " , " Hello " , " world " ) ;
}
/*
Lastly , we have a global variable , some people like to call it that , and consider it evil . It ' s not evil if you know the pros and cons of using them , which causes intellectual
overhead for certain sort of people . Pros , makes your code shorter and easier to refactor . Cons , it can potentially make your code less safe if you don ' t know what you ' re doing .
You should use them , external variables as I like to call them , when you ' re working on your programs or in a small group , but they can lead to bad things if you ' re working with a
lot of people , someone could change it in some bad place , and cause a bug that ' s difficult to track .
2023-11-09 09:41:53 -05:00
We could initialize it also in any part of our code later , in some function for example , as following :
@ C
// One by one:
hello_world [ 0 ] = hello_world_0 ;
hello_world [ 1 ] = hello_world_1 ;
hello_world [ 2 ] = hello_world_2 ;
hello_world [ 3 ] = hello_world_3 ;
// Or use it for some completely unrelated function if we wanted to, as long as function "kind" is same, they both take no input and no output:
static void unrelated_function ( void ) {
puts ( " I couldn't care less... " ) ;
exit ( EXIT_FAILURE ) ;
}
hello_world [ 2 ] = unrelated_function ;
@
Just know that an array ( s ) of function pointers is my favorite kind of variable for bot AI in simple games , index can be derived from bot state .
2023-11-07 10:14:07 -05:00
*/
void ( * hello_world [ 4 ] ) ( void ) = {
hello_world_0 ,
hello_world_1 ,
hello_world_2 ,
hello_world_3
} ;
2023-11-10 13:30:15 -05:00
/*
Now , we ' ll talk more about cursed functions ! I broke code formatting rules for this , but it ' s worth it , time to learn .
*/
static char curses_format [ CURSES_FORMAT + 1 ] = " \033 [-;3-m- \033 [0m " ;
static char curses_cursor [ CURSES_CURSOR + 1 ] = " \033 [---;---H " ;
static char * curses_screen = NULL ;
static int curses_action_count = 0 ;
static int * curses_activator = NULL ;
static void ( * * curses_action ) ( void ) = NULL ;
static struct termios curses_old_terminal ;
static struct termios curses_new_terminal ;
static void curses_free ( void ) {
curses_screen = deallocate ( curses_screen ) ;
curses_activator = deallocate ( curses_activator ) ;
curses_action = deallocate ( curses_action ) ;
terminal_clear ( ) ;
fatal_failure ( tcsetattr ( STDIN_FILENO , TCSAFLUSH , & curses_old_terminal ) = = - 1 , " tcsetattr: Failed to set default terminal attributes. " ) ;
}
static char * curses_screen_offset ( int x , int y ) {
// log_in (LOG_FAILURE, x <= -1, "curses_screen_offset: X position is below the lower bound.");
// log_in (LOG_FAILURE, y <= -1, "curses_screen_offset: Y position is below the lower bound.");
// log_in (LOG_FAILURE, x >= curses_screen_width, "curses_screen_offset: X position is above the upper bound.");
// log_in (LOG_FAILURE, y >= curses_screen_height, "curses_screen_offset: Y position is above the upper bound.");
limit ( & x , 0 , curses_screen_width - 1 ) ;
limit ( & y , 0 , curses_screen_height - 1 ) ;
return ( & curses_screen [ CURSES_REVERT + CURSES_FORMAT * ( y * curses_screen_width + x ) ] ) ;
}
static char * curses_format_character ( char character , int colour , int effect ) {
// log_in (LOG_WARNING, character_is_invisible (character), "curses_format_character: Can not format invisible characters.");
// log_in (LOG_FAILURE, colour >= COLOUR_COUNT, "curses_format_character: Colour is invalid enumeration value.");
// log_in (LOG_FAILURE, effect >= EFFECT_COUNT, "curses_format_character: Effect is invalid enumeration value.");
if ( character_is_invisible ( character ) ! = 0 ) {
character = ' ' ;
}
colour % = COLOUR_COUNT ;
effect % = EFFECT_COUNT ;
switch ( effect ) {
case EFFECT_NORMAL : effect = 0 ; break ;
case EFFECT_BOLD : effect = 1 ; break ;
case EFFECT_ITALIC : effect = 3 ; break ;
case EFFECT_UNDERLINE : effect = 4 ; break ;
case EFFECT_BLINK : effect = 5 ; break ;
case EFFECT_REVERSE : effect = 7 ; break ;
default : effect = 0 ; break ;
}
curses_format [ 2 ] = ( char ) effect + ' 0 ' ;
curses_format [ 5 ] = ( char ) colour + ' 0 ' ;
curses_format [ 7 ] = character ;
// log_out ("curses.log");
return ( curses_format ) ;
}
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 .
*/
void curses_configure ( void ) {
struct winsize screen_dimension ;
atexit ( curses_free ) ;
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_screen = allocate ( CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR + 1 ) ;
curses_bind ( SIGNAL_ESCAPE , curses_exit ) ;
terminal_clear ( ) ;
string_copy ( & curses_screen [ 0 ] , " \033 [H " ) ;
}
void curses_synchronize ( void ) {
int signal ;
curses_signal = curses_character = signal = 0 ;
out ( curses_screen , CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR ) ;
in ( & signal , 4 ) ;
curses_character = signal ;
if ( signal = = ' \033 ' ) {
curses_signal | = SIGNAL_ESCAPE ;
} else if ( character_is_digit ( signal ) ! = 0 ) {
curses_signal | = SIGNAL_0 + ( int ) ( signal - ' 0 ' ) ;
} else if ( character_is_lowercase ( signal ) ! = 0 ) {
curses_signal | = SIGNAL_A + ( int ) ( signal - ' a ' ) ;
} else if ( character_is_uppercase ( signal ) ! = 0 ) {
curses_signal | = SIGNAL_A + ( int ) ( signal - ' A ' ) ;
curses_signal | = SIGNAL_SHIFT ;
} else {
curses_signal = SIGNAL_NONE ;
}
for ( signal = 0 ; signal ! = curses_action_count ; + + signal ) {
if ( curses_signal = = curses_activator [ signal ] ) {
curses_action [ signal ] ( ) ;
}
}
}
void curses_bind ( int signal , void ( * action ) ( void ) ) {
+ + curses_action_count ;
curses_activator = reallocate ( curses_activator , curses_action_count * ( int ) sizeof ( * curses_activator ) ) ;
curses_action = reallocate ( curses_action , curses_action_count * ( int ) sizeof ( * curses_action ) ) ;
curses_activator [ curses_action_count - 1 ] = signal ;
curses_action [ curses_action_count - 1 ] = action ;
}
void curses_unbind ( int signal ) {
( void ) signal ;
curses_activator [ curses_action_count - 1 ] = SIGNAL_NONE ;
curses_action [ curses_action_count - 1 ] = curses_idle ;
- - curses_action_count ;
}
void curses_render_cursor ( int x , int y ) {
x % = 1000 ;
y % = 1000 ;
string_copy_limit ( curses_cursor + 2 , string_realign ( number_to_string ( y + 1 ) , 3 , ' 0 ' ) , 3 ) ;
string_copy_limit ( curses_cursor + 6 , string_realign ( number_to_string ( x + 1 ) , 3 , ' 0 ' ) , 3 ) ;
string_copy_limit ( & curses_screen [ CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height ] , curses_cursor , CURSES_CURSOR ) ;
}
void curses_render_character ( char character , int colour , int effect , int x , int y ) {
if ( ( x > = curses_screen_width ) | | ( y > = curses_screen_height ) ) {
return ;
}
string_copy_limit ( curses_screen_offset ( x , y ) , curses_format_character ( character , colour , effect ) , CURSES_FORMAT ) ;
}
void curses_render_background ( char character , int colour , int effect ) {
int x , y ;
for ( y = 0 ; y ! = curses_screen_height ; + + y ) {
for ( x = 0 ; x ! = curses_screen_width ; + + x ) {
curses_render_character ( character , colour , effect , x , y ) ;
}
}
}
/*
We ' ve mentioned before , in chapter zero , that you can implement ' string_ * ' functions by ' string_ * _limit ' , and here ' s an example of that .
*/
int curses_render_string ( char * string , int colour , int effect , int x , int y ) {
return ( curses_render_string_limit ( string , string_length ( string ) , colour , effect , x , y ) ) ;
}
int curses_render_number ( int number , int colour , int effect , int x , int y ) {
return ( curses_render_number_limit ( number , 32 , colour , effect , x , y ) ) ;
}
int curses_render_string_limit ( char * string , int limit , int colour , int effect , int x , int y ) {
int offset ;
for ( offset = 0 ; offset ! = limit ; + + offset ) {
if ( string [ offset ] = = ' \n ' ) {
x = 0 ;
y + = 1 ;
} else if ( string [ offset ] = = ' \t ' ) {
x + = 8 ;
} else {
curses_render_character ( string [ offset ] , colour , effect , x , y ) ;
x + = 1 ;
}
}
return ( limit ) ;
}
int curses_render_number_limit ( int number , int limit , int colour , int effect , int x , int y ) {
( void ) number ;
( void ) limit ;
( void ) colour ;
( void ) effect ;
( void ) x ;
( void ) y ;
return ( limit ) ;
}
2023-11-07 10:14:07 -05:00
# endif