aboutsummaryrefslogtreecommitdiff
path: root/TUI/module.jai
diff options
context:
space:
mode:
authordam <dam@gudinoff>2023-10-16 00:48:04 +0100
committerdam <dam@gudinoff>2023-10-16 00:48:04 +0100
commit3b9f4f990ab0e865ec8e6277381e5aff0b178665 (patch)
treee46c7a14b4c2accd97001708768884c101883933 /TUI/module.jai
parent27269b059e7a5ae72a85b243919ad849ed672643 (diff)
downloadtask-time-tracker-3b9f4f990ab0e865ec8e6277381e5aff0b178665.tar.zst
task-time-tracker-3b9f4f990ab0e865ec8e6277381e5aff0b178665.zip
First prototype of TUI reading the input using another thread.
Diffstat (limited to 'TUI/module.jai')
-rw-r--r--TUI/module.jai187
1 files changed, 157 insertions, 30 deletions
diff --git a/TUI/module.jai b/TUI/module.jai
index 034ec15..7a7dc54 100644
--- a/TUI/module.jai
+++ b/TUI/module.jai
@@ -8,6 +8,7 @@
#import "Basic";
#import "String";
+#import "Thread";
Drawings :: struct {
CornerBR :: "\x6A";
@@ -84,18 +85,144 @@ Commands :: struct {
initialized := false;
+input_buffer_mutex: Mutex;
+input_buffer: string;
+input_counter: s64;
+read_buffer: [4096] u8;
+
+input_process_thread: Thread;
+
+Key :: u8; // TODO To be improved.
+Keys :: enum u8 {
+ None :: 0;
+ Resize :: 1; //410; // TODO Why?!
+}
+
+input_semaphore: Semaphore;
+key_semaphore: Semaphore;
+key_mutex: Mutex;
+key_input := Keys.None;
+key_resize := Keys.None;
+// key_queue: [2] Key; // TODO Queue with size 1!? hmmm nice!
+// key_queue_idx := 0;
+
+
+
+// queue_semaphore: Semaphore;
+// queue_mutex: Mutex;
+// queue :: 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 {
+ 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 { // TODO Reading without mutex...
+ dam("waiting..");
+ sleep_milliseconds(100);
+ dam("!\n\r");
+ continue;
+ }
+ dam("reading..");
+ bytes_read := OS_read_input(*char, 1);
+ dam("!\n\r");
+ if bytes_read > 0 {
+ lock(*key_mutex);
+ defer unlock(*key_mutex);
+ key_input = xx char;
+ dam("signaling..");
+ dam("!\n\r");
+ signal(*key_semaphore);
+ }
+ }
+ return 0;
+}
+
+process_resize :: (signal : s32) #c_call {
+ new_context : Context;
+ push_context new_context {
+ if signal != SIGWINCH return;
+ 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 != Key.None continue;
+ key_resize = Keys.Resize;
+ signal(*key_semaphore);
+ }
+}
+
+get_key :: (wait_milliseconds: s32) -> Key {
+ wait_for(*key_semaphore, wait_milliseconds);
+
+ lock(*key_mutex);
+ defer unlock(*key_mutex);
+
+ if key_resize != Keys.None {
+ defer key_resize = Keys.None;
+ return xx key_resize;
+ }
+
+ defer key_input = Keys.None;
+ return xx key_input;
+}
+
+
start :: () {
- if initialized return;
- write_strings(Commands.HideCursor, Commands.SaveCursorPosition, Commands.EnterAlternateBuffer, Commands.SetUTF8);
+ if initialized == true return;
+dam("A");
+ // 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(*input_semaphore);
+ init(*key_semaphore);
+ init(*key_mutex, "key_mutex");
+dam("D");
initialized = true;
+ thread_start(*input_process_thread);
+dam("E");
}
stop :: () {
if initialized == false return;
initialized = false;
+
+ // signal(*input_semaphore); // TODO Maybe use tcflush ???
+ thread_deinit(*input_process_thread);
+
+ // destroy(*input_semaphore);
+ destroy(*key_semaphore);
+
OS_reset_terminal();
- write_strings(Commands.EnterMainBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor);
+ // 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) {
@@ -191,33 +318,33 @@ Input_Mode :: enum u8 {
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);
-}
+// 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 {