kcm/src/commands/cmd_watch.go

119 lines
3.1 KiB
Go

package commands
import (
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/vleeuwenmenno/kcm/src/config"
"github.com/vleeuwenmenno/kcm/src/models"
"golang.design/x/clipboard"
)
func NewWatchCmd(history *models.History) *cobra.Command {
cmd := &cobra.Command{
Use: "watch",
Short: "Watch clipboard and store history",
Aliases: []string{"--watch"},
Run: func(cmd *cobra.Command, args []string) {
noDaemon, _ := cmd.Flags().GetBool("no-daemon")
if !noDaemon {
// Safety net: check if kcm daemon is already running (ignore self)
out, err := exec.Command("ps", "-C", "kcm", "-o", "pid=").Output()
if err == nil {
pids := strings.Fields(string(out))
myPid := os.Getpid()
for _, pidStr := range pids {
pid, err := strconv.Atoi(pidStr)
if err == nil && pid != myPid {
log.Warn().Msg("kcm daemon is already running. Exiting to avoid duplicate daemons.")
os.Exit(1)
}
}
}
// Set log file path since we are daemonizing
cfg, _ := config.LoadConfig()
if cfg.Logging.Path != "" {
file, err := os.OpenFile(cfg.Logging.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal().AnErr("err", err).Msg("Failed to open log file")
}
multi := zerolog.MultiLevelWriter(file, os.Stderr)
log.Logger = log.Output(multi)
}
// Daemonize: fork and detach
execPath, err := os.Executable()
if err != nil {
log.Fatal().Err(err).Msg("Failed to get executable path for daemonizing")
}
if os.Getppid() != 1 {
attr := &os.ProcAttr{
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
Env: os.Environ(),
}
proc, err := os.StartProcess(execPath, os.Args, attr)
if err != nil {
log.Fatal().Err(err).Msg("Failed to daemonize")
}
log.Info().
Str("logging_path", cfg.Logging.Path).
Msgf("Daemon started with PID %d", proc.Pid)
os.Exit(0)
}
}
if err := clipboard.Init(); err != nil {
log.Fatal().Err(err).Msg("Failed to initialize clipboard")
}
var (
lastText string
lastImage []byte
)
for {
time.Sleep(250 * time.Millisecond)
textOut := clipboard.Read(clipboard.FmtText)
imgOut := clipboard.Read(clipboard.FmtImage)
if len(textOut) > 0 {
text := string(textOut)
if text != lastText {
item := models.HistoryItem{
Data: []byte(text),
DataType: 0, // 0 for text
Timestamp: time.Now(),
Pinned: false,
}
history.Add(item)
lastText = text
log.Info().Str("content", text).Msg("Text item added ")
}
}
if len(imgOut) > 0 && (lastImage == nil || string(imgOut) != string(lastImage)) {
item := models.HistoryItem{
Data: imgOut,
DataType: 1, // 1 for image/png
Timestamp: time.Now(),
Pinned: false,
}
history.Add(item)
lastImage = imgOut
log.Info().Msg("Image item added")
}
}
},
}
cmd.Flags().Bool("no-daemon", false, "Run in foreground (do not daemonize)")
return cmd
}