#scope_file #import "POSIX"; #import "System"; // Required to do unlocking input. libc :: #system_library "libc"; // TODO Remote this. cfmakeraw :: (termios: *Terminal_IO_Mode) -> void #foreign 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; // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc; // 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. TCSADRAIN :: 1; // Change when pending output is written. TCSAFLUSH :: 2; // Flush pending input before changing. } // Input modes. Input_Modes :: enum_flags u32 { IGNBRK :: 0000001; // Ignore break condition. BRKINT :: 0000002; // Signal interrupt on break. IGNPAR :: 0000004; // Ignore characters with parity errors. PARMRK :: 0000010; // Mark parity and framing errors. INPCK :: 0000020; // Enable input parity check. ISTRIP :: 0000040; // Strip 8th bit off characters. INLCR :: 0000100; // Map NL to CR on input. IGNCR :: 0000200; // Ignore CR. ICRNL :: 0000400; // Map CR to NL on input. IUCLC :: 0001000; // Translate upper case input to lower case (not in POSIX). IXON :: 0002000; // Enable start/stop output control. IXANY :: 0004000; // Any character will restart after stop. IXOFF :: 0010000; // Enable start/stop input control. IMAXBEL :: 0020000; // Ring bell when input queue is full (not in POSIX). IUTF8 :: 0040000; // Input is UTF8 (not in POSIX). } // Output modes. Output_Modes :: enum_flags u32 { OPOST :: 0000001; // Perform output processing. OLCUC :: 0000002; // Map lowercase characters to uppercase on output (not in POSIX). ONLCR :: 0000004; // Map NL to CR-NL on output. OCRNL :: 0000010; // Map CR to NL. ONOCR :: 0000020; // Discard CR's when on column 0. ONLRET :: 0000040; // Move to column 0 on NL. OFILL :: 0000100; // Send fill characters for delays. OFDEL :: 0000200; // Fill is DEL. VTDLY :: 0040000; // Select vertical-tab delays: VT0 :: 0000000; // Vertical-tab delay type 0. VT1 :: 0040000; // Vertical-tab delay type 1. } // Control modes. Control_Modes :: enum u32 { CS5 :: 0000000; // 5 bits per byte. CS6 :: 0000020; // 6 bits per byte. CS7 :: 0000040; // 7 bits per byte. CS8 :: 0000060; // 8 bits per byte. CSIZE :: 0000060; // Number of bits per byte (mask). CSTOPB :: 0000100; // Two stop bits instead of one. CREAD :: 0000200; // Enable receiver. PARENB :: 0000400; // Parity enable. PARODD :: 0001000; // Odd parity instead of even. HUPCL :: 0002000; // Hang up on last close. CLOCAL :: 0004000; } // Local modes. Local_Modes :: enum_flags u32 { ISIG :: 0000001; // Enable signals. ICANON :: 0000002; // Do erase and kill processing. ECHO :: 0000010; // Enable echo. ECHOE :: 0000020; // Visual erase for ERASE. ECHOK :: 0000040; // Echo NL after KILL. ECHONL :: 0000100; // Echo NL even if ECHO is off. NOFLSH :: 0000200; // Disable flush after interrupt. TOSTOP :: 0000400; // Send SIGTTOU for background output. IEXTEN :: 0100000; // Enable DISCARD and LNEXT. } // Control Characters Control_Chars :: enum u8 { VINTR :: 0; VQUIT :: 1; VERASE :: 2; VKILL :: 3; VEOF :: 4; VTIME :: 5; // Time-out value (tenths of a second) [!ICANON]. VMIN :: 6; // Minimum number of bytes read at once [!ICANON]. VSWTC :: 7; VSTART :: 8; VSTOP :: 9; VSUSP :: 10; VEOL :: 11; VREPRINT :: 12; VDISCARD :: 13; VWERASE :: 14; VLNEXT :: 15; VEOL2 :: 16; } // The struct termios. Terminal_IO_Mode :: struct { 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]Control_Chars;// Control characters. c_ispeed : u32; // Input speed (baud rates). c_ospeed : u32; // Output speed (baud rates). } initial_tio_mode: Terminal_IO_Mode; raw_tio_mode: Terminal_IO_Mode; #scope_export OS_prepare_terminal :: () { error: s32; tcgetattr(STDIN_FILENO, *initial_tio_mode); // TODO Log on error. 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); raw_tio_mode.c_lflag &= ~(.ECHO | .ECHONL | .ICANON | .ISIG | .IEXTEN); raw_tio_mode.c_cflag &= ~(.CSIZE | .PARENB); raw_tio_mode.c_cflag |= .CS8; raw_tio_mode.c_cc[Control_Chars.VMIN] = 0; raw_tio_mode.c_cc[Control_Chars.VTIME] = 1; tcsetattr(STDIN_FILENO, 0, *raw_tio_mode); // TODO Log on error. } OS_reset_terminal :: () { tcsetattr(STDIN_FILENO, 0, *initial_tio_mode); // TODO Log on error. } OS_get_terminal_size :: () -> rows: int, columns: int { auto_release_temp(); flush_input(); // input := talloc_string(64); write_string(Commands.QueryWindowSizeInChars); // input.count = OS_read_input(input.data, input.count); sleep_milliseconds(1); //TODO WHYYYY??? GOD DAMIT THREADS... input := get_str(); // input[0] = #char "x"; // print(">%<", xx input); // print("#<#"); // Expected message format: [8;;t // where is the number of rows and of columns. assert( input[0] == #char "\e" && input[1] == #char "[" && input[2] == #char "8", //input[input.count-1] == #char "t", "Query windows size in chars returned invalid response."); parts := split(input, ";"); rows := parse_int(*parts[1]); columns := parse_int(*parts[2]); return rows, columns; } // OS_set_input_mode :: (mode: Input_Mode) { // tio_mode: *Terminal_IO_Mode = ---; // if mode == { // case .HUMAN; // tio_mode = *human_tio_mode; // case; // tio_mode = *cooked_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 = "" { 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; }