WIP: progress (macOS).
This commit is contained in:
parent
02bbc4741c
commit
c74a75a4ca
5 changed files with 171 additions and 15 deletions
|
@ -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}`
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
7
progress.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
type ProgressMonitor interface {
|
||||||
|
Message(string) error
|
||||||
|
Progress(int) error
|
||||||
|
Close() error
|
||||||
|
}
|
Loading…
Reference in a new issue