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

View File

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

View File

@ -1,12 +1,16 @@
package zenutil
import (
"bytes"
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
)
// Run is internal.
@ -47,6 +51,123 @@ func Run(ctx context.Context, script string, data interface{}) ([]byte, error) {
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.
type File struct {
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
}