aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordam <devnull@localhost>2024-04-15 16:50:42 +0100
committerdam <devnull@localhost>2024-04-15 16:50:42 +0100
commitbb1d53daa526d734fb2e33c2090f8396f87544ec (patch)
tree52e90e50b02cb3a46143e92e129f9525dbfdc5c4
parent3112131c24ede1ea343383ffd4f8ca7bc4783f47 (diff)
downloadtask-time-tracker-bb1d53daa526d734fb2e33c2090f8396f87544ec.tar.zst
task-time-tracker-bb1d53daa526d734fb2e33c2090f8396f87544ec.zip
Improved TUI logger; added snake example using TUI.
-rw-r--r--modules/TUI/module.jai72
-rw-r--r--modules/TUI/unix.jai2
-rw-r--r--snake.jai157
-rw-r--r--ttt.jai10
4 files changed, 195 insertions, 46 deletions
diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai
index 070161c..b5866d5 100644
--- a/modules/TUI/module.jai
+++ b/modules/TUI/module.jai
@@ -59,38 +59,44 @@ Drawings :: struct {
}
Commands :: struct {
+ // Screen buffers
AlternateScreenBuffer :: "\e[?1049h";
- MainScreenBuffer :: "\e[?1049l";
+ MainScreenBuffer :: "\e[?1049l";
+ // Device.
+ Bell :: "\x07";
+ QueryDeviceAttributes :: "\e[0c";
+
+ // Draw/text.
DrawingMode :: "\e(0";
TextMode :: "\e(B";
ClearScreen :: "\e[2J";
ClearLine :: "\e[2K";
ClearScrollBack :: "\e[3J";
-
- Bell :: "\x07";
-
+ SetGraphicsRendition :: "\e[%m";
+
+ // Character encoding.
+ EncodingIEC2022 :: "\e%@";
+ EncodingUTF8 :: "\e%G";
+
+ // Window.
SetWindowTitle :: "\e]0;%\e\\";
-
- RefreshWindow :: "\e[7t"; // TODO Not yet tested. Is this required?
+ RefreshWindow :: "\e[7t";
+ QueryWindowSizeInChars :: "\e[18t";
- SetIEC2022 :: "\e%@"; // TODO Remove this!?
- SetUTF8 :: "\e%G"; // TODO Remove this!?
-
- SetGraphicsRendition :: "\e[%m";
-
- // Cursor Position
+ // Cursor position.
+ SaveCursorPosition :: "\e7";
+ RestoreCursorPosition :: "\e8";
SetCursorPosition :: "\e[%;%H";
-
- // Cursor Visibility
+ QueryCursorPosition :: "\e[6n";
+
+ // Cursor visibility.
ShowCursor :: "\e[?25h";
HideCursor :: "\e[?25l";
- StartBlinking :: "\e[?25h";
- StopBlinking :: "\e[?25l";
- SaveCursorPosition :: "\e7";
- RestoreCursorPosition :: "\e8";
-
- // Cursor Shape
+ StartBlinking :: "\e[?12h";
+ StopBlinking :: "\e[?12l";
+
+ // Cursor shape
DefaultShape :: "\e[0 q";
BlinkingBlockShape :: "\e[1 q";
SteadyBlockShape :: "\e[2 q";
@@ -98,17 +104,12 @@ Commands :: struct {
SteadyUnderlineShape :: "\e[4 q";
BlinkingBarShape :: "\e[5 q";
SteadyBarShape :: "\e[6 q";
-
- // Input Mode
+
+ // Input mode.
KeypadAppMode :: "\e=";
KeypadNumMode :: "\e>";
CursorAppMode :: "\e[?1h";
CursorNormalMode :: "\e[?1l";
-
- // Query State
- QueryCursorPosition :: "\e[6n"; // Emits the cursor position as: "ESC [ <r> ; <c> R" Where <r> = row and <c> = column.
- QueryDeviceAttributes :: "\e[0c";
- QueryWindowSizeInChars :: "\e[18t"; // Emits the window size as: "ESC [ 8 <r> ; <c> t" Where <r> = row and <c> = column. TODO Does not work on windows.
}
#if COLOR_MODE == 4 {
@@ -150,12 +151,16 @@ else {
Style :: struct {
#if COLOR_MODE == 4 || COLOR_MODE == 8 {
- background: Palette = .BLACK;
- foreground: Palette = .WHITE;
+ background: Palette;
+ foreground: Palette;
} else {
background: Color_24b;
foreground: Color_24b;
}
+
+ background = Palette.BLACK;
+ foreground = Palette.WHITE;
+
bold: bool;
underline: bool;
strike_through: bool;
@@ -279,12 +284,9 @@ input_override : Key;
previous_logger : (message: string, data: *void, info: Log_Info);
module_logger :: (message: string, data: *void, info: Log_Info) {
- // print("%0%0\n%0", Commands.MainScreenBuffer, message, Commands.AlternateScreenBuffer);
- x, y := get_cursor_position();
- write_string(Commands.MainScreenBuffer);
+ write_strings(Commands.SaveCursorPosition, Commands.MainScreenBuffer);
previous_logger(message, data, info);
- write_string(Commands.AlternateScreenBuffer);
- set_cursor_position(x, y);
+ write_strings(Commands.AlternateScreenBuffer, Commands.RestoreCursorPosition);
}
assert_is_active :: inline () {
@@ -534,7 +536,7 @@ start :: () {
Commands.HideCursor,
Commands.SaveCursorPosition,
Commands.AlternateScreenBuffer,
- Commands.SetUTF8,
+ Commands.EncodingUTF8,
Commands.CursorNormalMode,
Commands.KeypadNumMode);
diff --git a/modules/TUI/unix.jai b/modules/TUI/unix.jai
index 861fe11..7103d47 100644
--- a/modules/TUI/unix.jai
+++ b/modules/TUI/unix.jai
@@ -7,7 +7,7 @@
// Required to do unlocking input.
libc :: #system_library "libc";
- // TODO Remote this.
+ // TODO Remove this.
// cfmakeraw :: (termios: *Terminal_IO_Mode) -> void #foreign libc;
/* https://elixir.bootlin.com/glibc/glibc-2.28/source/termios/cfmakeraw.c#L22
void
diff --git a/snake.jai b/snake.jai
new file mode 100644
index 0000000..ea8926f
--- /dev/null
+++ b/snake.jai
@@ -0,0 +1,157 @@
+#import "Basic";
+#import "Random";
+TUI :: #import "TUI";
+
+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};
+
+ random_food :: () -> Vec2D {
+ return 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)
+ };
+ }
+
+ 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, " ");
+ food = random_food();
+
+ 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};
+ }
+
+ 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;
+
+ // 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 = random_food();
+ }
+
+ // 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)";
+
+ TUI.start();
+ 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.
+ TUI.draw_box(screen_size_x/3, screen_size_y/2-1, screen_size_x/3, 4);
+ TUI.set_cursor_position((screen_size_x-GAME_OVER_TEXT.count)/2, screen_size_y/2);
+ write_string(GAME_OVER_TEXT);
+ TUI.set_cursor_position((screen_size_x-GAME_OVER_TEXT.count)/2, screen_size_y/2+1);
+ write_string(INSTRUCTIONS_TEXT);
+ sleep_milliseconds(100);
+ TUI.flush_input();
+ if TUI.get_key() == TUI.Keys.Escape then break;
+ }
+
+ TUI.stop();
+}
diff --git a/ttt.jai b/ttt.jai
index 8dda204..8f39b56 100644
--- a/ttt.jai
+++ b/ttt.jai
@@ -1191,7 +1191,6 @@ main :: () {
print("TEST : module logger\n", to_standard_error = true);
log("- log: before module start.");
TUI.start();
-
TUI.set_cursor_position(3, 3);
print("wait");
sleep_milliseconds(1000);
@@ -1199,15 +1198,6 @@ main :: () {
sleep_milliseconds(1000);
print(" a bit");
sleep_milliseconds(1000);
-
- #import "Windows";
- handle: HANDLE = ---;
- initial_stdin_mode: u32;
- if xx GetConsoleMode(handle, *initial_stdin_mode) == false {
- error_code, error_string := get_error_value_and_string();
- log_error("- log: error code %, %", error_code, error_string);
- }
-
TUI.stop();
log("- log: after module stop.");
}