diff options
Diffstat (limited to 'modules/TUI')
| -rw-r--r-- | modules/TUI/module.jai | 8 | ||||
| -rw-r--r-- | modules/TUI/unix.jai | 140 |
2 files changed, 112 insertions, 36 deletions
diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai index b5866d5..5fd29f6 100644 --- a/modules/TUI/module.jai +++ b/modules/TUI/module.jai @@ -1,5 +1,7 @@ #module_parameters(COLOR_MODE := 24); +#scope_file + #if OS == { case .LINUX; #load "unix.jai"; @@ -22,6 +24,8 @@ assert(input_buffer.count >= KEY_SIZE, "The input buffer size must be capable to hold an entire Key, which should be able to hold an UTF8 code (4 bytes) or a terminal escape code (6 bytes)."); } +#scope_export; + // Special Graphics Characters Drawings :: struct { Blank :: "\x5F"; @@ -441,7 +445,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { // > https://unix.stackexchange.com/questions/255707/what-are-the-keyboard-shortcuts-for-the-command-line x, y := get_cursor_position(); - write_strings(Commands.StartBlinking, Commands.BlinkingBarShape); + write_strings(Commands.ShowCursor, Commands.StartBlinking, Commands.BlinkingBarShape); key := Keys.None; while true { @@ -515,7 +519,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { } } - write_strings(Commands.StopBlinking, Commands.DefaultShape); + write_strings(Commands.StopBlinking, Commands.DefaultShape, Commands.HideCursor); result := ifx key == Keys.Enter then str else ""; return result, key; diff --git a/modules/TUI/unix.jai b/modules/TUI/unix.jai index 7103d47..8eeb6c0 100644 --- a/modules/TUI/unix.jai +++ b/modules/TUI/unix.jai @@ -1,5 +1,10 @@ #scope_file +/* +TODO : then log all error on unix... +TODO : then do a good implementation of the libc functions about attributes... +*/ + #import "Atomics"; #import "System"; #import "POSIX"; @@ -7,22 +12,6 @@ // Required to do unlocking input. libc :: #system_library "libc"; - // TODO Remove this. - // 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) - { - t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - t->c_oflag &= ~OPOST; - t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - t->c_cflag &= ~(CSIZE|PARENB); - t->c_cflag |= CS8; - t->c_cc[VMIN] = 1; // read returns when one char is available. - 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 { @@ -82,10 +71,68 @@ } // 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 - // } + // tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc; + tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 { + 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 + }; + + + // int + // __tcgetattr (int fd, struct termios *termios_p) + // { + // struct __kernel_termios k_termios; + k_termios: __kernel_termios; + retval: int; + retval = ioctl(fd, TCGETS, *k_termios); + if retval == 0 { + termios_p.c_iflag = xx k_termios.c_iflag; + termios_p.c_oflag = xx k_termios.c_oflag; + termios_p.c_cflag = xx k_termios.c_cflag; + termios_p.c_lflag = xx k_termios.c_lflag; + termios_p.c_line = xx k_termios.c_line; + // #if _HAVE_STRUCT_TERMIOS_C_ISPEED + // # if _HAVE_C_ISPEED + // termios_p->c_ispeed = k_termios.c_ispeed; + // # else + // termios_p->c_ispeed = k_termios.c_cflag & (CBAUD | CBAUDEX); + // # endif + // #endif + // #if _HAVE_STRUCT_TERMIOS_C_OSPEED + // # if _HAVE_C_OSPEED + // termios_p->c_ospeed = k_termios.c_ospeed; + // # else + // termios_p->c_ospeed = k_termios.c_cflag & (CBAUD | CBAUDEX); + // # endif + // #endif + size_of_cc_t := __KERNEL_NCCS * 1; + memcpy(*termios_p.c_cc[0], *k_termios.c_cc[0], size_of_cc_t); + // memset(*termios_p.c_cc[0] + size_of_cc_t + 1, _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * 1); + // + // if (sizeof (cc_t) == 1 || _POSIX_VDISABLE == 0 || (unsigned char) _POSIX_VDISABLE == (unsigned char) -1) { + // memset (__mempcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)), _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * sizeof (cc_t)); + // } + // else + // { + // memcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)); + // for (size_t cnt = __KERNEL_NCCS; cnt < NCCS; ++cnt) { + // termios_p->c_cc[cnt] = _POSIX_VDISABLE; + // } + // } + } + return xx retval; + } // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html // tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc; @@ -232,8 +279,15 @@ restore_resize_handler :: () { #scope_export -OS_prepare_terminal :: () { - tcgetattr(STDIN_FILENO, *initial_tio_mode); // TODO Log error using `log()` from jai/modules/Basic/Print.jai ? +OS_prepare_terminal :: () -> success := true { // On critical paths, we may use the #must for the success return value. + error: int = ---; + + error = tcgetattr(STDIN_FILENO, *initial_tio_mode); + if error { + error_code, error_string := get_error_value_and_string(); + log_error("Failed to get initial_tio_mode: code %, %", error_code, error_string); + } + 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); @@ -242,28 +296,48 @@ OS_prepare_terminal :: () { raw_tio_mode.c_cflag |= .CS8; 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. - + + error = tcsetattr(STDIN_FILENO, 0, *raw_tio_mode); + if error { + error_code, error_string := get_error_value_and_string(); + log_error("Failed to set raw_tio_mode: code %, %", error_code, error_string); + return false; + } + was_resized = false; prepare_resize_handler(); + return; } -OS_reset_terminal :: () { +OS_reset_terminal :: inline () -> success := true { restore_resize_handler(); - tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); // TODO Log on error. + error := tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); + if error { + error_code, error_string := get_error_value_and_string(); + log_error("Failed to set initial_tio_mode: code %, %", error_code, error_string); + return false; + } + return; } -OS_flush_input :: inline () { +OS_flush_input :: inline () -> success := true { TCIFLUSH :: 0; // TODO Is this always zero in all systems? - tcflush(STDIN_FILENO, TCIFLUSH); + error := tcflush(STDIN_FILENO, TCIFLUSH); + if error { + error_code, error_string := get_error_value_and_string(); + log_error("Failed to flush input: code %, %", error_code, error_string); + return false; + } + return; } // TODO Nothing is checking for the errors returned by this... shame! -OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, error: bool = false, error_message: string = "" { +OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, success := true { bytes_read := read(STDIN_FILENO, buffer, xx bytes_to_read); if bytes_read < 0 { - error_code, error_message := get_error_value_and_string(); - return -1, true, error_message; + error_code, error_string := get_error_value_and_string(); + log_error("Failed to read input: code %, %", error_code, error_string); + return 0, false; } return bytes_read; } @@ -279,8 +353,6 @@ OS_wait_for_input :: (timeout_milliseconds: s32 = -1) -> is_input_available: boo return ifx poll_return > 0 then true else false; } -// TODO This procedure hides the behaviour of reseting on read. -// We should have the `was_resized` on module.jai so that we know it may be used in another thread. OS_was_terminal_resized :: () -> bool { - return atomic_swap(*was_resized, false); // TODO If the windows implementation is similar, we may push this into the main module file. + return atomic_swap(*was_resized, false); } |
