From 93fac1f3124f87009091230f61cc13b5e5473cb5 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 9 Apr 2021 20:16:35 +1000 Subject: reduce flicker without worrying about carriage returns --- go.mod | 4 +- go.sum | 8 ++ pkg/gui/tasks_adapter.go | 14 +++- pkg/tasks/tasks.go | 14 ++-- vendor/github.com/jesseduffield/gocui/gui.go | 5 +- vendor/github.com/jesseduffield/gocui/view.go | 76 ++++++++++++++---- vendor/golang.org/x/term/term_solaris.go | 111 -------------------------- vendor/golang.org/x/term/term_unix.go | 4 +- vendor/golang.org/x/term/term_unix_solaris.go | 10 +++ vendor/modules.txt | 4 +- 10 files changed, 108 insertions(+), 142 deletions(-) delete mode 100644 vendor/golang.org/x/term/term_solaris.go create mode 100644 vendor/golang.org/x/term/term_unix_solaris.go diff --git a/go.mod b/go.mod index 98853adb9..6ca0594de 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/imdario/mergo v0.3.11 github.com/integrii/flaggy v1.4.0 github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 - github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772 + github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe // indirect github.com/jesseduffield/yaml v2.1.0+incompatible github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 @@ -40,7 +40,7 @@ require ( golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect - golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 // indirect + golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect golang.org/x/text v0.3.6 // indirect ) diff --git a/go.sum b/go.sum index d2037e61e..7bcf90094 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,12 @@ github.com/jesseduffield/gocui v0.3.1-0.20210405041826-439abd8b6e07 h1:BymGR28au github.com/jesseduffield/gocui v0.3.1-0.20210405041826-439abd8b6e07/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772 h1:dg9krj10Udac4IcvlVCOAPktQkfggkgtqRmbDKk7Pzw= github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= +github.com/jesseduffield/gocui v0.3.1-0.20210406065811-95ef6e13779b h1:3+4+muhhikpls5FePXSRNFgcdoPx8dTdqaCy3AqLz98= +github.com/jesseduffield/gocui v0.3.1-0.20210406065811-95ef6e13779b/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= +github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064 h1:Oe+QJuUIOd2TU+A3BW5sT1eXqceoBcOOfyoHlGf7F8Y= +github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= +github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a h1:ocrSuZxQIgWWt27b+rjiyIIPz6fzfFeoL5Q4cpa2cAo= +github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe h1:qsVhCf2RFyyKIUe/+gJblbCpXMUki9rZrHuEctg6M/E= github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4= github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE= @@ -226,6 +232,8 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlp golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs= golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 h1:VqE9gduFZ4dbR7XoL77lHFp0/DyDUBKSXK7CMFkVcV0= +golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go index bc2fb8a24..71cde5b00 100644 --- a/pkg/gui/tasks_adapter.go +++ b/pkg/gui/tasks_adapter.go @@ -83,13 +83,23 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager { gui.Log, view, func() { - view.Clear() + // we could clear here, but that actually has the effect of causing a flicker + // where the view may contain no content momentarily as the gui refreshes. + // Instead, we're rewinding the write pointer so that we will just start + // overwriting the existing content from the top down. Once we've reached + // the end of the content do display, we call view.FlushStaleCells() to + // clear out the remaining content from the previous render. + view.Reset() }, func() { gui.g.Update(func(*gocui.Gui) error { return nil }) - }) + }, + func() { + view.FlushStaleCells() + }, + ) gui.viewBufferManagerMap[view.Name()] = manager } diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go index 5854b4f46..8287b3927 100644 --- a/pkg/tasks/tasks.go +++ b/pkg/tasks/tasks.go @@ -33,12 +33,13 @@ type ViewBufferManager struct { readLines chan int // beforeStart is the function that is called before starting a new task - beforeStart func() - refreshView func() + beforeStart func() + refreshView func() + flushStaleCells func() } -func NewViewBufferManager(log *logrus.Entry, writer io.Writer, beforeStart func(), refreshView func()) *ViewBufferManager { - return &ViewBufferManager{Log: log, writer: writer, beforeStart: beforeStart, refreshView: refreshView, readLines: make(chan int, 1024)} +func NewViewBufferManager(log *logrus.Entry, writer io.Writer, beforeStart func(), refreshView func(), flushStaleCells func()) *ViewBufferManager { + return &ViewBufferManager{Log: log, writer: writer, beforeStart: beforeStart, refreshView: refreshView, flushStaleCells: flushStaleCells, readLines: make(chan int, 1024)} } func (m *ViewBufferManager) ReadLines(n int) { @@ -75,7 +76,7 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string loaded := false go utils.Safe(func() { - ticker := time.NewTicker(time.Millisecond * 100) + ticker := time.NewTicker(time.Millisecond * 200) defer ticker.Stop() select { case <-ticker.C: @@ -114,6 +115,9 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string default: } if !ok { + // if we're here then there's nothing left to scan from the source + // so we're at the EOF and can flush the stale content + m.flushStaleCells() m.refreshView() break outer } diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index ed83f34c1..c537a32b6 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -266,11 +266,14 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er } if v, err := g.View(name); err == nil { + if v.x0 != x0 || v.x1 != x1 || v.y0 != y0 || v.y1 != y1 { + v.tainted = true + } + v.x0 = x0 v.y0 = y0 v.x1 = x1 v.y1 = y1 - v.tainted = true return v, nil } diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index b13074242..7b45c1352 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -518,16 +518,20 @@ func (v *View) writeCells(x, y int, cells []cell) { // of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must // be called to clear the view's buffer. func (v *View) Write(p []byte) (n int, err error) { - v.tainted = true v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.tainted = true v.makeWriteable(v.wx, v.wy) v.writeRunes(bytes.Runes(p)) - v.writeMutex.Unlock() return len(p), nil } func (v *View) WriteRunes(p []rune) { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + v.tainted = true // Fill with empty cells, if writing outside current view buffer @@ -640,8 +644,53 @@ func (v *View) Read(p []byte) (n int, err error) { return offset, io.EOF } +// Clear empties the view's internal buffer. +// And resets reading and writing offsets. +func (v *View) Clear() { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.rewind() + v.tainted = true + v.lines = nil + v.viewLines = nil + v.clearRunes() +} + // Rewind sets read and write pos to (0, 0). func (v *View) Rewind() { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.rewind() +} + +// similar to Rewind but clears lines. Also similar to Clear but doesn't reset +// viewLines +func (v *View) Reset() { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.rewind() + v.lines = nil +} + +// This is for when we've done a restart for the sake of avoiding a flicker and +// we've reached the end of the new content to display: we need to clear the remaining +// content from the previous round. We do this by setting v.viewLines to nil so that +// we just render the new content from v.lines directly +func (v *View) FlushStaleCells() { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.rewind() + v.tainted = true + v.viewLines = nil +} + +func (v *View) rewind() { + v.ei.reset() + if err := v.SetReadPos(0, 0); err != nil { // SetReadPos returns error only if x and y are negative // we are passing 0, 0, thus no error should occur. @@ -718,7 +767,7 @@ func (v *View) draw() error { v.ox = 0 } if v.tainted { - v.viewLines = nil + lineIdx := 0 lines := v.lines if v.HasLoader { lines = v.loaderLines() @@ -732,7 +781,13 @@ func (v *View) draw() error { ls := lineWrap(line, wrap) for j := range ls { vline := viewLine{linesX: j, linesY: i, line: ls[j]} - v.viewLines = append(v.viewLines, vline) + + if lineIdx > len(v.viewLines)-1 { + v.viewLines = append(v.viewLines, vline) + } else { + v.viewLines[lineIdx] = vline + } + lineIdx++ } } if !v.HasLoader { @@ -828,19 +883,6 @@ func (v *View) realPosition(vx, vy int) (x, y int, err error) { return x, y, nil } -// Clear empties the view's internal buffer. -// And resets reading and writing offsets. -func (v *View) Clear() { - v.writeMutex.Lock() - v.Rewind() - v.tainted = true - v.ei.reset() - v.lines = nil - v.viewLines = nil - v.clearRunes() - v.writeMutex.Unlock() -} - // clearRunes erases all the cells in the view. func (v *View) clearRunes() { maxX, maxY := v.Size() diff --git a/vendor/golang.org/x/term/term_solaris.go b/vendor/golang.org/x/term/term_solaris.go deleted file mode 100644 index b9da29744..000000000 --- a/vendor/golang.org/x/term/term_solaris.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package term - -import ( - "io" - "syscall" - - "golang.org/x/sys/unix" -) - -// State contains the state of a terminal. -type state struct { - termios unix.Termios -} - -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermio(fd, unix.TCGETA) - return err == nil -} - -func readPassword(fd int) ([]byte, error) { - // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c - val, err := unix.IoctlGetTermios(fd, unix.TCGETS) - if err != nil { - return nil, err - } - oldState := *val - - newState := oldState - newState.Lflag &^= syscall.ECHO - newState.Lflag |= syscall.ICANON | syscall.ISIG - newState.Iflag |= syscall.ICRNL - err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState) - if err != nil { - return nil, err - } - - defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState) - - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(fd, buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil -} - -func makeRaw(fd int) (*State, error) { - // see http://cr.illumos.org/~webrev/andy_js/1060/ - termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) - if err != nil { - return nil, err - } - - oldState := State{state{termios: *termios}} - - termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON - termios.Oflag &^= unix.OPOST - termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN - termios.Cflag &^= unix.CSIZE | unix.PARENB - termios.Cflag |= unix.CS8 - termios.Cc[unix.VMIN] = 1 - termios.Cc[unix.VTIME] = 0 - - if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil { - return nil, err - } - - return &oldState, nil -} - -func restore(fd int, oldState *State) error { - return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios) -} - -func getState(fd int) (*State, error) { - termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) - if err != nil { - return nil, err - } - - return &State{state{termios: *termios}}, nil -} - -func getSize(fd int) (width, height int, err error) { - ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) - if err != nil { - return 0, 0, err - } - return int(ws.Col), int(ws.Row), nil -} diff --git a/vendor/golang.org/x/term/term_unix.go b/vendor/golang.org/x/term/term_unix.go index 6849b6ee5..a4e31ab1b 100644 --- a/vendor/golang.org/x/term/term_unix.go +++ b/vendor/golang.org/x/term/term_unix.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd zos +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package term diff --git a/vendor/golang.org/x/term/term_unix_solaris.go b/vendor/golang.org/x/term/term_unix_solaris.go new file mode 100644 index 000000000..2d5efd26a --- /dev/null +++ b/vendor/golang.org/x/term/term_unix_solaris.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package term + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS +const ioctlWriteTermios = unix.TCSETS diff --git a/vendor/modules.txt b/vendor/modules.txt index 1bf4922de..6ee82a59c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -149,7 +149,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/noder -# github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772 +# github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a ## explicit github.com/jesseduffield/gocui # github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe @@ -242,7 +242,7 @@ golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 +# golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 ## explicit golang.org/x/term # golang.org/x/text v0.3.6 -- cgit v1.2.3