aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordam <devnull@localhost>2024-01-07 01:28:21 +0000
committerdam <devnull@localhost>2024-01-07 01:28:21 +0000
commite5d8eaa14407608a15a639da14bbea99dd8ef61a (patch)
treedc41da8f228531130a00e0c62e46fb4c0660a235
parent381e62b9128123d12294df4aeae5430cd62349e9 (diff)
downloadtask-time-tracker-e5d8eaa14407608a15a639da14bbea99dd8ef61a.tar.zst
task-time-tracker-e5d8eaa14407608a15a639da14bbea99dd8ef61a.zip
Fixed Windows raw IO modes.
-rw-r--r--TUI/module.jai62
-rw-r--r--TUI/windows.jai140
-rw-r--r--ttt.jai4
3 files changed, 122 insertions, 84 deletions
diff --git a/TUI/module.jai b/TUI/module.jai
index df88f0c..21f1dd7 100644
--- a/TUI/module.jai
+++ b/TUI/module.jai
@@ -667,7 +667,13 @@ start :: () {
input_string.count = 0;
input_override = xx Keys.None;
- write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.AlternateScreenBuffer, Commands.SetUTF8);
+ write_strings(
+ Commands.HideCursor,
+ Commands.SaveCursorPosition,
+ Commands.AlternateScreenBuffer,
+ Commands.SetUTF8,
+ Commands.CursorNormalMode,
+ Commands.KeypadNumMode);
OS_prepare_terminal();
initialized = true;
@@ -744,39 +750,37 @@ clear_terminal :: inline () {
// TODO Maybe rename to "get_size()"
get_terminal_size :: () -> rows: int, columns: int {
assert_is_initialized();
+
+ auto_release_temp();
+
rows, columns: int = ---;
- #if OS == .WINDOWS {
- rows, columns = OS_get_terminal_size();
- }
- else {
- auto_release_temp();
- flush_input();
- write_string(Commands.QueryWindowSizeInChars);
- input := get_string(64,, temporary_allocator);
-
- // Expected response format: \e[8;<r>;<c>t
- // where <r> is the number of rows and <c> of columns.
- FORMAT :: "\e[8;<r>;<c>t";
-
- // Discard head noise.
- while input.count >= 3 && (input[0] != FORMAT[0] || input[1] != FORMAT[1] || input[2] != FORMAT[2]) {
- advance(*input);
- }
+ flush_input();
+ write_string(Commands.QueryWindowSizeInChars);
+ input := get_string(64,, temporary_allocator);
- // Discard tail noise.
- 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.");
+ // Expected response format: \e[8;<r>;<c>t
+ // where <r> is the number of rows and <c> of columns.
+ FORMAT :: "\e[8;<r>;<c>t";
+
+ // Discard head noise.
+ while input.count >= 3 && (input[0] != FORMAT[0] || input[1] != FORMAT[1] || input[2] != FORMAT[2]) {
+ advance(*input);
+ }
- parts := split(input, ";",, temporary_allocator);
- rows = parse_int(*parts[1]);
- columns = parse_int(*parts[2]);
+ // Discard tail noise.
+ 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.");
+
+ parts := split(input, ";",, temporary_allocator);
+ rows = parse_int(*parts[1]);
+ columns = parse_int(*parts[2]);
+
return rows, columns;
}
diff --git a/TUI/windows.jai b/TUI/windows.jai
index ed8c996..f704262 100644
--- a/TUI/windows.jai
+++ b/TUI/windows.jai
@@ -4,33 +4,42 @@
#import "System";
#import "Windows";
-// 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
-
+ // https://learn.microsoft.com/windows/win32/winprog/windows-data-types
+ LPVOID :: *void;
+ LPDWORD :: *s32;
+ BOOL :: bool;
+ SHORT :: s16;
+ WORD :: u16;
+ DWORD :: s32;
+// https://learn.microsoft.com/windows/console/console-virtual-terminal-sequences
+// https://learn.microsoft.com/windows/console/console-virtual-terminal-sequences#designate-character-set
+// https://github.com/MicrosoftDocs/Console-Docs/blob/main/docs/console-virtual-terminal-sequences.md
- kernel32 :: #system_library "kernel32";
+ kernel32 :: #system_library "kernel32";
- // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
+ // https://learn.microsoft.com/windows/console/getconsolescreenbufferinfo
GetConsoleScreenBufferInfo :: (hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) -> 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/windows/console/readconsole
+ ReadConsoleA :: (hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: LPVOID, pInputControl := LPVOID) -> bool #foreign kernel32;
- // https://learn.microsoft.com/en-us/windows/console/getconsolemode
- GetConsoleMode :: (hConsoleHandle: HANDLE, lpMode: *u32) -> bool #foreign kernel32;
+ // https://learn.microsoft.com/windows/console/getconsolemode
+ GetConsoleMode :: (hConsoleHandle: HANDLE, lpMode: *DWORD) -> BOOL #foreign kernel32;
- // https://learn.microsoft.com/en-us/windows/console/setconsolemode
- SetConsoleMode :: (hConsoleHandle: HANDLE, dwMode: u32) -> bool #foreign kernel32;
+ // https://learn.microsoft.com/windows/console/setconsolemode
+ SetConsoleMode :: (hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL #foreign kernel32;
- // https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
+ // https://learn.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
GetLastError :: () -> s32 #foreign kernel32;
+ // https://learn.microsoft.com/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex
+ WaitForSingleObjectEx :: (hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL) -> s32 #foreign kernel32;
// https://learn.microsoft.com/en-us/windows/console/setconsolemode
+ // https://learn.microsoft.com/en-us/windows/console/high-level-console-modes
Console_Input_Mode :: enum_flags u32 {
- _UNUSED_0001_;
+ ENABLE_PROCESSED_INPUT; // If enable, control keys (Ctrl+C, Backspace, ...) are processed by the system.
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_;
@@ -45,6 +54,7 @@
}
// https://learn.microsoft.com/en-us/windows/console/setconsolemode
+ // https://learn.microsoft.com/en-us/windows/console/high-level-console-modes
Console_Output_Mode :: enum_flags u32 {
ENABLE_PROCESSED_OUTPUT; //
ENABLE_WRAP_AT_EOL_OUTPUT; //
@@ -56,12 +66,6 @@
_UNUSED_0080_;
}
-
- // https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
- SHORT :: s16;
- WORD :: u16;
- DWORD :: s32;
-
COORD :: struct {
X : SHORT;
Y : SHORT;
@@ -85,36 +89,69 @@
stdin: HANDLE;
initial_stdin_mode: u32;
- default_stdin_mode: Console_Input_Mode;
- blocking_stdin_mode: Console_Input_Mode;
- unblocking_stdin_mode: Console_Input_Mode;
+ raw_stdin_mode: Console_Input_Mode;
stdout: HANDLE;
initial_stdout_mode: u32;
- default_stdout_mode: Console_Output_Mode;
+ raw_stdout_mode: Console_Output_Mode;
-#scope_export
+////////////////////////////////////////////////////////////////////////////////
+// Resize detection
-OS_prepare_terminal :: () {
- print("TODO TUI\n", to_standard_error = true);
+resize_handler :: (signal_code : s32) #c_call {
+ /* TODO
+ Try to implement using:
+ https://learn.microsoft.com/en-us/windows/console/reading-input-buffer-events
+ https://learn.microsoft.com/en-us/windows/console/readconsoleinput
+ https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
+ */
+ new_context : Context;
+ push_context new_context {
+ if signal_code != SIGWINCH then return;
+ atomic_swap(*was_resized, true);
+ }
+}
+prepare_resize_handler :: () {
+ /* TODO
+ sa : sigaction_t;
+ sa.sa_handler = resize_handler;
+ sigemptyset(*(sa.sa_mask));
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, *sa, null);
+ */
+}
+restore_resize_handler :: () {
+ /* TODO
+ sa : sigaction_t;
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGWINCH, null, *sa);
+ */
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#scope_export
+
+OS_prepare_terminal :: () {
+
// stdin
stdin = GetStdHandle(STD_INPUT_HANDLE);
if stdin == INVALID_HANDLE_VALUE {
print("Invalid input handler.", to_standard_error = true);
return;
}
- if GetConsoleMode(stdin, *initial_stdin_mode) == false {
+ if xx GetConsoleMode(stdin, *initial_stdin_mode) == false {
print("Failed to get input mode.", to_standard_error = true);
return;
}
- default_stdin_mode = (cast(Console_Input_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;
+ raw_stdin_mode = (cast(Console_Input_Mode) initial_stdin_mode);
+ raw_stdin_mode |= (.ENABLE_VIRTUAL_TERMINAL_INPUT);
+ raw_stdin_mode &= ~(.ENABLE_LINE_INPUT | .ENABLE_PROCESSED_INPUT | .ENABLE_ECHO_INPUT);
- if SetConsoleMode(stdin, xx default_stdin_mode) == false {
+ if SetConsoleMode(stdin, xx raw_stdin_mode) == false {
print("Failed to set input mode: %.", GetLastError(), to_standard_error = true);
return;
}
@@ -125,24 +162,25 @@ OS_prepare_terminal :: () {
print("Invalid output handler.", to_standard_error = true);
return;
}
- if GetConsoleMode(stdout, *initial_stdout_mode) == false {
+ if xx GetConsoleMode(stdout, *initial_stdout_mode) == false {
print("Failed to get output mode.", to_standard_error = true);
return;
}
- default_stdout_mode = (cast(Console_Output_Mode) initial_stdout_mode) | .ENABLE_PROCESSED_OUTPUT| .ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ raw_stdout_mode = (cast(Console_Output_Mode) initial_stdout_mode);
+ raw_stdout_mode |= (.ENABLE_VIRTUAL_TERMINAL_PROCESSING | .ENABLE_PROCESSED_OUTPUT | .ENABLE_WRAP_AT_EOL_OUTPUT);
- if SetConsoleMode(stdout, xx default_stdout_mode) == false {
+ if SetConsoleMode(stdout, xx raw_stdout_mode) == false {
print("Failed to set output mode: %.", GetLastError(), to_standard_error = true);
return;
}
}
OS_reset_terminal :: () {
- if SetConsoleMode(stdin, initial_stdin_mode) == false {
+ if xx 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 {
+ if xx SetConsoleMode(stdout, initial_stdout_mode) == false {
print("Failed to reset output mode: %.", GetLastError(), to_standard_error = true);
return;
}
@@ -157,16 +195,6 @@ OS_flush_input :: inline () {
*/
}
-OS_get_terminal_size :: () -> rows: int, columns: int {
-
- ScreenBufferInfo: CONSOLE_SCREEN_BUFFER_INFO;
- GetConsoleScreenBufferInfo(stdout, *ScreenBufferInfo);
- columns := ScreenBufferInfo.srWindow.Right - ScreenBufferInfo.srWindow.Left + 1;
- rows := ScreenBufferInfo.srWindow.Bottom - ScreenBufferInfo.srWindow.Top + 1;
-
- return rows, columns;
-}
-
OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, error: bool = false, error_message: string = "" {
assert(bytes_to_read <= 0x7fff_ffff, "The Windows API only allows to read up to s32 bytes from the standard input.");
bytes_read: s32;
@@ -181,14 +209,20 @@ OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, error: bo
OS_wait_for_input :: (timeout_milliseconds: s32) -> is_input_available: bool {
/* TODO
Try to implement using:
- https://learn.microsoft.com/en-us/windows/console/reading-input-buffer-events
- https://learn.microsoft.com/en-us/windows/console/readconsoleinput
+ https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex
*/
- fds := pollfd.[ .{ fd = STDIN_FILENO, events = POLLIN, revents = 0 } ];
- nfds := fds.count;
- poll_return := poll(fds.data, xx nfds, xx timeout_milliseconds); // TODO Wait for input using poll syscall. This breaks and throws '-1 | 4 | Interrupted system call' when we resize the window while polling.
+
+ poll_return := WaitForSingleObjectEx(stdin, timeout_milliseconds, true);
error_code, error_message := get_error_value_and_string(); // FIXME Not used.
- return ifx poll_return > 0 then true else false;
+
+ // Possible values for poll_return TODO NOT BEING USED
+ WAIT_ABANDONED :: 0x00000080;
+ WAIT_IO_COMPLETION :: 0x000000C0;
+ WAIT_OBJECT_0 :: 0x00000000;
+ WAIT_TIMEOUT :: 0x00000102;
+ WAIT_FAILED :: 0xFFFFFFFF;
+
+ return ifx poll_return == 0 then true else false;
}
OS_was_terminal_resized :: () -> bool {
diff --git a/ttt.jai b/ttt.jai
index 654cde4..4540699 100644
--- a/ttt.jai
+++ b/ttt.jai
@@ -1239,7 +1239,7 @@ main :: () {
// TODO Test input
- if 0 {
+ if 1 {
print("TEST : set and get cursor position --\n", to_standard_error = true);
TUI.start();
ROW :: 3;
@@ -1251,7 +1251,7 @@ main :: () {
print("> success\n", to_standard_error = true);
}
- if 0 {
+ if 1 {
print("TEST : test key input --\n", to_standard_error = true);
auto_release_temp();
TUI.start();