You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
3.7 KiB

// +build windows
package clipboard
import (
"fmt"
"syscall"
"time"
"unsafe"
)
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")
setClipboardViewer = user32.MustFindProc("SetClipboardViewer")
CountClipboardFormats = user32.MustFindProc("CountClipboardFormats")
EnumClipboardFormats = user32.MustFindProc("EnumClipboardFormats")
GetClipboardFormatNameW = user32.MustFindProc("GetClipboardFormatNameW")
kernel32 = syscall.NewLazyDLL("kernel32")
globalAlloc = kernel32.NewProc("GlobalAlloc")
globalFree = kernel32.NewProc("GlobalFree")
globalLock = kernel32.NewProc("GlobalLock")
globalUnlock = kernel32.NewProc("GlobalUnlock")
lstrcpy = kernel32.NewProc("lstrcpyW")
)
const gmemMoveable = 0x0002
const (
CF_UNICODETEXT = 13
CF_TEXT = 1
CF_BITMAP = 2
CF_OEMTEXT = 7
CF_DIB = 8
CF_DIBV5 = 17
)
var supportedFormats [4]int = [4]int{CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT}
func isSupportedFormat(format int) bool {
for i := 0; i < len(supportedFormats); i++ {
if supportedFormats[i] == format {
return true
}
}
return false
}
// 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 error error
for time.Now().Before(limit) {
r, _, error = openClipboard.Call(0)
if r != 0 {
return nil
}
time.Sleep(time.Millisecond)
}
return error
}
func readAll() (string, error) {
err := waitOpenClipboard()
if err != nil {
return "", err
}
defer closeClipboard.Call()
formatsCount, _, _ := CountClipboardFormats.Call(0)
fmt.Printf("Available formats: %d\n", formatsCount)
availableFormat, _, _ := EnumClipboardFormats.Call(0)
fmt.Print("Available content formats: ")
hasSupportedFormat := false
for i := 0; i < (int)(formatsCount); i++ {
index := 0
if i > 0 {
index = i
}
format, _, _ := EnumClipboardFormats.Call(uintptr(index))
if format != 0 {
if isSupported := isSupportedFormat((int)(format)); isSupported {
hasSupportedFormat = true
break
}
}
}
if !hasSupportedFormat {
return "", &ErrFormatNotSupported{formatType: (int)(availableFormat)}
}
h, _, err := getClipboardData.Call(CF_UNICODETEXT)
if h == 0 {
return "", err
}
l, _, err := globalLock.Call(h)
if l == 0 {
return "", err
}
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
r, _, err := globalUnlock.Call(h)
if r == 0 {
return "", err
}
return text, nil
}
func writeAll(text string) error {
err := waitOpenClipboard()
if err != nil {
return err
}
defer closeClipboard.Call()
r, _, err := emptyClipboard.Call(0)
if r == 0 {
return err
}
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
}
defer func() {
if h != 0 {
globalFree.Call(h)
}
}()
l, _, err := globalLock.Call(h)
if l == 0 {
return err
}
r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0])))
if r == 0 {
return err
}
r, _, err = globalUnlock.Call(h)
if r == 0 {
if err.(syscall.Errno) != 0 {
return err
}
}
r, _, err = setClipboardData.Call(CF_UNICODETEXT, h)
if r == 0 {
return err
}
h = 0 // suppress deferred cleanup
return nil
}