diff --git a/config/notification_unix.go b/config/notification_unix.go new file mode 100644 index 0000000..58a121c --- /dev/null +++ b/config/notification_unix.go @@ -0,0 +1,7 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package config + +func defaultNotifyCommand() string { + return "/usr/share/barnard/barnard-sound.sh \"%event\" \"%who\" \"%what\"" +} diff --git a/config/notification_windows.go b/config/notification_windows.go new file mode 100644 index 0000000..fe62cb3 --- /dev/null +++ b/config/notification_windows.go @@ -0,0 +1,9 @@ +//go:build windows + +package config + +// Windows installations do not ship the POSIX notification helper. Users can +// configure a cmd.exe-compatible command explicitly if they want notifications. +func defaultNotifyCommand() string { + return "" +} diff --git a/config/user_config.go b/config/user_config.go index 529dea2..5cdfb22 100644 --- a/config/user_config.go +++ b/config/user_config.go @@ -150,7 +150,7 @@ func (c *Config) LoadConfig() { jc.Username = &username } if c.config.NotifyCommand == nil { - ncmd := string("/usr/share/barnard/barnard-sound.sh \"%event\" \"%who\" \"%what\"") + ncmd := defaultNotifyCommand() jc.NotifyCommand = &ncmd } if c.config.NoiseSuppressionEnabled == nil { diff --git a/main.go b/main.go index 4398e51..8ddde60 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "log" "net/http" "os" - "os/exec" "strings" "time" @@ -45,31 +44,28 @@ func do_list_devices() { show_devs("Inputs:", idevs) } -func setup_notify_runner(notify_command string) chan []string { - t := make(chan []string) - var do_nothing = false - var err error - if err != nil { - } - if notify_command == "" { - do_nothing = true - } - go func(events chan []string, cmd_template string, dummy bool) { - for { - event := <-events - if !dummy { - t := string(cmd_template) - t = strings.ReplaceAll(t, "%event", shellescape.Quote(event[0])) - t = strings.ReplaceAll(t, "%who", shellescape.Quote(event[1])) - t = strings.ReplaceAll(t, "%what", shellescape.Quote(event[2])) - cmd := "/bin/sh" - args := []string{"-c", t} - x := exec.Command(cmd, args...) - x.Run() - } //if we actually have a command to run - } //for - }(t, notify_command, do_nothing) - return t +const notificationQueueSize = 32 + +func setup_notify_runner(notifyCommand string) chan []string { + events := make(chan []string, notificationQueueSize) + go func() { + for event := range events { + if notifyCommand != "" { + runNotification(expandNotification(notifyCommand, event)) + } + } + }() + return events +} + +// expandNotification replaces placeholders in one pass so text supplied for +// one field cannot cause another placeholder to be expanded recursively. +func expandNotification(template string, event []string) string { + return strings.NewReplacer( + "%event", shellescape.Quote(event[0]), + "%who", shellescape.Quote(event[1]), + "%what", shellescape.Quote(event[2]), + ).Replace(template) } func main() { diff --git a/notification_unix.go b/notification_unix.go new file mode 100644 index 0000000..946395c --- /dev/null +++ b/notification_unix.go @@ -0,0 +1,9 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package main + +import "os/exec" + +func runNotification(command string) { + _ = exec.Command("/bin/sh", "-c", command).Run() +} diff --git a/notification_windows.go b/notification_windows.go new file mode 100644 index 0000000..5c969e1 --- /dev/null +++ b/notification_windows.go @@ -0,0 +1,9 @@ +//go:build windows + +package main + +import "os/exec" + +func runNotification(command string) { + _ = exec.Command("cmd.exe", "/C", command).Run() +} diff --git a/ui.go b/ui.go index 5afe6ca..0f67af3 100644 --- a/ui.go +++ b/ui.go @@ -39,7 +39,12 @@ func esc(str string) string { } func (b *Barnard) Notify(event string, who string, what string) { - b.notifyChannel <- []string{event, who, what} + // Notifications are best-effort: a slow external command must not block a + // UI or network callback. New events are dropped once the bounded queue is full. + select { + case b.notifyChannel <- []string{event, who, what}: + default: + } } func (b *Barnard) Beep() {