aboutsummaryrefslogtreecommitdiff
path: root/TUI/unix.jai
diff options
context:
space:
mode:
Diffstat (limited to 'TUI/unix.jai')
-rw-r--r--TUI/unix.jai141
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;
+}