// +build linux darwin freebsd netbsd openbsd solaris dragonfly // +build !appengine package pb import ( "errors" "fmt" "os" "os/signal" "runtime" "sync" "syscall" "unsafe" ) const ( TIOCGWINSZ = 0x5413 TIOCGWINSZ_OSX = 1074295912 ) var tty *os.File var ErrPoolWasStarted = errors.New("Bar pool was started") var echoLocked bool var echoLockMutex sync.Mutex func init() { var err error tty, err = os.Open("/dev/tty") if err != nil { tty = os.Stdin } } // terminalWidth returns width of the terminal. func terminalWidth() (int, error) { w := new(window) tio := syscall.TIOCGWINSZ if runtime.GOOS == "darwin" { tio = TIOCGWINSZ_OSX } res, _, err := syscall.Syscall(sysIoctl, tty.Fd(), uintptr(tio), uintptr(unsafe.Pointer(w)), ) if int(res) == -1 { return 0, err } return int(w.Col), nil } var oldState syscall.Termios func lockEcho() (quit chan int, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if echoLocked { err = ErrPoolWasStarted return } echoLocked = true fd := tty.Fd() if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't get terminal settings: %v", e) return } newState := oldState newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't set terminal settings: %v", e) return } quit = make(chan int, 1) go catchTerminate(quit) return } func unlockEcho() (err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if !echoLocked { return } echoLocked = false fd := tty.Fd() if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't set terminal settings") } return } // listen exit signals and restore terminal state func catchTerminate(quit chan int) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL) defer signal.Stop(sig) select { case <-quit: unlockEcho() case <-sig: unlockEcho() } }