From 31708f5773ca2fe4ba5a4ed440747c472f7c7d92 Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 29 Sep 2023 01:14:36 +0100 Subject: Implemented human TIO mode for TUI/unix. --- TUI/unix.jai | 136 +++++++++++++++++++++++++++++++++++++++++--------------- TUI/windows.jai | 64 +++++++++++++------------- 2 files changed, 132 insertions(+), 68 deletions(-) diff --git a/TUI/unix.jai b/TUI/unix.jai index 6bcf13a..20d1a2a 100644 --- a/TUI/unix.jai +++ b/TUI/unix.jai @@ -14,93 +14,158 @@ // Input modes. + // https://elixir.bootlin.com/glibc/latest/source/bits/termios.h 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__; + + _1000_; IMAXBEL; // Ring bell when input queue is full. IUCLC; // Translate upper case input to lower case. + _8000_; + } + + // Output modes. + // https://elixir.bootlin.com/glibc/latest/source/bits/termios.h + Output_Modes :: enum_flags u32 { + OPOST; // Perform output processing. + ONLCR; // Map NL to CR-NL on output. + _UNUSED_0004_; + ONOEOT; // Discard EOT (^D) on output. + OCRNL; // Map CR to NL. + ONOCR; // Discard CR's when on column 0. + ONLRET; // Move to column 0 on NL. + _UNUSED_0080_; } + // Control modes. + // https://elixir.bootlin.com/glibc/latest/source/bits/termios.h + Control_Modes :: enum_flags u32 { + CIGNORE; // Ignore these control flags. + _0002_; + _0004_; + _0008_; + + _0010_; + _0020_; + _0040_; + _0080_; + + _0100_; + _0200_; + CSTOPB; // Two stop bits instead of one. + CREAD; // Enable receiver. + + PARENB; // Parity enable. + PARODD; // Odd parity instead of even. + HUPCL; // Hang up on last close. + CLOCAL; // Ignore modem status lines. + + CRTSCTS; // RTS/CTS flow control. + CDTRCTS; // DTR/CTS flow control. + _0004_0000_; + _0008_0000_; + + MDMBUF; // DTR/DCD flow control. + } + + CHWFLOW :Control_Modes : (.MDMBUF | .CRTSCTS | .CDTRCTS); // All types of flow control. + CS5 :Control_Modes : 0x0000; // 5 bits per byte. + CS6 :Control_Modes : 0x0100; // 6 bits per byte. + CS7 :Control_Modes : 0x0200; // 7 bits per byte. + CS8 :Control_Modes : CS6|CS7; // 8 bits per byte. + CSIZE :Control_Modes : CS5|CS6|CS7|CS8; // Number of bits per byte (mask). + // Local modes. + // https://elixir.bootlin.com/glibc/latest/source/bits/termios.h 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. + + _1000_; + _2000_; + _4000_; + _8000_; + + _0001_0000_; + _0002_0000_; + // TODO Maybe reduce the amount of unused flags by setting the value when there are holes on the table. + // TOSTOP :: 0x0004_0000; // Send SIGTTOU for background output. TOSTOP; // Send SIGTTOU for background output. FLUSHO; // Output being flushed (state). + XCASE; // Canonical upper/lower case. NOKERNINFO; // Disable VSTATUS. + _0040_0000_; + _0080_0000_; + + _0100_0000_; PENDIN; // Retype pending input (state). + _0400_0000_; 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. - c_lflag : u32; // Local modes flags. - c_line : u8; // Line discipline. - c_cc : [32]u8; // Control characters. + c_iflag : Input_Modes; // Input mode flags. + c_oflag : Output_Modes; // Output mode flags. + c_cflag : Control_Modes; // Control modes flags. + c_lflag : Local_Modes; // Local modes flags. + c_line : u8; // Line discipline. + c_cc : [32]u8; // Control characters. c_ispeed : u32; // Input speed (baud rates). c_ospeed : u32; // Output speed (baud rates). } - initial_tio_mode : Terminal_IO_Mode; + initial_tio_mode: Terminal_IO_Mode; default_tio_mode: Terminal_IO_Mode; - blocking_tio_mode: Terminal_IO_Mode; - unblocking_tio_mode: Terminal_IO_Mode; + human_tio_mode: Terminal_IO_Mode; #scope_export OS_prepare_terminal :: () { - // TODO Required to do unlocking input. 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? + default_tio_mode = initial_tio_mode; + default_tio_mode.c_iflag &= ~(.IGNBRK | .BRKINT | .PARMRK | .ISTRIP | .INLCR | .IGNCR | .ICRNL | .IXOFF); + default_tio_mode.c_oflag &= ~(.OPOST); + default_tio_mode.c_cflag &= ~(CSIZE | .PARENB); + default_tio_mode.c_lflag &= ~(.ECHOCTL |.ECHO | .ECHOE | .ECHOKE); - 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); - - 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; + human_tio_mode = default_tio_mode; + human_tio_mode.c_iflag |= (.IXOFF | .ICRNL); + human_tio_mode.c_lflag |= (.NOKERNINFO | .ECHO | .ECHOE | .ECHOKE); tcsetattr(STDIN_FILENO, 0, *default_tio_mode); } OS_reset_terminal :: () { - tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); // return echo + tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); } OS_get_terminal_size :: () -> rows: int, columns: int { @@ -125,19 +190,16 @@ OS_get_terminal_size :: () -> rows: int, columns: int { 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) { + tio_mode: *Terminal_IO_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) + tio_mode = *human_tio_mode; case; - // TODO ERROR + tio_mode = *default_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 = "" { diff --git a/TUI/windows.jai b/TUI/windows.jai index 036b33b..68c2a1d 100644 --- a/TUI/windows.jai +++ b/TUI/windows.jai @@ -26,35 +26,36 @@ // https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror GetLastError :: () -> s32 #foreign kernel32; - ENABLE_VIRTUAL_TERMINAL_INPUT :: 0x0200; - - ENABLE_PROCESSED_OUTPUT :: 0x0001; - ENABLE_WRAP_AT_EOL_OUTPUT :: 0x0002; - ENABLE_VIRTUAL_TERMINAL_PROCESSING :: 0x0004; - DISABLE_NEWLINE_AUTO_RETURN :: 0x0008; - ENABLE_LVB_GRID_WORLDWIDE :: 0x0010; - - + // https://learn.microsoft.com/en-us/windows/console/setconsolemode - Console_Mode :: enum_flags u32 { + Console_Input_Mode :: enum_flags u32 { _UNUSED_0001_; - ENABLE_LINE_INPUT; // If enable, ReadFile or ReadConsole function return on CR; otherwise they return when one or more characters are available. - ENABLE_ECHO_INPUT; // Echoes input on screen. Only available if ENABLE_LINE_INPUT is set. + ENABLE_LINE_INPUT; // If enable, ReadFile or ReadConsole function return on CR; otherwise they return when one or more characters are available. + ENABLE_ECHO_INPUT; // Echoes input on screen. Only available if ENABLE_LINE_INPUT is set. _UNUSED_0008_; - ENABLE_MOUSE_INPUT; // - ENABLE_INSERT_MODE; // If enabled, text entered will be inserted at the current cursor location and all text following that location will not be overwritten. When disabled, all following text will be overwritten. - _UNSED_0040_; - _UNSED_0080_; - _UNSED_0100_; - ENABLE_VIRTUAL_TERMINAL_INPUT; // - _UNSED_0400_; - _UNSED_0800_; - _UNSED_1000_; - _UNSED_2000_; - _UNSED_4000_; - _UNSED_8000_; + ENABLE_MOUSE_INPUT; // + ENABLE_INSERT_MODE; // If enabled, text entered will be inserted at the current cursor location and all text following that location will not be overwritten. When disabled, all following text will be overwritten. + _UNUSED_0040_; + _UNUSED_0080_; + _UNUSED_0100_; + ENABLE_VIRTUAL_TERMINAL_INPUT; // + _UNUSED_0400_; + _UNUSED_0800_; + } + + // https://learn.microsoft.com/en-us/windows/console/setconsolemode + Console_Output_Mode :: enum_flags u32 { + ENABLE_PROCESSED_OUTPUT; // + ENABLE_WRAP_AT_EOL_OUTPUT; // + ENABLE_VIRTUAL_TERMINAL_PROCESSING; // + DISABLE_NEWLINE_AUTO_RETURN; // + ENABLE_LVB_GRID_WORLDWIDE; // + _UNUSED_0020_; + _UNUSED_0040_; + _UNUSED_0080_; } + // https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types SHORT :: s16; WORD :: u16; @@ -75,7 +76,6 @@ CONSOLE_SCREEN_BUFFER_INFO :: struct { dwSize : COORD; dwCursorPosition : COORD; - wAttributes : WORD; srWindow : SMALL_RECT; dwMaximumWindowSize : COORD; @@ -84,13 +84,13 @@ stdin: HANDLE; initial_stdin_mode: u32; - default_stdin_mode: Console_Mode; - blocking_stdin_mode: Console_Mode; - unblocking_stdin_mode: Console_Mode; + default_stdin_mode: Console_Input_Mode; + blocking_stdin_mode: Console_Input_Mode; + unblocking_stdin_mode: Console_Input_Mode; stdout: HANDLE; initial_stdout_mode: u32; - default_stdout_mode: Console_Mode; + default_stdout_mode: Console_Output_Mode; #scope_export @@ -109,7 +109,7 @@ OS_prepare_terminal :: () { print("Failed to get input mode.", to_standard_error = true); return; } - default_stdin_mode = (cast(Console_Mode) initial_stdin_mode) | .ENABLE_VIRTUAL_TERMINAL_INPUT; + default_stdin_mode = (cast(Console_Input_Mode) initial_stdin_mode) | .ENABLE_VIRTUAL_TERMINAL_INPUT; blocking_stdin_mode = default_stdin_mode | .ENABLE_LINE_INPUT | .ENABLE_ECHO_INPUT; unblocking_stdin_mode = default_stdin_mode & ~.ENABLE_LINE_INPUT & ~.ENABLE_ECHO_INPUT; @@ -128,7 +128,7 @@ OS_prepare_terminal :: () { print("Failed to get output mode.", to_standard_error = true); return; } - default_stdout_mode = (cast(Console_Mode) initial_stdout_mode) | ENABLE_PROCESSED_OUTPUT| ENABLE_VIRTUAL_TERMINAL_PROCESSING; + default_stdout_mode = (cast(Console_Output_Mode) initial_stdout_mode) | .ENABLE_PROCESSED_OUTPUT| .ENABLE_VIRTUAL_TERMINAL_PROCESSING; if SetConsoleMode(stdout, xx default_stdout_mode) == false { print("Failed to set output mode: %.", GetLastError(), to_standard_error = true); @@ -157,6 +157,8 @@ OS_get_terminal_size :: () -> rows: int, columns: int { 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; -- cgit v1.2.3