src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }