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 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
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 { 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