diff --git a/nonblock_bsd.go b/nonblock_bsd.go index df8fc054..49d4411a 100644 --- a/nonblock_bsd.go +++ b/nonblock_bsd.go @@ -30,8 +30,7 @@ import ( // that loop. Normally we use VMIN 1 and VTIME 0, which ensures we pick up bytes when // they come but don't spin burning cycles. func (t *tScreen) nonBlocking(on bool) { - fd := int(os.Stdin.Fd()) - tio, err := unix.IoctlGetTermios(fd, unix.TIOCGETA) + tio, err := unix.IoctlGetTermios(t.inFd, unix.TIOCGETA) if err != nil { return } @@ -44,7 +43,7 @@ func (t *tScreen) nonBlocking(on bool) { tio.Cc[unix.VMIN] = 1 } - _ = syscall.SetNonblock(fd, on) + _ = syscall.SetNonblock(t.inFd, on) // We want to set this *right now*. - _ = unix.IoctlSetTermios(fd, unix.TIOCSETA, tio) + _ = unix.IoctlSetTermios(t.inFd, unix.TIOCSETA, tio) } diff --git a/nonblock_unix.go b/nonblock_unix.go index c4c374cc..f88ce4f8 100644 --- a/nonblock_unix.go +++ b/nonblock_unix.go @@ -17,7 +17,6 @@ package tcell import ( - "os" "syscall" "golang.org/x/sys/unix" @@ -32,8 +31,7 @@ import ( // that loop. Normally we use VMIN 1 and VTIME 0, which ensures we pick up bytes when // they come but don't spin burning cycles. func (t *tScreen) nonBlocking(on bool) { - fd := int(os.Stdin.Fd()) - tio, err := unix.IoctlGetTermios(fd, unix.TCGETS) + tio, err := unix.IoctlGetTermios(t.inFd, unix.TCGETS) if err != nil { return } @@ -46,7 +44,7 @@ func (t *tScreen) nonBlocking(on bool) { tio.Cc[unix.VMIN] = 1 } - _ = syscall.SetNonblock(fd, on) + _ = syscall.SetNonblock(t.inFd, on) // We want to set this *right now*. - _ = unix.IoctlSetTermios(fd, unix.TCSETS, tio) + _ = unix.IoctlSetTermios(t.inFd, unix.TCSETS, tio) } diff --git a/tscreen.go b/tscreen.go index 3ee78b30..a9cffef9 100644 --- a/tscreen.go +++ b/tscreen.go @@ -16,6 +16,7 @@ package tcell import ( "bytes" + "errors" "io" "os" "strconv" @@ -82,6 +83,7 @@ type tScreen struct { fini bool cells CellBuffer in *os.File + inFd int out *os.File buffering bool // true if we are collecting writes to buf instead of sending directly to out buf bytes.Buffer @@ -1484,20 +1486,29 @@ func (t *tScreen) mainLoop(stopQ chan struct{}) { } func (t *tScreen) inputLoop(stopQ chan struct{}) { - defer t.wg.Done() + var ( + n int + err error + ) for { select { case <-stopQ: return default: } + + err = t.in.SetReadDeadline(time.Now().Add(250 * time.Millisecond)) + if err != nil { + panic(err) + } + chunk := make([]byte, 128) - n, e := t.in.Read(chunk) - switch e { - case nil: - default: - _ = t.PostEvent(NewEventError(e)) + n, err = t.in.Read(chunk) + if errors.Is(err, os.ErrDeadlineExceeded) { + continue + } else if err != nil { + _ = t.PostEvent(NewEventError(err)) return } if n > 0 { @@ -1575,7 +1586,6 @@ func (t *tScreen) HasKey(k Key) bool { func (t *tScreen) Resize(int, int, int, int) {} - func (t *tScreen) Suspend() error { t.disengage() return nil diff --git a/tscreen_unix.go b/tscreen_unix.go index 725785c3..7b56d758 100644 --- a/tscreen_unix.go +++ b/tscreen_unix.go @@ -34,10 +34,10 @@ func (t *tScreen) engage() error { if t.stopQ != nil { return errors.New("already engaged") } - if _, err := term.MakeRaw(int(t.in.Fd())); err != nil { + if _, err := term.MakeRaw(t.inFd); err != nil { return err } - if w, h, err := term.GetSize(int(t.in.Fd())); err == nil && w != 0 && h != 0 { + if w, h, err := term.GetSize(t.inFd); err == nil && w != 0 && h != 0 { t.cells.Resize(w, h) } stopQ := make(chan struct{}) @@ -93,7 +93,7 @@ func (t *tScreen) disengage() { t.enablePasting(false) // restore the termios that we were started with - _ = term.Restore(int(t.in.Fd()), t.saved) + _ = term.Restore(t.inFd, t.saved) } @@ -102,15 +102,19 @@ func (t *tScreen) disengage() { // so that it can be restored when the application terminates. func (t *tScreen) initialize() error { var err error - t.out = os.Stdout - if t.in, err = os.Open("/dev/tty"); err != nil { + t.inFd, err = syscall.Open("/dev/tty", syscall.O_RDONLY|syscall.O_NONBLOCK, 0644) + if err != nil { return err } + t.in = os.NewFile(uintptr(t.inFd), "/dev/tty") + + t.out = os.Stdout - t.saved, err = term.GetState(int(t.in.Fd())) - if err == nil { - return nil + t.saved, err = term.GetState(t.inFd) + if err != nil { + return err } + return nil } @@ -123,7 +127,7 @@ func (t *tScreen) finalize() { // getWinSize is called to obtain the terminal dimensions. func (t *tScreen) getWinSize() (int, int, error) { - return term.GetSize(int(t.in.Fd())) + return term.GetSize(t.inFd) } // Beep emits a beep to the terminal.