#module_parameters(ENABLE_SIGINT := true, ENABLE_SIGQUIT := true); #import "Basic"; // TODO Eventually, I'll need to add #scope_module ... terminal_state : struct { // size : ivec2; width: s32; height: s32; // cursor := ivec2.{-1, -1}; // {-1, -1} if cursor hidden x: s32 = -1; y: s32 = -1; last_mode : Graphics_Mode; } #if OS == .LINUX { #import "POSIX"; __term : My_Termios; My_Termios :: struct { c_iflag : u32; // Input mode flags. c_oflag : u32; // Output mode flags. c_cflag : u32; // Control mode flags. c_lflag : u32; // Local mode flags. c_line : u8; // Line discipline. c_cc : [32]u8; // Control characters. c_ispeed : u32; // Input speed. c_ospeed : u32; // Output speed. } 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; // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html tcgetattr :: (fd : s32, termios_p : *My_Termios) -> s32 #foreign libc; } else #if OS == .WINDOWS { #run print("TODO tio\n", to_standard_error = true); } else { #run assert(false, "unsupported OS\n"); } Graphics_Mode :: struct { foreground : Color; background : Color; attr_flags : enum_flags u8 { F_BOLD :: 0x1; F_DIM :: 0x2; F_ITALIC :: 0x4; F_UNDERLINE :: 0x8; F_BLINKING :: 0x10; F_INVERSE :: 0x20; F_STRIKETHROUGH :: 0x40; } // attrs : [MAX_ATTRS]bool; // 0 - bold (on/off/keep) // 1 - dim/faint // 2 - italic // 3 - underline // 4 - blinking // 5 - inverse // 6 - strikethrough fcol256 : u8; bcol256 : u8; } Key :: enum u64 { READ_ERROR :: 0xffffffff_ffffffff; UP :: 0x41_5b1b; DOWN :: 0x42_5b1b; RIGHT :: 0x43_5b1b; LEFT :: 0x44_5b1b; CTRL_UP :: 0x4135_3b315b1b; CTRL_DOWN :: 0x4235_3b315b1b; CTRL_RIGHT :: 0x4335_3b315b1b; CTRL_LEFT :: 0x4435_3b315b1b; SHIFT_UP :: 0x4132_3b315b1b; SHIFT_DOWN :: 0x4232_3b315b1b; SHIFT_RIGHT :: 0x4332_3b315b1b; SHIFT_LEFT :: 0x4432_3b315b1b; CTRL_SHIFT_UP :: 0x4136_3b315b1b; CTRL_SHIFT_DOWN :: 0x4236_3b315b1b; CTRL_SHIFT_RIGHT:: 0x4336_3b315b1b; CTRL_SHIFT_LEFT :: 0x4436_3b315b1b; ALT_UP :: 0x4133_3b315b1b; ALT_DOWN :: 0x4233_3b315b1b; ALT_RIGHT :: 0x4333_3b315b1b; ALT_LEFT :: 0x4433_3b315b1b; CTRL_C :: 0x03; CTRL_V :: 0x16; CTRL_X :: 0x18; CTRL_Y :: 0x19; CTRL_Z :: 0x1A; CTRL_BACKSLASH :: 0x1C; // ALT_SHIFT_UP :: 0x4133_3b315b1b; // ALT_SHIFT_DOWN :: 0x4233_3b315b1b; // ALT_SHIFT_RIGHT :: 0x4333_3b315b1b; // ALT_SHIFT_LEFT :: 0x4433_3b315b1b; ENTER :: 0x0D; ESCAPE :: 0x1B; BACKSPACE :: 0x7F; DELETE :: 0x7E335B1B; } Color :: enum u8 { RESET :: 0; DEFAULT :: 39; COLOR256 :: 38; BLACK :: 30; RED :: 31; GREEN :: 32; YELLOW :: 33; BLUE :: 34; MAGENTA :: 35; CYAN :: 36; WHITE :: 37; BRIGHT_BLACK :: 90; BRIGHT_RED :: 91; BRIGHT_GREEN :: 92; BRIGHT_YELLOW :: 93; BRIGHT_BLUE :: 94; BRIGHT_MAGENTA :: 95; BRIGHT_CYAN :: 96; BRIGHT_WHITE :: 97; } update_terminal_size :: () { #if OS == .LINUX { TIOCGWINSZ :: 0x5413; winsize : struct { ws_row, ws_col, ws_xpixel, ws_ypixel : u16; } ioctl(0, TIOCGWINSZ, *winsize); terminal_state.width = xx winsize.ws_col; terminal_state.height = xx winsize.ws_row; } else #if OS == .WINDOWS { print("TODO tio\n", to_standard_error = true); } else { assert(false, "unsupported OS\n"); } } initialize :: () { #if OS == .LINUX { tui_write("\e[?25l"); // hide cursor tui_write("\e7"); // save cursor position tui_write("\e[?1047h"); // switch screen tui_write("\e[?30l"); // hide scrollbar tui_write("\e[H"); // move to top left corner tui_write("\e[0m"); // set default mode tui_write("\e[2J"); // clear screen { tcgetattr(STDIN_FILENO, *__term); term_new := __term; 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; tcsetattr(STDIN_FILENO, 0, *term_new); } update_terminal_size(); } else #if OS == .WINDOWS { print("TODO tio\n", to_standard_error = true); } else { assert(false, "unsupported OS\n"); } } terminate :: () { #if OS == .LINUX { tcsetattr(STDIN_FILENO, 0, *__term); // return echo tui_write("\e[?47l"); // restore screen tui_write("\e8"); // restore cursor tui_write("\e[?25h"); // show cursor tui_write("\e[?30h"); // show scrollbar terminal_state = .{}; } else #if OS == .WINDOWS { print("TODO tio\n", to_standard_error = true); } else { assert(false, "unsupported OS\n"); } } tui_write :: (str : string) { #if OS == .LINUX { write(STDIN_FILENO, str.data, xx str.count); } else #if OS == .WINDOWS { #run print("TODO tio\n", to_standard_error = true); } else { #run assert(false, "unsupported OS\n"); } } buffer: [..] Key; tui_ungetch :: (key: Key) { array_add(*buffer, key); } tui_getch :: (block := true) -> Key { #if OS == .LINUX { buf : Key = xx 0; if buffer.count > 0 return pop(*buffer); l := read(STDIN_FILENO, (cast(*u8)*buf), 8); //!!! check_signal :: inline (key : Key) { #if ENABLE_SIGINT if key == .CTRL_C raise(SIGINT); //!!! #if ENABLE_SIGQUIT if key == .CTRL_BACKSLASH raise(SIGQUIT); } check_signal(buf); return ifx l <= 0 then Key.READ_ERROR else buf; } else #if OS == .WINDOWS { print("TODO tio\n", to_standard_error = true); return .READ_ERROR; } else { assert(false, "unsupported OS\n"); return .READ_ERROR; } } tui_clear_screen :: inline () { tui_write("\e[2J"); }