diff --git a/server/internal/desktop/clipboard.go b/server/internal/desktop/clipboard.go index b67ecff0..fff20d8b 100644 --- a/server/internal/desktop/clipboard.go +++ b/server/internal/desktop/clipboard.go @@ -58,6 +58,23 @@ func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error return stdout.Bytes(), nil } +func (manager *DesktopManagerCtx) replaceClipboardCommand(newCmd *exec.Cmd) { + // Swap the current clipboard command with the new one. + oldCmd := manager.clipboardCommand.Swap(newCmd) + + // If the command is already running, we need to shutdown it properly. + if oldCmd == nil || oldCmd.ProcessState != nil { + return + } + + // If there is a previous command running and it was not stopped yet, we need to kill it. + if err := oldCmd.Process.Kill(); err != nil { + manager.logger.Err(err).Msg("failed to kill previous clipboard command") + } else { + manager.logger.Debug().Msg("killed previous clipboard command") + } +} + func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) error { cmd := exec.Command("xclip", "-selection", "clipboard", "-in", "-target", mime) @@ -69,7 +86,9 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e return err } - // TODO: Refactor. + // Shutdown previous command if it exists and replace it with the new one. + manager.replaceClipboardCommand(cmd) + // We need to wait until the data came to the clipboard. wait := make(chan struct{}) xevent.Emmiter.Once("clipboard-updated", func(payload ...any) { @@ -89,9 +108,23 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e stdin.Close() - // TODO: Refactor. - // cmd.Wait() - <-wait + select { + case <-manager.shutdown: + return fmt.Errorf("clipboard manager is shutting down") + case <-wait: + } + + manager.wg.Add(1) + go func() { + defer manager.wg.Done() + + if err := cmd.Wait(); err != nil { + msg := strings.TrimSpace(stderr.String()) + manager.logger.Err(err).Msgf("clipboard command finished with error: %s", msg) + } else { + manager.logger.Debug().Msg("clipboard command finished successfully") + } + }() return nil } diff --git a/server/internal/desktop/manager.go b/server/internal/desktop/manager.go index 0ffadd73..aca5fe1a 100644 --- a/server/internal/desktop/manager.go +++ b/server/internal/desktop/manager.go @@ -1,7 +1,9 @@ package desktop import ( + "os/exec" "sync" + "sync/atomic" "time" "github.com/kataras/go-events" @@ -25,6 +27,11 @@ type DesktopManagerCtx struct { config *config.Desktop screenSize types.ScreenSize // cached screen size input xinput.Driver + + // Clipboard process holding the most recent clipboard data. + // It must remain running to allow pasting clipboard data. + // The last command is kept running until it is replaced or shutdown. + clipboardCommand atomic.Pointer[exec.Cmd] } func New(config *config.Desktop) *DesktopManagerCtx { @@ -131,6 +138,8 @@ func (manager *DesktopManagerCtx) Shutdown() error { manager.logger.Info().Msgf("shutdown") close(manager.shutdown) + + manager.replaceClipboardCommand(nil) manager.wg.Wait() xorg.DisplayClose()