feat: add desktop notifications
This commit is contained in:
parent
f58fb5133e
commit
4240d6f017
5 changed files with 71 additions and 18 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,13 @@
|
||||||
|
# 1.4.0
|
||||||
|
|
||||||
|
**New features**
|
||||||
|
|
||||||
|
- optional desktop notifications on errors
|
||||||
|
|
||||||
|
**Notable bug fixes**
|
||||||
|
|
||||||
|
- the toolArgs option now understands complex patterns (spaces, quotes)
|
||||||
|
|
||||||
# 1.3.0
|
# 1.3.0
|
||||||
|
|
||||||
**Breaking changes**
|
**Breaking changes**
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
- upgrade version number
|
|
||||||
- in main
|
|
||||||
- in man page
|
|
||||||
- update changelog
|
|
|
@ -1,4 +1,4 @@
|
||||||
.TH clipman 1 1.3.0 ""
|
.TH clipman 1 1.4.0 ""
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
clipman
|
clipman
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
|
@ -15,6 +15,9 @@ Show context-sensitive help (also try --help-long and --help-man).
|
||||||
\fB--histpath="~/.local/share/clipman.json"\fR
|
\fB--histpath="~/.local/share/clipman.json"\fR
|
||||||
Path of history file
|
Path of history file
|
||||||
.TP
|
.TP
|
||||||
|
\fB--notify\fR
|
||||||
|
Send desktop notifications on errors
|
||||||
|
.TP
|
||||||
\fB-v, --version\fR
|
\fB-v, --version\fR
|
||||||
Show application version.
|
Show application version.
|
||||||
.SH "COMMANDS"
|
.SH "COMMANDS"
|
||||||
|
|
26
main.go
26
main.go
|
@ -7,7 +7,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,11 +14,12 @@ import (
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "1.3.0"
|
const version = "1.4.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
app = kingpin.New("clipman", "A clipboard manager for Wayland")
|
app = kingpin.New("clipman", "A clipboard manager for Wayland")
|
||||||
histpath = app.Flag("histpath", "Path of history file").Default("~/.local/share/clipman.json").String()
|
histpath = app.Flag("histpath", "Path of history file").Default("~/.local/share/clipman.json").String()
|
||||||
|
alert = app.Flag("notify", "Send desktop notifications on errors").Bool()
|
||||||
|
|
||||||
storer = app.Command("store", "Record clipboard events (run as argument to `wl-paste --watch`)")
|
storer = app.Command("store", "Record clipboard events (run as argument to `wl-paste --watch`)")
|
||||||
maxDemon = storer.Flag("max-items", "history size").Default("15").Int()
|
maxDemon = storer.Flag("max-items", "history size").Default("15").Int()
|
||||||
|
@ -47,7 +47,7 @@ func main() {
|
||||||
|
|
||||||
histfile, history, err := getHistory(*histpath)
|
histfile, history, err := getHistory(*histpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "critical", *alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
|
@ -59,18 +59,18 @@ func main() {
|
||||||
stdin = append(stdin, scanner.Text())
|
stdin = append(stdin, scanner.Text())
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
log.Fatal("Error getting input from stdin.")
|
smartLog("Couldn't get input from stdin.", "critical", *alert)
|
||||||
}
|
}
|
||||||
text := strings.Join(stdin, "\n")
|
text := strings.Join(stdin, "\n")
|
||||||
|
|
||||||
persist := !*noPersist
|
persist := !*noPersist
|
||||||
if err := store(text, history, histfile, *maxDemon, persist); err != nil {
|
if err := store(text, history, histfile, *maxDemon, persist); err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "critical", *alert)
|
||||||
}
|
}
|
||||||
case "pick":
|
case "pick":
|
||||||
selection, err := selector(history, *maxPicker, *pickTool, "pick", *pickToolArgs)
|
selection, err := selector(history, *maxPicker, *pickTool, "pick", *pickToolArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "normal", *alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
if selection != "" {
|
if selection != "" {
|
||||||
|
@ -79,7 +79,7 @@ func main() {
|
||||||
}
|
}
|
||||||
case "restore":
|
case "restore":
|
||||||
if len(history) == 0 {
|
if len(history) == 0 {
|
||||||
log.Println("Nothing to restore")
|
fmt.Println("Nothing to restore")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ func main() {
|
||||||
// remove all history
|
// remove all history
|
||||||
if *clearAll {
|
if *clearAll {
|
||||||
if err := wipeAll(histfile); err != nil {
|
if err := wipeAll(histfile); err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "normal", *alert)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ func main() {
|
||||||
|
|
||||||
selection, err := selector(history, *maxClearer, *clearTool, "clear", *clearToolArgs)
|
selection, err := selector(history, *maxClearer, *clearTool, "clear", *clearToolArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "normal", *alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
if selection == "" {
|
if selection == "" {
|
||||||
|
@ -111,7 +111,7 @@ func main() {
|
||||||
// there was only one possible item we could select, and we selected it,
|
// there was only one possible item we could select, and we selected it,
|
||||||
// so wipe everything
|
// so wipe everything
|
||||||
if err := wipeAll(histfile); err != nil {
|
if err := wipeAll(histfile); err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "normal", *alert)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := write(filter(history, selection), histfile); err != nil {
|
if err := write(filter(history, selection), histfile); err != nil {
|
||||||
log.Fatal(err)
|
smartLog(err.Error(), "critical", *alert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,12 +171,12 @@ func getHistory(rawPath string) (string, []string, error) {
|
||||||
func serveTxt(s string) {
|
func serveTxt(s string) {
|
||||||
bin, err := exec.LookPath("wl-copy")
|
bin, err := exec.LookPath("wl-copy")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("couldn't find wl-copy: %v\n", err)
|
smartLog(fmt.Sprintf("couldn't find wl-copy: %v\n", err), "low", *alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we mandate the mime type because we know we can only serve text; not doing this leads to weird bugs like #35
|
// we mandate the mime type because we know we can only serve text; not doing this leads to weird bugs like #35
|
||||||
cmd := exec.Cmd{Path: bin, Args: []string{bin, "-t", "TEXT"}, Stdin: strings.NewReader(s)}
|
cmd := exec.Cmd{Path: bin, Args: []string{bin, "-t", "TEXT"}, Stdin: strings.NewReader(s)}
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Printf("error running wl-copy: %s\n", err)
|
smartLog(fmt.Sprintf("error running wl-copy: %s\n", err), "low", *alert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
notify.go
Normal file
44
notify.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func smartLog(message, urgency string, alert bool) {
|
||||||
|
if alert {
|
||||||
|
if err := notify(message, urgency); err != nil {
|
||||||
|
log.Printf("failure sending notification: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch urgency {
|
||||||
|
case "critical", "normal":
|
||||||
|
log.Fatal(message)
|
||||||
|
default:
|
||||||
|
log.Println(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func notify(message string, urgency string) error {
|
||||||
|
var timeout time.Duration
|
||||||
|
switch urgency {
|
||||||
|
// cases accepted by notify-send: low, normal, critical
|
||||||
|
case "critical":
|
||||||
|
timeout = 5 * time.Second
|
||||||
|
case "low":
|
||||||
|
timeout = 2 * time.Second
|
||||||
|
default:
|
||||||
|
timeout = 3 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify-send only accepts milliseconds
|
||||||
|
millisec := fmt.Sprintf("%v", timeout.Seconds()*1000)
|
||||||
|
|
||||||
|
args := []string{"-a", "Clipman", "-u", urgency, "-t", millisec, message}
|
||||||
|
|
||||||
|
return exec.Command("notify-send", args...).Run()
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue