189 lines
4.3 KiB
Go
189 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/getlantern/systray"
|
|
"github.com/gorilla/websocket"
|
|
"golang.design/x/clipboard"
|
|
)
|
|
|
|
var (
|
|
conn *websocket.Conn
|
|
lastClipboard []byte
|
|
identification string
|
|
isConnected bool
|
|
serverURL string
|
|
)
|
|
|
|
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
|
|
serverURL = "ws://" + server_ip + ":" + server_port + "/ws"
|
|
|
|
if identification == "" || server_ip == "" || server_port == "" {
|
|
log.Println("Missing configuration")
|
|
statusItem.SetTitle("Status: Config error")
|
|
return
|
|
}
|
|
|
|
// Reconnection loop with exponential backoff
|
|
backoff := 1 * time.Second
|
|
maxBackoff := 60 * time.Second
|
|
|
|
for {
|
|
if connectToServer(statusItem) {
|
|
// Successfully connected, reset backoff
|
|
backoff = 1 * time.Second
|
|
|
|
// Listen for messages until connection drops
|
|
listenForMessages(statusItem)
|
|
}
|
|
|
|
// Connection failed or dropped, wait before retry
|
|
isConnected = false
|
|
statusItem.SetTitle(fmt.Sprintf("Status: Reconnecting in %ds...", int(backoff.Seconds())))
|
|
log.Printf("Reconnecting in %s...", backoff)
|
|
time.Sleep(backoff)
|
|
|
|
// Increase backoff exponentially
|
|
backoff *= 2
|
|
if backoff > maxBackoff {
|
|
backoff = maxBackoff
|
|
}
|
|
}
|
|
}
|
|
|
|
func connectToServer(statusItem *systray.MenuItem) bool {
|
|
statusItem.SetTitle("Status: Connecting...")
|
|
|
|
var err error
|
|
conn, _, err = websocket.DefaultDialer.Dial(serverURL, nil)
|
|
if err != nil {
|
|
log.Println("Error connecting to server:", err)
|
|
statusItem.SetTitle("Status: Connection failed")
|
|
return false
|
|
}
|
|
|
|
// Send identification to the server
|
|
err = conn.WriteMessage(websocket.TextMessage, []byte(identification))
|
|
if err != nil {
|
|
log.Println("Error sending identification:", err)
|
|
conn.Close()
|
|
conn = nil
|
|
return false
|
|
}
|
|
|
|
isConnected = true
|
|
statusItem.SetTitle(fmt.Sprintf("Status: Connected (%s)", identification))
|
|
log.Println("Connected to server")
|
|
return true
|
|
}
|
|
|
|
func listenForMessages(statusItem *systray.MenuItem) {
|
|
for {
|
|
messageType, message, err := conn.ReadMessage()
|
|
if err != nil {
|
|
log.Println("Error reading message:", err)
|
|
isConnected = false
|
|
statusItem.SetTitle("Status: Disconnected")
|
|
if conn != nil {
|
|
conn.Close()
|
|
conn = nil
|
|
}
|
|
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)
|
|
isConnected = false
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|