aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TUI/module.jai102
-rw-r--r--TUI/windows.jai87
-rw-r--r--ttt.jai12
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;
+};
diff --git a/ttt.jai b/ttt.jai
index 3910cd0..9154a60 100644
--- a/ttt.jai
+++ b/ttt.jai
@@ -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;