clipboard.go RawHistoryFolder
1package clipboard
2
3import (
4 "fmt"
5 "log"
6 "os/exec"
7)
8
9// CopyToClipboard copies the given text to the system clipboard.
10// It tries wl-copy first (Wayland), then falls back to xclip (X11).
11// Returns an error if both fail.
12func CopyToClipboard(text string) error {
13 if text == "" {
14 return fmt.Errorf("cannot copy empty text to clipboard")
15 }
16
17 // Try wl-copy first (Wayland)
18 if err := tryWlCopy(text); err == nil {
19 return nil
20 }
21
22 // Fallback to xclip (X11)
23 if err := tryXclip(text); err == nil {
24 return nil
25 }
26
27 return fmt.Errorf("clipboard copy failed: neither wl-copy nor xclip available")
28}
29
30// tryWlCopy attempts to copy text using wl-copy (Wayland clipboard)
31func tryWlCopy(text string) error {
32 cmd := exec.Command("wl-copy")
33 cmd.Stdin = nil
34
35 // Set up stdin pipe to pass the text
36 stdin, err := cmd.StdinPipe()
37 if err != nil {
38 return fmt.Errorf("failed to create stdin pipe for wl-copy: %w", err)
39 }
40
41 // Start the command
42 if err := cmd.Start(); err != nil {
43 if closeErr := stdin.Close(); closeErr != nil {
44 log.Printf("Warning: failed to close stdin: %v", closeErr)
45 }
46 return fmt.Errorf("failed to start wl-copy: %w", err)
47 }
48
49 // Write text to stdin and close it
50 _, writeErr := stdin.Write([]byte(text))
51 closeErr := stdin.Close()
52
53 // Wait for command to complete
54 waitErr := cmd.Wait()
55
56 // Return the first error encountered
57 if writeErr != nil {
58 return fmt.Errorf("failed to write to wl-copy stdin: %w", writeErr)
59 }
60 if closeErr != nil {
61 return fmt.Errorf("failed to close wl-copy stdin: %w", closeErr)
62 }
63 if waitErr != nil {
64 return fmt.Errorf("wl-copy command failed: %w", waitErr)
65 }
66
67 return nil
68}
69
70// tryXclip attempts to copy text using xclip (X11 clipboard)
71func tryXclip(text string) error {
72 cmd := exec.Command("xclip", "-selection", "clipboard")
73
74 // Set up stdin pipe to pass the text
75 stdin, err := cmd.StdinPipe()
76 if err != nil {
77 return fmt.Errorf("failed to create stdin pipe for xclip: %w", err)
78 }
79
80 // Start the command
81 if err := cmd.Start(); err != nil {
82 if closeErr := stdin.Close(); closeErr != nil {
83 log.Printf("Warning: failed to close stdin: %v", closeErr)
84 }
85 return fmt.Errorf("failed to start xclip: %w", err)
86 }
87
88 // Write text to stdin and close it
89 _, writeErr := stdin.Write([]byte(text))
90 closeErr := stdin.Close()
91
92 // Wait for command to complete
93 waitErr := cmd.Wait()
94
95 // Return the first error encountered
96 if writeErr != nil {
97 return fmt.Errorf("failed to write to xclip stdin: %w", writeErr)
98 }
99 if closeErr != nil {
100 return fmt.Errorf("failed to close xclip stdin: %w", closeErr)
101 }
102 if waitErr != nil {
103 return fmt.Errorf("xclip command failed: %w", waitErr)
104 }
105
106 return nil
107}
108
109// IsAvailable checks if clipboard functionality is available on the system
110func IsAvailable() bool {
111 // Check if wl-copy is available
112 if _, err := exec.LookPath("wl-copy"); err == nil {
113 return true
114 }
115
116 // Check if xclip is available
117 if _, err := exec.LookPath("xclip"); err == nil {
118 return true
119 }
120
121 return false
122}