aboutsummaryrefslogtreecommitdiff
path: root/TUI/module.jai
diff options
context:
space:
mode:
Diffstat (limited to 'TUI/module.jai')
-rw-r--r--TUI/module.jai280
1 files changed, 128 insertions, 152 deletions
diff --git a/TUI/module.jai b/TUI/module.jai
index 6854d59..6ca6f59 100644
--- a/TUI/module.jai
+++ b/TUI/module.jai
@@ -84,13 +84,8 @@ Commands :: struct {
initialized := false;
-
-input_buffer_mutex: Mutex;
-input_buffer: string;
-input_counter: s64;
-read_buffer: [4096] u8;
-
-input_process_thread: Thread;
+input_buffer : [64] u8;
+input_string : string;
Key :: u8; // TODO To be improved.
Keys :: enum u8 {
@@ -98,152 +93,94 @@ Keys :: enum u8 {
Resize :: 1; //410; // TODO Why?!
}
-key_semaphore: Semaphore;
-key_mutex: Mutex;
-key_input := Keys.None;
-key_resize := Keys.None;
-key_buffer: [64] Keys;
-
-// Notes
-// So, the semaphore usage should indicate the items on the key_queue.
-// If we don't want to use a queue, maybe we can use two Key items, one for user input, and another, with higher priority, for Resize.
dam :: (msg: string) {
print(msg, to_standard_error = true);
}
-
-process_input :: (thread: *Thread) -> s64 {
- buffer: [64] u8;
- input: string;
- // char: u8;
- dam(">START signal_input\n");
- defer dam(">STOP signal_input\n");
- while(true) {
- if initialized == false return 0;
- if key_input != Keys.None {
- dam("waiting..");
- sleep_milliseconds(100); // We could increase this value as a push-back mechanism if we're just hitting it repeateadly.
- dam("!\n\r");
- continue;
- }
- if input.count > 0 {
- lock(*key_mutex);
- key_input = xx input[0];
- unlock(*key_mutex);
- signal(*key_semaphore);
-
- advance(*input);
- continue;
- }
-
- // dam("reading..");
- bytes_read := OS_read_input(buffer.data, buffer.count);
- input.data = buffer.data;
- input.count = bytes_read;
- }
- return 0;
+push_key :: (key: Key) {
+ // TODO
}
-process_resize :: (signal_code : s32) #c_call {
- new_context : Context;
- push_context new_context {
- print("SIGNAL:%", signal_code);
- if signal_code != SIGWINCH return;
- print("RESIZE\n");
- lock(*key_mutex);
- defer unlock(*key_mutex);
- // TODO Only signal the key_semaphore if we don't already have a Resize on the queue.
- if key_resize != Keys.None return;
- key_resize = Keys.Resize;
- signal(*key_semaphore);
- }
-}
+get_key :: (timeout_milliseconds: s32 = -1) -> Key {
-get_key :: (wait_milliseconds: s32) -> Key {
- wait_for(*key_semaphore, wait_milliseconds);
-
- lock(*key_mutex);
- defer unlock(*key_mutex);
+ if OS_was_terminal_resized() return xx Keys.Resize;
- if key_resize != Keys.None {
- defer key_resize = Keys.None;
- return xx key_resize;
+ if input_string.count > 0 {
+ defer advance(*input_string, 1);
+ return input_string[0];
}
- defer key_input = Keys.None;
- return xx key_input;
-}
+ is_input_available := OS_wait_for_input(timeout_milliseconds);
+ if OS_was_terminal_resized() return xx Keys.Resize;
-// TODO Rename this procedure.
-set_handler :: () {
- sa : sigaction_t;
- sa.sa_handler = process_resize;
- sigemptyset(*(sa.sa_mask));
- sa.sa_flags = SA_RESTART;
- sigaction(SIGWINCH, *sa, null);
+ if is_input_available {
+ bytes_read := OS_read_input(input_buffer.data, input_buffer.count); // TODO Does not check for read errors.
+ if bytes_read > 0 {
+ input_string.data = input_buffer.data;
+ input_string.count = bytes_read;
+ defer advance(*input_string, 1);
+ return input_string[0];
+ }
+ }
+ return xx Keys.None;
}
-// TODO Rename this procedure.
-restore_handler :: () {
- sa : sigaction_t;
- sa.sa_handler = SIG_DFL;
- sigaction(SIGWINCH, null, *sa);
+get_str :: (str_limit: int = -1, allocator: Allocator = temp) -> string {
+ assert(allocator.proc != null, "Argument 'allocator.proc' has invalid null value.");
+
+ if str_limit < 0 {
+ 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 < buffer.allocated break;
+ expand(*builder);
+ }
+ return builder_to_string(*builder, allocator);
+ }
+ else {
+ buffer := alloc_string(str_limit, allocator);
+ buffer.count = OS_read_input(buffer.data, str_limit);
+ return buffer;
+ }
}
start :: () {
if initialized == true return;
-dam("A");
- // write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.EnterAlternateBuffer, Commands.SetUTF8);
+
+ write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.EnterAlternateBuffer, Commands.SetUTF8);
OS_prepare_terminal();
-dam("B");
- input_buffer = alloc_string(4096);
- init(*input_buffer_mutex, "input_buffer_mutex");
- assert(thread_init(*input_process_thread, process_input), "Failed to initialize thread.");
-dam("C");
- init(*key_semaphore);
- init(*key_mutex, "key_mutex");
-dam("D");
+
+ was_resized = false;
+ init(*resize_mutex, "resize_mutex");
+ input_string.data = input_buffer.data;
+ input_string.count = 0;
+
initialized = true;
- thread_start(*input_process_thread);
+
set_handler(); // TODO Move to beter place.
-dam("E");
-
}
stop :: () {
if initialized == false return;
initialized = false;
-
restore_handler(); // TODO Move to beter place.
- print(">A<");
- thread_deinit(*input_process_thread);
- print(">B<");
- destroy(*key_semaphore);
- print(">C<");
OS_reset_terminal();
write_strings(Commands.EnterMainBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor);
}
-get_str :: () -> string {
- lock(*input_buffer_mutex);
- defer unlock(*input_buffer_mutex);
- return input_buffer;
- // return tprint("%", to_string(input_buffer));
- // print("%", tprint("%", input_buffer));
- // return tprint("%", input_buffer);
-}
-
flush_input :: () {
// TODO
- lock(*input_buffer_mutex);
- defer unlock(*input_buffer_mutex);
- input_buffer.count = 0;
}
draw_box :: (x: int, y: int, width: int, height: int) {
@@ -299,7 +236,31 @@ clear_terminal :: inline () {
// 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();
+ rows, columns: int = ---;
+ #if OS == .WINDOWS {
+ rows, columns = OS_get_terminal_size();
+ }
+ else {
+ auto_release_temp();
+
+ flush_input();
+ write_string(Commands.QueryWindowSizeInChars);
+
+ input := get_str(64);
+
+ // Expected message format: [8;<r>;<c>t
+ // where <r> is the number of rows and <c> of columns.
+ assert(
+ input[0] == #char "\e" &&
+ input[1] == #char "[" &&
+ input[2] == #char "8",
+ //input[input.count-1] == #char "t",
+ "Query window size in chars returned invalid response.");
+
+ parts := split(input, ";");
+ rows = parse_int(*parts[1]);
+ columns = parse_int(*parts[2]);
+ }
return rows, columns;
}
@@ -313,11 +274,11 @@ get_cursor_position :: () -> row: int, column: int {
assert(initialized, "TUI is not ready."); // TODO Should I use this inside each and every procedure?
auto_release_temp();
-
+
+ flush_input();
write_string(Commands.QueryCursorPosition);
- input := talloc_string(64);
- input.count = OS_read_input(input.data, input.count); // TODO Does not check for read errors.
+ input := get_str(64);
// Expected message format: \e[<r>;<c>R
// where <r> is the number of rows and <c> of columns.
@@ -334,43 +295,58 @@ get_cursor_position :: () -> row: int, column: int {
return row, column;
}
-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 :: (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 {
// Prototyping zone... keep clear!
}
else #if OS == .LINUX || OS == .MACOS {
// Prototyping zone... keep clear!
+
+ OS_wait_for_input :: (timeout_milliseconds: s32) -> is_input_available: bool {
+ 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.
+ error_code, error_message := get_error_value_and_string();
+ return ifx poll_return > 0 then true else false;
+ }
+
+ resize_mutex : Mutex;
+ was_resized : bool;
+
+ OS_was_terminal_resized :: () -> bool {
+ lock(*resize_mutex);
+ defer unlock(*resize_mutex);
+ defer was_resized = false;
+ return was_resized;
+ }
+
+ process_resize :: (signal_code : s32) #c_call {
+ new_context : Context;
+ push_context new_context {
+ print("SIGNAL:%", signal_code);
+ if signal_code != SIGWINCH then return;
+ if was_resized == true then return;
+ print("RESIZE\n");
+ lock(*resize_mutex);
+ defer unlock(*resize_mutex);
+ was_resized = true;
+ }
+ }
+
+ // TODO Rename this procedure.
+ set_handler :: () {
+ sa : sigaction_t;
+ sa.sa_handler = process_resize;
+ sigemptyset(*(sa.sa_mask));
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, *sa, null);
+ }
+
+ // TODO Rename this procedure.
+ restore_handler :: () {
+ sa : sigaction_t;
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGWINCH, null, *sa);
+ }
+
}