#+private #+build linux, darwin package vtty import "base:intrinsics" import "core:fmt" import "core:io" import "core:os" _Console :: struct { input_stream: io.Reader, prev_input_state: Termios, } when ODIN_OS == .Linux { IOC_NRBITS :: 8 IOC_TYPEBITS :: 8 IOC_SIZEBITS :: 14 IOC_DIRBITS :: 2 IOC_NRSHIFT :: 0 IOC_TYPESHIFT :: IOC_NRSHIFT + IOC_NRBITS IOC_SIZESHIFT :: IOC_TYPESHIFT + IOC_TYPEBITS IOC_DIRSHIFT :: IOC_SIZESHIFT + IOC_SIZEBITS IOC_NONE: uint : 0 IOC_WRITE: uint : 1 IOC_READ: uint : 2 // TODO: Fix these definitions TCGETS :: 0x5401 TCSETS :: 0x5402 TIOCWINSZ :: 0x5413 } when ODIN_OS == .Darwin { IOCPARM_MASK :: 0x1fff IOC_VOID: u32 : 0x20000000 IOC_OUT: u32 : 0x40000000 IOC_IN: u32 : 0x80000000 IOC_INOUT :: (IOC_IN | IOC_OUT) _IOC :: proc(inout: u32, group: u8, num: u32, len: u32) -> uintptr { return uintptr(inout | ((len & IOCPARM_MASK) << 16) | (u32(group) << 8) | num) } _IOR :: proc(group: u8, num: u32, type: typeid) -> uintptr { return _IOC(IOC_OUT, group, num, size_of(type)) } _IOW :: proc(group: u8, num: u32, type: typeid) -> uintptr { return _IOC(IOC_IN, group, num, size_of(type)) } _IOWR :: proc(group: u8, num: u32, type: typeid) -> uintptr { return _IOC(IOC_INOUT, group, num, size_of(type)) } // TIOCGETA // TCGETS :: uintptr(IOC_OUT | ((size_of(Termios) & IOCPARM_MASK) << 16) | ('t' << 8) | 19) TCGETS := _IOR('t', 19, Termios) // TIOCSETA // TCSETS :: uintptr(IOC_IN | ((size_of(Termios) & IOCPARM_MASK) << 16) | ('t' << 8) | 20) TCSETS := _IOW('t', 20, Termios) // TIOCGWINSZ //TIOCWINSZ :: uintptr(IOC_OUT | ((size_of(Win_Size) & IOCPARM_MASK) << 16) | ('t' << 8) | 104) TIOCWINSZ := _IOR('t', 104, Win_Size) /* #define _IOC(inout, group, num, len) \ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) #define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0) #define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t)) #define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t)) /* this should be _IORW, but stdio got there first */ #define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) */ // #define TIOCGETA _IOR('t', 19, struct termios) /* get termios struct */ // #define TIOCSETA _IOW('t', 20, struct termios) /* set termios struct */ // #define TIOCGWINSZ _IOR('t', 104, struct winsize) /* get window size */ } 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) Win_Size :: struct { ws_row: u16, ws_col: u16, ws_xpixel: u16, ws_ypixel: u16, } 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(con: ^Console) { con.input_stream = os.to_reader(os.stdin) _write(con, CURSOR_HIDE) term: Termios intrinsics.syscall(ioctl, uintptr(os.stdin), TCGETS, uintptr(&term)) con.prev_input_state = term term.c_lflag = ISIG | EXTPROC intrinsics.syscall(ioctl, uintptr(os.stdin), TCSETS, uintptr(&term)) con.width, con.height = _get_size(con) } _destroy :: proc(con: ^Console) { intrinsics.syscall(ioctl, uintptr(os.stdin), TCSETS, uintptr(&con.prev_input_state)) _write(con, CURSOR_SHOW) } _clear :: proc(con: ^Console) { _write(con, CLEAR) } _write :: proc(con: ^Console, msg: string) { fmt.print(msg) } _write_at :: proc(con: ^Console, msg: string, x, y: int) { _write(con, fmt.tprintf(CURSOR_POSITION + "%s", y, x, msg)) } _putch :: proc(con: ^Console, c: rune, x, y: int) { _write(con, fmt.tprintf(CURSOR_POSITION + "%c", y, x, c)) } _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 } _get_size :: proc(con: ^Console) -> (width: int, height: int) { size: Win_Size intrinsics.syscall(ioctl, uintptr(os.stdin), TIOCWINSZ, uintptr(&size)) width = int(size.ws_col) height = int(size.ws_row) return }