From ae6a6568997cf50ed8add65295acea5a7f89d2ef Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 6 Mar 2026 22:12:24 -0500 Subject: [PATCH] fix(Clipboard): Epic RAM Growth - Closes #1920 --- core/cmd/dms/commands_clipboard.go | 12 +++- core/internal/clipboard/clipboard.go | 100 +++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/core/cmd/dms/commands_clipboard.go b/core/cmd/dms/commands_clipboard.go index 0cf4c4b7..8a9b8585 100644 --- a/core/cmd/dms/commands_clipboard.go +++ b/core/cmd/dms/commands_clipboard.go @@ -222,16 +222,19 @@ func init() { func runClipCopy(cmd *cobra.Command, args []string) { var data []byte + copyFromStdin := false switch { case len(args) > 0: data = []byte(args[0]) - default: + case clipCopyDownload || clipCopyType == "__multi__": var err error data, err = io.ReadAll(os.Stdin) if err != nil { log.Fatalf("read stdin: %v", err) } + default: + copyFromStdin = true } if clipCopyDownload { @@ -257,6 +260,13 @@ func runClipCopy(cmd *cobra.Command, args []string) { return } + if copyFromStdin { + if err := clipboard.CopyReader(os.Stdin, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil { + log.Fatalf("copy: %v", err) + } + return + } + if err := clipboard.CopyOpts(data, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil { log.Fatalf("copy: %v", err) } diff --git a/core/internal/clipboard/clipboard.go b/core/internal/clipboard/clipboard.go index 7d117942..be5c8d7e 100644 --- a/core/internal/clipboard/clipboard.go +++ b/core/internal/clipboard/clipboard.go @@ -1,10 +1,12 @@ package clipboard import ( + "bytes" "fmt" "io" "os" "os/exec" + "path/filepath" "syscall" "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control" @@ -12,17 +14,21 @@ import ( ) func Copy(data []byte, mimeType string) error { - return CopyOpts(data, mimeType, false, false) + return CopyReader(bytes.NewReader(data), mimeType, false, false) } func CopyOpts(data []byte, mimeType string, foreground, pasteOnce bool) error { + return CopyReader(bytes.NewReader(data), mimeType, foreground, pasteOnce) +} + +func CopyReader(data io.Reader, mimeType string, foreground, pasteOnce bool) error { if !foreground { return copyFork(data, mimeType, pasteOnce) } - return copyServe(data, mimeType, pasteOnce) + return copyServeReader(data, mimeType, pasteOnce) } -func copyFork(data []byte, mimeType string, pasteOnce bool) error { +func copyFork(data io.Reader, mimeType string, pasteOnce bool) error { args := []string{os.Args[0], "cl", "copy", "--foreground"} if pasteOnce { args = append(args, "--paste-once") @@ -30,11 +36,15 @@ func copyFork(data []byte, mimeType string, pasteOnce bool) error { args = append(args, "--type", mimeType) cmd := exec.Command(args[0], args[1:]...) - cmd.Stdin = nil cmd.Stdout = nil cmd.Stderr = nil cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + if stdinSource, ok := data.(*os.File); ok { + cmd.Stdin = stdinSource + return cmd.Start() + } + stdin, err := cmd.StdinPipe() if err != nil { return fmt.Errorf("stdin pipe: %w", err) @@ -44,16 +54,84 @@ func copyFork(data []byte, mimeType string, pasteOnce bool) error { return fmt.Errorf("start: %w", err) } - if _, err := stdin.Write(data); err != nil { + if _, err := io.Copy(stdin, data); err != nil { stdin.Close() return fmt.Errorf("write stdin: %w", err) } - stdin.Close() + if err := stdin.Close(); err != nil { + return fmt.Errorf("close stdin: %w", err) + } return nil } func copyServe(data []byte, mimeType string, pasteOnce bool) error { + return copyServeWithWriter(func(writer io.Writer) error { + total := 0 + + for total < len(data) { + n, err := writer.Write(data[total:]) + total += n + if err != nil { + return err + } + } + if total != len(data) { + return io.ErrShortWrite + } + return nil + }, mimeType, pasteOnce) +} + +func copyServeReader(data io.Reader, mimeType string, pasteOnce bool) error { + cachedData, err := createClipboardCacheFile() + if err != nil { + return fmt.Errorf("create clipboard cache file: %w", err) + } + defer os.Remove(cachedData.Name()) + + if _, err := io.Copy(cachedData, data); err != nil { + return fmt.Errorf("cache clipboard data: %w", err) + } + if err := cachedData.Close(); err != nil { + return fmt.Errorf("close temp cache file: %w", err) + } + + return copyServeWithWriter(func(writer io.Writer) error { + cachedFile, err := os.Open(cachedData.Name()) + if err != nil { + return fmt.Errorf("open temp cache file: %w", err) + } + defer cachedFile.Close() + + if _, err := io.Copy(writer, cachedFile); err != nil { + return fmt.Errorf("write clipboard data: %w", err) + } + return nil + }, mimeType, pasteOnce) +} + +func createClipboardCacheFile() (*os.File, error) { + preferredDirs := []string{} + + if cacheDir, err := os.UserCacheDir(); err == nil { + preferredDirs = append(preferredDirs, filepath.Join(cacheDir, "dms", "clipboard")) + } + preferredDirs = append(preferredDirs, "/var/tmp/dms/clipboard") + + for _, dir := range preferredDirs { + if err := os.MkdirAll(dir, 0o700); err != nil { + continue + } + cachedData, err := os.CreateTemp(dir, "dms-clipboard-*") + if err == nil { + return cachedData, nil + } + } + return os.CreateTemp("", "dms-clipboard-*") +} + +func copyServeWithWriter(writeTo func(io.Writer) error, mimeType string, pasteOnce bool) error { display, err := wlclient.Connect("") if err != nil { return fmt.Errorf("wayland connect: %w", err) @@ -139,12 +217,18 @@ func copyServe(data []byte, mimeType string, pasteOnce bool) error { cancelled := make(chan struct{}) pasted := make(chan struct{}, 1) + sendErr := make(chan error, 1) source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { defer syscall.Close(e.Fd) file := os.NewFile(uintptr(e.Fd), "pipe") defer file.Close() - file.Write(data) + if err := writeTo(file); err != nil { + select { + case sendErr <- err: + default: + } + } select { case pasted <- struct{}{}: default: @@ -165,6 +249,8 @@ func copyServe(data []byte, mimeType string, pasteOnce bool) error { select { case <-cancelled: return nil + case err := <-sendErr: + return err case <-pasted: if pasteOnce { return nil