Notification listen.

This commit is contained in:
Nuno Cruces 2021-05-11 14:51:56 +01:00
parent d6dcc4b176
commit a1e9ae327f
5 changed files with 211 additions and 25 deletions

View file

@ -47,11 +47,11 @@ var (
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
View 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
}

View 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)
}

View 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)
}
}
}

View file

@ -38,17 +38,17 @@ type options struct {
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