From 32621b9b18a285dfd5e9e225073809bc49b14f39 Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 28 Sep 2023 00:51:40 +0100 Subject: Working read_input prototype. --- TUI/module.jai | 170 +++++++++++++++++---------------------------------------- 1 file changed, 49 insertions(+), 121 deletions(-) (limited to 'TUI/module.jai') diff --git a/TUI/module.jai b/TUI/module.jai index 3298e59..ec319d7 100644 --- a/TUI/module.jai +++ b/TUI/module.jai @@ -9,13 +9,6 @@ #import "Basic"; #import "String"; -// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences -// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#designate-character-set -// https://github.com/MicrosoftDocs/Console-Docs/blob/main/docs/console-virtual-terminal-sequences.md - -isTUIActive := false; // TODO Rename this variable. - - Drawings :: struct { CornerBR :: "\x6A"; CornerTR :: "\x6B"; @@ -86,16 +79,20 @@ Commands :: struct { } -// TODO Maybe rename to "setup()" + +initialized := false; + + start :: () { + if initialized return; write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.EnterAlternateBuffer, Commands.SetUTF8); OS_prepare_terminal(); - isTUIActive = true; + initialized = true; } -// TODO Maybe rename to "reset()" stop :: () { - isTUIActive = false; + if initialized == false return; + initialized = false; OS_reset_terminal(); write_strings(Commands.EnterMainBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor); } @@ -146,19 +143,17 @@ draw_box :: (x: int, y: int, width: int, height: int) { // TODO Maybe rename to "clear()" clear_terminal :: inline () { + assert(initialized, "TUI is not ready."); write_string(Commands.ClearScreen); } // TODO Maybe rename to "get_size()" get_terminal_size :: () -> rows: int, columns: int { + assert(initialized, "TUI is not ready."); rows, columns := OS_get_terminal_size(); return rows, columns; } -// read_input: () -> string { - // return OS_read_input(); -// } - set_cursor_position :: (row: int, column: int) { auto_release_temp(); tmp_string := tprint(Commands.SetCursorPosition, row, column); @@ -166,21 +161,24 @@ set_cursor_position :: (row: int, column: int) { } get_cursor_position :: () -> row: int, column: int { - assert(isTUIActive, "TUI is not active."); // TODO + assert(initialized, "TUI is not ready."); // TODO Should I use this inside each and every procedure? + + auto_release_temp(); + + write_string(Commands.QueryCursorPosition); - // TODO Hide input echo. + input := talloc_string(64); + input.count = OS_read_input(input.data, input.count); // TODO Does not check for read errors. - write_string(Commands.QueryCursorPosition); // Returned - input := read_input(.MACHINE); - // Result: \e[21;1R + // Expected message format: \e[;R + // where is the number of rows and of columns. assert( - input.data[0] == #char "\e" && - input.data[1] == #char "[" && - input.data[input.count-1] == #char "R", + input[0] == #char "\e" && + input[1] == #char "[" && + input[input.count-1] == #char "R", "Query cursor position returned invalid response."); - input.data += 2; - print(">%<\n", input); + advance(*input, 2); parts := split(input, ";"); row := parse_int(*parts[0]); column := parse_int(*parts[1]); @@ -192,12 +190,33 @@ Input_Mode :: enum u8 { MACHINE; // Hides cursor, hides input, and reads right away once the first input is available. } -read_input :: (mode: Input_Mode = .HUMAN) -> string { - if mode == .HUMAN write_string(Commands.ShowCursor); - defer if mode == .HUMAN write_string(Commands.HideCursor); - return OS_read_input(mode); -} +read_input :: (allocator: Allocator = temp, $mode: Input_Mode = .HUMAN) -> string { + #if mode == .HUMAN { + write_string(Commands.ShowCursor); + defer write_string(Commands.HideCursor); + + OS_set_input_mode(.HUMAN); + defer OS_set_input_mode(.MACHINE); + } + assert(allocator.proc != null, "Argument 'allocator.proc' has invalid null value."); + + #assert(mode != .MACHINE); // TODO Keep an eye if I try to use read_input for machine read. Eventually, remove mode from the procedure arguments. + + builder: String_Builder(); + builder.allocator = allocator; + init_string_builder(*builder); + + while(1) { + buffer := get_current_buffer(*builder); + buffer_data := get_buffer_data(buffer); + buffer.count = OS_read_input(buffer_data, buffer.allocated); // TODO Does not check for read errors. + if buffer.count == 0 || buffer_data[buffer.count-1] == #char "\n" break; + assert(buffer.count == buffer.allocated); // TODO If newline wasn't detected, it's because the buffer got full. + expand(*builder); + } + return builder_to_string(*builder, allocator); +} #if OS == .WINDOWS { @@ -205,95 +224,4 @@ read_input :: (mode: Input_Mode = .HUMAN) -> string { } else #if OS == .LINUX || OS == .MACOS { // Prototyping zone... keep clear! - OS_read_input :: (mode: Input_Mode) -> string { - - term : My_Termios; - tcgetattr(STDIN_FILENO, *term); - - // Input modes. - Input_Modes :: enum_flags u32 { - IGNBRK; // Ignore break condition. - BRKINT; // Signal interrupt on break. - IGNPAR; // Ignore characters with parity errors. - PARMRK; // Mark parity and framing errors. - INPCK; // Enable input parity check. - ISTRIP; // Strip 8th bit off characters. - INLCR; // Map NL to CR on input. - IGNCR; // Ignore CR. - ICRNL; // Map CR to NL on input. - IXON; // Enable start/stop output control. - IXOFF; // Enable start/stop input control. - IXANY; // Any character will restart after stop. - __NOT_USED__; - IMAXBEL; // Ring bell when input queue is full. - IUCLC; // Translate upper case input to lower case. - } - - // Local modes. - Local_Modes :: enum_flags u32 { - ECHOKE; // Visual erase for KILL. - ECHOE; // Visual erase for ERASE. - ECHOK; // Echo NL after KILL. - ECHO; // Enable echo. - ECHONL; // Echo NL even if ECHO is off. - ECHOPRT; // Hardcopy visual erase. - ECHOCTL; // Echo control characters as ^X. - ISIG; // Enable signals. - ICANON; // Do erase and kill processing. - ALTWERASE; // Alternate WERASE algorithm. - IEXTEN; // Enable DISCARD and LNEXT. - EXTPROC; // External processing. - TOSTOP; // Send SIGTTOU for background output. - FLUSHO; // Output being flushed (state). - XCASE; // Canonical upper/lower case. - NOKERNINFO; // Disable VSTATUS. - PENDIN; // Retype pending input (state). - NOFLSH; // Disable flush after interrupt. - - } - - backup: My_Termios; - tcgetattr(STDIN_FILENO, *backup); - - if mode == { - case .HUMAN; - term.c_iflag |= xx cast(Input_Modes)(.IXOFF | .ICRNL); - term.c_lflag |= xx cast(Local_Modes)(.NOKERNINFO | .ECHO | .ECHOE | .ECHOKE); - - case .MACHINE; - iflags: Input_Modes = (.IGNBRK | .BRKINT | .PARMRK | .ISTRIP | .INLCR | .IGNCR | .ICRNL | .IXON); - term.c_iflag &= xx ~(iflags); - lflags: Local_Modes = (.ECHO | .ECHONL | .ICANON | .IEXTEN); - term.c_lflag &= xx ~lflags; - } - - // term.c_iflag &= 0xFFFFFA14;// ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - // term.c_oflag &= 0xFFFFFFFE;// ~OPOST; - // term.c_lflag &= 0xFFFF7FB4;// ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - // term.c_cflag &= 0xFFFFFECF;// ~(CSIZE | PARENB); - // term.c_cflag |= 0x00000030; - - tcsetattr(STDIN_FILENO, 0, *term); - - result: string = ---; - buffer: [8192] u8; - write_string(Commands.ShowCursor); - bytes_read := read(STDIN_FILENO, buffer.data, buffer.count-1); - write_string(Commands.HideCursor); - - // TODO WIP WIP WIP - result.data = alloc(bytes_read, temp); - memcpy(result.data, buffer.data, bytes_read); - result.count = bytes_read; - // result = "jat"; - // result.count = bytes_read; - // result.data = buffer.data; - // result = copy_temporary_string(buffer); - // result = tprint("%", xx buffer.data); - // result = to_string(buffer.data, bytes_read); // TODO WIP WIP WIP This is still using the stack allocated buffer and WILL FAIL! - - tcsetattr(STDIN_FILENO, 0, *backup); - - return result; - }; } -- cgit v1.2.3