#+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
}