WIP: progress (macOS).

This commit is contained in:
Nuno Cruces 2021-04-22 15:03:08 +01:00
parent 02bbc4741c
commit c74a75a4ca
5 changed files with 171 additions and 15 deletions

View file

@ -49,3 +49,23 @@ var app=Application.currentApplication()
app.includeStandardAdditions=true app.includeStandardAdditions=true
void app.displayNotification({{json .Text}},{{json .Options}}) void app.displayNotification({{json .Text}},{{json .Options}})
{{- end}}`)) {{- end}}`))
var progress =`
var app=Application.currentApplication()
app.includeStandardAdditions=true
app.activate()
ObjC.import('stdlib')
ObjC.import('readline')
function run(args){Progress.totalUnitCount=100
Progress.completedUnitCount=0
Progress.description=args[0]||"Progress"
Progress.additionalDescription=args[1]||"Running..."
while(true){var s
try{s=$.readline('')}catch(e){if(e.errorNumber===-128)$.exit(1)
break}
if(s.indexOf('#')===0){Progress.additionalDescription=s.slice(1).trim()
continue}
var i=parseInt(s)
if(Number.isSafeInteger(i)){Progress.completedUnitCount=i
continue}}
Progress.completedUnitCount=100}`

View file

@ -4,7 +4,6 @@ package main
import ( import (
"bytes" "bytes"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -17,21 +16,21 @@ import (
func main() { func main() {
dir := os.Args[1] dir := os.Args[1]
files, err := ioutil.ReadDir(dir) files, err := os.ReadDir(dir)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
var args struct {
Templates string
Progress string
}
var str strings.Builder var str strings.Builder
for _, file := range files { for _, file := range files {
name := file.Name() name := file.Name()
data, err := os.ReadFile(filepath.Join(dir, name))
str.WriteString("\n" + `{{define "`)
str.WriteString(strings.TrimSuffix(name, filepath.Ext(name)))
str.WriteString(`" -}}` + "\n")
data, err := ioutil.ReadFile(filepath.Join(dir, name))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -40,16 +39,25 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
name = strings.TrimSuffix(name, filepath.Ext(name))
if name == "progress" {
args.Progress = string(data)
} else {
str.WriteString("\n" + `{{define "`)
str.WriteString(name)
str.WriteString(`" -}}` + "\n")
str.Write(data) str.Write(data)
str.WriteString("\n{{- end}}") str.WriteString("\n{{- end}}")
} }
}
out, err := os.Create("osa_generated.go") out, err := os.Create("osa_generated.go")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = generator.Execute(out, str.String()) args.Templates = str.String()
err = generator.Execute(out, args)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -108,5 +116,6 @@ import (
var scripts = template.Must(template.New("").Funcs(template.FuncMap{"json": func(v interface{}) (string, error) { var scripts = template.Must(template.New("").Funcs(template.FuncMap{"json": func(v interface{}) (string, error) {
b, err := json.Marshal(v) b, err := json.Marshal(v)
return string(b), err return string(b), err
}}).Parse(` + "`{{.}}`" + `)) }}).Parse(` + "`{{.Templates}}`" + `))
`))
var progress =` + "`\n{{.Progress}}`\n"))

View file

@ -15,8 +15,7 @@ function run(args) {
var s var s
try { try {
s = $.readline('') s = $.readline('')
} } catch (e) {
catch (e) {
if (e.errorNumber === -128) $.exit(1) if (e.errorNumber === -128) $.exit(1)
break break
} }

View file

@ -1,12 +1,16 @@
package zenutil package zenutil
import ( import (
"bytes"
"context" "context"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time"
) )
// Run is internal. // Run is internal.
@ -47,6 +51,123 @@ func Run(ctx context.Context, script string, data interface{}) ([]byte, error) {
return cmd.Output() return cmd.Output()
} }
func RunProgress(ctx context.Context) (m *progressMonitor, err error) {
t, err := ioutil.TempDir("", "")
if err != nil {
return nil, err
}
defer func() {
if err != nil {
os.RemoveAll(t)
}
}()
var cmd *exec.Cmd
name := filepath.Join(t, "progress.app")
cmd = exec.Command("osacompile", "-l", "JavaScript", "-o", name)
cmd.Stdin = strings.NewReader(progress)
if err := cmd.Run(); err != nil {
return nil, err
}
plist := filepath.Join(name, "Contents/Info.plist")
cmd = exec.Command("defaults", "write", plist, "LSUIElement", "true")
if err := cmd.Run(); err != nil {
return nil, err
}
cmd = exec.Command("defaults", "write", plist, "CFBundleName", "")
if err := cmd.Run(); err != nil {
return nil, err
}
var executable string
cmd = exec.Command("defaults", "read", plist, "CFBundleExecutable")
if out, err := cmd.Output(); err != nil {
return nil, err
} else {
out = bytes.TrimSuffix(out, []byte{'\n'})
executable = filepath.Join(name, "Contents/MacOS", string(out))
}
cmd = exec.Command(executable)
pipe, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
if err = cmd.Start(); err != nil {
return nil, err
}
if ctx == nil {
ctx = context.Background()
}
m = &progressMonitor{
done: make(chan struct{}),
lines: make(chan string),
}
go func() {
defer func() {
pipe.Close()
err := cmd.Wait()
if cerr := ctx.Err(); cerr != nil {
err = cerr
}
m.err = err
close(m.done)
os.RemoveAll(t)
}()
for {
var line string
select {
case s, ok := <-m.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
}
type progressMonitor struct {
err error
done chan struct{}
lines chan string
}
func (m *progressMonitor) send(line string) error {
select {
case m.lines <- line:
return nil
case <-m.done:
return m.err
}
}
func (m *progressMonitor) Close() error {
close(m.lines)
<-m.done
return m.err
}
func (m *progressMonitor) Message(msg string) error {
return m.send("#" + msg)
}
func (m *progressMonitor) Progress(progress int) error {
return m.send(strconv.Itoa(progress))
}
// File is internal. // File is internal.
type File struct { type File struct {
Operation string Operation string

7
progress.go Normal file
View file

@ -0,0 +1,7 @@
package zenity
type ProgressMonitor interface {
Message(string) error
Progress(int) error
Close() error
}