#+private #+build linux package console import "core:fmt" import "core:io" import "core:os" import "core:sys/linux" import "core:sys/posix" _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) IUTF8 :: (1 << 15) 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, } should_quit_flag := false should_quit_handler :: proc "c" (sig: posix.Signal) { should_quit_flag = true } _create :: proc() -> Console { con: Console con.input_stream = io.Reader(os.stream_from_handle(os.stdin)) _write(con, CURSOR_HIDE) term: Termios linux.ioctl(linux.Fd(os.stdin), TCGETS, uintptr(&term)) con.prev_input_state = term term.c_lflag = ISIG | EXTPROC | IUTF8 linux.ioctl(linux.Fd(os.stdin), TCSETS, uintptr(&term)) flags, _ := linux.fcntl(linux.Fd(os.stdin), linux.FCntl_Command_GETFL.GETFL) flags |= {.NONBLOCK} linux.fcntl(linux.Fd(os.stdin), linux.FCntl_Command_SETFL.SETFL, flags) con.width, con.height = _get_size(con) posix.signal(.SIGINT, should_quit_handler) posix.signal(.SIGTERM, should_quit_handler) return con } _destroy :: proc(con: ^Console) { linux.ioctl(linux.Fd(os.stdin), TCSETS, uintptr(&con.prev_input_state)) _write(con^, CURSOR_SHOW) } _should_quit :: proc() -> bool { flag := should_quit_flag return flag } _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 read, _ := io.read(con.input_stream, buffer[:], &to_read) if read == 0 { return .none } 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 linux.ioctl(linux.Fd(os.stdin), TIOCWINSZ, uintptr(&size)) width = int(size.ws_col) height = int(size.ws_row) return }