WIP: progress.
This commit is contained in:
parent
0a67c8f698
commit
aeaa608758
8 changed files with 97 additions and 74 deletions
|
@ -10,4 +10,5 @@ var (
|
|||
Command bool
|
||||
Timeout int
|
||||
Separator = "\x00"
|
||||
Canceled error
|
||||
)
|
||||
|
|
|
@ -13,4 +13,5 @@ var (
|
|||
Command bool
|
||||
Timeout int
|
||||
Separator = "\x1e"
|
||||
Canceled error
|
||||
)
|
||||
|
|
|
@ -10,4 +10,5 @@ var (
|
|||
Command bool
|
||||
Timeout int
|
||||
Separator string
|
||||
Canceled error
|
||||
)
|
||||
|
|
|
@ -106,37 +106,15 @@ func RunProgress(ctx context.Context, max int, env []string) (*progressDialog, e
|
|||
}
|
||||
|
||||
dlg := &progressDialog{
|
||||
done: make(chan struct{}),
|
||||
lines: make(chan string),
|
||||
cmd: cmd,
|
||||
max: max,
|
||||
lines: make(chan string),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
go dlg.pipe(pipe)
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
dlg.err = err
|
||||
close(dlg.done)
|
||||
os.RemoveAll(t)
|
||||
}()
|
||||
go func() {
|
||||
defer pipe.Close()
|
||||
for {
|
||||
var line string
|
||||
select {
|
||||
case s, ok := <-dlg.lines:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
line = s
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(40 * time.Millisecond):
|
||||
}
|
||||
if _, err := pipe.Write([]byte(line + "\n")); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(t)
|
||||
dlg.wait()
|
||||
}()
|
||||
return dlg, nil
|
||||
}
|
||||
|
|
|
@ -3,15 +3,25 @@
|
|||
package zenutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type progressDialog struct {
|
||||
err error
|
||||
done chan struct{}
|
||||
lines chan string
|
||||
percent bool
|
||||
ctx context.Context
|
||||
cmd *exec.Cmd
|
||||
max int
|
||||
percent bool
|
||||
closed int32
|
||||
lines chan string
|
||||
done chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (d *progressDialog) send(line string) error {
|
||||
|
@ -23,12 +33,6 @@ func (d *progressDialog) send(line string) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (d *progressDialog) Close() error {
|
||||
close(d.lines)
|
||||
<-d.done
|
||||
return d.err
|
||||
}
|
||||
|
||||
func (d *progressDialog) Text(text string) error {
|
||||
return d.send("#" + text)
|
||||
}
|
||||
|
@ -48,3 +52,61 @@ func (d *progressDialog) MaxValue() int {
|
|||
func (d *progressDialog) Done() <-chan struct{} {
|
||||
return d.done
|
||||
}
|
||||
|
||||
func (d *progressDialog) Complete() error {
|
||||
close(d.lines)
|
||||
select {
|
||||
case <-d.done:
|
||||
return d.err
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *progressDialog) Close() error {
|
||||
atomic.StoreInt32(&d.closed, 1)
|
||||
d.cmd.Process.Signal(os.Interrupt)
|
||||
<-d.done
|
||||
return d.err
|
||||
}
|
||||
|
||||
func (d *progressDialog) wait() {
|
||||
err := d.cmd.Wait()
|
||||
if cerr := d.ctx.Err(); cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
switch {
|
||||
case eerr.ExitCode() == -1 && atomic.LoadInt32(&d.closed) != 0:
|
||||
err = nil
|
||||
case eerr.ExitCode() == 1:
|
||||
err = Canceled
|
||||
}
|
||||
}
|
||||
d.err = err
|
||||
close(d.done)
|
||||
}
|
||||
|
||||
func (d *progressDialog) pipe(w io.WriteCloser) {
|
||||
defer w.Close()
|
||||
var timeout = time.Second
|
||||
if runtime.GOOS == "darwin" {
|
||||
timeout = 40 * time.Millisecond
|
||||
}
|
||||
for {
|
||||
var line string
|
||||
select {
|
||||
case s, ok := <-d.lines:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
line = s
|
||||
case <-d.ctx.Done():
|
||||
return
|
||||
case <-time.After(timeout):
|
||||
}
|
||||
if _, err := w.Write([]byte(line + "\n")); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,43 +63,14 @@ func RunProgress(ctx context.Context, max int, args []string) (*progressDialog,
|
|||
}
|
||||
|
||||
dlg := &progressDialog{
|
||||
done: make(chan struct{}),
|
||||
lines: make(chan string),
|
||||
percent: true,
|
||||
ctx: ctx,
|
||||
cmd: cmd,
|
||||
max: max,
|
||||
percent: true,
|
||||
lines: make(chan string),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
select {
|
||||
case _, ok := <-dlg.lines:
|
||||
if !ok {
|
||||
err = nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
dlg.err = err
|
||||
close(dlg.done)
|
||||
}()
|
||||
go func() {
|
||||
defer cmd.Process.Signal(syscall.SIGTERM)
|
||||
for {
|
||||
var line string
|
||||
select {
|
||||
case s, ok := <-dlg.lines:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
line = s
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if _, err := pipe.Write([]byte(line + "\n")); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
go dlg.pipe(pipe)
|
||||
go dlg.wait()
|
||||
return dlg, nil
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ type ProgressDialog interface {
|
|||
// MaxValue gets how much work the task requires in total.
|
||||
MaxValue() int
|
||||
|
||||
// Complete marks the task completed.
|
||||
Complete() error
|
||||
|
||||
// Close closes the dialog.
|
||||
Close() error
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ package zenity
|
|||
import (
|
||||
"context"
|
||||
"image/color"
|
||||
|
||||
"github.com/ncruces/zenity/internal/zenutil"
|
||||
)
|
||||
|
||||
type stringErr string
|
||||
|
@ -31,6 +33,10 @@ const ErrExtraButton = stringErr("extra button pressed")
|
|||
// ErrUnsupported is returned when a combination of options is not supported.
|
||||
const ErrUnsupported = stringErr("unsupported option")
|
||||
|
||||
func init() {
|
||||
zenutil.Canceled = ErrCanceled
|
||||
}
|
||||
|
||||
type options struct {
|
||||
// General options
|
||||
title *string
|
||||
|
|
Loading…
Reference in a new issue