75 lines
2.4 KiB
Go
75 lines
2.4 KiB
Go
// Package clipboard writes text to the system clipboard by shelling out to the
|
|
// platform's clipboard utility (US-009, #125). It deliberately avoids any cgo or
|
|
// third-party dependency: it probes for the standard command-line tools
|
|
// (pbcopy on macOS, wl-copy / xclip / xsel on Linux) and pipes text to the first
|
|
// one found. When no utility is available it reports ErrUnavailable so the
|
|
// caller can degrade gracefully (e.g. print the text instead).
|
|
package clipboard
|
|
|
|
import (
|
|
"errors"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
// ErrUnavailable is returned by Copy when no supported clipboard utility is
|
|
// found on the host, so the caller can fall back to printing the content.
|
|
var ErrUnavailable = errors.New("clipboard: no clipboard utility available")
|
|
|
|
// candidate is a clipboard-writing command: the executable plus the args that
|
|
// make it read the payload from stdin.
|
|
type candidate struct {
|
|
name string
|
|
args []string
|
|
}
|
|
|
|
// candidates returns the clipboard-write commands to try, in priority order,
|
|
// for the current OS. On macOS pbcopy is always present; on Linux the Wayland
|
|
// tool is preferred, then the two common X11 tools.
|
|
func candidates() []candidate {
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
return []candidate{{name: "pbcopy"}}
|
|
case "windows":
|
|
return []candidate{{name: "clip"}}
|
|
default: // linux and other unixes
|
|
return []candidate{
|
|
{name: "wl-copy"},
|
|
{name: "xclip", args: []string{"-selection", "clipboard"}},
|
|
{name: "xsel", args: []string{"--clipboard", "--input"}},
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy writes text to the system clipboard using the first available platform
|
|
// utility. It returns ErrUnavailable if none is found (so callers can fall back
|
|
// to printing), or the underlying exec error if a utility was found but failed.
|
|
func Copy(text string) error {
|
|
for _, c := range candidates() {
|
|
path, err := exec.LookPath(c.name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
cmd := exec.Command(path, c.args...)
|
|
cmd.Stdin = strings.NewReader(text)
|
|
if err := cmd.Run(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
return ErrUnavailable
|
|
}
|
|
|
|
// Available reports whether a clipboard utility is present, without writing
|
|
// anything. Useful for a caller that wants to phrase its output differently when
|
|
// it knows the copy will fail.
|
|
func Available() bool {
|
|
for _, c := range candidates() {
|
|
if _, err := exec.LookPath(c.name); err == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|