diff options
| author | dam <dam@gudinoff> | 2023-09-23 10:58:35 +0100 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2023-09-23 10:58:35 +0100 |
| commit | 8b640d9e236641eefea93338e3a7092678e3c5e5 (patch) | |
| tree | 94a787527055ca5ffb58257852c50f7c23dbbed3 | |
| parent | 1b76de83f52f7199a51e8e5d563b4ddfa8e2f289 (diff) | |
| download | task-time-tracker-8b640d9e236641eefea93338e3a7092678e3c5e5.tar.zst task-time-tracker-8b640d9e236641eefea93338e3a7092678e3c5e5.zip | |
Implemented basic read_input for windows.
| -rw-r--r-- | TUI/module.jai | 102 | ||||
| -rw-r--r-- | TUI/windows.jai | 87 | ||||
| -rw-r--r-- | ttt.jai | 12 |
3 files changed, 118 insertions, 83 deletions
diff --git a/TUI/module.jai b/TUI/module.jai index fae022f..3298e59 100644 --- a/TUI/module.jai +++ b/TUI/module.jai @@ -88,16 +88,16 @@ Commands :: struct { // TODO Maybe rename to "setup()" start :: () { - OS_prepare_terminal(); write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.EnterAlternateBuffer, Commands.SetUTF8); + OS_prepare_terminal(); isTUIActive = true; } // TODO Maybe rename to "reset()" stop :: () { isTUIActive = false; - write_strings(Commands.EnterMainBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor); OS_reset_terminal(); + write_strings(Commands.EnterMainBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor); } draw_box :: (x: int, y: int, width: int, height: int) { @@ -167,8 +167,11 @@ set_cursor_position :: (row: int, column: int) { get_cursor_position :: () -> row: int, column: int { assert(isTUIActive, "TUI is not active."); // TODO + + // TODO Hide input echo. + write_string(Commands.QueryCursorPosition); // Returned - input := read_input(false); + input := read_input(.MACHINE); // Result: \e[21;1R assert( input.data[0] == #char "\e" && @@ -184,32 +187,25 @@ get_cursor_position :: () -> row: int, column: int { return row, column; } -read_input :: (blocking: bool = true) -> string { - return OS_read_input(blocking); +Input_Mode :: enum u8 { + HUMAN; // Shows cursor, echoes input, and expects an enter at the end of the line. + MACHINE; // Hides cursor, hides input, and reads right away once the first input is available. } -// read_input: () -> string; - -#if OS == .WINDOWS { +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); +} - OS_read_input :: (blocking: bool) -> string { - result: string = ---; - MAX_BYTES_TO_READ :: 1024; - temp : [MAX_BYTES_TO_READ] u8; - bytes_read : s32; - if ReadConsoleA(stdin, temp.data, xx temp.count, *bytes_read) { - result.data = alloc(bytes_read); - result.count = bytes_read; - memcpy(result.data, temp.data, bytes_read); - } - return result; - }; +#if OS == .WINDOWS { + // Prototyping zone... keep clear! } else #if OS == .LINUX || OS == .MACOS { - - OS_read_input :: (blocking: bool) -> string { + // Prototyping zone... keep clear! + OS_read_input :: (mode: Input_Mode) -> string { term : My_Termios; tcgetattr(STDIN_FILENO, *term); @@ -228,7 +224,7 @@ else #if OS == .LINUX || OS == .MACOS { IXON; // Enable start/stop output control. IXOFF; // Enable start/stop input control. IXANY; // Any character will restart after stop. - __NOT_USED__; + __NOT_USED__; IMAXBEL; // Ring bell when input queue is full. IUCLC; // Translate upper case input to lower case. } @@ -253,58 +249,24 @@ else #if OS == .LINUX || OS == .MACOS { NOKERNINFO; // Disable VSTATUS. PENDIN; // Retype pending input (state). NOFLSH; // Disable flush after interrupt. - + } backup: My_Termios; tcgetattr(STDIN_FILENO, *backup); - if blocking { - // write_number(term.c_iflag, 2); - // write_string(":"); - // write_number(term.c_lflag, 2); - // write_string(":"); - // write_number(__term.c_iflag, 2); - // write_string(":"); - // write_number(__term.c_lflag, 2); - - // c_iflag - // 10 - // 8 - // 0101 0101 0000 0000 : __term - // 1111 1010 0001 0100 : &MASK - // 0101 0000 0000 0000 : - - // 0101 0101 0000 0000 : NOW - - - // c_lflag - // 15 - // 3 - // 1 - // 0 - // 1000 1010 0011 1011 : __term - // 0111 1111 1011 0100 : &MASK - // 0000 1010 0011 0000 : - - // 1000 1010 0011 1011 : NOW - - - // print("%\n", term.c_iflag, to_standard_error = true); - // print("%\n", term.c_lflag, to_standard_error = true); - // print("%\n", term, to_standard_error = true); - - // term = __term; - term.c_iflag |= xx cast(Input_Modes)(.IXOFF | .ICRNL); - term.c_lflag |= xx cast(Local_Modes)(.NOKERNINFO | .ECHO | .ECHOE | .ECHOKE); - } - else { - 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; + 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); @@ -331,7 +293,7 @@ else #if OS == .LINUX || OS == .MACOS { // 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; }; } diff --git a/TUI/windows.jai b/TUI/windows.jai index ef0cfa8..24dbece 100644 --- a/TUI/windows.jai +++ b/TUI/windows.jai @@ -3,11 +3,19 @@ kernel32 :: #system_library "kernel32"; + // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo GetConsoleScreenBufferInfo :: (hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) -> bool #foreign kernel32; - ReadConsoleA :: (hConsoleHandle: HANDLE, buff : *u8, chars_to_read : s32, chars_read : *s32, lpInputControl := *void ) -> bool #foreign kernel32; - // ReadConsole :: (hConsoleInput: HANDLE, lpBuffer: *u8, nNumberOfCharsToRead: s32, lpNumberOfCharsRead: *s32, pInputControl := *void) -> bool #foreign kernel32; + + // https://learn.microsoft.com/en-us/windows/console/readconsole + ReadConsoleA :: (hConsoleInput: HANDLE, lpBuffer: *u8, nNumberOfCharsToRead: s32, lpNumberOfCharsRead: *s32, pInputControl := *void) -> bool #foreign kernel32; + + // https://learn.microsoft.com/en-us/windows/console/getconsolemode GetConsoleMode :: (hConsoleHandle: HANDLE, lpMode: *u32) -> bool #foreign kernel32; + + // https://learn.microsoft.com/en-us/windows/console/setconsolemode SetConsoleMode :: (hConsoleHandle: HANDLE, dwMode: u32) -> bool #foreign kernel32; + + // https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror GetLastError :: () -> s32 #foreign kernel32; ENABLE_VIRTUAL_TERMINAL_INPUT :: 0x0200; @@ -18,6 +26,28 @@ DISABLE_NEWLINE_AUTO_RETURN :: 0x0008; ENABLE_LVB_GRID_WORLDWIDE :: 0x0010; + + // https://learn.microsoft.com/en-us/windows/console/setconsolemode + Console_Mode :: enum_flags u32 { + _UNUSED_0001_; + ENABLE_LINE_INPUT; // If enable, ReadFile or ReadConsole function return on CR; otherwise they return when one or more characters are available. + ENABLE_ECHO_INPUT; // Echoes input on screen. Only available if ENABLE_LINE_INPUT is set. + _UNUSED_0008_; + ENABLE_MOUSE_INPUT; // + ENABLE_INSERT_MODE; // If enabled, text entered will be inserted at the current cursor location and all text following that location will not be overwritten. When disabled, all following text will be overwritten. + _UNSED_0040_; + _UNSED_0080_; + _UNSED_0100_; + ENABLE_VIRTUAL_TERMINAL_INPUT; // + _UNSED_0400_; + _UNSED_0800_; + _UNSED_1000_; + _UNSED_2000_; + _UNSED_4000_; + _UNSED_8000_; + } + + // https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types SHORT :: s16; WORD :: u16; DWORD :: s32; @@ -46,8 +76,13 @@ stdin: HANDLE; initial_stdin_mode: u32; + default_stdin_mode: Console_Mode; + blocking_stdin_mode: Console_Mode; + unblocking_stdin_mode: Console_Mode; + stdout: HANDLE; initial_stdout_mode: u32; + default_stdout_mode: Console_Mode; OS_prepare_terminal :: () { @@ -64,14 +99,17 @@ OS_prepare_terminal :: () { print("Failed to get input mode.", to_standard_error = true); return; } - if SetConsoleMode(stdin, initial_stdin_mode | ENABLE_VIRTUAL_TERMINAL_INPUT) == false { + default_stdin_mode = (cast(Console_Mode) initial_stdin_mode) | .ENABLE_VIRTUAL_TERMINAL_INPUT; + blocking_stdin_mode = default_stdin_mode | .ENABLE_LINE_INPUT | .ENABLE_ECHO_INPUT; + unblocking_stdin_mode = default_stdin_mode & ~.ENABLE_LINE_INPUT & ~.ENABLE_ECHO_INPUT; + + if SetConsoleMode(stdin, xx default_stdin_mode) == false { print("Failed to set input mode: %.", GetLastError(), to_standard_error = true); return; } // stdout stdout = GetStdHandle(STD_OUTPUT_HANDLE); - outMode: u32 = 0; if stdout == INVALID_HANDLE_VALUE { print("Invalid output handler.", to_standard_error = true); return; @@ -80,20 +118,19 @@ OS_prepare_terminal :: () { print("Failed to get output mode.", to_standard_error = true); return; } - if SetConsoleMode(stdout, initial_stdout_mode | ENABLE_PROCESSED_OUTPUT| ENABLE_VIRTUAL_TERMINAL_PROCESSING) == false { + default_stdout_mode = (cast(Console_Mode) initial_stdout_mode) | ENABLE_PROCESSED_OUTPUT| ENABLE_VIRTUAL_TERMINAL_PROCESSING; + + if SetConsoleMode(stdout, xx default_stdout_mode) == false { print("Failed to set output mode: %.", GetLastError(), to_standard_error = true); return; } } OS_reset_terminal :: () { - print("TODO TUI\n", to_standard_error = true); - if SetConsoleMode(stdin, initial_stdin_mode) == false { print("Failed to reset input mode: %.", GetLastError(), to_standard_error = true); return; } - if SetConsoleMode(stdout, initial_stdout_mode) == false { print("Failed to reset output mode: %.", GetLastError(), to_standard_error = true); return; @@ -109,3 +146,37 @@ OS_get_terminal_size :: () -> rows: int, columns: int { return rows, columns; } + +OS_read_input :: (mode: Input_Mode) -> string { + result: string = ---; + + if mode == { + case .HUMAN; + assert(SetConsoleMode(stdin, xx blocking_stdin_mode), "Failed to set input mode to blocking mode."); // @speed TODO Could check if was already set before applying. + + case .MACHINE; + // assert(SetConsoleMode(stdin, xx unblocking_stdin_mode), "Failed to set input mode to unblocking mode."); // @speed TODO Could check if was already set before applying. + if SetConsoleMode(stdin, xx unblocking_stdin_mode) == false { + print("Failed to set input mode: %.", GetLastError(), to_standard_error = true); + exit(0); + } + } + + MAX_BYTES_TO_READ :: 10; + temp : [MAX_BYTES_TO_READ] u8; + bytes_read : s32; + + if ReadConsoleA(stdin, temp.data, xx temp.count, *bytes_read) { + + // TODO If the number of bytes_read is equal to the buffer size... we may have some more data to read?! + // ---> To fix this, we should check the last read byte and see if it's a newline! + + result.data = alloc(bytes_read); + result.count = bytes_read; + memcpy(result.data, temp.data, bytes_read); + } + + assert(SetConsoleMode(stdin, xx default_stdin_mode), "Failed to set default input mode."); // @speed TODO Maybe compare current mode with default one before applying?! + + return result; +}; @@ -1186,15 +1186,17 @@ main :: () { TUI.clear_terminal(); rows, columns := TUI.get_terminal_size(); TUI.draw_box(1, 1, columns, rows); - TUI.set_cursor_position(3, 3); - input := TUI.read_input(true); - TUI.set_cursor_position(3, 3); + TUI.set_cursor_position(4, 4); c_row, c_column := TUI.get_cursor_position(); + TUI.set_cursor_position(3, 3); + input := TUI.read_input(); + // sleep_milliseconds(1500); TUI.stop(); - print("\ninput = %\n", input); - print("cursor r:c = %:%\n", c_row, c_column); + print("- - -\n"); print("window r:c = %:%\n", rows, columns); + print("cursor r:c = %:%\n", c_row, c_column); + print("input = %\n", input); return; |
