Unverified Commit 5930aaeb authored by Jeremy Schlatter's avatar Jeremy Schlatter
Browse files

cmd: fix cursor flickering in progress bar

The previous commit fixed flickering in the progress bar itself. Cursor
flickering is harder to address.

Cursor flickering could be fixed by hiding the cursor altogether while
the progress bar is displayed. The downside of this is that if the
program is killed in such a way that it can't clean up its state, it
would leave the cursor invisible.

Instead, this commit introduces an output buffer. All of the escape
codes and content for a single progress update are written to a buffer,
which is then flushed to the terminal all at once. This significantly
decreases the time during which the terminal has seen the cursor-hiding
code but has not yet seen the cursor-showing code, thus minimizing (but
not 100% eliminating) cursor flickering.

For more context, see:
https://gitlab.gnome.org/GNOME/vte/-/issues/2837#note_2269501
parent faf67db0
package progress package progress
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"sync" "sync"
...@@ -12,8 +13,9 @@ type State interface { ...@@ -12,8 +13,9 @@ type State interface {
} }
type Progress struct { type Progress struct {
mu sync.Mutex mu sync.Mutex
w io.Writer w io.Writer
buf bytes.Buffer
pos int pos int
...@@ -81,21 +83,26 @@ func (p *Progress) render() { ...@@ -81,21 +83,26 @@ func (p *Progress) render() {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
fmt.Fprint(p.w, "\033[?25l") // buffer the terminal update to minimize cursor flickering
defer fmt.Fprint(p.w, "\033[?25h") // https://gitlab.gnome.org/GNOME/vte/-/issues/2837#note_2269501
p.buf.Reset()
defer p.buf.WriteTo(p.w)
fmt.Fprint(&p.buf, "\033[?25l")
defer fmt.Fprint(&p.buf, "\033[?25h")
// move the cursor back to the beginning // move the cursor back to the beginning
for range p.pos - 1 { for range p.pos - 1 {
fmt.Fprint(p.w, "\033[A") fmt.Fprint(&p.buf, "\033[A")
} }
fmt.Fprint(p.w, "\033[1G") fmt.Fprint(&p.buf, "\033[1G")
// render progress lines // render progress lines
for i, state := range p.states { for i, state := range p.states {
fmt.Fprint(p.w, state.String()) fmt.Fprint(&p.buf, state.String())
fmt.Fprintf(p.w, "\033[K") fmt.Fprintf(&p.buf, "\033[K")
if i < len(p.states)-1 { if i < len(p.states)-1 {
fmt.Fprint(p.w, "\n") fmt.Fprint(&p.buf, "\n")
} }
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment