Skip to content

Commit

Permalink
support clipboard integration via OSC52
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jul 13, 2024
1 parent f40f870 commit a7ad81a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 22 deletions.
8 changes: 8 additions & 0 deletions README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ DefaultDownloadPath = /Users/username/Downloads/

- 关于进度条,己传文件大小和传输速度不是精确值,会有一些偏差,它的主要作用只是指示传输正在进行中。

## 剪贴板集成

- 使用 `-o``--osc52` 启用剪贴板集成功能,例如 `trzsz -o ssh remote_server`

- 启用剪贴板集成功能后,支持远程服务器通过 OSC52 序列写入本地剪贴板。

- 在 Linux 系统,剪贴板集成功能需要安装 `xclip``xsel` 命令。

## 常见问题

- 如果 [MSYS2](https://www.msys2.org/)[Git Bash](https://www.atlassian.com/git/tutorials/git-bash) 遇到错误 `The handle is invalid`
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ DefaultDownloadPath = /Users/username/Downloads/
- About the progress, the transferred and speed are not precise, there will be some deviation. It just indicating that the transfer is in progress.
## Clipboard integration
- Use `-o` or `--osc52` to enable the clipboard integration feature. e.g., `trzsz -o ssh remote_server`.
- Clipboard integration allows remote servers to write to the local clipboard via OSC52 sequences.
- On Linux, clipboard integration requires `xclip` or `xsel` command to be installed.
## Trouble shooting
- If using [MSYS2](https://www.msys2.org/) or [Git Bash](https://www.atlassian.com/git/tutorials/git-bash) on windows, and getting an error `The handle is invalid`.
Expand Down
15 changes: 8 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ module github.com/trzsz/trzsz-go
go 1.20

require (
github.com/UserExistsError/conpty v0.1.3
github.com/UserExistsError/conpty v0.1.4
github.com/atotto/clipboard v0.1.4
github.com/creack/pty v1.1.21
github.com/klauspost/compress v1.17.8
github.com/klauspost/compress v1.17.9
github.com/mattn/go-runewidth v0.0.15
github.com/ncruces/zenity v0.10.12
github.com/ncruces/zenity v0.10.13
github.com/stretchr/testify v1.8.4
github.com/trzsz/go-arg v1.5.3
github.com/trzsz/promptui v0.10.7
golang.org/x/sys v0.20.0
golang.org/x/term v0.20.0
golang.org/x/text v0.15.0
golang.org/x/sys v0.22.0
golang.org/x/term v0.22.0
golang.org/x/text v0.16.0
)

require (
Expand All @@ -26,6 +27,6 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/image v0.16.0 // indirect
golang.org/x/image v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
30 changes: 16 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
github.com/UserExistsError/conpty v0.1.3 h1:YzGQkHAiBBkAihOCO5J2cAnahzb8ePvje2YxG7et1E0=
github.com/UserExistsError/conpty v0.1.3/go.mod h1:PDglKIkX3O/2xVk0MV9a6bCWxRmPVfxqZoTG/5sSd9I=
github.com/UserExistsError/conpty v0.1.4 h1:+3FhJhiqhyEJa+K5qaK3/w6w+sN3Nh9O9VbJyBS02to=
github.com/UserExistsError/conpty v0.1.4/go.mod h1:PDglKIkX3O/2xVk0MV9a6bCWxRmPVfxqZoTG/5sSd9I=
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
Expand All @@ -19,12 +21,12 @@ github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f h1:OGqDDftRTwrvUoL6pO
github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f/go.mod h1:Dv9D0NUlAsaQcGQZa5kc5mqR9ua72SmA8VXi4cd+cBw=
github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/ncruces/zenity v0.10.12 h1:o4SErDa0kQijlqG6W4OYYzO6kA0fGu34uegvJGcMLBI=
github.com/ncruces/zenity v0.10.12/go.mod h1:5OZIERViRR2fN0FcJCcisqxI+lYMDGzEDCEwB/+8iao=
github.com/ncruces/zenity v0.10.13 h1:0Gd/EdjjEQIhrFaJ05Q5ZvyjlcjnorlZpdzgUzqQIH0=
github.com/ncruces/zenity v0.10.13/go.mod h1:UyAUPSjHm1hOdeZa3Lrh/zmItyGq+iH5AP6xSCx8CFM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 h1:GranzK4hv1/pqTIhMTXt2X8MmMOuH3hMeUR0o9SP5yc=
Expand All @@ -43,17 +45,17 @@ github.com/trzsz/go-arg v1.5.3/go.mod h1:IC6Z/FiVH7uYvcbp1/gJhDYCFPS/GkL0APYakVv
github.com/trzsz/promptui v0.10.7 h1:77uBrmsIPYYJS/9n+zwFRhwOz82EKXkkdjOiWSEUPpk=
github.com/trzsz/promptui v0.10.7/go.mod h1:9dp59ixe32qBV9GjDxQ1PDWwbzHjTzveZenQwEoVHbg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
58 changes: 58 additions & 0 deletions trzsz/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ package trzsz

import (
"bufio"
"bytes"
"encoding/base64"
"fmt"
"io"
"net"
Expand All @@ -39,6 +41,7 @@ import (
"sync/atomic"
"time"

"github.com/atotto/clipboard"
"github.com/ncruces/zenity"
"github.com/trzsz/promptui"
)
Expand All @@ -55,6 +58,8 @@ type TrzszOptions struct {
DetectTraceLog bool
// EnableZmodem enable zmodem lrzsz ( rz / sz ) feature.
EnableZmodem bool
// EnableOSC52 enable OSC52 clipboard feature.
EnableOSC52 bool
}

// TrzszFilter is a filter that supports trzsz ( trz / tsz ).
Expand All @@ -79,6 +84,7 @@ type TrzszFilter struct {
defaultUploadPath atomic.Pointer[string]
defaultDownloadPath atomic.Pointer[string]
tunnelConnector atomic.Pointer[func(int) net.Conn]
osc52Sequence *bytes.Buffer
}

// NewTrzszFilter create a TrzszFilter to support trzsz ( trz / tsz ).
Expand Down Expand Up @@ -695,6 +701,9 @@ func (filter *TrzszFilter) wrapOutput() {
}
}
}
if filter.options.EnableOSC52 {
filter.detectOSC52(buf)
}

var trigger *trzszTrigger
buf, trigger = detector.detectTrzsz(buf, filter.tunnelConnector.Load() != nil)
Expand Down Expand Up @@ -738,3 +747,52 @@ func (filter *TrzszFilter) wrapOutput() {
}
}
}

func (filter *TrzszFilter) detectOSC52(buf []byte) {
if filter.osc52Sequence == nil {
pos := bytes.Index(buf, []byte("\x1b]52;c;"))
if pos < 0 {
return
}
buf = buf[pos+7:]
pos = bytes.IndexByte(buf, '\a')
if pos < 0 {
filter.osc52Sequence = bytes.NewBuffer(nil)
filter.osc52Sequence.Write(buf)
return
}
writeToClipboard(buf[:pos])
buf = buf[pos+1:]
if len(buf) > 0 {
filter.detectOSC52(buf)
}
return
}

pos := bytes.IndexByte(buf, '\a')
if pos < 0 {
filter.osc52Sequence.Write(buf)
if filter.osc52Sequence.Len() > 100000 {
// something went wrong, just ignore it
filter.osc52Sequence = nil
}
return
}

filter.osc52Sequence.Write(buf[:pos])
writeToClipboard(filter.osc52Sequence.Bytes())
filter.osc52Sequence = nil

buf = buf[pos+1:]
if len(buf) > 0 {
filter.detectOSC52(buf)
}
}

func writeToClipboard(buf []byte) {
text, err := base64.StdEncoding.DecodeString(string(buf))
if err != nil {
return
}
_ = clipboard.WriteAll(string(text))
}
7 changes: 6 additions & 1 deletion trzsz/trzsz.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type trzszArgs struct {
TraceLog bool
DragFile bool
Zmodem bool
OSC52 bool
Name string
Args []string
}
Expand All @@ -59,7 +60,8 @@ func printHelp() {
" -r, --relay run as a trzsz relay server\n" +
" -t, --tracelog eanble trace log for debugging\n" +
" -d, --dragfile enable drag file(s) to upload\n" +
" -z, --zmodem enable zmodem lrzsz ( rz / sz )\n")
" -z, --zmodem enable zmodem lrzsz (rz / sz)\n" +
" -o, --osc52 enable clipboard integration\n")
}

func parseTrzszArgs() *trzszArgs {
Expand All @@ -80,6 +82,8 @@ func parseTrzszArgs() *trzszArgs {
args.DragFile = true
} else if os.Args[i] == "-z" || os.Args[i] == "--zmodem" {
args.Zmodem = true
} else if os.Args[i] == "-o" || os.Args[i] == "--osc52" {
args.OSC52 = true
} else {
break
}
Expand Down Expand Up @@ -173,6 +177,7 @@ func TrzszMain() int {
DetectDragFile: args.DragFile,
DetectTraceLog: args.TraceLog,
EnableZmodem: args.Zmodem,
EnableOSC52: args.OSC52,
})
pty.OnResize(filter.SetTerminalColumns)
// handle signal
Expand Down

0 comments on commit a7ad81a

Please sign in to comment.