update
This commit is contained in:
parent
568dd318c3
commit
0d790f2f68
54
clipboard.go
54
clipboard.go
@ -1,6 +1,7 @@
|
||||
package clipboard
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -21,13 +22,33 @@ type Clipboard struct {
|
||||
secondaryOriType string
|
||||
secondaryType FileType
|
||||
secondaryData []byte
|
||||
secondarySize int
|
||||
|
||||
primaryOriType string
|
||||
primaryType FileType
|
||||
primaryData []byte
|
||||
primarySize int
|
||||
hash string
|
||||
}
|
||||
|
||||
func Set(types FileType, data []byte) error {
|
||||
switch types {
|
||||
case Text:
|
||||
return AutoSetter("CF_UNICODETEXT", data)
|
||||
case File:
|
||||
return AutoSetter("CF_HDROP", data)
|
||||
case Image:
|
||||
return AutoSetter("PNG", data)
|
||||
case HTML:
|
||||
return AutoSetter("HTML Format", data)
|
||||
}
|
||||
return errors.New("not support type:" + string(types))
|
||||
}
|
||||
|
||||
func SetOrigin(types string, data []byte) error {
|
||||
return AutoSetter(types, data)
|
||||
}
|
||||
|
||||
func (c *Clipboard) PrimaryType() FileType {
|
||||
return c.primaryType
|
||||
}
|
||||
@ -57,6 +78,16 @@ func (c *Clipboard) Text() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Clipboard) TextSize() int {
|
||||
if c.primaryType == Text {
|
||||
return c.primarySize
|
||||
}
|
||||
if c.secondaryType == Text {
|
||||
return c.secondarySize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Clipboard) IsHTML() bool {
|
||||
return (c.primaryType == HTML || c.secondaryType == HTML) || c.IsText()
|
||||
}
|
||||
@ -87,6 +118,10 @@ func (c *Clipboard) FilePaths() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Clipboard) IsFile() bool {
|
||||
return c.primaryType == File || c.secondaryType == File
|
||||
}
|
||||
|
||||
func (c *Clipboard) FirstFilePath() string {
|
||||
if c.primaryType == File {
|
||||
return strings.Split(string(c.primaryData), "|")[0]
|
||||
@ -107,6 +142,25 @@ func (c *Clipboard) Image() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Clipboard) ImageSize() int {
|
||||
if c.primaryType == Image {
|
||||
return c.primarySize
|
||||
}
|
||||
if c.secondaryType == Image {
|
||||
return c.secondarySize
|
||||
}
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
func (c *Clipboard) IsImage() bool {
|
||||
return c.primaryType == Image || c.secondaryType == Image
|
||||
}
|
||||
|
||||
func (c *Clipboard) PrimaryTypeSize() int {
|
||||
return c.primarySize
|
||||
}
|
||||
|
||||
func (c *Clipboard) SecondaryTypeSize() int {
|
||||
return c.secondarySize
|
||||
}
|
||||
|
@ -1,13 +1,18 @@
|
||||
package clipboard
|
||||
|
||||
import (
|
||||
"b612.me/win32api"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
lsn, err := Listen()
|
||||
fmt.Println(win32api.RegisterClipboardFormat("Preferred DropEffect"))
|
||||
lsn, err := Listen(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -15,9 +20,17 @@ func TestGet(t *testing.T) {
|
||||
select {
|
||||
case cb := <-lsn:
|
||||
fmt.Println(cb.plateform)
|
||||
fmt.Println(cb.winOriginTypes)
|
||||
fmt.Println(cb.AvailableTypes())
|
||||
fmt.Println(cb.Text())
|
||||
fmt.Println(cb.HTML())
|
||||
if cb.IsText() {
|
||||
fmt.Println(cb.Text())
|
||||
}
|
||||
if cb.IsHTML() {
|
||||
fmt.Println(cb.HTML())
|
||||
}
|
||||
if cb.IsFile() {
|
||||
fmt.Println(cb.FilePaths())
|
||||
}
|
||||
case <-time.After(60 * time.Second):
|
||||
fmt.Println("not get clipboard data in 60s")
|
||||
StopListen()
|
||||
@ -26,3 +39,60 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMeta(t *testing.T) {
|
||||
fmt.Println(win32api.RegisterClipboardFormat("Preferred DropEffect"))
|
||||
lsn, err := Listen(true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case cb := <-lsn:
|
||||
fmt.Println(cb.plateform)
|
||||
fmt.Println(cb.winOriginTypes)
|
||||
fmt.Println(cb.AvailableTypes())
|
||||
fmt.Println(cb.primarySize)
|
||||
fmt.Println(cb.secondarySize)
|
||||
case <-time.After(60 * time.Second):
|
||||
fmt.Println("not get clipboard data in 60s")
|
||||
StopListen()
|
||||
time.Sleep(time.Second * 15)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoSetter(t *testing.T) {
|
||||
//samp := "天狼星、测试,123.hello.love.what??"
|
||||
/*
|
||||
err := AutoSetter("File", []string{"C:\\Users\\Starainrt\\Desktop\\haruhi.jpg"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
*/
|
||||
f, err := os.ReadFile("C:\\Users\\Starainrt\\Desktop\\60.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = AutoSetter("Image", f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTextOrigin(t *testing.T) {
|
||||
samp := "天狼星、测试,123.hello.love.what"
|
||||
u, err := syscall.UTF16FromString(samp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := make([]byte, 2*len(u))
|
||||
for i, v := range u {
|
||||
binary.LittleEndian.PutUint16(b[i*2:], v)
|
||||
}
|
||||
err = setClipboardData(win32api.CF_UNICODETEXT, b, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -42,12 +42,17 @@ var formatRank = map[string]int{
|
||||
}
|
||||
|
||||
func Get() (Clipboard, error) {
|
||||
return innerGetClipboard()
|
||||
return innerGetClipboard(true)
|
||||
}
|
||||
|
||||
func innerGetClipboard() (Clipboard, error) {
|
||||
func GetMeta() (Clipboard, error) {
|
||||
return innerGetClipboard(false)
|
||||
}
|
||||
|
||||
func innerGetClipboard(withFetch bool) (Clipboard, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
var tmpData interface{}
|
||||
var c = Clipboard{
|
||||
plateform: "windows",
|
||||
}
|
||||
@ -107,10 +112,20 @@ func innerGetClipboard() (Clipboard, error) {
|
||||
case "CF_HDROP":
|
||||
c.primaryType = File
|
||||
}
|
||||
c.primaryData, err = AutoFetcher(firstFormatName)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("AutoFetcher error: %v", err)
|
||||
if withFetch {
|
||||
tmpData, err = AutoFetcher(firstFormatName)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("AutoFetcher error: %v", err)
|
||||
}
|
||||
c.primaryData = tmpData.([]byte)
|
||||
c.primarySize = len(c.primaryData)
|
||||
} else {
|
||||
c.primarySize, err = ClipSize(firstFormatName)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("ClipSize error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if secondFormatName != "" {
|
||||
switch secondFormatName {
|
||||
case "CF_UNICODETEXT":
|
||||
@ -123,7 +138,19 @@ func innerGetClipboard() (Clipboard, error) {
|
||||
c.secondaryType = File
|
||||
}
|
||||
c.secondaryOriType = secondFormatName
|
||||
c.secondaryData, err = AutoFetcher(secondFormatName)
|
||||
if withFetch {
|
||||
tmpData, err = AutoFetcher(secondFormatName)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("AutoFetcher error: %v", err)
|
||||
}
|
||||
c.secondaryData = tmpData.([]byte)
|
||||
c.secondarySize = len(c.secondaryData)
|
||||
} else {
|
||||
c.secondarySize, err = ClipSize(secondFormatName)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("ClipSize error: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("AutoFetcher error: %v", err)
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module b612.me/clipboard
|
||||
go 1.21.2
|
||||
|
||||
require (
|
||||
b612.me/win32api v0.0.0-20240328010943-f10bafb4e804
|
||||
b612.me/win32api v0.0.0-20240402021613-0959dfb96afa
|
||||
golang.org/x/image v0.15.0
|
||||
)
|
||||
|
||||
|
6
go.sum
6
go.sum
@ -1,7 +1,5 @@
|
||||
b612.me/win32api v0.0.0-20240326080749-ad19f5cd4247 h1:fDTZ1HzVtVpEcXqlsQB9O2AbtrbqiAruaRX1Yd7M9Z8=
|
||||
b612.me/win32api v0.0.0-20240326080749-ad19f5cd4247/go.mod h1:sj66sFJDKElEjOR+0YhdSW6b4kq4jsXu4T5/Hnpyot0=
|
||||
b612.me/win32api v0.0.0-20240328010943-f10bafb4e804 h1:eLeVqlAmljdycU1cP7svO8cY7vklan6mAuSR/BcfHMs=
|
||||
b612.me/win32api v0.0.0-20240328010943-f10bafb4e804/go.mod h1:sj66sFJDKElEjOR+0YhdSW6b4kq4jsXu4T5/Hnpyot0=
|
||||
b612.me/win32api v0.0.0-20240402021613-0959dfb96afa h1:BsFIbLbjQqq9Yuh+eWs7JmmXcw2RKerP1NT7X8+GKR4=
|
||||
b612.me/win32api v0.0.0-20240402021613-0959dfb96afa/go.mod h1:sj66sFJDKElEjOR+0YhdSW6b4kq4jsXu4T5/Hnpyot0=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
|
@ -102,7 +102,7 @@ func StopListen() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Listen() (<-chan Clipboard, error) {
|
||||
func Listen(onlyMeta bool) (<-chan Clipboard, error) {
|
||||
if atomic.LoadUint32(&isListening) != 0 {
|
||||
return nil, errors.New("Already listening")
|
||||
}
|
||||
@ -127,13 +127,24 @@ func Listen() (<-chan Clipboard, error) {
|
||||
storeSeq = seq
|
||||
//fmt.Println("Clipboard updated", seq, storeSeq)
|
||||
if atomic.LoadUint32(&isListening) == 1 {
|
||||
cb, err := Get()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if atomic.LoadUint32(&isListening) == 1 {
|
||||
res <- cb
|
||||
continue
|
||||
if !onlyMeta {
|
||||
cb, err := Get()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if atomic.LoadUint32(&isListening) == 1 {
|
||||
res <- cb
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
cb, err := GetMeta()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if atomic.LoadUint32(&isListening) == 1 {
|
||||
res <- cb
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size uint32) ([]byte, error)) ([]byte, error) {
|
||||
func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size uint32) (interface{}, error)) (interface{}, error) {
|
||||
mem, err := win32api.GetClipboardData(uFormat)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetClipboardData failed: %v", err)
|
||||
@ -36,7 +36,7 @@ func fetchClipboardData(uFormat win32api.DWORD, fn func(p unsafe.Pointer, size u
|
||||
return fn(p, uint32(size))
|
||||
}
|
||||
|
||||
func defaultFetchFn(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
func defaultFetchFn(p unsafe.Pointer, size uint32) (interface{}, error) {
|
||||
var buf []byte
|
||||
for i := 0; i < int(size); i++ {
|
||||
buf = append(buf, *(*byte)(unsafe.Pointer(uintptr(p) + uintptr(i))))
|
||||
@ -44,7 +44,7 @@ func defaultFetchFn(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func AutoFetcher(uFormat string) ([]byte, error) {
|
||||
func AutoFetcher(uFormat string) (interface{}, error) {
|
||||
switch uFormat {
|
||||
case "CF_TEXT", "CF_UNICODETEXT":
|
||||
return fetchClipboardData(win32api.CF_UNICODETEXT, textFetcher)
|
||||
@ -62,7 +62,17 @@ func AutoFetcher(uFormat string) ([]byte, error) {
|
||||
return nil, errors.New("not support uFormat:" + uFormat)
|
||||
}
|
||||
|
||||
func textFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
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) {
|
||||
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))
|
||||
@ -70,7 +80,7 @@ func textFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
return []byte(syscall.UTF16ToString(buf)), nil
|
||||
}
|
||||
|
||||
func filedropFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
func filedropFetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
|
||||
var res []string
|
||||
c, err := win32api.DragQueryFile(win32api.HDROP(p), 0xFFFFFFFF, nil, 0)
|
||||
if err != nil {
|
||||
@ -93,7 +103,7 @@ func filedropFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
return []byte(strings.Join(res, "|")), nil
|
||||
}
|
||||
|
||||
func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
|
||||
// inspect header information
|
||||
info := (*bitmapV5Header)(unsafe.Pointer(p))
|
||||
// maybe deal with other formats?
|
||||
@ -130,7 +140,7 @@ func cfDIBv5Fetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func cfDIBFetcher(p unsafe.Pointer, size uint32) ([]byte, error) {
|
||||
func cfDIBFetcher(p unsafe.Pointer, size uint32) (interface{}, error) {
|
||||
// 函数意外报错,待修正
|
||||
const (
|
||||
fileHeaderLen = 14
|
||||
|
@ -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…
x
Reference in New Issue
Block a user