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.
clipboard/readhandle_windows.go

227 lines
6.0 KiB
Go

2 months ago
package clipboard
import (
"b612.me/win32api"
"bytes"
"encoding/binary"
"errors"
2 months ago
"fmt"
2 months ago
"golang.org/x/image/bmp"
"image"
"image/color"
"image/png"
"reflect"
"strings"
"syscall"
"unsafe"
)
2 months ago
func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size uint32) (interface{}, error)) (interface{}, error) {
2 months ago
mem, err := win32api.GetClipboardData(uFormat)
if err != nil {
2 months ago
return nil, fmt.Errorf("GetClipboardData failed: %v", err)
2 months ago
}
p, err := win32api.GlobalLock(mem)
if err != nil {
2 months ago
return nil, fmt.Errorf("GlobalLock failed: %v", err)
2 months ago
}
defer win32api.GlobalUnlock(mem)
size, err := win32api.GlobalSize(mem)
if err != nil {
2 months ago
return nil, fmt.Errorf("GlobalSize failed: %v", err)
2 months ago
}
if fn == nil {
return defaultFetchFn(p, uint32(size))
}
return fn(p, uint32(size))
}
2 months ago
func defaultFetchFn(p unsafe.Pointer, size uint32) (interface{}, error) {
2 months ago
var buf []byte
for i := 0; i < int(size); i++ {
buf = append(buf, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(i))))
}
return buf, nil
}
2 months ago
func AutoFetcher(uFormat string) (interface{}, error) {
2 months ago
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)
}
2 months ago
func ClipSize(uFormat string) (int, error) {
s, err := fetchClipboardData(getFormat(uFormat), func(p unsafe.Pointer, size uint32) (interface{}, error) {
return int(size), nil
})
if err != nil {
return 0, err
}
return s.(int), nil
}
func textFetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
2 months ago
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
}
2 months ago
func filedropFetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
2 months ago
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
}
2 months ago
func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
2 months ago
// 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
}
2 months ago
func cfDIBFetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
2 months ago
// 函数意外报错,待修正
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
}