Notification listen.
This commit is contained in:
parent
d6dcc4b176
commit
a1e9ae327f
5 changed files with 211 additions and 25 deletions
|
@ -38,20 +38,20 @@ var (
|
||||||
notification bool
|
notification bool
|
||||||
|
|
||||||
// General options
|
// General options
|
||||||
title string
|
title string
|
||||||
width uint
|
width uint
|
||||||
height uint
|
height uint
|
||||||
okLabel string
|
okLabel string
|
||||||
cancelLabel string
|
cancelLabel string
|
||||||
extraButton string
|
extraButton string
|
||||||
text string
|
text string
|
||||||
icon string
|
icon string
|
||||||
multiple bool
|
multiple bool
|
||||||
|
defaultCancel bool
|
||||||
|
|
||||||
// Message options
|
// Message options
|
||||||
noWrap bool
|
noWrap bool
|
||||||
ellipsize bool
|
ellipsize bool
|
||||||
defaultCancel bool
|
|
||||||
|
|
||||||
// Entry options
|
// Entry options
|
||||||
entryText string
|
entryText string
|
||||||
|
@ -81,6 +81,9 @@ var (
|
||||||
autoKill bool
|
autoKill bool
|
||||||
noCancel bool
|
noCancel bool
|
||||||
|
|
||||||
|
// Notify options
|
||||||
|
listen bool
|
||||||
|
|
||||||
// Windows specific options
|
// Windows specific options
|
||||||
unixeol bool
|
unixeol bool
|
||||||
cygpath bool
|
cygpath bool
|
||||||
|
@ -151,7 +154,7 @@ func main() {
|
||||||
errResult(progress(opts...))
|
errResult(progress(opts...))
|
||||||
|
|
||||||
case notification:
|
case notification:
|
||||||
errResult(zenity.Notify(text, opts...))
|
errResult(notify(opts...))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
|
@ -220,6 +223,9 @@ func setupFlags() {
|
||||||
flag.BoolVar(&autoKill, "auto-kill", false, "Kill parent process if Cancel button is pressed (macOS and Unix only)")
|
flag.BoolVar(&autoKill, "auto-kill", false, "Kill parent process if Cancel button is pressed (macOS and Unix only)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify options
|
||||||
|
flag.BoolVar(&listen, "listen", false, "Listen for commands on stdin")
|
||||||
|
|
||||||
// Windows specific options
|
// Windows specific options
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
flag.BoolVar(&unixeol, "unixeol", false, "Use Unix line endings (Windows only)")
|
flag.BoolVar(&unixeol, "unixeol", false, "Use Unix line endings (Windows only)")
|
||||||
|
@ -332,6 +338,8 @@ func loadFlags() []zenity.Option {
|
||||||
setDefault(&text, "Running...")
|
setDefault(&text, "Running...")
|
||||||
setDefault(&okLabel, "OK")
|
setDefault(&okLabel, "OK")
|
||||||
setDefault(&cancelLabel, "Cancel")
|
setDefault(&cancelLabel, "Cancel")
|
||||||
|
case notification:
|
||||||
|
setDefault(&icon, "dialog-information")
|
||||||
default:
|
default:
|
||||||
setDefault(&text, "")
|
setDefault(&text, "")
|
||||||
}
|
}
|
||||||
|
|
61
cmd/zenity/notify.go
Normal file
61
cmd/zenity/notify.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ncruces/zenity"
|
||||||
|
"github.com/ncruces/zenity/internal/zenutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func notify(opts ...zenity.Option) error {
|
||||||
|
if !listen {
|
||||||
|
return zenity.Notify(text, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
zenutil.Command = false
|
||||||
|
var icon zenity.DialogIcon
|
||||||
|
for scanner := bufio.NewScanner(os.Stdin); scanner.Scan(); {
|
||||||
|
line := scanner.Text()
|
||||||
|
var cmd, msg string
|
||||||
|
if n := strings.IndexByte(line, ':'); n >= 0 {
|
||||||
|
cmd = strings.TrimSpace(line[:n])
|
||||||
|
msg = strings.TrimSpace(zenutil.Unescape(line[n+1:]))
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(os.Stderr, "Could not parse command from stdin")
|
||||||
|
}
|
||||||
|
switch cmd {
|
||||||
|
case "icon":
|
||||||
|
switch msg {
|
||||||
|
case "error", "dialog-error":
|
||||||
|
icon = zenity.ErrorIcon
|
||||||
|
case "info", "dialog-information":
|
||||||
|
icon = zenity.InfoIcon
|
||||||
|
case "question", "dialog-question":
|
||||||
|
icon = zenity.QuestionIcon
|
||||||
|
case "important", "warning", "dialog-warning":
|
||||||
|
icon = zenity.WarningIcon
|
||||||
|
case "dialog-password":
|
||||||
|
icon = zenity.PasswordIcon
|
||||||
|
default:
|
||||||
|
icon = zenity.NoIcon
|
||||||
|
}
|
||||||
|
case "message", "tooltip":
|
||||||
|
opts := []zenity.Option{zenity.Icon(icon)}
|
||||||
|
if n := strings.IndexByte(msg, '\n'); n >= 0 {
|
||||||
|
opts = append(opts, zenity.Title(msg[:n]))
|
||||||
|
msg = msg[n+1:]
|
||||||
|
}
|
||||||
|
if err := zenity.Notify(msg, opts...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "visible", "hints":
|
||||||
|
// ignored
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "Unknown command %q", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
89
internal/zenutil/unescape.go
Normal file
89
internal/zenutil/unescape.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package zenutil
|
||||||
|
|
||||||
|
// Unescape is internal.
|
||||||
|
func Unescape(s string) string {
|
||||||
|
// Apply rules described in:
|
||||||
|
// https://developer.gnome.org/glib/stable/glib-String-Utility-Functions.html#g-strescape
|
||||||
|
|
||||||
|
const (
|
||||||
|
initial = iota
|
||||||
|
escape1
|
||||||
|
escape2
|
||||||
|
escape3
|
||||||
|
)
|
||||||
|
var oct byte
|
||||||
|
var res []byte
|
||||||
|
state := initial
|
||||||
|
for _, b := range []byte(s) {
|
||||||
|
switch state {
|
||||||
|
case initial:
|
||||||
|
switch b {
|
||||||
|
case '\\':
|
||||||
|
state = escape1
|
||||||
|
default:
|
||||||
|
res = append(res, b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
|
||||||
|
case escape1:
|
||||||
|
switch b {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
oct = b - '0'
|
||||||
|
state = escape2
|
||||||
|
case 'b':
|
||||||
|
res = append(res, '\b')
|
||||||
|
state = initial
|
||||||
|
case 'f':
|
||||||
|
res = append(res, '\f')
|
||||||
|
state = initial
|
||||||
|
case 'n':
|
||||||
|
res = append(res, '\n')
|
||||||
|
state = initial
|
||||||
|
case 'r':
|
||||||
|
res = append(res, '\r')
|
||||||
|
state = initial
|
||||||
|
case 't':
|
||||||
|
res = append(res, '\t')
|
||||||
|
state = initial
|
||||||
|
case 'v':
|
||||||
|
res = append(res, '\v')
|
||||||
|
state = initial
|
||||||
|
default:
|
||||||
|
res = append(res, b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
|
||||||
|
case escape2:
|
||||||
|
switch b {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
oct = oct<<3 | (b - '0')
|
||||||
|
state = escape3
|
||||||
|
case '\\':
|
||||||
|
res = append(res, oct)
|
||||||
|
state = escape1
|
||||||
|
default:
|
||||||
|
res = append(res, oct, b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
|
||||||
|
case escape3:
|
||||||
|
switch b {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
oct = oct<<3 | (b - '0')
|
||||||
|
res = append(res, oct)
|
||||||
|
state = initial
|
||||||
|
case '\\':
|
||||||
|
res = append(res, oct)
|
||||||
|
state = escape1
|
||||||
|
default:
|
||||||
|
res = append(res, oct, b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if state == escape2 || state == escape3 {
|
||||||
|
res = append(res, oct)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(res)
|
||||||
|
}
|
28
internal/zenutil/unescape_test.go
Normal file
28
internal/zenutil/unescape_test.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package zenutil
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUnescape(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
data string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{`abc`, "abc"},
|
||||||
|
{`ab\c`, "abc"},
|
||||||
|
{`a\bc`, "a\bc"},
|
||||||
|
{`a\1c`, "a\001c"},
|
||||||
|
{`a\12c`, "a\012c"},
|
||||||
|
{`a\123c`, "a\123c"},
|
||||||
|
{`a\1\b`, "a\001\b"},
|
||||||
|
{`a\12\b`, "a\012\b"},
|
||||||
|
{`a\123\b`, "a\123\b"},
|
||||||
|
{`abc\1`, "abc\001"},
|
||||||
|
{`abc\12`, "abc\012"},
|
||||||
|
{`abc\123`, "abc\123"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
if got := Unescape(tt.data); got != tt.want {
|
||||||
|
t.Errorf("Unescape(%q) = %q, want %q", tt.data, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
zenity.go
24
zenity.go
|
@ -31,24 +31,24 @@ const ErrUnsupported = zenutil.ErrUnsupported
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
// General options
|
// General options
|
||||||
title *string
|
title *string
|
||||||
width uint
|
width uint
|
||||||
height uint
|
height uint
|
||||||
okLabel *string
|
okLabel *string
|
||||||
cancelLabel *string
|
cancelLabel *string
|
||||||
extraButton *string
|
extraButton *string
|
||||||
icon DialogIcon
|
icon DialogIcon
|
||||||
|
defaultCancel bool
|
||||||
|
|
||||||
|
// Message options
|
||||||
|
noWrap bool
|
||||||
|
ellipsize bool
|
||||||
|
|
||||||
// Entry options
|
// Entry options
|
||||||
entryText string
|
entryText string
|
||||||
hideText bool
|
hideText bool
|
||||||
username bool
|
username bool
|
||||||
|
|
||||||
// Message options
|
|
||||||
noWrap bool
|
|
||||||
ellipsize bool
|
|
||||||
defaultCancel bool
|
|
||||||
|
|
||||||
// List options
|
// List options
|
||||||
disallowEmpty bool
|
disallowEmpty bool
|
||||||
defaultItems []string
|
defaultItems []string
|
||||||
|
|
Loading…
Reference in a new issue