diff options
| -rw-r--r-- | TUI/module.jai | 280 | ||||
| -rw-r--r-- | TUI/unix.jai | 56 | ||||
| -rw-r--r-- | sizeof.c | 35 | ||||
| -rw-r--r-- | ttt.jai | 58 |
4 files changed, 198 insertions, 231 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); + } + } diff --git a/TUI/unix.jai b/TUI/unix.jai index 1bfa400..a35a60a 100644 --- a/TUI/unix.jai +++ b/TUI/unix.jai @@ -6,6 +6,16 @@ // Required to do unlocking input. libc :: #system_library "libc"; + + // int poll(struct pollfd *fds, nfds_t nfds, int timeout); + pollfd :: struct { + fd : s32; // File descriptor. + events : s16; // Requested events. + revents : s16; // Returned events. + }; + + poll :: (fds: *pollfd, nfds: u32, timeout: s32) -> s32 #foreign libc; + // TODO Remote this. cfmakeraw :: (termios: *Terminal_IO_Mode) -> void #foreign libc; @@ -135,8 +145,8 @@ OS_prepare_terminal :: () { raw_tio_mode.c_lflag &= ~(.ECHO | .ECHONL | .ICANON | .ISIG | .IEXTEN); raw_tio_mode.c_cflag &= ~(.CSIZE | .PARENB); raw_tio_mode.c_cflag |= .CS8; - raw_tio_mode.c_cc[Control_Chars.VMIN] = 0; - raw_tio_mode.c_cc[Control_Chars.VTIME] = 1; + raw_tio_mode.c_cc[Control_Chars.VMIN] = 1; + raw_tio_mode.c_cc[Control_Chars.VTIME] = 0; tcsetattr(STDIN_FILENO, 0, *raw_tio_mode); // TODO Log on error. } @@ -144,48 +154,6 @@ OS_reset_terminal :: () { tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); // TODO Log on error. } -OS_get_terminal_size :: () -> rows: int, columns: int { - - auto_release_temp(); - - flush_input(); - - // input := talloc_string(64); - write_string(Commands.QueryWindowSizeInChars); - // input.count = OS_read_input(input.data, input.count); - sleep_milliseconds(1); //TODO WHYYYY??? GOD DAMIT THREADS... - input := get_str(); - // input[0] = #char "x"; - // print(">%<", xx input); - // print("#<#"); - - // 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 windows size in chars returned invalid response."); - - parts := split(input, ";"); - rows := parse_int(*parts[1]); - columns := parse_int(*parts[2]); - return rows, columns; -} - -// OS_set_input_mode :: (mode: Input_Mode) { - // tio_mode: *Terminal_IO_Mode = ---; - // if mode == { - // case .HUMAN; - // tio_mode = *human_tio_mode; - // case; - // tio_mode = *cooked_tio_mode; - // } - // tcsetattr(STDIN_FILENO, 0, tio_mode); - // // TODO get_error_value_and_string :: () -> (error_code: OS_Error_Code, description: string) -// } - OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, error: bool = false, error_message: string = "" { bytes_read := read(STDIN_FILENO, buffer, xx bytes_to_read); if bytes_read < 0 { @@ -18,32 +18,33 @@ #include <time.h> int main(int argc, char **argv) { - initscr(); + // initscr(); fprintf(stderr, "sizeof char: %d\n", sizeof(char)); fprintf(stderr, "sizeof short: %d\n", sizeof(short)); fprintf(stderr, "sizeof int: %d\n", sizeof(int)); + fprintf(stderr, "sizeof long: %d\n", sizeof(long)); fprintf(stderr, "sizeof unsigned: %d\n", sizeof(unsigned)); fprintf(stderr, "sizeof chtype: %d\n", sizeof(chtype)); - int w_size_x, w_size_y; - getmaxyx(stdscr, w_size_y, w_size_x); - char str[64]; - memset(str, 0, 64); - sprintf(str, "x,y : %dx%d\n", w_size_x, w_size_y); - mvaddstr(2, 2, str); - sprintf(str, "resize:%d\n", KEY_RESIZE); - mvaddstr(3, 2, str); + // int w_size_x, w_size_y; + // getmaxyx(stdscr, w_size_y, w_size_x); + // char str[64]; + // memset(str, 0, 64); + // sprintf(str, "x,y : %dx%d\n", w_size_x, w_size_y); + // mvaddstr(2, 2, str); + // sprintf(str, "resize:%d\n", KEY_RESIZE); + // mvaddstr(3, 2, str); - unsigned m = ACS_DIAMOND; + // unsigned m = ACS_DIAMOND; fprintf(stderr, "sizeof ACS %d\n", sizeof(ACS_DIAMOND)); - - if (ACS_DIAMOND != 0 || ACS_URCORNER != 0){ - fprintf(stderr, "BAZINGA\n"); - } + // + // if (ACS_DIAMOND != 0 || ACS_URCORNER != 0){ + // fprintf(stderr, "BAZINGA\n"); + // } // fprintf(stderr, ">%d<\n", strlen(ACS_DIAMOND)); - mvaddch(0, 0, m); - getch(); - endwin(); + // mvaddch(0, 0, m); + // getch(); + // endwin(); } @@ -1221,7 +1221,17 @@ main :: () { return; } - #if 1 { + #if 0 { + print("testing select\n"); + TUI.start(); + TUI.test(); + TUI.test(); + TUI.test(); + TUI.stop(); + return; + } + + #if 0 { print("test 3\n"); TUI.start(); key: TUI.Key = #char "d"; @@ -1242,23 +1252,35 @@ main :: () { print("!\n\r"); return; } - - // TUI.start_new_mode(); - // sleep_milliseconds(3000); - xrows, xcolumns := TUI.get_terminal_size(); - TUI.draw_box(1, 1, xcolumns, xrows); - // print("<beep>"); - bbb: [64] u8; - - br, err, err_msg := TUI.OS_read_input(bbb.data, 5); - print(">%/%/%/%\n", cast(string)bbb, br, err, err_msg); - sleep_milliseconds(3000); - // coisa := TUI.get_str(); - // print("\n>%<\n", coisa); - print("stopping\n"); - // TUI.stop_new_mode(); - TUI.stop(); - return; + + #if 1 { + print("test 4\n", to_standard_error = true); + TUI.start(); + xcolumns, xrows: int; + key: TUI.Key = #char "d"; + while(key != #char "q") { + __mark := get_temporary_storage_mark(); + TUI.set_cursor_position(3, 3); + write_string("dam "); + print("%:%", xcolumns, xrows); + key = TUI.get_key(3000); + if key == xx TUI.Keys.None { + write_string(">bazinga<"); + sleep_milliseconds(1000); + } + else if key == xx TUI.Keys.Resize { + TUI.clear_terminal(); + xrows, xcolumns = TUI.get_terminal_size(); + TUI.draw_box(1, 1, xcolumns, xrows); + } + else { + print_character(key); + } + } + TUI.stop(); + print("size(CxR): %x%\n", xcolumns, xrows); + return; + } // -- -- -- TODO WIP Testing TUI -- START |
