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

@ -38,20 +38,20 @@ var (
notification bool
// General options
title string
width uint
height uint
okLabel string
cancelLabel string
extraButton string
text string
icon string
multiple bool
title string
width uint
height uint
okLabel string
cancelLabel string
extraButton string
text string
icon string
multiple bool
defaultCancel bool
// Message options
noWrap bool
ellipsize bool
defaultCancel bool
noWrap bool
ellipsize bool
// Entry options
entryText string
@ -81,6 +81,9 @@ var (
autoKill bool
noCancel bool
// Notify options
listen bool
// Windows specific options
unixeol bool
cygpath bool
@ -151,7 +154,7 @@ func main() {
errResult(progress(opts...))
case notification:
errResult(zenity.Notify(text, opts...))
errResult(notify(opts...))
default:
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)")
}
// Notify options
flag.BoolVar(&listen, "listen", false, "Listen for commands on stdin")
// Windows specific options
if runtime.GOOS == "windows" {
flag.BoolVar(&unixeol, "unixeol", false, "Use Unix line endings (Windows only)")
@ -332,6 +338,8 @@ func loadFlags() []zenity.Option {
setDefault(&text, "Running...")
setDefault(&okLabel, "OK")
setDefault(&cancelLabel, "Cancel")
case notification:
setDefault(&icon, "dialog-information")
default:
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

@ -31,24 +31,24 @@ const ErrUnsupported = zenutil.ErrUnsupported
type options struct {
// General options
title *string
width uint
height uint
okLabel *string
cancelLabel *string
extraButton *string
icon DialogIcon
title *string
width uint
height uint
okLabel *string
cancelLabel *string
extraButton *string
icon DialogIcon
defaultCancel bool
// Message options
noWrap bool
ellipsize bool
// Entry options
entryText string
hideText bool
username bool
// Message options
noWrap bool
ellipsize bool
defaultCancel bool
// List options
disallowEmpty bool
defaultItems []string