108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
//go:build linux
|
|
|
|
package clipboard
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
type linuxClipboard struct {
|
|
copyCmd string // "xclip" or "wl-copy"
|
|
pasteCmd string // "xclip" or "wl-paste"
|
|
copyArgs []string // e.g. ["-selection", "clipboard"] for xclip
|
|
}
|
|
|
|
// New creates a Linux clipboard (X11 xclip or Wayland wl-clipboard).
|
|
func New() (Clipboard, error) {
|
|
return newLinuxClipboard()
|
|
}
|
|
|
|
func newLinuxClipboard() (*linuxClipboard, error) {
|
|
sessionType := os.Getenv("XDG_SESSION_TYPE")
|
|
|
|
if strings.EqualFold(sessionType, "wayland") {
|
|
if err := checkBinary("wl-copy"); err != nil {
|
|
return nil, fmt.Errorf("clip-sync: Wayland detected but wl-copy not found: %w", err)
|
|
}
|
|
if err := checkBinary("wl-paste"); err != nil {
|
|
return nil, fmt.Errorf("clip-sync: Wayland detected but wl-paste not found: %w", err)
|
|
}
|
|
return &linuxClipboard{
|
|
copyCmd: "wl-copy",
|
|
pasteCmd: "wl-paste",
|
|
copyArgs: nil, // wl-copy reads from stdin by default
|
|
}, nil
|
|
}
|
|
|
|
// Default: X11 via xclip
|
|
if err := checkBinary("xclip"); err != nil {
|
|
return nil, fmt.Errorf("clip-sync: xclip not found (install xclip for X11 or wl-clipboard for Wayland): %w", err)
|
|
}
|
|
return &linuxClipboard{
|
|
copyCmd: "xclip",
|
|
pasteCmd: "xclip",
|
|
copyArgs: []string{"-selection", "clipboard"},
|
|
}, nil
|
|
}
|
|
|
|
func (c *linuxClipboard) Read() (string, error) {
|
|
args := []string{"-selection", "clipboard", "-o"}
|
|
if c.pasteCmd == "wl-paste" {
|
|
args = nil // wl-paste needs no args
|
|
}
|
|
|
|
cmd := exec.Command(c.pasteCmd, args...)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("clipboard read (%s): %w", c.pasteCmd, err)
|
|
}
|
|
return strings.TrimRight(string(out), "\n\r"), nil
|
|
}
|
|
|
|
func (c *linuxClipboard) Write(text string) error {
|
|
cmd := exec.Command(c.copyCmd, c.copyArgs...)
|
|
cmd.Stdin = strings.NewReader(text)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("clipboard write (%s): %w", c.copyCmd, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadImage returns the PNG image from the clipboard, if any.
|
|
func (c *linuxClipboard) ReadImage() (string, []byte, error) {
|
|
var args []string
|
|
if c.pasteCmd == "wl-paste" {
|
|
args = []string{"--type", "image/png"}
|
|
} else {
|
|
args = []string{"-selection", "clipboard", "-t", "image/png", "-o"}
|
|
}
|
|
|
|
cmd := exec.Command(c.pasteCmd, args...)
|
|
out, err := cmd.Output()
|
|
if err != nil || len(out) == 0 {
|
|
return "", nil, ErrNoImage
|
|
}
|
|
return "image/png", out, nil
|
|
}
|
|
|
|
// WriteImage writes image bytes to the clipboard using the given MIME type.
|
|
func (c *linuxClipboard) WriteImage(mime string, data []byte) error {
|
|
var args []string
|
|
if c.copyCmd == "wl-copy" {
|
|
args = []string{"--type", mime}
|
|
} else {
|
|
args = []string{"-selection", "clipboard", "-t", mime}
|
|
}
|
|
|
|
cmd := exec.Command(c.copyCmd, args...)
|
|
cmd.Stdin = bytes.NewReader(data)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("clipboard write image (%s): %w", c.copyCmd, err)
|
|
}
|
|
return nil
|
|
}
|