diff --git a/CHANGELOG.md b/CHANGELOG.md index 959efca..f3763f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,5 +2,6 @@ **Breaking changes**: -- switch from flags to subcommands: `clipman listen` instead than `clipman -d` and `clipman pick` instead than `clipman -s` -- switch demon from polling to event-driven: requires wl-clipboard 2.0 (or latest git HEAD) +- switch from flags to subcommands: `wl-paste -t text --watch clipman store` instead than `clipman -d` and `clipman pick` instead than `clipman -s` +- switch demon from polling to event-driven: requires wl-clipboard >= 2.0 +- primary clipboard support: `wl-paste -p -t text --watch clipman store --histpath="~/.local/share/clipman-primary.json` and `clipman pick --histpath="~/.local/share/clipman-primary.json` diff --git a/README.md b/README.md index 7b30706..e504aa8 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ Archlinux users can find a PKGBUILD [here](https://aur.archlinux.org/packages/cl ## Usage -Run the binary in your Sway session by adding `exec clipman -d` (or `exec clipman -d 1>> PATH/TO/LOGFILE 2>&1 &` to log errors) at the beginning of your config. +Run the binary in your Sway session by adding `exec wl-paste -t text --watch clipman store` (or `exec wl-paste -t text --watch clipman store 1>> PATH/TO/LOGFILE 2>&1 &` to log errors) at the beginning of your config. +For primary clipboard support, also add `exec wl-paste -p -t text --watch clipman store --histpath="~/.local/share/clipman-primary.json`. -To query the history and select items, run the binary as `clipman -s`. You can assign it to a keybinding: `bindsym $mod+h exec clipman -s`. +To query the history and select items, run the binary as `clipman pick`. You can assign it to a keybinding: `bindsym $mod+h exec clipman pick`. +For primary clipboard support, `clipman pick --histpath="~/.local/share/clipman-primary.json`. For more options: `clipman -h`. diff --git a/go.mod b/go.mod index 6c943e2..2064ee5 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,12 @@ module github.com/yory8/clipman -go 1.12 +go 1.13 require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect - github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect + github.com/alecthomas/units v0.0.0-20190910110746-680d30ca3117 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/stretchr/testify v1.4.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index fc34b0d..acff885 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190910110746-680d30ca3117 h1:aUo+WrWZtRRfc6WITdEKzEczFRlEpfW15NhNeLRc17U= +github.com/alecthomas/units v0.0.0-20190910110746-680d30ca3117/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/main.go b/main.go index 482bd35..19b312e 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "encoding/json" "fmt" "io/ioutil" @@ -14,10 +15,10 @@ import ( var ( app = kingpin.New("clipman", "A clipboard manager for Wayland") histpath = app.Flag("histpath", "Path of history file").Default("~/.local/share/clipman.json").String() - demon = app.Command("listen", "Run as a demon to record clipboard events") + storer = app.Command("store", "Run from `wl-paste --watch` to record clipboard events") picker = app.Command("pick", "Pick an item from clipboard history") - noPersist = demon.Flag("no-persist", "Don't persist a copy buffer after a program exits").Short('P').Default("false").Bool() - maxDemon = demon.Flag("max-items", "history size").Default("15").Int() + noPersist = storer.Flag("no-persist", "Don't persist a copy buffer after a program exits").Short('P').Default("false").Bool() + maxDemon = storer.Flag("max-items", "history size").Default("15").Int() maxPicker = picker.Flag("max-items", "scrollview length").Default("15").Int() tool = picker.Flag("selector", "Which selector to use: dmenu/rofi/-").Default("dmenu").String() ) @@ -25,13 +26,26 @@ var ( func main() { app.HelpFlag.Short('h') switch kingpin.MustParse(app.Parse(os.Args[1:])) { - case "listen": + case "store": persist := !*noPersist + histfile, history, err := getHistory() if err != nil { log.Fatal(err) } - listen(history, histfile, persist, *maxDemon) + + // read copy from stdin + var stdin []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + stdin = append(stdin, scanner.Text()) + } + if err := scanner.Err(); err != nil { + log.Fatal("Error getting input from stdin.") + } + text := strings.Join(stdin, "\n") + + store(text, history, histfile, *maxDemon, persist) case "pick": _, history, err := getHistory() if err != nil { diff --git a/demon.go b/storer.go similarity index 65% rename from demon.go rename to storer.go index f4a316e..238fe03 100644 --- a/demon.go +++ b/storer.go @@ -4,55 +4,9 @@ import ( "encoding/json" "io/ioutil" "log" - "os" "os/exec" ) -type historyBuf struct { - buf []string // field name as required by io.Writer, don't change - histfile string - max int - persist bool -} - -func (hb *historyBuf) Write(p []byte) (n int, err error) { - hb.buf = store(string(p), hb.buf, hb.histfile, hb.max, hb.persist) - return len(p), err // signature as required by io.Writer, don't change -} - -func write(history []string, histfile string) error { - histlog, err := json.Marshal(history) - if err != nil { - return err - } - err = ioutil.WriteFile(histfile, histlog, 0644) - - return err -} - -func filter(history []string, text string) []string { - var ( - found bool - idx int - ) - - for i, el := range history { - if el == text { - found = true - idx = i - break - } - } - - if found { - // we know that idx can't be the last element, because - // we never get to call this function if that's the case - history = append(history[:idx], history[idx+1:]...) - } - - return history -} - func store(text string, history []string, histfile string, max int, persist bool) []string { l := len(history) if l > 0 { @@ -87,16 +41,35 @@ func store(text string, history []string, histfile string, max int, persist bool return history } -func listen(history []string, histfile string, persist bool, max int) { - cmd := exec.Command("wl-paste", "-t", "text", "--watch", "cat") - cmd.Stdout = &historyBuf{history, histfile, max, persist} - cmd.Stderr = os.Stderr +func filter(history []string, text string) []string { + var ( + found bool + idx int + ) - if err := cmd.Start(); err != nil { - log.Fatalf("Error running wl-paste (cmd.Start): %s", err) + for i, el := range history { + if el == text { + found = true + idx = i + break + } } - if err := cmd.Wait(); err != nil { - log.Fatalf("Error running wl-paste (cmd.Wait): %s", err) + if found { + // we know that idx can't be the last element, because + // we never get to call this function if that's the case + history = append(history[:idx], history[idx+1:]...) } + + return history +} + +func write(history []string, histfile string) error { + histlog, err := json.Marshal(history) + if err != nil { + return err + } + err = ioutil.WriteFile(histfile, histlog, 0644) + + return err }