diff options
Diffstat (limited to 'TUI/unix.jai')
| -rw-r--r-- | TUI/unix.jai | 141 |
1 files changed, 114 insertions, 27 deletions
diff --git a/TUI/unix.jai b/TUI/unix.jai index e5c81c3..6bcf13a 100644 --- a/TUI/unix.jai +++ b/TUI/unix.jai @@ -1,9 +1,61 @@ +#scope_file + #import "POSIX"; +#import "System"; + + // Required to do unlocking input. + libc :: #system_library "libc"; + // 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; - __term : My_Termios; + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html + tcgetattr :: (fd : s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc; - My_Termios :: struct { + + // Input modes. + Input_Modes :: enum_flags u32 { + IGNBRK; // Ignore break condition. + BRKINT; // Signal interrupt on break. + IGNPAR; // Ignore characters with parity errors. + PARMRK; // Mark parity and framing errors. + INPCK; // Enable input parity check. + ISTRIP; // Strip 8th bit off characters. + INLCR; // Map NL to CR on input. + IGNCR; // Ignore CR. + ICRNL; // Map CR to NL on input. + IXON; // Enable start/stop output control. + IXOFF; // Enable start/stop input control. + IXANY; // Any character will restart after stop. + __NOT_USED__; + IMAXBEL; // Ring bell when input queue is full. + IUCLC; // Translate upper case input to lower case. + } + + // Local modes. + Local_Modes :: enum_flags u32 { + ECHOKE; // Visual erase for KILL. + ECHOE; // Visual erase for ERASE. + ECHOK; // Echo NL after KILL. + ECHO; // Enable echo. + ECHONL; // Echo NL even if ECHO is off. + ECHOPRT; // Hardcopy visual erase. + ECHOCTL; // Echo control characters as ^X. + ISIG; // Enable signals. + ICANON; // Do erase and kill processing. + ALTWERASE; // Alternate WERASE algorithm. + IEXTEN; // Enable DISCARD and LNEXT. + EXTPROC; // External processing. + TOSTOP; // Send SIGTTOU for background output. + FLUSHO; // Output being flushed (state). + XCASE; // Canonical upper/lower case. + NOKERNINFO; // Disable VSTATUS. + PENDIN; // Retype pending input (state). + NOFLSH; // Disable flush after interrupt. + + } + + Terminal_IO_Mode :: struct { c_iflag : u32; // Input mode flags. c_oflag : u32; // Output mode flags. c_cflag : u32; // Control modes flags. @@ -14,50 +66,85 @@ c_ospeed : u32; // Output speed (baud rates). } - // Required to do unlocking input. - libc :: #system_library "libc"; - // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html - tcsetattr :: (fd : s32, optional_actions : s32, termios_p : *My_Termios) -> s32 #foreign libc; + initial_tio_mode : Terminal_IO_Mode; + default_tio_mode: Terminal_IO_Mode; + blocking_tio_mode: Terminal_IO_Mode; + unblocking_tio_mode: Terminal_IO_Mode; - // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html - tcgetattr :: (fd : s32, termios_p : *My_Termios) -> s32 #foreign libc; - + +#scope_export OS_prepare_terminal :: () { // TODO Required to do unlocking input. - tcgetattr(STDIN_FILENO, *__term); - term_new := __term; + tcgetattr(STDIN_FILENO, *initial_tio_mode); + default_tio_mode := initial_tio_mode; + + default_tio_mode.c_iflag &= 0xFFFFFA14;// ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + default_tio_mode.c_oflag &= 0xFFFFFFFE;// ~OPOST; + default_tio_mode.c_lflag &= 0xFFFF7FB4;// ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + default_tio_mode.c_cflag &= 0xFFFFFECF;// ~(CSIZE | PARENB); + default_tio_mode.c_cflag |= 0x00000030; // TODO WHAT IS THIS? - term_new.c_iflag &= 0xFFFFFA14;// ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - term_new.c_oflag &= 0xFFFFFFFE;// ~OPOST; - term_new.c_lflag &= 0xFFFF7FB4;// ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - term_new.c_cflag &= 0xFFFFFECF;// ~(CSIZE | PARENB); - term_new.c_cflag |= 0x00000030; // TODO WHAT IS THIS? + blocking_tio_mode = default_tio_mode; + blocking_tio_mode.c_iflag |= xx cast(Input_Modes)(.IXOFF | .ICRNL); + blocking_tio_mode.c_lflag |= xx cast(Local_Modes)(.NOKERNINFO | .ECHO | .ECHOE | .ECHOKE); - tcsetattr(STDIN_FILENO, 0, *term_new); + unblocking_tio_mode = default_tio_mode; + iflags: Input_Modes = (.IGNBRK | .BRKINT | .PARMRK | .ISTRIP | .INLCR | .IGNCR | .ICRNL | .IXON); + unblocking_tio_mode.c_iflag &= xx ~(iflags); + lflags: Local_Modes = (.ECHO | .ECHONL | .ICANON | .IEXTEN); + unblocking_tio_mode.c_lflag &= xx ~lflags; + + tcsetattr(STDIN_FILENO, 0, *default_tio_mode); } OS_reset_terminal :: () { - tcsetattr(STDIN_FILENO, 0, *__term); // return echo + tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); // return echo } OS_get_terminal_size :: () -> rows: int, columns: int { - buffer: [512] u8; - write_string(Commands.QueryWindowSizeInChars); - bytes_read := read(STDIN_FILENO, buffer.data, buffer.count-1); - str := to_string(buffer.data, bytes_read); + auto_release_temp(); + + input := talloc_string(64); + write_string(Commands.QueryWindowSizeInChars); + input.count = OS_read_input(input.data, input.count); - // Result: [8;79;156t + // Expected message format: [8;<r>;<c>t + // where <r> is the number of rows and <c> of columns. assert( - buffer.data[0] == #char "\e" && - buffer.data[1] == #char "[" && - buffer.data[2] == #char "8", + input[0] == #char "\e" && + input[1] == #char "[" && + input[2] == #char "8", "Query windows size in chars returned invalid response."); - parts := split(str, ";"); + parts := split(input, ";"); rows := parse_int(*parts[1]); columns := parse_int(*parts[2]); return rows, columns; } + +// TODO Maybe we should use a NON-BLOCKING state by default... and only change to blocking when performing a HUMAN read...? + +OS_set_input_mode :: (mode: Input_Mode) { + if mode == { + case .HUMAN; + tcsetattr(STDIN_FILENO, 0, *blocking_tio_mode); + // TODO get_error_value_and_string :: () -> (error_code: OS_Error_Code, description: string) + case .MACHINE; + tcsetattr(STDIN_FILENO, 0, *unblocking_tio_mode); + // TODO get_error_value_and_string :: () -> (error_code: OS_Error_Code, description: string) + case; + // TODO ERROR + } +} + +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 { + error_code, error_message := get_error_value_and_string(); + return -1, true, error_message; + } + return bytes_read; +} |
