#+build linux package console import "base:intrinsics" import "core:fmt" import "core:io" import "core:os" import "core:sys/unix" Console :: struct { input_stream: io.Reader, prev_input_state: termios, width: int, height: int, destroy: proc(con: ^Console), clear: proc(con: ^Console), write: proc(con: ^Console, msg: string), write_at: proc(con: ^Console, msg: string, x, y: int), putch: proc(con: ^Console, c: rune, x, y: int), putch_s: proc(con: ^Console, c: rune, x, y: int) -> string, wait_for_input: proc(con: ^Console) -> Keys, read_input: proc(con: ^Console) -> Keys, get_size: proc(con: ^Console) -> (int, int), } @(private) TCGETS :: 0x5401 @(private) TCSETS :: 0x5402 @(private) TIOCWINSZ :: 0x5413 ECHOKE :: (1 << 0) ECHOE :: (1 << 1) ECHOK :: (1 << 2) ECHO :: (1 << 3) ECHONL :: (1 << 4) ECHOPRT :: (1 << 5) ECHOCTL :: (1 << 6) ISIG :: (1 << 7) ICANON :: (1 << 8) ALTWERASE :: (1 << 9) IEXTEN :: (1 << 10) EXTPROC :: (1 << 11) TOSTOP :: (1 << 22) FLUSHO :: (1 << 23) XCASE :: (1 << 24) NOKERNINFO :: (1 << 25) PENDIN :: (1 << 29) NOFLSH :: (1 << 31) @(private) winsize :: struct { ws_row: u16, ws_col: u16, ws_xpixel: u16, ws_ypixel: u16, } @(private) termios :: struct { c_iflag: u32, c_oflag: u32, c_cflag: u32, c_lflag: u32, c_cc: [20]byte, c_ispeed: i32, c_ospeed: i32, } create :: proc() -> Console { con := Console { input_stream = io.Reader(os.stream_from_handle(os.stdin)), destroy = con_destroy, clear = con_clear, write = con_write, write_at = con_write_at, putch = con_putch, putch_s = con_putch_s, wait_for_input = con_wait_for_input, get_size = con_get_size, } con->write(CURSOR_HIDE) term: termios intrinsics.syscall(unix.SYS_ioctl, uintptr(os.stdin), TCGETS, uintptr(&term)) con.prev_input_state = term term.c_lflag = ISIG | EXTPROC intrinsics.syscall(unix.SYS_ioctl, uintptr(os.stdin), TCSETS, uintptr(&term)) con.width, con.height = con->get_size() return con } @(private) con_destroy :: proc(con: ^Console) { intrinsics.syscall(unix.SYS_ioctl, uintptr(os.stdin), TCSETS, uintptr(&con.prev_input_state)) con->write(CURSOR_SHOW) } @(private) con_clear :: proc(con: ^Console) { con->write(CLEAR) } @(private) con_write :: proc(con: ^Console, msg: string) { fmt.print(msg) } @(private) con_write_at :: proc(con: ^Console, msg: string, x, y: int) { con->write(fmt.tprintf(CURSOR_POSITION + "%s", y, x, msg)) } @(private) con_putch :: proc(con: ^Console, c: rune, x, y: int) { con->write(fmt.tprintf(CURSOR_POSITION + "%c", y, x, c)) } @(private) con_putch_s :: proc(con: ^Console, c: rune, x, y: int) -> string { return fmt.tprintf(CURSOR_POSITION + "%c", y, x, c) } @(private) con_wait_for_input :: proc(con: ^Console) -> Keys { buffer: [256]u8 to_read := 10 io.read(con.input_stream, buffer[:], &to_read) if (buffer[0] == 27 && buffer[1] == 0) || buffer[0] == 3 || buffer[0] == 113 { // 27 = Escape, 3 = CTRL+C, 113 = q return .quit } else if buffer[0] == 32 { // 32 = Space return .forward } else { input := [?]u8{buffer[0], buffer[1], buffer[2]} if (input == [?]u8{27, 91, 68}) { return .back } else if (input == [?]u8{27, 91, 67}) { return .forward } } return .unk } @(private) con_get_size :: proc(con: ^Console) -> (width: int, height: int) { size: winsize intrinsics.syscall(unix.SYS_ioctl, uintptr(os.stdin), TIOCWINSZ, uintptr(&size)) width = int(size.ws_col) height = int(size.ws_row) return }