// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows plan9 aix package pb import ( "io" "sync" "time" "github.com/cheggaaa/pb/v3/termutil" ) // Create and start new pool with given bars // You need call pool.Stop() after work func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { pool = new(Pool) if err = pool.Start(); err != nil { return } pool.Add(pbs...) return } // NewPool initialises a pool with progress bars, but // doesn't start it. You need to call Start manually func NewPool(pbs ...*ProgressBar) (pool *Pool) { pool = new(Pool) pool.Add(pbs...) return } type Pool struct { Output io.Writer RefreshRate time.Duration bars []*ProgressBar lastBarsCount int shutdownCh chan struct{} workerCh chan struct{} m sync.Mutex finishOnce sync.Once } // Add progress bars. func (p *Pool) Add(pbs ...*ProgressBar) { p.m.Lock() defer p.m.Unlock() for _, bar := range pbs { bar.Set(Static, true) bar.Start() p.bars = append(p.bars, bar) } } func (p *Pool) Start() (err error) { p.RefreshRate = defaultRefreshRate p.shutdownCh, err = termutil.RawModeOn() if err != nil { return } p.workerCh = make(chan struct{}) go p.writer() return } func (p *Pool) writer() { var first = true defer func() { if first == false { p.print(false) } else { p.print(true) p.print(false) } close(p.workerCh) }() for { select { case <-time.After(p.RefreshRate): if p.print(first) { p.print(false) return } first = false case <-p.shutdownCh: return } } } // Restore terminal state and close pool func (p *Pool) Stop() error { p.finishOnce.Do(func() { if p.shutdownCh != nil { close(p.shutdownCh) } }) // Wait for the worker to complete select { case <-p.workerCh: } return termutil.RawModeOff() }