aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/TUI/module.jai26
-rw-r--r--modules/TUI/snake.jai165
2 files changed, 20 insertions, 171 deletions
diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai
index d0373f9..2fb1233 100644
--- a/modules/TUI/module.jai
+++ b/modules/TUI/module.jai
@@ -125,10 +125,21 @@ Commands :: struct #type_info_none {
// Draw/text.
DrawingMode :: "\e(0";
TextMode :: "\e(B";
- ClearScreen :: "\e[2J";
- ClearLine :: "\e[2K";
+ ClearToEndOfScreen :: "\e[0J"; // From current cursor position (inclusive) to end of screen.
+ ClearFromStartOfScreen :: "\e[1J"; // From start of screen to current cursor position.
+ ClearScreen :: "\e[2J"; // Leaves cursor in top left corner position.
ClearScrollBack :: "\e[3J";
+ ClearToEndOfLine :: "\e[0K"; // From current cursor position (inclusive) to end of line.
+ ClearFromStartOfLine :: "\e[1K"; // From start of line to current cursor position.
+ ClearLine :: "\e[2K";
SetGraphicsRendition :: "\e[%m";
+
+ // Text Modification.
+ InsertCharacters :: "\e[%@"; // Insert % spaces at curret cursor position (shifts existing text to the right).
+ DeleteCharacters :: "\e[%P"; // Delete % characters at the current cursor position (inserts space characters from the right).
+ EraseCharacters :: "\e[%X"; // Erase % characters from the current cursor position by overwriting them with space characters.
+ InsertLines :: "\e[%L"; // Insert % lines into the buffer at the current cursor position.
+ DeleteLines :: "\e[%M"; // Deletes % lines from the buffer, starting with the row the cursor is on.
// Character encoding.
EncodingIEC2022 :: "\e%@";
@@ -536,12 +547,12 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key {
if is_visible {
print_to_builder(*builder, Commands.SetCursorPosition, y, x);
append(*builder, str);
- for chars_count..count_limit-1 append(*builder, " ");
+ if count_limit > chars_count then print_to_builder(*builder, Commands.EraseCharacters, count_limit-chars_count);
}
else {
print_to_builder(*builder, Commands.SetCursorPosition, y, x);
for 1..chars_count append(*builder, "*");
- for chars_count..count_limit-1 append(*builder, " ");
+ if count_limit > chars_count print_to_builder(*builder, Commands.EraseCharacters, count_limit-chars_count);
}
print_to_builder(*builder, Commands.SetCursorPosition, y, x+idx);
write_builder(*builder);
@@ -615,7 +626,7 @@ flush_input :: () {
input_string.count = 0;
}
-draw_box :: (x: int, y: int, width: int, height: int) {
+draw_box :: (x: int, y: int, width: int, height: int, clear_inside := false) {
assert_is_active();
assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments passed to draw_box(): 'x' and 'y' must be greater-than 0; 'width' and 'height' must be greater-than 1.");
@@ -641,10 +652,13 @@ draw_box :: (x: int, y: int, width: int, height: int) {
for idx: y+1..y+height-2 {
print_to_builder(builder, Commands.SetCursorPosition, idx, x);
append(builder, Drawings.LineV);
+ if clear_inside {
+ print_to_builder(builder, Commands.EraseCharacters, width-2);
+ }
print_to_builder(builder, Commands.SetCursorPosition, idx, x+width-1);
append(builder, Drawings.LineV);
}
-
+
// Draw bottom line.
print_to_builder(builder, Commands.SetCursorPosition, y+height-1, x);
append(builder, Drawings.CornerBL);
diff --git a/modules/TUI/snake.jai b/modules/TUI/snake.jai
deleted file mode 100644
index b35c0fb..0000000
--- a/modules/TUI/snake.jai
+++ /dev/null
@@ -1,165 +0,0 @@
-#import "Basic";
-#import "Random";
-TUI :: #import "TUI"(COLOR_MODE_BITS = 8);
-
-Vec2D :: struct {
- x: int;
- y: int;
-}
-
-operator == :: (a: Vec2D, b: Vec2D) -> bool {
- return a.x == b.x && a.y == b.y;
-}
-
-screen_size_x: int = ---;
-screen_size_y: int = ---;
-player_name: string = ---;
-
-main :: () {
-
- game_loop :: () {
-
- LOOP_PERIOD_MS :: 30;
-
- score := 0;
- dir := Vec2D.{1, 0};
- food := Vec2D.{5, 5};
-
- snake_parts: [..] Vec2D;
- for 0..13 array_add(*snake_parts, Vec2D.{3, 3});
- snake_parts[0].x += 1;
-
- TUI.flush_input();
- TUI.set_next_key(TUI.Keys.Resize);
- timer := current_time_monotonic();
- while main_loop := true {
-
- timestamp := current_time_monotonic();
- key := TUI.get_key(LOOP_PERIOD_MS);
-
- if key == {
- case TUI.Keys.Resize;
- TUI.clear_terminal();
- screen_size_x, screen_size_y = TUI.get_terminal_size();
- TUI.draw_box(1, 1, screen_size_x, screen_size_y);
- TUI.set_cursor_position(3, screen_size_y);
- write_strings(" ", player_name, " ");
-
- case #char "q"; #through;
- case #char "Q"; #through;
- case TUI.Keys.Escape;
- break main_loop;
-
- case TUI.Keys.Up;
- if dir != Vec2D.{0, 1} then dir = Vec2D.{0, -1};
-
- case TUI.Keys.Down;
- if dir != Vec2D.{0, -1} then dir = Vec2D.{0, 1};
-
- case TUI.Keys.Left;
- if dir != Vec2D.{1, 0} then dir = Vec2D.{-1, 0};
-
- case TUI.Keys.Right;
- if dir != Vec2D.{-1, 0} then dir = Vec2D.{1, 0};
- }
-
- if screen_size_x < 15 || screen_size_y < 15 {
- TUI.clear_terminal();
- TUI.set_cursor_position(1,1);
- write_string("~ paused : increase window size ~");
- continue;
- }
-
- last_pos := snake_parts[snake_parts.count-1];
-
- // Update position.
- for < snake_parts.count-1..1 {
- if snake_parts[it] != snake_parts[it-1] {
- snake_parts[it] = snake_parts[it-1];
- }
- }
- snake_parts[0].x += dir.x;
- snake_parts[0].y += dir.y;
-
- // Teleport on borders.
- if snake_parts[0].x < 2 then snake_parts[0].x = screen_size_x - 1;
- if snake_parts[0].x >= screen_size_x then snake_parts[0].x = 2;
- if snake_parts[0].y < 2 then snake_parts[0].y = screen_size_y - 1;
- if snake_parts[0].y >= screen_size_y then snake_parts[0].y = 2;
- food.x = clamp(food.x, 2, screen_size_x-1);
- food.y = clamp(food.y, 2, screen_size_y-1);
-
- // Check for game-over.
- for 1..snake_parts.count-1 {
- if snake_parts[it] == snake_parts[0] {
- break main_loop;
- }
- }
-
- // Check for food.
- if snake_parts[0] == food {
- score += 1;
- array_add(*snake_parts, snake_parts[snake_parts.count-1]);
- food = Vec2D.{
- cast(int)(random_get_zero_to_one_open() * (screen_size_x-3) + 2),
- cast(int)(random_get_zero_to_one_open() * (screen_size_y-3) + 2)
- };
- }
-
- // Wait to match game loop time.
- delta := to_milliseconds(current_time_monotonic() - timestamp);
- if delta < LOOP_PERIOD_MS {
- sleep_milliseconds(xx (LOOP_PERIOD_MS - delta));
- }
-
- // Draw snake.
- write_string(TUI.Commands.DrawingMode);
- TUI.set_cursor_position(last_pos.x, last_pos.y);
- write_string(TUI.Drawings.Blank);
- for snake_parts {
- TUI.set_cursor_position(it.x, it.y);
- write_string(TUI.Drawings.Checkerboard);
- }
- // Draw food.
- {
- TUI.using_style(TUI.Style.{ foreground = TUI.Palette.RED, bold = true, });
- TUI.set_cursor_position(food.x, food.y);
- write_string(TUI.Drawings.Diamond);
- }
- write_string(TUI.Commands.TextMode);
-
- // Set score
- TUI.set_cursor_position(3, 1);
- print(" % ", score);
- }
- }
-
- GAME_OVER_TEXT :: "~ game over ~";
- INSTRUCTIONS_TEXT :: "(esc to exit)";
-
- seed: u64 = xx to_milliseconds(current_time_monotonic()) | 0x01; // Seed must be odd.
- random_seed(seed);
-
- assert(TUI.setup_terminal(), "Failed to setup TUI.");
- TUI.set_cursor_position(1, 1);
-
- write_string("Please enter player name: ");
- player_name = TUI.read_input_line(64);
-
- while true {
- game_loop();
-
- // Game over screen.
- box_size := Vec2D.{19, 4};
- TUI.draw_box((screen_size_x-box_size.x)/2, (screen_size_y-box_size.y)/2, box_size.x, box_size.y);
- TUI.set_cursor_position((screen_size_x-GAME_OVER_TEXT.count)/2, (screen_size_y-box_size.y)/2 + 1);
- write_string(GAME_OVER_TEXT);
- TUI.set_cursor_position((screen_size_x-GAME_OVER_TEXT.count)/2, (screen_size_y-box_size.y)/2 + 2);
- write_string(INSTRUCTIONS_TEXT);
- sleep_milliseconds(100);
- TUI.flush_input();
- if TUI.get_key() == TUI.Keys.Escape then break;
- }
-
- assert(TUI.reset_terminal(), "Failed to reset TUI.");
-}