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 }