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
|
Command bool
|
||||||
Timeout int
|
Timeout int
|
||||||
Separator = "\x00"
|
Separator = "\x00"
|
||||||
|
Canceled error
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,4 +13,5 @@ var (
|
||||||
Command bool
|
Command bool
|
||||||
Timeout int
|
Timeout int
|
||||||
Separator = "\x1e"
|
Separator = "\x1e"
|
||||||
|
Canceled error
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,4 +10,5 @@ var (
|
||||||
Command bool
|
Command bool
|
||||||
Timeout int
|
Timeout int
|
||||||
Separator string
|
Separator string
|
||||||
|
Canceled error
|
||||||
)
|
)
|
||||||
|
|
|
@ -106,37 +106,15 @@ func RunProgress(ctx context.Context, max int, env []string) (*progressDialog, e
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg := &progressDialog{
|
dlg := &progressDialog{
|
||||||
done: make(chan struct{}),
|
cmd: cmd,
|
||||||
lines: make(chan string),
|
|
||||||
max: max,
|
max: max,
|
||||||
|
lines: make(chan string),
|
||||||
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
go dlg.pipe(pipe)
|
||||||
go func() {
|
go func() {
|
||||||
err := cmd.Wait()
|
defer os.RemoveAll(t)
|
||||||
if cerr := ctx.Err(); cerr != nil {
|
dlg.wait()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
return dlg, nil
|
return dlg, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,25 @@
|
||||||
package zenutil
|
package zenutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type progressDialog struct {
|
type progressDialog struct {
|
||||||
err error
|
ctx context.Context
|
||||||
done chan struct{}
|
cmd *exec.Cmd
|
||||||
lines chan string
|
|
||||||
percent bool
|
|
||||||
max int
|
max int
|
||||||
|
percent bool
|
||||||
|
closed int32
|
||||||
|
lines chan string
|
||||||
|
done chan struct{}
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *progressDialog) send(line string) 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 {
|
func (d *progressDialog) Text(text string) error {
|
||||||
return d.send("#" + text)
|
return d.send("#" + text)
|
||||||
}
|
}
|
||||||
|
@ -48,3 +52,61 @@ func (d *progressDialog) MaxValue() int {
|
||||||
func (d *progressDialog) Done() <-chan struct{} {
|
func (d *progressDialog) Done() <-chan struct{} {
|
||||||
return d.done
|
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{
|
dlg := &progressDialog{
|
||||||
done: make(chan struct{}),
|
ctx: ctx,
|
||||||
lines: make(chan string),
|
cmd: cmd,
|
||||||
percent: true,
|
|
||||||
max: max,
|
max: max,
|
||||||
|
percent: true,
|
||||||
|
lines: make(chan string),
|
||||||
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
go func() {
|
go dlg.pipe(pipe)
|
||||||
err := cmd.Wait()
|
go dlg.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return dlg, nil
|
return dlg, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ type ProgressDialog interface {
|
||||||
// MaxValue gets how much work the task requires in total.
|
// MaxValue gets how much work the task requires in total.
|
||||||
MaxValue() int
|
MaxValue() int
|
||||||
|
|
||||||
|
// Complete marks the task completed.
|
||||||
|
Complete() error
|
||||||
|
|
||||||
// Close closes the dialog.
|
// Close closes the dialog.
|
||||||
Close() error
|
Close() error
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ package zenity
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/ncruces/zenity/internal/zenutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stringErr string
|
type stringErr string
|
||||||
|
@ -31,6 +33,10 @@ const ErrExtraButton = stringErr("extra button pressed")
|
||||||
// ErrUnsupported is returned when a combination of options is not supported.
|
// ErrUnsupported is returned when a combination of options is not supported.
|
||||||
const ErrUnsupported = stringErr("unsupported option")
|
const ErrUnsupported = stringErr("unsupported option")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
zenutil.Canceled = ErrCanceled
|
||||||
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
// General options
|
// General options
|
||||||
title *string
|
title *string
|
||||||
|
|
Loading…
Reference in a new issue