package main import ( "bytes" "fmt" "log" "github.com/getlantern/systray" "github.com/gorilla/websocket" "golang.design/x/clipboard" ) var ( conn *websocket.Conn lastClipboard []byte identification string isConnected bool ) func onReady() { // Use embedded icon systray.SetIcon(EmbeddedIcon) systray.SetTitle("Clipboard Sync") systray.SetTooltip("Clipboard synchronization active") mStatus := systray.AddMenuItem("Status: Connecting...", "Connection status") mStatus.Disable() systray.AddSeparator() mQuit := systray.AddMenuItem("Quit", "Quit the application") // Start clipboard monitoring go monitorClipboard() // Start client connection go startClient(mStatus) // Handle menu interactions go func() { for { select { case <-mQuit.ClickedCh: systray.Quit() } } }() } func onExit() { if conn != nil { conn.Close() } log.Println("Exiting clipboard sync client") } func startClient(statusItem *systray.MenuItem) { // Use embedded configuration identification = EmbeddedIdentification server_ip := EmbeddedServerIP server_port := EmbeddedServerPort server_connection := "ws://" + server_ip + ":" + server_port + "/ws" if identification == "" || server_ip == "" || server_port == "" { log.Println("Missing configuration") statusItem.SetTitle("Status: Config error") return } // Connect to the server var connectErr error conn, _, connectErr = websocket.DefaultDialer.Dial(server_connection, nil) if connectErr != nil { log.Println("Error connecting to server:", connectErr) statusItem.SetTitle("Status: Connection failed") return } // Send identification to the server sendMessage(conn, websocket.TextMessage, []byte(identification)) isConnected = true statusItem.SetTitle(fmt.Sprintf("Status: Connected (%s)", identification)) log.Println("Connected to server") // Listen for messages from the server for { messageType, message, err := conn.ReadMessage() if err != nil { log.Println("Error reading message:", err) isConnected = false statusItem.SetTitle("Status: Disconnected") return } handleMessage(messageType, message) } } func handleMessage(messageType int, message []byte) { log.Printf("Received clipboard update: %d bytes", len(message)) // Update local clipboard tracking to avoid re-sending lastClipboard = make([]byte, len(message)) copy(lastClipboard, message) // Write to clipboard clipboard.Write(clipboard.FmtText, message) log.Println("Clipboard updated") } func sendMessage(conn *websocket.Conn, messageType int, message []byte) { err := conn.WriteMessage(messageType, message) if err != nil { log.Println("Error sending message:", err) } } func monitorClipboard() { for { if isConnected && conn != nil { data := clipboard.Read(clipboard.FmtText) // Check if clipboard has changed if len(data) > 0 && !bytes.Equal(data, lastClipboard) { log.Printf("Clipboard changed, sending to server: %d bytes", len(data)) sendMessage(conn, websocket.TextMessage, data) lastClipboard = make([]byte, len(data)) copy(lastClipboard, data) } } } } func getDefaultIcon() []byte { return EmbeddedIcon } func main() { // Initialize clipboard clip_err := clipboard.Init() if clip_err != nil { log.Fatal("Failed to initialize clipboard:", clip_err) } // Run systray application systray.Run(onReady, onExit) }