update
parent
568dd318c3
commit
0d790f2f68
@ -1 +1,304 @@
|
||||
package clipboard
|
||||
|
||||
import (
|
||||
"b612.me/win32api"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func allocNewMem(size uintptr) (win32api.HGLOBAL, unsafe.Pointer, error) {
|
||||
mem, err := win32api.GlobalAlloc(win32api.GMEM_MOVEABLE, size)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("GlobalAlloc failed: %v", err)
|
||||
}
|
||||
p, err := win32api.GlobalLock(mem)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("GlobalLock failed: %v", err)
|
||||
}
|
||||
return 0, p, nil
|
||||
}
|
||||
|
||||
var winformatrev = map[string]win32api.DWORD{
|
||||
"CF_TEXT": 1,
|
||||
"CF_BITMAP": 2,
|
||||
"CF_METAFILEPICT": 3,
|
||||
"CF_SYLK": 4,
|
||||
"CF_DIF": 5,
|
||||
"CF_TIFF": 6,
|
||||
"CF_OEMTEXT": 7,
|
||||
"CF_DIB": 8,
|
||||
"CF_PALETTE": 9,
|
||||
"CF_PENDATA": 10,
|
||||
"CF_RIFF": 11,
|
||||
"CF_WAVE": 12,
|
||||
"CF_UNICODETEXT": 13,
|
||||
"CF_ENHMETAFILE": 14,
|
||||
"CF_HDROP": 15,
|
||||
"CF_LOCALE": 16,
|
||||
"CF_DIBV5": 17,
|
||||
"CF_DSPBITMAP": 130,
|
||||
"CF_DSPTEXT": 129,
|
||||
"CF_DSPMETAFILEPICT": 131,
|
||||
"CF_DSPENHMETAFILE": 142,
|
||||
"CF_GDIOBJLAST": 0x03FF,
|
||||
"CF_PRIVATEFIRST": 0x0200,
|
||||
}
|
||||
|
||||
func getFormat(uFormat string) win32api.DWORD {
|
||||
if v, ok := winformatrev[uFormat]; ok {
|
||||
return v
|
||||
}
|
||||
return win32api.RegisterClipboardFormat(uFormat)
|
||||
}
|
||||
|
||||
func AutoSetter(uFormat string, data interface{}) error {
|
||||
switch uFormat {
|
||||
case "CF_TEXT", "CF_UNICODETEXT", "TEXT":
|
||||
return setClipboardData(win32api.CF_UNICODETEXT, data, textSetFn)
|
||||
case "File":
|
||||
return setClipboardData(win32api.CF_HDROP, data, fileSetFn)
|
||||
case "Image", "PNG":
|
||||
return setClipboardData(0, data, imageSetFn)
|
||||
default:
|
||||
}
|
||||
return setClipboardData(getFormat(uFormat), data, nil)
|
||||
}
|
||||
|
||||
func setClipboardData(uFormat win32api.DWORD, data interface{}, fn func(uFormat win32api.DWORD, data interface{}) error) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
err := win32api.OpenClipboard(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenClipboard failed: %v", err)
|
||||
}
|
||||
defer win32api.CloseClipboard()
|
||||
err = win32api.EmptyClipboard()
|
||||
if err != nil {
|
||||
return fmt.Errorf("EmptyClipboard failed: %v", err)
|
||||
}
|
||||
if fn == nil {
|
||||
return defaultSetFn(uFormat, data)
|
||||
}
|
||||
return fn(uFormat, data)
|
||||
}
|
||||
|
||||
func defaultSetFn(uFormat win32api.DWORD, idata interface{}) error {
|
||||
data, ok := idata.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("data is not a byte slice")
|
||||
}
|
||||
mem, p, err := allocNewMem(uintptr(uint32(len(data))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
|
||||
err = win32api.RtlMoveMemory(p, unsafe.Pointer(&data[0]), uintptr(len(data)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
_, err = win32api.SetClipboardData(uFormat, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func textSetFn(uFormat win32api.DWORD, idata interface{}) error {
|
||||
data, ok := idata.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("data is not a byte slice")
|
||||
}
|
||||
str, err := syscall.UTF16FromString(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UTF16FromString failed: %v", err)
|
||||
}
|
||||
size := uintptr(len(str) * int(unsafe.Sizeof(str[0])))
|
||||
mem, p, err := allocNewMem(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
|
||||
err = win32api.RtlMoveMemory(p, unsafe.Pointer(&str[0]), size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
_, err = win32api.SetClipboardData(uFormat, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type dropfiles struct {
|
||||
pFiles uint32 // 4 bytes
|
||||
pt point // 2 * 4 = 8 bytes
|
||||
fNC uint32 // 2 byte
|
||||
fWide uint32 // 2 byte
|
||||
}
|
||||
|
||||
type point struct {
|
||||
X int32 // 4 bytes
|
||||
Y int32 // 4 bytes
|
||||
}
|
||||
|
||||
func fileSetFn(uFormat win32api.DWORD, idata interface{}) error {
|
||||
data, ok := idata.([]string)
|
||||
if !ok {
|
||||
return fmt.Errorf("data is not a filepath string slice")
|
||||
}
|
||||
var utf16s = make([][]uint16, 0, len(data))
|
||||
var size uintptr = 20 + 2
|
||||
for _, d := range data {
|
||||
str, err := syscall.UTF16FromString(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UTF16FromString failed: %v", err)
|
||||
}
|
||||
utf16s = append(utf16s, str)
|
||||
size += uintptr((len(str)) * int(unsafe.Sizeof(str[0])))
|
||||
}
|
||||
{
|
||||
mem, p, err := allocNewMem(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
var fdrop = dropfiles{
|
||||
pFiles: 20,
|
||||
pt: point{0, 0},
|
||||
fNC: 0,
|
||||
fWide: 1,
|
||||
}
|
||||
offset := unsafe.Sizeof(fdrop)
|
||||
err = win32api.RtlMoveMemory(unsafe.Pointer(p), unsafe.Pointer(&fdrop), unsafe.Sizeof(fdrop))
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
for _, v := range utf16s {
|
||||
size = uintptr(len(v) * int(unsafe.Sizeof(v[0])))
|
||||
err = win32api.RtlMoveMemory(unsafe.Pointer(uintptr(p)+offset), unsafe.Pointer(&v[0]), size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
offset += size
|
||||
}
|
||||
*(*uint16)(unsafe.Pointer(uintptr(p) + offset)) = 0
|
||||
_, err = win32api.SetClipboardData(win32api.CF_HDROP, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
mem, p, err := allocNewMem(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
for idx, v := range []byte{5, 0, 0, 0} {
|
||||
*(*byte)(unsafe.Pointer(uintptr(p) + uintptr(idx))) = v
|
||||
}
|
||||
format := win32api.RegisterClipboardFormat("Preferred DropEffect")
|
||||
_, err = win32api.SetClipboardData(format, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func imageSetFn(uFormat win32api.DWORD, idata interface{}) error {
|
||||
data, ok := idata.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("data is not a byte slice")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
{
|
||||
img, err := png.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("input bytes is not PNG encoded: %w", err)
|
||||
}
|
||||
|
||||
offset := unsafe.Sizeof(bitmapV5Header{})
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
imageSize := 4 * width * height
|
||||
|
||||
datas := make([]byte, int(offset)+imageSize)
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
idx := int(offset) + 4*(y*width+x)
|
||||
r, g, b, a := img.At(x, height-1-y).RGBA()
|
||||
datas[idx+2] = uint8(r)
|
||||
datas[idx+1] = uint8(g)
|
||||
datas[idx+0] = uint8(b)
|
||||
datas[idx+3] = uint8(a)
|
||||
}
|
||||
}
|
||||
|
||||
info := bitmapV5Header{}
|
||||
info.Size = uint32(offset)
|
||||
info.Width = int32(width)
|
||||
info.Height = int32(height)
|
||||
info.Planes = 1
|
||||
info.Compression = 0 // BI_RGB
|
||||
info.SizeImage = uint32(4 * info.Width * info.Height)
|
||||
info.RedMask = 0xff0000 // default mask
|
||||
info.GreenMask = 0xff00
|
||||
info.BlueMask = 0xff
|
||||
info.AlphaMask = 0xff000000
|
||||
info.BitCount = 32 // we only deal with 32 bpp at the moment.
|
||||
info.CSType = 0x73524742
|
||||
info.Intent = 4 // LCS_GM_IMAGES
|
||||
|
||||
infob := make([]byte, int(unsafe.Sizeof(info)))
|
||||
for i, v := range *(*[unsafe.Sizeof(info)]byte)(unsafe.Pointer(&info)) {
|
||||
infob[i] = v
|
||||
}
|
||||
copy(datas[:], infob[:])
|
||||
size := uintptr(len(datas) * int(unsafe.Sizeof(datas[0])))
|
||||
mem, p, err := allocNewMem(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
err = win32api.RtlMoveMemory(p, unsafe.Pointer(&datas[0]), size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
_, err = win32api.SetClipboardData(win32api.CF_DIBV5, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
mem, p, err := allocNewMem(uintptr(len(data) * int(unsafe.Sizeof(data[0]))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer win32api.GlobalUnlock(mem)
|
||||
err = win32api.RtlMoveMemory(p, unsafe.Pointer(&data[0]), uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
|
||||
if err != nil {
|
||||
return fmt.Errorf("RtlMoveMemory failed: %v", err)
|
||||
}
|
||||
format := win32api.RegisterClipboardFormat("PNG")
|
||||
_, err = win32api.SetClipboardData(format, win32api.HGLOBAL(p))
|
||||
if err != nil {
|
||||
win32api.GlobalFree(win32api.HGLOBAL(p))
|
||||
return fmt.Errorf("SetClipboardData failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue