You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
5.7 KiB
Go
217 lines
5.7 KiB
Go
package clipboard
|
|
|
|
import (
|
|
"b612.me/win32api"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"golang.org/x/image/bmp"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"reflect"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size uint32) ([]byte, error)) ([]byte, error) {
|
|
mem, err := win32api.GetClipboardData(uFormat)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetClipboardData failed: %v", err)
|
|
}
|
|
p, err := win32api.GlobalLock(mem)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GlobalLock failed: %v", err)
|
|
}
|
|
defer win32api.GlobalUnlock(mem)
|
|
size, err := win32api.GlobalSize(mem)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GlobalSize failed: %v", err)
|
|
}
|
|
if fn == nil {
|
|
return defaultFetchFn(p, uint32(size))
|
|
}
|
|
return fn(p, uint32(size))
|
|
}
|
|
|
|
func defaultFetchFn(p unsafe.Pointer, size uint32) ([]byte, error) {
|
|
var buf []byte
|
|
for i := 0; i < int(size); i++ {
|
|
buf = append(buf, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(i))))
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
func AutoFetcher(uFormat string) ([]byte, error) {
|
|
switch uFormat {
|
|
case "CF_TEXT", "CF_UNICODETEXT":
|
|
return fetchClipboardData(win32api.CF_UNICODETEXT, textFetcher)
|
|
case "HTML Format":
|
|
return fetchClipboardData(win32api.RegisterClipboardFormat("HTML Format"), nil)
|
|
case "CF_HDROP":
|
|
return fetchClipboardData(win32api.CF_HDROP, filedropFetcher)
|
|
case "CF_DIBV5":
|
|
return fetchClipboardData(win32api.CF_DIBV5, cfDIBv5Fetcher)
|
|
case "CF_DIB":
|
|
return fetchClipboardData(win32api.CF_DIB, cfDIBFetcher)
|
|
case "PNG":
|
|
return fetchClipboardData(win32api.RegisterClipboardFormat("PNG"), nil)
|
|
}
|
|
return nil, errors.New("not support uFormat:" + uFormat)
|
|
}
|
|
|
|
func textFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
|
var buf []uint16
|
|
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*((*uint16)(unsafe.Pointer(p))))) {
|
|
buf = append(buf, *(*uint16)(ptr))
|
|
}
|
|
return []byte(syscall.UTF16ToString(buf)), nil
|
|
}
|
|
|
|
func filedropFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
|
var res []string
|
|
c, err := win32api.DragQueryFile(win32api.HDROP(p), 0xFFFFFFFF, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
count := int(c)
|
|
|
|
for i := 0; i < count; i++ {
|
|
c, err = win32api.DragQueryFile(win32api.HDROP(p), win32api.DWORD(i), nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
length := int(c)
|
|
buffer := make([]uint16, length+1)
|
|
|
|
_, err = win32api.DragQueryFile(win32api.HDROP(p), win32api.DWORD(i),
|
|
&buffer[0], win32api.DWORD(len(buffer)))
|
|
res = append(res, syscall.UTF16ToString(buffer))
|
|
}
|
|
return []byte(strings.Join(res, "|")), nil
|
|
}
|
|
|
|
func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
|
// inspect header information
|
|
info := (*bitmapV5Header)(unsafe.Pointer(p))
|
|
// maybe deal with other formats?
|
|
if info.BitCount != 32 {
|
|
return nil, errors.New("not support image format")
|
|
}
|
|
|
|
var data []byte
|
|
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
|
sh.Data = uintptr(p)
|
|
sh.Cap = int(info.Size + 4*uint32(info.Width)*uint32(info.Height))
|
|
sh.Len = int(info.Size + 4*uint32(info.Width)*uint32(info.Height))
|
|
img := image.NewRGBA(image.Rect(0, 0, int(info.Width), int(info.Height)))
|
|
offset := int(info.Size)
|
|
stride := int(info.Width)
|
|
for y := 0; y < int(info.Height); y++ {
|
|
for x := 0; x < int(info.Width); x++ {
|
|
idx := offset + 4*(y*stride+x)
|
|
xhat := (x + int(info.Width)) % int(info.Width)
|
|
yhat := int(info.Height) - 1 - y
|
|
r := data[idx+2]
|
|
g := data[idx+1]
|
|
b := data[idx+0]
|
|
a := data[idx+3]
|
|
img.SetRGBA(xhat, yhat, color.RGBA{r, g, b, a})
|
|
}
|
|
}
|
|
// always use PNG encoding.
|
|
var buf bytes.Buffer
|
|
err := png.Encode(&buf, img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func cfDIBFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
|
// 函数意外报错,待修正
|
|
const (
|
|
fileHeaderLen = 14
|
|
infoHeaderLen = 40
|
|
)
|
|
bmpHeader := (*bitmapHeader)(p)
|
|
dataSize := bmpHeader.SizeImage + fileHeaderLen + infoHeaderLen
|
|
|
|
if bmpHeader.SizeImage == 0 && bmpHeader.Compression == 0 {
|
|
iSizeImage := bmpHeader.Height * ((bmpHeader.Width*uint32(bmpHeader.BitCount)/8 + 3) &^ 3)
|
|
dataSize += iSizeImage
|
|
}
|
|
buf := new(bytes.Buffer)
|
|
binary.Write(buf, binary.LittleEndian, uint16('B')|(uint16('M')<<8))
|
|
binary.Write(buf, binary.LittleEndian, uint32(dataSize))
|
|
binary.Write(buf, binary.LittleEndian, uint32(0))
|
|
const sizeof_colorbar = 0
|
|
binary.Write(buf, binary.LittleEndian, uint32(fileHeaderLen+infoHeaderLen+sizeof_colorbar))
|
|
j := 0
|
|
for i := fileHeaderLen; i < int(dataSize); i++ {
|
|
binary.Write(buf, binary.BigEndian, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(j))))
|
|
j++
|
|
}
|
|
return bmpToPng(buf)
|
|
}
|
|
|
|
func bmpToPng(bmpBuf *bytes.Buffer) (buf []byte, err error) {
|
|
var f bytes.Buffer
|
|
original_image, err := bmp.Decode(bmpBuf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = png.Encode(&f, original_image)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return f.Bytes(), nil
|
|
}
|
|
|
|
type bitmapV5Header struct {
|
|
Size uint32
|
|
Width int32
|
|
Height int32
|
|
Planes uint16
|
|
BitCount uint16
|
|
Compression uint32
|
|
SizeImage uint32
|
|
XPelsPerMeter int32
|
|
YPelsPerMeter int32
|
|
ClrUsed uint32
|
|
ClrImportant uint32
|
|
RedMask uint32
|
|
GreenMask uint32
|
|
BlueMask uint32
|
|
AlphaMask uint32
|
|
CSType uint32
|
|
Endpoints struct {
|
|
CiexyzRed, CiexyzGreen, CiexyzBlue struct {
|
|
CiexyzX, CiexyzY, CiexyzZ int32 // FXPT2DOT30
|
|
}
|
|
}
|
|
GammaRed uint32
|
|
GammaGreen uint32
|
|
GammaBlue uint32
|
|
Intent uint32
|
|
ProfileData uint32
|
|
ProfileSize uint32
|
|
Reserved uint32
|
|
}
|
|
|
|
type bitmapHeader struct {
|
|
Size uint32
|
|
Width uint32
|
|
Height uint32
|
|
PLanes uint16
|
|
BitCount uint16
|
|
Compression uint32
|
|
SizeImage uint32
|
|
XPelsPerMeter uint32
|
|
YPelsPerMeter uint32
|
|
ClrUsed uint32
|
|
ClrImportant uint32
|
|
}
|