diff options
| author | dam <dam@gudinoff> | 2023-12-12 03:03:45 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2023-12-12 03:03:45 +0000 |
| commit | e3f4ad1b4db98f612f097ba76116ab5d85bed912 (patch) | |
| tree | 56923af600794d1ea8b06f8443d8e97149d987ed | |
| parent | 58716ceff7f82ee8b01ea43734cc755cacff25a6 (diff) | |
| download | task-time-tracker-e3f4ad1b4db98f612f097ba76116ab5d85bed912.tar.zst task-time-tracker-e3f4ad1b4db98f612f097ba76116ab5d85bed912.zip | |
Improved tests and tried to implement tcsetattr by relying only on ioctl.
| -rw-r--r-- | TUI/module.jai | 46 | ||||
| -rw-r--r-- | TUI/unix.jai | 88 | ||||
| -rw-r--r-- | ttt.jai | 114 |
3 files changed, 170 insertions, 78 deletions
diff --git a/TUI/module.jai b/TUI/module.jai index 044fd65..bfed510 100644 --- a/TUI/module.jai +++ b/TUI/module.jai @@ -108,11 +108,13 @@ assert_is_initialized :: inline () { } set_next_key :: (key: Key) { + assert_is_initialized(); input_override = key; } get_key :: (timeout_milliseconds: s32 = -1) -> Key { - + assert_is_initialized(); + // BBBB BBBB & 1100 0000 == 10XX XXXX -> is continuation byte is_utf8_continuation_byte :: inline (byte: u8) -> bool { return (byte & 0xC0) == 0x80; @@ -196,21 +198,21 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key { /// /// /// /// /// /// /// /// /// // DEBUG - br, bc := get_cursor_position(); - column := 3; - row += 1; - nr, rc := get_terminal_size(); - - if row >= (rc - 5) then row = 5; - - set_cursor_position(row, column); - for 0..input_string.count-1 { - print("%:% | ", - FormatInt.{base= 2, minimum_digits = 8, value = input_string[it]}, - FormatInt.{base= 16, minimum_digits = 2, value = input_string[it]}, - ); // TODO DEBUG - } - set_cursor_position(br, bc); + // br, bc := get_cursor_position(); + // column := 3; + // row += 1; + // nr, rc := get_terminal_size(); + // + // if row >= (rc - 5) then row = 5; +// + // set_cursor_position(row, column); + // for 0..input_string.count-1 { + // print("%:% | ", + // FormatInt.{base= 2, minimum_digits = 8, value = input_string[it]}, + // FormatInt.{base= 16, minimum_digits = 2, value = input_string[it]}, + // ); // TODO DEBUG + // } + // set_cursor_position(br, bc); /// /// /// /// /// /// /// /// /// advance(*input_string, utf8_bytes); @@ -221,6 +223,7 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key { } get_str :: (count_limit: int = -1, allocator: Allocator = temp) -> string { + assert_is_initialized(); assert(allocator.proc != null, "Argument 'allocator.proc' has invalid null value."); if count_limit < 0 { @@ -270,6 +273,7 @@ flush_input :: () { } draw_box :: (x: int, y: int, width: int, height: int) { + assert_is_initialized(); // TODO Check if using a String_Builder improves performance (measure it)! // TODO Validate input parameters against the terminal size. @@ -315,13 +319,13 @@ draw_box :: (x: int, y: int, width: int, height: int) { // TODO Maybe rename to "clear()" clear_terminal :: inline () { - assert(initialized, "TUI is not ready."); + assert_is_initialized(); write_string(Commands.ClearScreen); } // TODO Maybe rename to "get_size()" get_terminal_size :: () -> rows: int, columns: int { - assert(initialized, "TUI is not ready."); + assert_is_initialized(); rows, columns: int = ---; #if OS == .WINDOWS { rows, columns = OS_get_terminal_size(); @@ -359,15 +363,15 @@ get_terminal_size :: () -> rows: int, columns: int { } set_cursor_position :: (row: int, column: int) { - assert(initialized, "TUI is not ready."); + assert_is_initialized(); auto_release_temp(); tmp_string := tprint(Commands.SetCursorPosition, row, column); write_string(tmp_string); } get_cursor_position :: () -> row: int, column: int { - assert(initialized, "TUI is not ready."); // TODO Should I use this inside each and every procedure? - + assert_is_initialized(); + auto_release_temp(); flush_input(); diff --git a/TUI/unix.jai b/TUI/unix.jai index 2c34122..03a371b 100644 --- a/TUI/unix.jai +++ b/TUI/unix.jai @@ -6,19 +6,9 @@ // 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; + // cfmakeraw :: (termios: *Terminal_IO_Mode) -> void #foreign libc; /* https://elixir.bootlin.com/glibc/glibc-2.28/source/termios/cfmakeraw.c#L22 void cfmakeraw (struct termios *t) @@ -32,19 +22,81 @@ t->c_cc[VTIME] = 0; } */ - + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html - tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc; + // tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc; + tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 { + // TODO IMPLEMENT ME + + #if OS == .LINUX { + TCSETS :: 0x5402; + TCSETSW :: 0x5403; + TCSETSF :: 0x5404; + tcflag_t :: u32; + cc_t :: u8; + __KERNEL_NCCS :: 19; + __kernel_termios :: struct { + c_iflag : tcflag_t; // input mode flags + c_oflag : tcflag_t; // output mode flags + c_cflag : tcflag_t; // control mode flags + c_lflag : tcflag_t; // local mode flags + c_line : cc_t; // line discipline + c_cc : [__KERNEL_NCCS]cc_t; // control characters + }; + + k_termios: __kernel_termios; + cmd: u64; + if optional_actions == { + case xx SetAttributesActions.TCSANOW; + cmd = TCSETS; + case xx SetAttributesActions.TCSADRAIN; + cmd = TCSETSW; + + case xx SetAttributesActions.TCSAFLUSH; + cmd = TCSETSF; + + case; + return EINVAL; + } + // k_termios.c_iflag = termios_p.c_iflag & ~IBAUD0; + k_termios.c_iflag = xx termios_p.c_iflag; + k_termios.c_oflag = xx termios_p.c_oflag; + k_termios.c_cflag = xx termios_p.c_cflag; + k_termios.c_lflag = xx termios_p.c_lflag; + k_termios.c_line = xx termios_p.c_line; + // #if _HAVE_C_ISPEED && _HAVE_STRUCT_TERMIOS_C_ISPEED + // k_termios.c_ispeed = termios_p->c_ispeed; + // #endif + // #if _HAVE_C_OSPEED && _HAVE_STRUCT_TERMIOS_C_OSPEED + // k_termios.c_ospeed = termios_p->c_ospeed; + // #endif + memcpy(*k_termios.c_cc[0], *termios_p.c_cc[0], __KERNEL_NCCS * 1);//size_of(cc_t)); + return ioctl(fd, cmd, *k_termios); + } + #if OS == .MACOS { + // return __ioctl (fd, TIOCSETAF, termios_p); + #assert(false, "NOT IMPLEMENTED"); + } + return 0; + } + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc; - + // tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 { + // TODO IMPLEMENT ME + // } + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html - tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc; - + // tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc; + tcflush :: inline (fd: s32, queue_selector: s32) -> s32 { + TCFLSH :: 0x540B; + return ioctl(fd, TCFLSH, queue_selector); + } + // Information for the termios.h enums is platform dependent and was retrieved from: // https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/bits/termios.h - + // Set Attributes Actions. SetAttributesActions :: enum u32 { TCSANOW :: 0; // Change immediately. @@ -187,7 +239,7 @@ OS_flush_input :: inline () { } OS_prepare_terminal :: () { - tcgetattr(STDIN_FILENO, *initial_tio_mode); // TODO Log on error. + tcgetattr(STDIN_FILENO, *initial_tio_mode); // TODO Log error using `log()` from jai/modules/Basic/Print.jai ? raw_tio_mode = initial_tio_mode; raw_tio_mode.c_iflag &= ~(.IGNBRK | .BRKINT | .PARMRK | .ISTRIP | .INLCR | .IGNCR | .ICRNL | .IXON); raw_tio_mode.c_oflag &= ~(.OPOST); @@ -1182,36 +1182,40 @@ read_enter_confirmation :: inline (row: int, style: int, message: string) -> boo main :: () { // -- -- -- Testing TUI -- START - #if 0 { - print("test 0\n"); - TUI.test(); - return; - } + + // TODO Test input #if 0 { print("test 1\n"); TUI.start(); buffer: [8] u8; - brss, error, msg := TUI.OS_read_input(buffer.data, buffer.count); + bytes_read, error, error_msg := TUI.OS_read_input(buffer.data, buffer.count); TUI.stop(); - if error == true print("error:%", msg); - print("br:%", brss); - print("input:%", cast(string)buffer); + if error == true print("error:%\n", error_msg); + print("read % bytes:", bytes_read); + for 0..bytes_read-1 { + char := "-NA-"; + if buffer[it] >= 33 && buffer[it] <= 126 { + char.data = *buffer[it]; + char.count = 1; + } + print(" 0x% (%)", FormatInt.{minimum_digits=2, base=10, value=buffer[it]}, char); + } + print("\n"); TUI.stop(); - return; } #if 0 { print("test 2\n"); - key: TUI.Key = #char "d"; - while(key != #char "q") { + _key: TUI.Key = #char "d"; + while(_key != #char "q") { // key = TUI.get_key(10); buffer: [8] u8; buffer[0] = 0; - brss := TUI.OS_read_input(buffer.data, 1); - if brss > 0 { - key = buffer[0]; - print(">%<", xx key); + bytes_read := TUI.OS_read_input(buffer.data, 1); + if bytes_read > 0 { + _key = buffer[0]; + print(">%<", xx _key); } else { print(":"); @@ -1219,43 +1223,72 @@ main :: () { sleep_milliseconds(10); } TUI.stop(); - return; } - #if 0 { - print("testing select\n"); + if 1 { + print("TEST : set and get cursor position --\n", to_standard_error = true); TUI.start(); - TUI.test(); - TUI.test(); - TUI.test(); + ROW :: 3; + COLUMN :: 3; + TUI.set_cursor_position(ROW, COLUMN); + row, column := TUI.get_cursor_position(); TUI.stop(); - return; + assert(row == ROW && column == COLUMN, "Failed set/get cursor position.\n"); + print("> success\n", to_standard_error = true); } - - #if 0 { - print("test 3\n"); + + if 1 { + print("TEST : test key input --\n", to_standard_error = true); + auto_release_temp(); TUI.start(); - key: TUI.Key = #char "d"; + TUI.clear_terminal(); + TUI.set_cursor_position(1, 1); + write_string("Press q to exit, other key to print it to screen, wait 1s to see animation."); + TUI.set_cursor_position(2, 1); + key: TUI.Key; while(key != #char "q") { __mark := get_temporary_storage_mark(); - // sleep_milliseconds(1000); - key = TUI.get_key(3000); - - if key != xx TUI.Keys.None print_character(key); + key = TUI.get_key(1000); + if key != xx TUI.Keys.None print_character(cast,force(u8)key); else write_string("-"); set_temporary_storage_mark(__mark); } - print("Waiting 1s.."); - sleep_milliseconds(1000); - print("!\n\r"); - print("Stoping.."); TUI.stop(); - print("!\n\r"); - return; + print("> success\n", to_standard_error = true); + } + + if 1 { + print("TEST : draw box --\n", to_standard_error = true); + auto_release_temp(); + TUI.start(); + TUI.clear_terminal(); + TUI.draw_box(1, 2, 5, 3); + TUI.set_cursor_position(1, 1); + print("Can you see the box below? (y/n)"); + key := TUI.get_key(); + TUI.stop(); + assert(key == #char "y", "Failed to draw box.\n"); + print("> success\n", to_standard_error = true); + } + + if 1 { + print("TEST : get terminal size --\n", to_standard_error = true); + auto_release_temp(); + TUI.start(); + TUI.clear_terminal(); + rows, columns := TUI.get_terminal_size(); + TUI.set_cursor_position(1, 1); + print("Is terminal size % columns and % rows? (y/n)", columns, rows); + key := TUI.get_key(); + TUI.stop(); + assert(key == #char "y", "Failed to get terminal size.\n"); + print("> success\n", to_standard_error = true); } - #if 1 { - print("test 4\n", to_standard_error = true); + // TODO Resizing terminal breaks the tests... try to fix it. + + #if 0 { + print("test 5\n", to_standard_error = true); TUI.start(); TUI.set_terminal_title("bazinga"); xcolumns, xrows: int; @@ -1286,6 +1319,9 @@ main :: () { } TUI.stop(); print("size(CxR): %x%\n", xcolumns, xrows); + } + + #if true { return; } |
