From 8517bae33a83e92458c2f311983ed074ca673a1b Mon Sep 17 00:00:00 2001 From: xolatile Date: Wed, 29 Nov 2023 05:22:44 -0500 Subject: [PATCH] Added anti-word-wrap in curses and changed program 2... --- chapter/chapter_2.c | 53 +++++++++++++++++++++++++++++++++-------------------- program/program_2.c | 25 ++++++++++++++----------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/chapter/chapter_2.c b/chapter/chapter_2.c index 43aa9b4..07628a0 100644 --- a/chapter/chapter_2.c +++ b/chapter/chapter_2.c @@ -195,7 +195,7 @@ static void curses_exit (void) { curses_active = 0; } // And this is our main fu static void curses_initialize (void) { // This function will be called when 'curses_configure' is called, automatically. struct winsize screen_dimension; // We need this ugly structure for our 'ioctl' function to get the dimensions. - int screen_memory; // And you can use local variables to shorten some lines of code if you want. + int screen_memory, lines; // And you can use local variables to shorten some lines of code if you want. fatal_failure (ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension) == -1, // If function 'ioctl' failed, we immediately aborting the entire program. "ioctl: Failed to get terminal dimensions."); // I split those error messages, you can find your own formatting style. @@ -220,13 +220,20 @@ static void curses_initialize (void) { "tcsetattr: Failed to set reverse terminal attributes."); screen_memory = CURSES_FORMAT * curses_screen_width * curses_screen_height; // This is square area of our terminal, and multiplied by 12, size of FORMAT. + lines = (curses_screen_height - 1) * 2; // This is size of line feed and carriage return to avoid word-wrapping. - curses_screen = allocate (CURSES_REVERT + screen_memory + CURSES_CURSOR + 1); // We're requesting new memory for framebuffer. + curses_screen = allocate (CURSES_REVERT + screen_memory + lines + CURSES_CURSOR + 1); // We're requesting new memory for framebuffer. curses_bind (SIGNAL_ESCAPE, curses_exit); // Binding universal exit key (signal). string_copy (& curses_screen [0], "\033[H"); // ASCII black magic to always clear screen. + for (lines = 1; lines < curses_screen_height; ++lines) { // Now it's time to put forced line breaks in raw terminal, without the last one. + int skip = CURSES_REVERT + 2 * (lines - 1); // We skip first 3 bytes and previously copied amount of line breaks. + int next = lines * CURSES_FORMAT * curses_screen_width; // And now we offset full width of our terminal, this makes it faster... + string_copy_limit (curses_screen + skip + next, "\r\n", string_length ("\r\n")); // And lastly, we copy those line breaks at this offset into our screen buffer. + } // Keep in mind that word-wrapping is slow on some terminals, hence I use this. + terminal_clear (); } @@ -235,8 +242,8 @@ static void curses_deinitialize (void) { curses_activator = deallocate (curses_activator); curses_action = deallocate (curses_action); - curses_action_count = 0; - curses_character = 0; + curses_action_count = 0; // I just set everthing into default state, so we can use curses multiple times. + curses_character = 0; // This way, it's all safe and clean, even with (de)initializing it twice. curses_signal = SIGNAL_NONE; curses_screen_width = 0; curses_screen_height = 0; @@ -290,10 +297,10 @@ static char * curses_screen_offset (int x, int y) { // 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); // We're limiting the values of X and Y coordinates to screen dimensions. - limit (& y, 0, curses_screen_height - 1); // Function 'limit' uses inclusive values for minimum and maximum. + limit (& x, 0, curses_screen_width - 1); // We're limiting the values of X and Y coordinates to screen dimensions. + limit (& y, 0, curses_screen_height - 1); // Function 'limit' uses inclusive values for minimum and maximum. - return (& curses_screen [CURSES_REVERT + CURSES_FORMAT * (y * curses_screen_width + x)]); // And returning the offset of the screen buffer. + return (& curses_screen [CURSES_REVERT + 2 * y + CURSES_FORMAT * (y * curses_screen_width + x)]); // And returning the offset of the screen buffer. } static char * curses_format_character (char character, int colour, int effect) { @@ -344,11 +351,13 @@ void curses_configure (void) { } void curses_synchronize (void) { - int signal; + int signal, length; curses_signal = curses_character = signal = 0; // Reassigning signals to 0. - out (curses_screen, CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + CURSES_CURSOR); // We output the entire framebuffer to terminal (present). + length = CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height + 2 * (curses_screen_height - 1) + CURSES_CURSOR; + + out (curses_screen, length); // We output the entire framebuffer to terminal (present). in (& signal, (int) sizeof (signal)); // We're now checking for user input to modify it. @@ -420,20 +429,20 @@ void curses_render_cursor (int x, int y) { // We're adding one because ASCII uses 1 ... 1000, and we use 0 ... 999, so it doesn't causes hidden bugs, and realign them to be prefixed with '0' characters. // If we have set of values (x = 31, y = 37), then the copied string would look like "\033[031;037H". It's not complex as it may sound. // And remember our ASCII table, thing scary thing (\033) is just octal value for number 27, which is CHARACTER_ESCAPE, hence the escape sequences start with it. - x %= 1000; + int offset; + + x %= 1000; // I don't care for terminals bigger than this... 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_cursor + 2, string_realign (number_to_string (y + 1), 3, '0'), 3); // We're copying 0...999 number as string, with 0s. + string_copy_limit (curses_cursor + 6, string_realign (number_to_string (x + 1), 3, '0'), 3); // Those prefix 0s must be used with this. - string_copy_limit (& curses_screen [CURSES_REVERT + CURSES_FORMAT * curses_screen_width * curses_screen_height], curses_cursor, CURSES_CURSOR); // Actual rendering. + offset = CURSES_REVERT + 2 * (curses_screen_height - 1) + CURSES_FORMAT * curses_screen_width * curses_screen_height; // We need to offset it at the end of our screen. + + string_copy_limit (& curses_screen [offset], curses_cursor, CURSES_CURSOR); // And only then copy cursor data into screen. } void curses_render_character (char character, int colour, int effect, int x, int y) { - if ((x < 0) || (x > curses_screen_width - 1) || (y < 0) || (y > curses_screen_height - 1)) { // If any of these are true, we don't render. - return; - } - // Again, lets show some code formatting examples: // if ((x < 0) // || (y < 0) @@ -451,6 +460,10 @@ void curses_render_character (char character, int colour, int effect, int x, int // Or if you really hate adding 2 more lines of code and curly braces: // if ((x < 0) || (x > curses_screen_width - 1) || (y < 0) || (y > curses_screen_height - 1)) return; + if ((x < 0) || (x > curses_screen_width - 1) || (y < 0) || (y > curses_screen_height - 1)) { // If any of these are true, we don't render. + return; + } + string_copy_limit (curses_screen_offset (x, y), curses_format_character (character, colour, effect), CURSES_FORMAT); // Again, actual rendering, copying a value to offset. } @@ -465,9 +478,9 @@ void curses_render_background (char character, int colour, int effect) { } void curses_render_rectangle (char character, int colour, int effect, int x, int y, int width, int height) { - for (int j = 0; j < height; ++j) { // Iterating through rows (by height) of our framebuffer ('curses_screen'). - for (int i = 0; i < width; ++i) { // Iterating through columns (by width) of our framebuffer. - curses_render_character (character, colour, effect, x + i, y + j); // Now, we can use function 'curses_render_character' to simplify our life... + for (int j = 0; j < height; ++j) { // You can declare type of those iterators in for loops. + for (int i = 0; i < width; ++i) { // This only works if you're not using ANSI C (C89 / C90) standard. + curses_render_character (character, colour, effect, x + i, y + j); // Now, we render character by character again... } } } diff --git a/program/program_2.c b/program/program_2.c index c866df8..5c279a4 100644 --- a/program/program_2.c +++ b/program/program_2.c @@ -28,18 +28,21 @@ int main (void) { curses_bind (SIGNAL_ARROW_LEFT, player_move_left); curses_bind (SIGNAL_ARROW_RIGHT, player_move_right); - while (curses_active) { - curses_render_background (' ', COLOUR_WHITE, EFFECT_NORMAL); - curses_render_rectangle ('.', COLOUR_GREY, EFFECT_BOLD, 0, 0, 80, 24); - curses_render_character ('@', COLOUR_CYAN, EFFECT_BOLD, player_x, player_y); + curses_bind (SIGNAL_W, player_move_up); + curses_bind (SIGNAL_S, player_move_down); + curses_bind (SIGNAL_A, player_move_left); + curses_bind (SIGNAL_D, player_move_right); - //~switch (curses_character) { - //~case 'w': player_move_up (); break; - //~case 's': player_move_down (); break; - //~case 'a': player_move_left (); break; - //~case 'd': player_move_right (); break; - //~default: break; - //~} + while (curses_active) { + curses_render_background ('.', COLOUR_GREY, EFFECT_BOLD); + + curses_render_rectangle (',', COLOUR_GREEN, EFFECT_NORMAL, 10, 10, 80, 24); + + curses_render_character ('@', COLOUR_CYAN, EFFECT_BOLD, player_x, player_y); + + for (int i = 0; i < 50; ++i) { + curses_render_character ('#', COLOUR_BLUE, EFFECT_BOLD, i, i); + } curses_synchronize (); }