WIP: progress.

This commit is contained in:
Nuno Cruces 2021-04-30 19:05:49 +01:00
parent 0a67c8f698
commit aeaa608758
8 changed files with 97 additions and 74 deletions

View File

@ -10,4 +10,5 @@ var (
Command bool
Timeout int
Separator = "\x00"
Canceled error
)

View File

@ -13,4 +13,5 @@ var (
Command bool
Timeout int
Separator = "\x1e"
Canceled error
)

View File

@ -10,4 +10,5 @@ var (
Command bool
Timeout int
Separator string
Canceled error
)

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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