terminal.go (2215B)
1 package terminal 2 3 import ( 4 "fmt" 5 "syscall" 6 "unsafe" 7 ) 8 9 // TODO: support terminfo. 10 11 const ( 12 ClearScreen = "\u001Bc" 13 CursorHide = "\u001B[?25l" 14 CursorShow = "\u001B[?25h" 15 CursorTopLeft = "\u001B[H" 16 EraseLine = "\u001B[K" 17 18 SetCursorFmt = "\u001B[%d;%dH" 19 ) 20 21 type Terminal struct { 22 fd uintptr 23 termios *syscall.Termios 24 } 25 26 type Size struct { 27 Rows uint16 28 Columns uint16 29 XPixels uint16 30 YPixels uint16 31 } 32 33 func New(fd uintptr) (*Terminal, error) { 34 var t = &Terminal{ 35 termios: &syscall.Termios{}, 36 } 37 38 if _, _, err := syscall.Syscall( 39 syscall.SYS_IOCTL, 40 fd, 41 syscall.TCGETS, 42 uintptr(unsafe.Pointer(t.termios)), 43 ); err != 0 { 44 return nil, fmt.Errorf("ioctl syscall: %w", err) 45 } 46 47 return t, nil 48 } 49 50 // Reset the terminal into the original termios. 51 func (t *Terminal) Reset() error { 52 if _, _, err := syscall.Syscall( 53 syscall.SYS_IOCTL, 54 t.fd, 55 uintptr(syscall.TCSETS), 56 uintptr(unsafe.Pointer(t.termios)), 57 ); err != 0 { 58 return fmt.Errorf("ioctl syscall: %w", err) 59 } 60 61 return nil 62 } 63 64 // SetRaw enables raw mode. 65 func (t *Terminal) SetRaw() error { 66 var termios = *t.termios 67 termios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON 68 termios.Oflag &^= syscall.OPOST 69 termios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN 70 termios.Cflag &^= syscall.CSIZE | syscall.PARENB 71 termios.Cflag |= syscall.CS8 72 termios.Cc[syscall.VMIN] = 1 73 termios.Cc[syscall.VTIME] = 0 74 75 if _, _, err := syscall.Syscall( 76 syscall.SYS_IOCTL, 77 t.fd, 78 uintptr(syscall.TCSETS), 79 uintptr(unsafe.Pointer(&termios)), 80 ); err != 0 { 81 return fmt.Errorf("ioctl syscall: %w", err) 82 } 83 84 return nil 85 } 86 87 func (t *Terminal) size() (*Size, error) { 88 var s = &Size{} 89 if _, _, err := syscall.Syscall( 90 syscall.SYS_IOCTL, 91 t.fd, 92 syscall.TIOCGWINSZ, 93 uintptr(unsafe.Pointer(s)), 94 ); err != 0 { 95 return nil, fmt.Errorf("ioctl syscall: %w", err) 96 } 97 98 return s, nil 99 } 100 101 // Size returns the zero-indexed size of the terminal. 102 func (t *Terminal) Size() (*Size, error) { 103 size, err := t.size() 104 if err != nil { 105 return nil, err 106 } 107 108 size.Columns-- 109 size.Rows-- 110 111 return size, nil 112 }