diff options
| author | citrons <citrons@mondecitronne.com> | 2025-06-05 14:56:22 -0500 |
|---|---|---|
| committer | citrons <citrons@mondecitronne.com> | 2025-06-07 16:02:18 -0500 |
| commit | cf78bc0515fa38a6cc570afd7a7e0000bdcda09e (patch) | |
| tree | 6a72a3ef0f67dde072d0a9010851ee06441041dc | |
| parent | 8352123b5c2c479cea31c991eeebf7868559db29 (diff) | |
clipboard support
| -rw-r--r-- | client/application.go | 1 | ||||
| -rw-r--r-- | client/clipboard/clipboard.go | 19 | ||||
| -rw-r--r-- | client/clipboard/shell_clipboard.go | 78 | ||||
| -rw-r--r-- | client/main.go | 7 | ||||
| -rw-r--r-- | client/ui.go | 23 |
5 files changed, 122 insertions, 6 deletions
diff --git a/client/application.go b/client/application.go index 5b60ca4..388272f 100644 --- a/client/application.go +++ b/client/application.go @@ -21,6 +21,7 @@ type application struct { channelList channelList cmdWindow cmdWindow prompts []window.Prompt + activePaste <-chan string } func newApplication(serverAddress string) *application { diff --git a/client/clipboard/clipboard.go b/client/clipboard/clipboard.go index b110378..b81b500 100644 --- a/client/clipboard/clipboard.go +++ b/client/clipboard/clipboard.go @@ -1,4 +1,4 @@ -package tui +package clipboard import ( "sync" @@ -12,23 +12,30 @@ type Clipboard interface { type virtualClipboard string func (clip *virtualClipboard) Copy(text string) { - *clip = text + *clip = virtualClipboard(text) } var ch = make(chan string) func (clip *virtualClipboard) Paste() <-chan string { go func() { - ch <- *clip + ch <- string(*clip) }() return ch } -var clipboard Clipboard = virtualClipboard {} -var mut RWMutex +var defaultClipboard virtualClipboard +var clipboard Clipboard = &defaultClipboard +var mut sync.RWMutex -func Get() { +func Get() Clipboard { mut.RLock() c := clipboard mut.RUnlock() return c } + +func Set(c Clipboard) { + mut.Lock() + clipboard = c + mut.Unlock() +} diff --git a/client/clipboard/shell_clipboard.go b/client/clipboard/shell_clipboard.go new file mode 100644 index 0000000..9d8ecc8 --- /dev/null +++ b/client/clipboard/shell_clipboard.go @@ -0,0 +1,78 @@ +package clipboard + +import ( + "os/exec" + "strings" + "bytes" +) + +type ShellClipboard struct { + copyCommand string + pasteCommand string + testCommand string +} + +var try = []ShellClipboard { + ShellClipboard {"wl-copy", "wl-paste -n", "wl-copy -h"}, + ShellClipboard { + "xclip -selection clipboard", "xclip -selection clipboard -o", + "xclip -h", + }, + ShellClipboard {"pbcopy", "pbpaste", "pbcopy -help"}, +} + +func command(command string) *exec.Cmd { + args := strings.Split(command, " ") + return exec.Command(args[0], args[1:]...) +} + +func (c ShellClipboard) Test() bool { + cmd := command(c.testCommand) + return cmd.Run() == nil +} + +func (c ShellClipboard) Copy(text string) { + cmd := command(c.copyCommand) + pipe, err := cmd.StdinPipe() + cmd.Start() + go func() { + if err != nil { + return + } + defer pipe.Close() + + buf := bytes.NewBuffer([]byte(text)) + buf.WriteTo(pipe) + }() +} + +func (c ShellClipboard) Paste() <-chan string { + cmd := command(c.pasteCommand) + pipe, err := cmd.StdoutPipe() + cmd.Start() + ch := make(chan string, 1) + go func() { + if err != nil { + return + } + defer pipe.Close() + + buf := bytes.NewBuffer(nil) + _, err = buf.ReadFrom(pipe) + if err != nil { + return + } + ch <- buf.String() + close(ch) + }() + return ch +} + +func DiscoverCommand() { + for _, c := range try { + if c.Test() { + Set(c) + break + } + } +} diff --git a/client/main.go b/client/main.go index 5b88117..c9da6c3 100644 --- a/client/main.go +++ b/client/main.go @@ -2,6 +2,7 @@ package main import ( "citrons.xyz/talk/tui" + "citrons.xyz/talk/client/clipboard" "time" "fmt" "os" @@ -10,6 +11,8 @@ import ( var globalApp *application func main() { + go clipboard.DiscoverCommand() + err := tui.Start() if err != nil { fmt.Fprintln(os.Stderr, "error initializing terminal: ", err) @@ -39,6 +42,10 @@ func main() { globalApp.show() redraw = false } + case text := <-globalApp.activePaste: + globalApp.onPaste(text) + globalApp.activePaste = nil + redraw = true } if globalApp.quit == true { return diff --git a/client/ui.go b/client/ui.go index 51d64e5..a71a106 100644 --- a/client/ui.go +++ b/client/ui.go @@ -1,6 +1,7 @@ package main import ( + "citrons.xyz/talk/client/clipboard" "citrons.xyz/talk/client/window" "citrons.xyz/talk/tui" "citrons.xyz/talk/proto" @@ -68,18 +69,40 @@ func (a *application) onInput(ev tui.Event) { input.SetText("") } } + + case 'c' | keys.Ctrl: + sel := prompt.Input().Selection() + if sel != "" { + clipboard.Get().Copy(sel) + } + case 'v' | keys.Ctrl: + if globalApp.activePaste == nil { + globalApp.activePaste = clipboard.Get().Paste() + } + case 'x' | keys.Ctrl: + sel := prompt.Input().Selection() + if sel != "" { + clipboard.Get().Copy(sel) + prompt.Input().Write("") + } + case keys.Up | keys.Alt: a.channelList.traverse(-1) case keys.Down | keys.Alt: a.channelList.traverse(1) + case 'p' | keys.Ctrl: a.traverseHistory(-1) case 'n' | keys.Ctrl: a.traverseHistory(1) + case '0' | keys.Alt: a.goTo(cmdWindowLocation{}) } +} +func (a *application) onPaste(text string) { + a.getPrompt().Input().Write(text) } func (a *application) logUserUpdate(uid string, update proto.Object) { |
