diff --git a/lib/clipboard/clipboard_windows.go b/lib/clipboard/clipboard_windows.go index 93285dd..01f456b 100644 --- a/lib/clipboard/clipboard_windows.go +++ b/lib/clipboard/clipboard_windows.go @@ -7,126 +7,55 @@ package clipboard import ( + "errors" "syscall" - "time" "unsafe" + + "github.com/lxn/win" ) -const ( - cfUnicodetext = 13 - // gmemFixed = 0x0000 - gmemMoveable = 0x0002 -) - -var ( - user32 = syscall.MustLoadDLL("user32") - openClipboard = user32.MustFindProc("OpenClipboard") - closeClipboard = user32.MustFindProc("CloseClipboard") - emptyClipboard = user32.MustFindProc("EmptyClipboard") - getClipboardData = user32.MustFindProc("GetClipboardData") - setClipboardData = user32.MustFindProc("SetClipboardData") - - kernel32 = syscall.NewLazyDLL("kernel32") - globalAlloc = kernel32.NewProc("GlobalAlloc") - globalFree = kernel32.NewProc("GlobalFree") - globalLock = kernel32.NewProc("GlobalLock") - globalUnlock = kernel32.NewProc("GlobalUnlock") - lstrcpy = kernel32.NewProc("lstrcpyW") -) - -// waitOpenClipboard opens the clipboard, waiting for up to a second to do so. -func waitOpenClipboard() error { - started := time.Now() - limit := started.Add(time.Second) - var r uintptr - var err error - for time.Now().Before(limit) { - r, _, err = openClipboard.Call(0) - if r != 0 { - return nil - } - time.Sleep(time.Millisecond) - } - return err -} - func readAll() (string, error) { - // r, _, err := openClipboard.Call(0) - err := waitOpenClipboard() - if err != nil { - return "", err + if !win.OpenClipboard(0) { + return "", errors.New("OpenClipboard") } - defer closeClipboard.Call() - - h, _, err := getClipboardData.Call(cfUnicodetext) - if h == 0 { - return "", err + defer win.CloseClipboard() + hMem := win.HGLOBAL(win.GetClipboardData(win.CF_UNICODETEXT)) + if hMem == 0 { + return "", errors.New("GetClipboardData") } - - l, _, err := globalLock.Call(h) - if l == 0 { - return "", err + p := win.GlobalLock(hMem) + if p == nil { + return "", errors.New("GlobalLock()") } - - text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) - - r, _, err := globalUnlock.Call(h) - if r == 0 { - return "", err - } - + defer win.GlobalUnlock(hMem) + text := win.UTF16PtrToString((*uint16)(p)) return text, nil } func writeAll(text string) error { - err := waitOpenClipboard() + if !win.OpenClipboard(0) { + return errors.New("OpenClipboard") + } + defer win.CloseClipboard() + utf16, err := syscall.UTF16FromString(text) if err != nil { return err } - defer closeClipboard.Call() - - r, _, err := emptyClipboard.Call(0) - if r == 0 { - return err + hMem := win.GlobalAlloc(win.GMEM_MOVEABLE, uintptr(len(utf16)*2)) + if hMem == 0 { + return errors.New("GlobalAlloc") } - - data := syscall.StringToUTF16(text) - - // "If the hMem parameter identifies a memory object, the object must have - // been allocated using the function with the GMEM_MOVEABLE flag." - h, _, err := globalAlloc.Call(gmemMoveable, - uintptr(len(data)*int(unsafe.Sizeof(data[0])))) - if h == 0 { - return err + p := win.GlobalLock(hMem) + if p == nil { + return errors.New("GlobalLock()") } - defer func() { - if h != 0 { - globalFree.Call(h) - } - }() - - l, _, err := globalLock.Call(h) - if l == 0 { - return err + win.MoveMemory(p, unsafe.Pointer(&utf16[0]), uintptr(len(utf16)*2)) + win.GlobalUnlock(hMem) + if 0 == win.SetClipboardData(win.CF_UNICODETEXT, win.HANDLE(hMem)) { + // We need to free hMem. + defer win.GlobalFree(hMem) + return errors.New("SetClipboardData") } - - r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) - if r == 0 { - return err - } - - r, _, err = globalUnlock.Call(h) - if r == 0 { - return err - } - - r, _, err = setClipboardData.Call(cfUnicodetext, h) - if r == 0 { - if err.(syscall.Errno) != 0 { - return err - } - } - - h = 0 // suppress deferred cleanup + // The system now owns the memory referred to by hMem. return nil } diff --git a/main.go b/main.go index 0eab17c..adb5a5c 100644 --- a/main.go +++ b/main.go @@ -27,22 +27,22 @@ func main() { } const ( - TextHeader = "Go-Clipboard-Text" - TimeHeader = "Go-Clipboard-Time" + textHeader = "Go-Clipboard-Text" + timeHeader = "Go-Clipboard-Time" ) func startServer(b string) { text := "" time := time.Now().Unix() - var err error - g := gin.Default() + g := gin.New() g.GET("/", func(c *gin.Context) { - c.Header(TextHeader, text) - c.Header(TimeHeader, strconv.FormatInt(time, 10)) + c.Header(textHeader, text) + c.Header(timeHeader, strconv.FormatInt(time, 10)) }) g.POST("/", func(c *gin.Context) { text = c.Query("text") - time, err = strconv.ParseInt(c.Query("time"), 10, 64) + time, _ = strconv.ParseInt(c.Query("time"), 10, 64) + fmt.Printf("Update Clipboard: %s\n", text) }) g.Run(b) } @@ -63,11 +63,11 @@ func readClipboard(a string) { client.Do(request) } else { response, _ := http.Get(a) - texts, ok := response.Header[TextHeader] + texts, ok := response.Header[textHeader] if ok { sText := texts[0] - sTime, _ := strconv.ParseInt(response.Header[TimeHeader][0], 10, 64) - if sTime > updateTime { + sTime, _ := strconv.ParseInt(response.Header[timeHeader][0], 10, 64) + if sTime > updateTime && sText != old { old = sText updateTime = time.Now().Unix() clipboard.WriteAll(sText)