forked from xolatile/xhartae
Added cursed functions...
This commit is contained in:
parent
e4228e8d8b
commit
cb619d884c
@ -220,6 +220,10 @@ char * string_concatenate (char * string_0, char * string_1) {
|
|||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char * string_reverse (char * string) { // Example of implementing "unlimited" version by calling "limited" version.
|
||||||
|
return (string_reverse_limit (string, string_length (string)));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
As for "limited" versions of previous 3 functions, they do the same thing, but are capped to some variable 'limit'. These functions have their own use-case, for example, if
|
As for "limited" versions of previous 3 functions, they do the same thing, but are capped to some variable 'limit'. These functions have their own use-case, for example, if
|
||||||
strings aren't null terminated, if you're not sure that they are null terminated, if we're dealing with binary (not textual) data (casted to char *), and many more cases.
|
strings aren't null terminated, if you're not sure that they are null terminated, if we're dealing with binary (not textual) data (casted to char *), and many more cases.
|
||||||
@ -228,11 +232,11 @@ strings aren't null terminated, if you're not sure that they are null terminated
|
|||||||
int string_compare_limit (char * string_0, char * string_1, int limit) {
|
int string_compare_limit (char * string_0, char * string_1, int limit) {
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
fatal_failure (string_0 == NULL, "string_compare_limit: Destination string is null pointer.");
|
fatal_failure (string_0 == NULL, "string_compare_limit: Destination string is null pointer."); // This is the new trend, check for unimportant things.
|
||||||
fatal_failure (string_1 == NULL, "string_compare_limit: Source string is null pointer.");
|
fatal_failure (string_1 == NULL, "string_compare_limit: Source string is null pointer."); // At least this isn't too verbose. I hope...
|
||||||
|
|
||||||
for (offset = 0; offset < limit; ++offset) {
|
for (offset = 0; offset < limit; ++offset) { // Now, we'll iterate until 'limit' is reached, but it can overrun.
|
||||||
if (string_0 [offset] != string_1 [offset]) {
|
if (string_0 [offset] != string_1 [offset]) { // All said here applies to next two functions as well...
|
||||||
return (FALSE);
|
return (FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,4 +281,141 @@ char * string_concatenate_limit (char * string_0, char * string_1, int limit) {
|
|||||||
return (string_0);
|
return (string_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char * string_reverse_limit (char * string, int limit) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fatal_failure (string == NULL, "string_reverse: String is null pointer.");
|
||||||
|
|
||||||
|
for (i = 0; i < limit / 2; ++i) {
|
||||||
|
char temporary = string [i];
|
||||||
|
string [i] = string [limit - 1 - i];
|
||||||
|
string [limit - 1 - i] = temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * string_realign (char * string, int amount, char character) {
|
||||||
|
int offset, length;
|
||||||
|
|
||||||
|
length = string_length (string);
|
||||||
|
|
||||||
|
for (offset = 0; offset != length; ++offset) {
|
||||||
|
string [amount - offset - 1] = string [length - offset - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (offset = 0; offset != amount - length; ++offset) {
|
||||||
|
string [offset] = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
string [amount] = '\0';
|
||||||
|
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminal_clear (void) {
|
||||||
|
echo ("\033[2J\033[H");
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminal_colour (int colour, int effect) {
|
||||||
|
char format [8] = "\033[ ;3 m";
|
||||||
|
|
||||||
|
format [2] = (char) (effect % EFFECT_COUNT) + '0';
|
||||||
|
format [5] = (char) (colour % COLOUR_COUNT) + '0';
|
||||||
|
|
||||||
|
echo (format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminal_cancel (void) {
|
||||||
|
echo ("\033[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminal_show_cursor (int show) {
|
||||||
|
if (show != 0) {
|
||||||
|
echo ("\033[?25h");
|
||||||
|
} else {
|
||||||
|
echo ("\033[?25l");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char * number_to_string (int number) {
|
||||||
|
int i, sign;
|
||||||
|
|
||||||
|
static char string [32];
|
||||||
|
|
||||||
|
for (i = 0; i != 32; ++i) {
|
||||||
|
string [i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number == 0) {
|
||||||
|
string [0] = '0';
|
||||||
|
string [1] = '\0';
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number < 0) {
|
||||||
|
number *= -1;
|
||||||
|
sign = 1;
|
||||||
|
} else {
|
||||||
|
sign = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = (string [0] == '-'); number != 0; ++i) {
|
||||||
|
string [i] = (char) (number % 10) + '0';
|
||||||
|
number /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sign != 0) {
|
||||||
|
string [i] = '-';
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
string [i] = '\0';
|
||||||
|
|
||||||
|
string_reverse (string);
|
||||||
|
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * format_to_string (int number, int sign, int base, int amount, char character) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
static char string [32];
|
||||||
|
|
||||||
|
for (i = 0; i != 32; ++i) {
|
||||||
|
string [i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number == 0) {
|
||||||
|
string [0] = '0';
|
||||||
|
string [1] = '\0';
|
||||||
|
|
||||||
|
string_realign (string, amount, character);
|
||||||
|
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number < 0) {
|
||||||
|
number *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = (string [0] == '-'); number != 0; ++i) {
|
||||||
|
string [i] = "0123456789ABCDEF" [number % base];
|
||||||
|
number /= base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sign != 0) {
|
||||||
|
string [i] = '-';
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
string [i] = '\0';
|
||||||
|
|
||||||
|
string_reverse (string);
|
||||||
|
|
||||||
|
string_realign (string, amount, character);
|
||||||
|
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -162,11 +162,6 @@ eye on how are they aligned and named. I'll reimplement some standard functions,
|
|||||||
#include <fcntl.h> // This one for open and O_ flags.
|
#include <fcntl.h> // This one for open and O_ flags.
|
||||||
#include <unistd.h> // And this one for read, write, close and lseek.
|
#include <unistd.h> // And this one for read, write, close and lseek.
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
||||||
enum { // This is completely unnecesary, but I don't care, it's a good showcase how boolean type can work, true is 1 and false is 0.
|
enum { // This is completely unnecesary, but I don't care, it's a good showcase how boolean type can work, true is 1 and false is 0.
|
||||||
FALSE,
|
FALSE,
|
||||||
TRUE
|
TRUE
|
||||||
@ -212,9 +207,21 @@ extern int string_length (char * string); // We deal with strings a lot in this
|
|||||||
extern int string_compare (char * string_0, char * string_1); // See how nicely they align, right?
|
extern int string_compare (char * string_0, char * string_1); // See how nicely they align, right?
|
||||||
extern char * string_copy (char * string_0, char * string_1);
|
extern char * string_copy (char * string_0, char * string_1);
|
||||||
extern char * string_concatenate (char * string_0, char * string_1);
|
extern char * string_concatenate (char * string_0, char * string_1);
|
||||||
|
extern char * string_reverse (char * string); // Notice last function, we didn't align ');'...
|
||||||
|
|
||||||
extern int string_compare_limit (char * string_0, char * string_1, int limit); // These ones too, it's beautiful (in my opinion), tho some consider it useless.
|
extern int string_compare_limit (char * string_0, char * string_1, int limit); // These ones too, it's beautiful (in my opinion), tho some consider it useless.
|
||||||
extern char * string_copy_limit (char * string_0, char * string_1, int limit);
|
extern char * string_copy_limit (char * string_0, char * string_1, int limit);
|
||||||
extern char * string_concatenate_limit (char * string_0, char * string_1, int limit);
|
extern char * string_concatenate_limit (char * string_0, char * string_1, int limit);
|
||||||
|
extern char * string_reverse_limit (char * string, int limit); // And we align the last argument in this case, use whatever you prefer.
|
||||||
|
|
||||||
|
extern char * string_realign (char * string, int amount, char character);
|
||||||
|
|
||||||
|
extern void terminal_clear (void);
|
||||||
|
extern void terminal_colour (int colour, int effect);
|
||||||
|
extern void terminal_cancel (void);
|
||||||
|
extern void terminal_show_cursor (int show);
|
||||||
|
|
||||||
|
extern char * number_to_string (int number);
|
||||||
|
extern char * format_to_string (int number, int sign, int base, int amount, char character);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -190,23 +190,22 @@ header files, with prefix 'h', it's cancer. Why the extension wasn't just '.c=c+
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int file_type (char * name) {
|
int file_type (char * name) {
|
||||||
char * file_type_data [FILE_TYPE_COUNT] = {
|
// Keep in mind that I'm intentionally being inconsistent, so you can see several ways to properly align your code, readability is the key to safety!
|
||||||
".txt", ".s", ".fasm", ".gasm", ".nasm", ".yasm", ".c", ".h", ".adb", ".ads", ".cpp", ".hpp"
|
// Spacing between separate strings in array below is 10 characters, including comma and double quotes, and I "joined" curly braces too, it fits in 180 characters.
|
||||||
};
|
// You could break it up on curly braces, or put each string in it's own line if you wanted.
|
||||||
|
char * file_type_data [FILE_TYPE_COUNT] = { ".txt", ".s", ".fasm", ".gasm", ".nasm", ".yasm", ".c", ".h", ".adb", ".ads", ".cpp", ".hpp" };
|
||||||
|
|
||||||
int type;
|
int type;
|
||||||
|
|
||||||
while (* name != '.') {
|
for (; * name != '.'; ++name); // We offset the 'name' until we reach fullstop character.
|
||||||
++name;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (type = 0; type != FILE_TYPE_COUNT; ++type) {
|
for (type = 0; type != FILE_TYPE_COUNT; ++type) { // Then we check if it's one from this array by comparing them sequentially.
|
||||||
if (string_compare (name, file_type_data [type]) != 0) {
|
if (string_compare (name, file_type_data [type]) != 0) { // If it is, we return the value of enumeration of file types.
|
||||||
return (type);
|
return (type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (-1);
|
return (-1); // If it's not in array, we return -1, so we don't access the wrong value in some other array.
|
||||||
}
|
}
|
||||||
|
|
||||||
void * file_record (char * name) {
|
void * file_record (char * name) {
|
||||||
|
@ -193,6 +193,30 @@ ASCII table:
|
|||||||
|_______________________________________________________________________________|_______________________________________________________________________________|
|
|_______________________________________________________________________________|_______________________________________________________________________________|
|
||||||
|
|
||||||
You can see that values of 'A' ... 'Z', 'a' ... 'z' and '0' ... '9' are sequential, but symbols and "system" characters are mixed up.
|
You can see that values of 'A' ... 'Z', 'a' ... 'z' and '0' ... '9' are sequential, but symbols and "system" characters are mixed up.
|
||||||
|
|
||||||
|
In C language, we have C source files with the extension '.c', and C header files with the extension '.h'. Both of those are just plain text files, and please use 7-bit ASCII
|
||||||
|
encoding, since it's common sense, UTF is cancer, and 8-bit ASCII is for enlightened people like Terrence Andrew Davis. C language is completely separate (on some C compilers)
|
||||||
|
from its' preprocessor, whose directives start with '#' character, continue on '\' character and break on '\n' (read: LINE FEED) character.
|
||||||
|
|
||||||
|
@C
|
||||||
|
#include <path/to/file/file_name.h> // Copy the entire file from '/usr/include/' directory into this file, on the place where it was specified.
|
||||||
|
#include "path/to/file/file_name.h" // Copy the entire file from current directory into this file, again on the place where it was specified.
|
||||||
|
|
||||||
|
#define SOMETHING // This will add additional information to the preprocessor about this file, it's mostly used for flags and header-guards.
|
||||||
|
#undef SOMETHING // This will remove that additional information you've provided...
|
||||||
|
#if SOMETHING // If SOMETHING (condition obviously) is true, then code until '#elif', '#else' or '#endif' will be included.
|
||||||
|
#ifdef SOMETHING // If SOMETHING was previously '#define'-d, then code until '#elif', '#else' or '#endif' will be included.
|
||||||
|
#ifndef SOMETHING // If SOMETHING wasn't (NOT!) previously '#define'-d, then code until '#elif', '#else' or '#endif' will be included.
|
||||||
|
#elif // Essentially "else if" for preprocessor, it's very ugly, and nesting them looks bad and is a bad practice.
|
||||||
|
#else // Essentially "else" for preprocessor, I don't think I ever used it in my entire life, but I saw other people use it.
|
||||||
|
#endif // End if... Self-explanatory, and a sad thing that we need to ruin the beautiful C code with it.
|
||||||
|
@
|
||||||
|
|
||||||
|
Okay, that's all you really need to know about C preprocessor, since we won't use it much. You can write a completely pure C project, using only C language, but you'll end up with
|
||||||
|
copying and pasting a lot of code, especially external function and variable declarations. Because of that we need '#include' directive, and because of it, we need header guards,
|
||||||
|
so it's all C-hating in the end. However, we need to cover some simple macros, so you can deal with other peoples' code bases. Remember, the less "building blocks" you have, if
|
||||||
|
you learn them well, you can make anything, and you should be proud of "reinventing the wheel". If wheels weren't reinvented over and over again, then some expensive BMW would've
|
||||||
|
wooden wheels attached to it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern int character_is_uppercase (char character); // Notice how we align those functions, I believe this improves the readability of any program, in any programming language.
|
extern int character_is_uppercase (char character); // Notice how we align those functions, I believe this improves the readability of any program, in any programming language.
|
||||||
|
@ -18,8 +18,8 @@ instruction, which magically does something, like open or close a file descripto
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
@C
|
@C
|
||||||
write ( // mov rax 1 ; Literal of Linux write system call.
|
write ( // mov rax 1 ; Literal of Linux write system call, defined internally.
|
||||||
STDOUT_FILENO, // mov rdi 1 ; Literal of standard output file descriptor.
|
STDOUT_FILENO, // mov rdi 1 ; Literal of standard output file descriptor, defined as 'STDOUT_FILENO'.
|
||||||
"Hello world!\n", // mov rsi X ; Address of our string.
|
"Hello world!\n", // mov rsi X ; Address of our string.
|
||||||
sizeof ("Hello world!\n") // mov rdx [Y] ; Literal of size of our string.
|
sizeof ("Hello world!\n") // mov rdx [Y] ; Literal of size of our string.
|
||||||
); // syscall ; Ask kernel to do some work.
|
); // syscall ; Ask kernel to do some work.
|
||||||
@ -97,4 +97,242 @@ void (* hello_world [4]) (void) = {
|
|||||||
hello_world_3
|
hello_world_3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -70,4 +70,70 @@ 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'.
|
extern void (* hello_world [4]) (void); // External (global) variable with name 'hello_world' of type 'array of 4 function pointers with no input/output'.
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
#endif
|
||||||
|
24
xhartae.c
24
xhartae.c
@ -90,30 +90,6 @@ One sane C program should have the following structure (please keep in mind that
|
|||||||
6) Internal function then variable definition.
|
6) Internal function then variable definition.
|
||||||
7) External function then variable definition.
|
7) External function then variable definition.
|
||||||
8) Main function.
|
8) Main function.
|
||||||
|
|
||||||
In C language, we have C source files with the extension '.c', and C header files with the extension '.h'. Both of those are just plain text files, and please use 7-bit ASCII
|
|
||||||
encoding, since it's common sense, UTF is cancer, and 8-bit ASCII is for enlightened people like Terrence Andrew Davis. C language is completely separate (on some C compilers)
|
|
||||||
from its' preprocessor, whose directives start with '#' character, continue on '\' character and break on '\n' (read: LINE FEED) character.
|
|
||||||
|
|
||||||
@C
|
|
||||||
#include <path/to/file/file_name.h> // Copy the entire file from '/usr/include/' directory into this file, on the place where it was specified.
|
|
||||||
#include "path/to/file/file_name.h" // Copy the entire file from current directory into this file, again on the place where it was specified.
|
|
||||||
|
|
||||||
#define SOMETHING // This will add additional information to the preprocessor about this file, it's mostly used for flags and header-guards.
|
|
||||||
#undef SOMETHING // This will remove that additional information you've provided...
|
|
||||||
#if SOMETHING // If SOMETHING (condition obviously) is true, then code until '#elif', '#else' or '#endif' will be included.
|
|
||||||
#ifdef SOMETHING // If SOMETHING was previously '#define'-d, then code until '#elif', '#else' or '#endif' will be included.
|
|
||||||
#ifndef SOMETHING // If SOMETHING wasn't (NOT!) previously '#define'-d, then code until '#elif', '#else' or '#endif' will be included.
|
|
||||||
#elif // Essentially "else if" for preprocessor, it's very ugly, and nesting them looks bad and is a bad practice.
|
|
||||||
#else // Essentially "else" for preprocessor, I don't think I ever used it in my entire life, but I saw other people use it.
|
|
||||||
#endif // End if... Self-explanatory, and a sad thing that we need to ruin the beautiful C code with it.
|
|
||||||
@
|
|
||||||
|
|
||||||
Okay, that's all you really need to know about C preprocessor, since we won't use it much. You can write a completely pure C project, using only C language, but you'll end up with
|
|
||||||
copying and pasting a lot of code, especially external function and variable declarations. Because of that we need '#include' directive, and because of it, we need header guards,
|
|
||||||
so it's all C-hating in the end. However, we need to cover some simple macros, so you can deal with other peoples' code bases. Remember, the less "building blocks" you have, if
|
|
||||||
you learn them well, you can make anything, and you should be proud of "reinventing the wheel". If wheels weren't reinvented over and over again, then some expensive BMW would've
|
|
||||||
wooden wheels attached to it.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main (int argc, char * * argv) {
|
int main (int argc, char * * argv) {
|
||||||
|
Loading…
Reference in New Issue
Block a user