1// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows 2 3package pb 4 5import ( 6 "io" 7 "sync" 8 "time" 9) 10 11// Create and start new pool with given bars 12// You need call pool.Stop() after work 13func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { 14 pool = new(Pool) 15 if err = pool.Start(); err != nil { 16 return 17 } 18 pool.Add(pbs...) 19 return 20} 21 22// NewPool initialises a pool with progress bars, but 23// doesn't start it. You need to call Start manually 24func NewPool(pbs ...*ProgressBar) (pool *Pool) { 25 pool = new(Pool) 26 pool.Add(pbs...) 27 return 28} 29 30type Pool struct { 31 Output io.Writer 32 RefreshRate time.Duration 33 bars []*ProgressBar 34 lastBarsCount int 35 shutdownCh chan struct{} 36 workerCh chan struct{} 37 m sync.Mutex 38 finishOnce sync.Once 39} 40 41// Add progress bars. 42func (p *Pool) Add(pbs ...*ProgressBar) { 43 p.m.Lock() 44 defer p.m.Unlock() 45 for _, bar := range pbs { 46 bar.ManualUpdate = true 47 bar.NotPrint = true 48 bar.Start() 49 p.bars = append(p.bars, bar) 50 } 51} 52 53func (p *Pool) Start() (err error) { 54 p.RefreshRate = DefaultRefreshRate 55 p.shutdownCh, err = lockEcho() 56 if err != nil { 57 return 58 } 59 p.workerCh = make(chan struct{}) 60 go p.writer() 61 return 62} 63 64func (p *Pool) writer() { 65 var first = true 66 defer func() { 67 if first == false { 68 p.print(false) 69 } else { 70 p.print(true) 71 p.print(false) 72 } 73 close(p.workerCh) 74 }() 75 76 for { 77 select { 78 case <-time.After(p.RefreshRate): 79 if p.print(first) { 80 p.print(false) 81 return 82 } 83 first = false 84 case <-p.shutdownCh: 85 return 86 } 87 } 88} 89 90// Restore terminal state and close pool 91func (p *Pool) Stop() error { 92 p.finishOnce.Do(func() { 93 if p.shutdownCh != nil { 94 close(p.shutdownCh) 95 } 96 }) 97 98 // Wait for the worker to complete 99 select { 100 case <-p.workerCh: 101 } 102 103 return unlockEcho() 104} 105