From ddb3e4f4192009a47fa094aaad5e57da9b8c2711 Mon Sep 17 00:00:00 2001 From: dam Date: Sat, 6 Apr 2024 01:51:16 +0100 Subject: Added styles. --- modules/TUI/palette_24b.jai | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 modules/TUI/palette_24b.jai (limited to 'modules/TUI/palette_24b.jai') diff --git a/modules/TUI/palette_24b.jai b/modules/TUI/palette_24b.jai new file mode 100644 index 0000000..7a263a0 --- /dev/null +++ b/modules/TUI/palette_24b.jai @@ -0,0 +1,44 @@ +// https://www.ditig.com/publications/256-colors-cheat-sheet +Palette :: struct { + BLACK :: Color_24b.{0x00, 0x00, 0x00}; + MAROON :: Color_24b.{0x80, 0x00, 0x00}; + GREEN :: Color_24b.{0x00, 0x80, 0x00}; + OLIVE :: Color_24b.{0x80, 0x80, 0x00}; + NAVY :: Color_24b.{0x00, 0x00, 0x80}; + PURPLE :: Color_24b.{0x80, 0x00, 0x80}; + TEAL :: Color_24b.{0x00, 0x80, 0x80}; + SILVER :: Color_24b.{0xC0, 0xC0, 0xC0}; + GRAY :: Color_24b.{0x80, 0x80, 0x80}; + RED :: Color_24b.{0xFF, 0x00, 0x00}; + LIME :: Color_24b.{0x00, 0xFF, 0x00}; + YELLOW :: Color_24b.{0xFF, 0xFF, 0x00}; + BLUE :: Color_24b.{0x00, 0x00, 0xFF}; + MAGENTA :: Color_24b.{0xFF, 0x00, 0xFF}; + CYAN :: Color_24b.{0x00, 0xFF, 0xFF}; + WHITE :: Color_24b.{0xFF, 0xFF, 0xFF}; + + GRAY_3 :: Color_24b.{0x08, 0x08, 0x08}; + GRAY_7 :: Color_24b.{0x12, 0x12, 0x12}; + GRAY_10 :: Color_24b.{0x1C, 0x1C, 0x1C}; + GRAY_14 :: Color_24b.{0x26, 0x26, 0x26}; + GRAY_18 :: Color_24b.{0x30, 0x30, 0x30}; + GRAY_22 :: Color_24b.{0x3A, 0x3A, 0x3A}; + GRAY_26 :: Color_24b.{0x44, 0x44, 0x44}; + GRAY_30 :: Color_24b.{0x4E, 0x4E, 0x4E}; + GRAY_34 :: Color_24b.{0x58, 0x58, 0x58}; + GRAY_37 :: Color_24b.{0x62, 0x62, 0x62}; + GRAY_40 :: Color_24b.{0x6C, 0x6C, 0x6C}; + GRAY_46 :: Color_24b.{0x76, 0x76, 0x76}; + GRAY_50 :: GRAY; + GRAY_54 :: Color_24b.{0x8A, 0x8A, 0x8A}; + GRAY_58 :: Color_24b.{0x94, 0x94, 0x94}; + GRAY_61 :: Color_24b.{0x9E, 0x9E, 0x9E}; + GRAY_65 :: Color_24b.{0xA8, 0xA8, 0xA8}; + GRAY_69 :: Color_24b.{0xB2, 0xB2, 0xB2}; + GRAY_73 :: Color_24b.{0xBC, 0xBC, 0xBC}; + GRAY_77 :: Color_24b.{0xC6, 0xC6, 0xC6}; + GRAY_81 :: Color_24b.{0xD0, 0xD0, 0xD0}; + GRAY_85 :: Color_24b.{0xDA, 0xDA, 0xDA}; + GRAY_89 :: Color_24b.{0xE4, 0xE4, 0xE4}; + GRAY_93 :: Color_24b.{0xEE, 0xEE, 0xEE}; +} -- cgit v1.2.3 From 8b312bad33617f9b718232141d2855d80cb8b912 Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 3 May 2024 00:32:06 +0100 Subject: WIP : Cleaning up TUI module. --- modules/TUI/module.jai | 178 +++++++++++++------------------------------- modules/TUI/palette_24b.jai | 2 +- snake.jai | 6 +- ttt.jai | 51 ++++++------- unused.jai | 15 ++++ 5 files changed, 95 insertions(+), 157 deletions(-) (limited to 'modules/TUI/palette_24b.jai') diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai index 991dc74..d44edab 100644 --- a/modules/TUI/module.jai +++ b/modules/TUI/module.jai @@ -1,9 +1,7 @@ -#module_parameters(COLOR_MODE := 24); +#module_parameters(COLOR_MODE_BITS := 24); #scope_file -// - fix/implement/finish `TODO` on `TUI\module` (use some sort of buffet to reduce io/print calls) - #if OS == { case .LINUX; #load "unix.jai"; @@ -22,14 +20,14 @@ #load "key_map.jai"; #run { - assert(COLOR_MODE == 4 || COLOR_MODE == 8 || COLOR_MODE == 24, "Invalid COLOR_MODE. Valid values are 4, 8, or 24 (default)."); + assert(COLOR_MODE_BITS == 4 || COLOR_MODE_BITS == 8 || COLOR_MODE_BITS == 24, "Invalid COLOR_MODE_BITS. Valid values are 4, 8, or 24 (default)."); assert(input_buffer.count >= KEY_SIZE, "The input buffer size must be capable to hold an entire Key, which should be able to hold an UTF8 code (4 bytes) or a terminal escape code (6 bytes)."); } #scope_export; -// Special Graphics Characters -Drawings :: struct { +// Special Graphics Characters. +Drawings :: struct #type_info_none { Blank :: "\x5F"; Diamond :: "\x60"; Checkerboard :: "\x61"; @@ -64,7 +62,9 @@ Drawings :: struct { CenteredDot :: "\x7E"; } -Commands :: struct { +// Terminal Escape Codes. +Commands :: struct #type_info_none { + // Screen buffers AlternateScreenBuffer :: "\e[?1049h"; MainScreenBuffer :: "\e[?1049l"; @@ -118,7 +118,7 @@ Commands :: struct { CursorNormalMode :: "\e[?1l"; } -#if COLOR_MODE == 4 { +#if COLOR_MODE_BITS == 4 { #load "palette_4b.jai"; set_colors :: inline (foreground: Palette, background: Palette) { @@ -127,7 +127,7 @@ Commands :: struct { cast(u8)foreground + 30, cast(u8)background + 40); } } -else #if COLOR_MODE == 8 { +else #if COLOR_MODE_BITS == 8 { #load "palette_8b.jai"; set_colors :: inline (foreground: Palette, background: Palette) { @@ -153,19 +153,21 @@ else { } } -#add_context tui_style: Style; - Style :: struct { - #if COLOR_MODE == 4 || COLOR_MODE == 8 { + #if COLOR_MODE_BITS == 4 || COLOR_MODE_BITS == 8 { background: Palette; foreground: Palette; } else { background: Color_24b; foreground: Color_24b; } - + background = Palette.BLACK; foreground = Palette.WHITE; + + // TODO Make this work... + use_default_background_color := false; + use_default_foreground_color := false; bold: bool; underline: bool; @@ -198,9 +200,6 @@ using_style :: (style: Style) #expand { `defer set_style(__style); } -// TODO Maybe make the OS_* procedures as inline?! -// TODO Terminal action codes are encoded with values incompatible with UTF-8 to avoid collisions. - /* We wanted the Key type to represent either UTF-8 encoded characters and also keyboard keys. The UTF-8 only requires up to 4 bytes, but some keyboard keys return up to 6 bytes. @@ -238,7 +237,7 @@ to_string :: inline (key: Key) -> string { // TODO FIXME TEMPORARY MEMORY return str; } -is_escape_code :: inline (key: Key) -> bool { +is_escape_code :: (key: Key) -> bool { beginsWithEscape := ((key & 0xFF) ^ #char "#") == 0; hasSomethingElse := (key & (~0xFF)) != 0; return beginsWithEscape && hasSomethingElse; @@ -281,6 +280,8 @@ Keys :: struct #type_info_none { F12 : Key : #run to_key("#f12"); } +#add_context tui_style: Style; + active := false; input_buffer : [1024] u8; @@ -296,7 +297,7 @@ module_logger :: (message: string, data: *void, info: Log_Info) { } assert_is_active :: inline () { - assert(active, "TUI is not ready. Please call TUI.start() before."); // TODO Improve error message... and maybe use the logger and return success=false flag + assert(active, "Please call TUI.setup_terminal() before using this procedure."); // TODO Improve error message... and maybe use the logger and return success=false flag } //////////////////////////////////////////////////////////////////////////////// @@ -376,12 +377,15 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key { return to_key(to_parse); } -// TODO Review me! -read_input :: (count_limit: int = -1, terminators: .. u8) -> string { +// TODO Review me and add some comments. +read_input :: (count_limit: int = -1, terminators: .. u8) -> string, success := true { assert_is_active(); - - assert(count_limit >= 0 || terminators.count > 0, "Infinite loop detected, aborting."); // TODO Maybe just return!? Or simply return success=false with #must - + + if count_limit < 0 && terminators.count <= 0 { + log_error("Invalid arguments passed to read_input(): (%).\n", count_limit); // TODO Improve error message. + return "", false; + } + if count_limit < 0 { builder: String_Builder; init_string_builder(*builder); @@ -436,7 +440,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { Escape discards the input returning an empty string and a Escape key. Resize discards the input returning an empty string and a Resize key. */ - + assert_is_active(); assert(count_limit >= 0, "Invalid value on count_limit parameter."); // TODO Too agressive str := alloc_string(count_limit); @@ -452,7 +456,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { chars_count := count_characters(str); - // Preview input. + // Preview input. TODO Optimize using temp_builder if is_visible { set_cursor_position(x, y); write_string(str); @@ -524,13 +528,8 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { return result, key; } -start :: () -> success := true #must { +setup_terminal :: () -> success := true #must { if active == true return; - - if inited == false { - init_string_builder(*temp_builder,, temp); // TODO Testing - inited = true; - } input_string.data = input_buffer.data; input_string.count = 0; @@ -557,7 +556,7 @@ start :: () -> success := true #must { return; } -stop :: () -> success := true #must { +reset_terminal :: () -> success := true #must { if active == false return; active = false; @@ -578,138 +577,61 @@ stop :: () -> success := true #must { } flush_input :: () { + assert_is_active(); OS_flush_input(); input_string.data = input_buffer.data; input_string.count = 0; } - - -// Using String_Builder improves: -// - procedure time is now ~12x faster; -// - reduces CPU usage by ~ -average: float64 = 0; -counter: float64 = 0; -inited:=false; -temp_builder: String_Builder; - -#if false { +// TODO What if we return success and fail when input arguments are invalid? draw_box :: (x: int, y: int, width: int, height: int) { - t0 := current_time_monotonic(); - assert_is_active(); - - // TODO Check if using a String_Builder improves performance (measure it)! - // TODO Validate input parameters against the terminal size. - assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments."); - - auto_release_temp(); - - tmp_string: string; - - tmp_string = tprint(Commands.SetCursorPosition, y, x); - write_strings( - Commands.DrawingMode, - tmp_string, - Drawings.CornerTL - ); + assert_is_active(); //TODO NOT NEEDED - for 1..width-2 { - write_string(Drawings.LineH); - } - write_string(Drawings.CornerTR); - - for idx: y+1..y+height-2 { - tmpL := tprint(Commands.SetCursorPosition, idx, x); - tmpR := tprint(Commands.SetCursorPosition, idx, x+width-1); - write_strings( - tmpL, - Drawings.LineV, - tmpR, - Drawings.LineV); - } - - tmpBL := tprint(Commands.SetCursorPosition, y+height-1, x); - write_strings( - tmpBL, - Drawings.CornerBL); - for 1..width-2 { - write_string(Drawings.LineH); + if x <= 0 || y <= 0 || width <= 1 || height <= 1 { + log_error("Invalid arguments passed to draw_box(): (%, %, %, %).\n", x, y, width, height); // TODO Improve error message. + return; } - write_string(Drawings.CornerBR); - write_string(Commands.TextMode); - - t1 := current_time_monotonic(); - set_cursor_position(2, 47); - sample := cast(float64)to_nanoseconds(t1-t0)/1000; - average = (sample + counter * average) / (counter + 1); - counter += 1; - print(">%<", average); -} - -} else { - - -// TODO Not sure how... but this seems to be leaking memory... maybe it's the String_Builder!? -draw_box :: (x: int, y: int, width: int, height: int) { - t0 := current_time_monotonic(); - - assert_is_active(); - - // TODO Check if using a String_Builder improves performance (measure it)! - // TODO Validate input parameters against the terminal size. - assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments."); - auto_release_temp(); - builder := temp_builder; - // init_string_builder(*builder); - + builder := String_Builder.{ allocator = temporary_allocator }; + append(*builder, Commands.DrawingMode); + + // Draw top line append(*builder, tprint(Commands.SetCursorPosition, y, x)); append(*builder, Drawings.CornerTL); - for 1..width-2 { append(*builder, Drawings.LineH); } append(*builder, Drawings.CornerTR); + // Draw left and right sides. for idx: y+1..y+height-2 { - tmpL := tprint(Commands.SetCursorPosition, idx, x); - tmpR := tprint(Commands.SetCursorPosition, idx, x+width-1); - append(*builder, tmpL); + append(*builder, tprint(Commands.SetCursorPosition, idx, x)); append(*builder, Drawings.LineV); - append(*builder, tmpR); + append(*builder, tprint(Commands.SetCursorPosition, idx, x+width-1)); append(*builder, Drawings.LineV); } + // Draw bottom line. append(*builder, tprint(Commands.SetCursorPosition, y+height-1, x)); append(*builder, Drawings.CornerBL); for 1..width-2 { append(*builder, Drawings.LineH); } append(*builder, Drawings.CornerBR); - + append(*builder, Commands.TextMode); write_string(builder_to_string(*builder)); - - t1 := current_time_monotonic(); - set_cursor_position(2, 47); - sample := cast(float64)to_nanoseconds(t1-t0)/1000; - average = (sample + counter * average) / (counter + 1); - counter += 1; - print(">%<", average); -} } -// TODO Maybe rename to "clear()" clear_terminal :: inline () { assert_is_active(); write_string(Commands.ClearScreen); } -// TODO Maybe rename to "get_size()" get_terminal_size :: () -> width: int, height: int { assert_is_active(); @@ -735,7 +657,7 @@ get_terminal_size :: () -> width: int, height: int { while input.count >= 3 && input[input.count-1] != FORMAT[FORMAT.count-1] { input.count -= 1; } - + assert(input.count >= 3 && input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[2] == FORMAT[2] && input[input.count-1] == FORMAT[FORMAT.count-1], "Query window size in chars returned invalid response."); @@ -757,7 +679,7 @@ get_terminal_size :: () -> width: int, height: int { return columns, rows; } -set_cursor_position :: (x: int, y: int) { +set_cursor_position :: inline (x: int, y: int) { assert_is_active(); print(Commands.SetCursorPosition, y, x); } @@ -784,7 +706,7 @@ get_cursor_position :: () -> x: int, y: int { while input.count >= 2 && input[input.count-1] != FORMAT[FORMAT.count-1] { input.count -= 1; } - + assert(input.count >= 2 && input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[input.count-1] == FORMAT[FORMAT.count-1], "Query cursor position returned invalid response."); @@ -796,7 +718,7 @@ get_cursor_position :: () -> x: int, y: int { return column, row; } -set_terminal_title :: (title: string) { +set_terminal_title :: inline (title: string) { assert_is_active(); print(Commands.SetWindowTitle, title); } diff --git a/modules/TUI/palette_24b.jai b/modules/TUI/palette_24b.jai index 7a263a0..ea0191c 100644 --- a/modules/TUI/palette_24b.jai +++ b/modules/TUI/palette_24b.jai @@ -1,5 +1,5 @@ // https://www.ditig.com/publications/256-colors-cheat-sheet -Palette :: struct { +Palette :: struct #type_info_none { BLACK :: Color_24b.{0x00, 0x00, 0x00}; MAROON :: Color_24b.{0x80, 0x00, 0x00}; GREEN :: Color_24b.{0x00, 0x80, 0x00}; diff --git a/snake.jai b/snake.jai index 465ae4d..b35c0fb 100644 --- a/snake.jai +++ b/snake.jai @@ -1,6 +1,6 @@ #import "Basic"; #import "Random"; -TUI :: #import "TUI"(COLOR_MODE = 8); +TUI :: #import "TUI"(COLOR_MODE_BITS = 8); Vec2D :: struct { x: int; @@ -140,7 +140,7 @@ main :: () { seed: u64 = xx to_milliseconds(current_time_monotonic()) | 0x01; // Seed must be odd. random_seed(seed); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_cursor_position(1, 1); write_string("Please enter player name: "); @@ -161,5 +161,5 @@ main :: () { if TUI.get_key() == TUI.Keys.Escape then break; } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); } diff --git a/ttt.jai b/ttt.jai index 50fa198..3512d20 100644 --- a/ttt.jai +++ b/ttt.jai @@ -17,7 +17,8 @@ // - release : jai ttt.jai -quiet -x64 -release // - debug : jai ttt.jai -quiet -x64 -#import "Basic"()(MEMORY_DEBUGGER=true); // TODO Remove after final debug sessions. This takes up ~30MB of RAM. +// #import "Basic"()(MEMORY_DEBUGGER=true); // TODO Remove after final debug sessions. This takes up ~30MB of RAM. +#import "Basic"; #import "System"; #import "Sort"; #import "Math"; @@ -26,7 +27,7 @@ #import "String"; #import "Integer_Saturating_Arithmetic"; #import "UTF8"; -TUI :: #import "TUI"(COLOR_MODE=4); +TUI :: #import "TUI"(COLOR_MODE_BITS=4); // - fix/implement/finish TODO : use `dirty_bit_flag` to only update ehat has been changed @@ -868,7 +869,7 @@ initialize_tui :: () { } } - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); } update_layout :: () { @@ -1167,7 +1168,7 @@ main :: () { print("- success\n", to_standard_error = true); } else { - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); print("- ERROR: %", error_message, to_standard_error = true); exit(1); } @@ -1180,19 +1181,19 @@ main :: () { if perform_test && 1 { print("TEST : set and get cursor position\n", to_standard_error = true); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); X :: 2; Y :: 3; TUI.set_cursor_position(X, Y); x, y := TUI.get_cursor_position(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(x == X && y == Y, "Failed set/get cursor position.\n"); } if perform_test && 1 { print("TEST : module logger\n", to_standard_error = true); log("- log: before module start."); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_cursor_position(3, 3); print("wait"); sleep_milliseconds(1000); @@ -1200,14 +1201,14 @@ main :: () { sleep_milliseconds(1000); print(" a bit"); sleep_milliseconds(1000); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); log("- log: after module stop."); } if perform_test && 1 { print("TEST : test key input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); write_string("Press q to exit, other key to print it to screen, wait 1s to see animation."); @@ -1226,28 +1227,28 @@ main :: () { write_string(TUI.to_string(key)); } } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); print("- success\n", to_standard_error = true); } if perform_test && 1 { print("TEST : draw box\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.flush_input(); TUI.clear_terminal(); TUI.draw_box(1, 2, 5, 3); TUI.set_cursor_position(1, 1); print("Can you see the box below? (y/n)"); key := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to draw box.\n"); } if perform_test && 1 { print("TEST : get terminal size\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); width, height := TUI.get_terminal_size(); TUI.set_cursor_position(1, 1); @@ -1256,13 +1257,13 @@ main :: () { while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { key = TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to get terminal size.\n"); } if perform_test && 1 { print("TEST : set terminal title\n", to_standard_error = true); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); title := "BAZINGA"; TUI.set_terminal_title(title); TUI.set_cursor_position(1, 1); @@ -1271,14 +1272,14 @@ main :: () { while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { key = TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to set terminal title.\n"); } if perform_test && 1 { print("TEST : print keys and set terminal title\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_terminal_title("bazinga"); key: TUI.Key = #char "d"; last_none_char := "X"; @@ -1337,13 +1338,13 @@ main :: () { // set_temporary_storage_mark(__mark); } print("- success"); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); } if perform_test && 1 { print("TEST : user input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); print("Enter some text (use Enter to finish, Esc to cancel, or resize to abort):"); @@ -1368,14 +1369,14 @@ main :: () { } } answer := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(answer == #char "y", error_message); } if perform_test && 1 { print("TEST : hidden user input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); print("Enter some secret (use Enter to finish, Esc to cancel, or resize to abort):"); @@ -1399,14 +1400,14 @@ main :: () { } } answer := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(answer == #char "y", error_message); } // -- -- -- Testing TUI -- STOP - defer report_memory_leaks(); // TODO Remove after final debug sessions. + // defer report_memory_leaks(); // TODO Remove after final debug sessions. defer free_memory(); @@ -1833,7 +1834,7 @@ main :: () { if (active_task == null) continue; select_task(db, db.active_idx); - // Start/Stop + // Start and stop. case TUI.Keys.Enter; #through; case TUI.Keys.Space; if (db != *database || selected_task == null) continue; @@ -2021,7 +2022,7 @@ main :: () { TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); exit(xx ifx error_saving then 1 else 0); } diff --git a/unused.jai b/unused.jai index 0425f8d..a8dddfc 100644 --- a/unused.jai +++ b/unused.jai @@ -38,6 +38,21 @@ print_database :: (db: Database) { } } +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + + +// Average cumulative calculation. +average: float64 = 0; +counter: float64 = 0; +t0 := current_time_monotonic(); +t1 := current_time_monotonic(); +set_cursor_position(2, 47); +sample := cast(float64)to_nanoseconds(t1-t0)/1000; +average = (sample + counter * average) / (counter + 1); +counter += 1; +print(">%us<", average); + + // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // // Implementation of tcsetattr, tcgetattr, and tcflush using only 'ioctl' which is provided by jai. -- cgit v1.2.3 From cb6c3caaa285bc8bd12c356d57bbfad86d70c0eb Mon Sep 17 00:00:00 2001 From: dam Date: Sun, 5 May 2024 00:31:46 +0100 Subject: WIP : Cleanup TUI module scope. --- modules/TUI/module.jai | 222 +++++++++++++++++++++++--------------------- modules/TUI/palette_24b.jai | 6 ++ ttt.jai | 2 +- 3 files changed, 124 insertions(+), 106 deletions(-) (limited to 'modules/TUI/palette_24b.jai') diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai index 772b30c..ab2567d 100644 --- a/modules/TUI/module.jai +++ b/modules/TUI/module.jai @@ -1,7 +1,9 @@ #module_parameters(COLOR_MODE_BITS := 24); + #scope_file + #if OS == { case .LINUX; #load "unix.jai"; @@ -26,8 +28,27 @@ KEY_SIZE :: #run type_info(Key).runtime_size; assert(input_buffer.count >= KEY_SIZE, "The input buffer size must be capable to hold an entire Key, which should be able to hold an UTF8 code (4 bytes) or a terminal escape code (6 bytes)."); } +active := false; +input_buffer : [1024] u8; +input_string : string; +input_override : Key; + +previous_logger : (message: string, data: *void, info: Log_Info); + +module_logger :: (message: string, data: *void, info: Log_Info) { + write_strings(Commands.SaveCursorPosition, Commands.MainScreenBuffer); + previous_logger(message, data, info); + write_strings(Commands.AlternateScreenBuffer, Commands.RestoreCursorPosition); +} + +assert_is_active :: inline () { + assert(active, "Please call setup_terminal() to start using this module."); +} + + #scope_export; + // Special Graphics Characters. Drawings :: struct #type_info_none { Blank :: "\x5F"; @@ -123,6 +144,12 @@ Commands :: struct #type_info_none { // TODO Check which procedures need the assert_is_active call. // TODO Review the error messages on the asserts. +//////////////////////////////////////////////////////////////////////////////// + + +#scope_file + + #if COLOR_MODE_BITS == 4 { #load "palette_4b.jai"; @@ -144,12 +171,6 @@ else #if COLOR_MODE_BITS == 8 { else { #load "palette_24b.jai"; - Color_24b :: struct { - r: u8; - g: u8; - b: u8; - } - set_colors :: inline (foreground: Color_24b, background: Color_24b) { print( #run sprint(Commands.SetGraphicsRendition, "38;2;%;%;%;48;2;%;%;%"), @@ -158,6 +179,19 @@ else { } } +set_font_style :: inline (bold: bool, underline: bool = false, strike_through: bool = false, negative: bool = false) { + print( + #run sprint(Commands.SetGraphicsRendition, "%;%;%;%"), + ifx bold then 1 else 22, + ifx underline then 4 else 24, + ifx strike_through then 9 else 29, + ifx negative then 7 else 27); +} + + +#scope_export + + Style :: struct { #if COLOR_MODE_BITS == 4 || COLOR_MODE_BITS == 8 { background: Palette; @@ -180,16 +214,9 @@ Style :: struct { negative: bool; } -set_font_style :: inline (bold: bool, underline: bool = false, strike_through: bool = false, negative: bool = false) { - print( - #run sprint(Commands.SetGraphicsRendition, "%;%;%;%"), - ifx bold then 1 else 22, - ifx underline then 4 else 24, - ifx strike_through then 9 else 29, - ifx negative then 7 else 27); -} +#add_context terminal_style: Style; -set_style :: (style: Style) { +set_style :: inline (style: Style) { set_font_style(style.bold, style.underline, style.strike_through, style.negative); set_colors(style.foreground, style.background); context.terminal_style = style; @@ -205,6 +232,9 @@ using_style :: (style: Style) #expand { `defer set_style(__style); } + +//////////////////////////////////////////////////////////////////////////////// + /* We wanted the Key type to represent either UTF-8 encoded characters and also keyboard keys. The UTF-8 only requires up to 4 bytes, but some keyboard keys return up to 6 bytes. @@ -216,34 +246,7 @@ using_style :: (style: Style) #expand { key/u64 |0|0|c|b|a| -> that in memory lays as (BE:|0|0|c|b|a|) and (LE:|a|b|c|0|0|) */ -Key :: u64; // Terminal key-codes have 1 to 6 bytes so we'll use 8 bytes. - -to_key :: (str: $T) -> Key #modify { return T == ([]u8) || T == string; } { - assert(str.count <= KEY_SIZE, "Invalid argument passed to to_key(): 'str.count' must be less-than or equal to %, but it was %.", KEY_SIZE, str.count); - - k: Key; - for 0..str.count-1 { - k |= ((cast(u64)str[it]) << (it*8)); - } - return k; -} - -to_string :: (key: Key) -> string { - str := alloc_string(KEY_SIZE); - str.count = 0; - while key != 0 { - str.count += 1; - str[str.count-1] = xx key & 0xFF; - key >>= 8; - } - return str; -} - -is_escape_code :: (key: Key) -> bool { - beginsWithEscape := ((key & 0xFF) ^ #char "#") == 0; - hasSomethingElse := (key & (~0xFF)) != 0; - return beginsWithEscape && hasSomethingElse; -} +Key :: u64; Keys :: struct #type_info_none { None : Key : #run to_key("#none"); @@ -282,29 +285,86 @@ Keys :: struct #type_info_none { F12 : Key : #run to_key("#f12"); } -#add_context terminal_style: Style; +to_key :: (str: $T) -> Key #modify { return T == ([]u8) || T == string; } { + assert(str.count <= KEY_SIZE, "Invalid argument passed to to_key(): 'str.count' must be less-than or equal to %, but it was %.", KEY_SIZE, str.count); + + k: Key; + for 0..str.count-1 { + k |= ((cast(u64)str[it]) << (it*8)); + } + return k; +} -active := false; +to_string :: (key: Key) -> string { + str := alloc_string(KEY_SIZE); + str.count = 0; + while key != 0 { + str.count += 1; + str[str.count-1] = xx key & 0xFF; + key >>= 8; + } + return str; +} -input_buffer : [1024] u8; -input_string : string; -input_override : Key; +is_escape_code :: (key: Key) -> bool { + beginsWithEscape := ((key & 0xFF) ^ #char "#") == 0; + hasSomethingElse := (key & (~0xFF)) != 0; + return beginsWithEscape && hasSomethingElse; +} -previous_logger : (message: string, data: *void, info: Log_Info); +//////////////////////////////////////////////////////////////////////////////// -module_logger :: (message: string, data: *void, info: Log_Info) { - write_strings(Commands.SaveCursorPosition, Commands.MainScreenBuffer); - previous_logger(message, data, info); - write_strings(Commands.AlternateScreenBuffer, Commands.RestoreCursorPosition); +is_active :: inline () -> bool { + return active; } -assert_is_active :: inline () { - assert(active, "Please call setup_terminal() to start using this module."); +setup_terminal :: () -> success := true #must { + if active == true return; + + input_string.data = input_buffer.data; + input_string.count = 0; + input_override = xx Keys.None; + + previous_logger = context.logger; + context.logger = module_logger; + + setup_key_map(); + + write_strings( + Commands.HideCursor, + Commands.SaveCursorPosition, + Commands.AlternateScreenBuffer, + Commands.EncodingUTF8, + Commands.CursorNormalMode, + Commands.KeypadNumMode + ); + + if !OS_prepare_terminal() then return false; + + active = true; + + return; } -//////////////////////////////////////////////////////////////////////////////// +reset_terminal :: () -> success := true #must { + if active == false return; + + active = false; -// #scope_export TODO Setup the scope_export and scope_file + clear_style(); + + if !OS_reset_terminal() then return false; + + context.logger = previous_logger; + + write_strings( + Commands.MainScreenBuffer, + Commands.RestoreCursorPosition, + Commands.ShowCursor + ); + + return; +} set_next_key :: inline (key: Key) { assert_is_active(); @@ -535,54 +595,6 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { return result, key; } -setup_terminal :: () -> success := true #must { - if active == true return; - - input_string.data = input_buffer.data; - input_string.count = 0; - input_override = xx Keys.None; - - previous_logger = context.logger; - context.logger = module_logger; - - setup_key_map(); - - write_strings( - Commands.HideCursor, - Commands.SaveCursorPosition, - Commands.AlternateScreenBuffer, - Commands.EncodingUTF8, - Commands.CursorNormalMode, - Commands.KeypadNumMode - ); - - if !OS_prepare_terminal() then return false; - - active = true; - - return; -} - -reset_terminal :: () -> success := true #must { - if active == false return; - - active = false; - - clear_style(); - - if !OS_reset_terminal() then return false; - - context.logger = previous_logger; - - write_strings( - Commands.MainScreenBuffer, - Commands.RestoreCursorPosition, - Commands.ShowCursor - ); - - return; -} - flush_input :: () { assert_is_active(); OS_flush_input(); diff --git a/modules/TUI/palette_24b.jai b/modules/TUI/palette_24b.jai index ea0191c..7545a0b 100644 --- a/modules/TUI/palette_24b.jai +++ b/modules/TUI/palette_24b.jai @@ -1,4 +1,10 @@ // https://www.ditig.com/publications/256-colors-cheat-sheet +Color_24b :: struct { + r: u8; + g: u8; + b: u8; +} + Palette :: struct #type_info_none { BLACK :: Color_24b.{0x00, 0x00, 0x00}; MAROON :: Color_24b.{0x80, 0x00, 0x00}; diff --git a/ttt.jai b/ttt.jai index d471661..f3f1905 100644 --- a/ttt.jai +++ b/ttt.jai @@ -124,7 +124,7 @@ error_time_limit := Apollo_Time.{0, 0}; print_error :: (format :string, args : .. Any) { - if TUI.active == false { + if TUI.is_active() == false { print(format, ..args, to_standard_error = true); print("\n"); return; -- cgit v1.2.3