From 568dd318c3eb6f4fad96f3ae51bfa6e6b91cd723 Mon Sep 17 00:00:00 2001 From: Starainrt Date: Sat, 30 Mar 2024 15:07:20 +0800 Subject: [PATCH] add more feature --- clipboard_test.go | 26 ++-- clipboard_windows.go | 16 +-- go.mod | 2 +- go.sum | 2 + listener_windows.go | 146 ++++++++++++++++++++- handle_windows.go => readhandle_windows.go | 7 +- writehandle_windows.go | 1 + 7 files changed, 176 insertions(+), 24 deletions(-) rename handle_windows.go => readhandle_windows.go (96%) create mode 100644 writehandle_windows.go diff --git a/clipboard_test.go b/clipboard_test.go index 491f096..d91874d 100644 --- a/clipboard_test.go +++ b/clipboard_test.go @@ -3,18 +3,26 @@ package clipboard import ( "fmt" "testing" + "time" ) func TestGet(t *testing.T) { - c, err := Get() + lsn, err := Listen() if err != nil { - t.Error(err) + t.Fatal(err) + } + for { + select { + case cb := <-lsn: + fmt.Println(cb.plateform) + fmt.Println(cb.AvailableTypes()) + fmt.Println(cb.Text()) + fmt.Println(cb.HTML()) + case <-time.After(60 * time.Second): + fmt.Println("not get clipboard data in 60s") + StopListen() + time.Sleep(time.Second * 15) + return + } } - fmt.Println(c.plateform) - fmt.Println(c.winOriginTypes) - fmt.Println(c.PrimaryType()) - fmt.Println(c.AvailableTypes()) - fmt.Println(c.Text()) - fmt.Println(c.HTML()) - fmt.Println(c.FilePaths()) } diff --git a/clipboard_windows.go b/clipboard_windows.go index 71d588a..10d03c5 100644 --- a/clipboard_windows.go +++ b/clipboard_windows.go @@ -2,6 +2,8 @@ package clipboard import ( "b612.me/win32api" + "fmt" + "runtime" ) var winformat = map[win32api.DWORD]string{ @@ -44,17 +46,19 @@ func Get() (Clipboard, error) { } func innerGetClipboard() (Clipboard, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() var c = Clipboard{ plateform: "windows", } err := win32api.OpenClipboard(0) if err != nil { - return c, err + return c, fmt.Errorf("OpenClipboard error: %v", err) } defer win32api.CloseClipboard() formats, err := win32api.GetUpdatedClipboardFormatsAll() if err != nil { - return c, err + return c, fmt.Errorf("GetUpdatedClipboardFormatsAll error: %v", err) } var firstFormatName, secondFormatName string var firstFormat, secondFormat int = 65535, 65535 @@ -105,7 +109,7 @@ func innerGetClipboard() (Clipboard, error) { } c.primaryData, err = AutoFetcher(firstFormatName) if err != nil { - return c, err + return c, fmt.Errorf("AutoFetcher error: %v", err) } if secondFormatName != "" { switch secondFormatName { @@ -121,12 +125,8 @@ func innerGetClipboard() (Clipboard, error) { c.secondaryOriType = secondFormatName c.secondaryData, err = AutoFetcher(secondFormatName) if err != nil { - return c, err + return c, fmt.Errorf("AutoFetcher error: %v", err) } } return c, nil } - -func GetTopMatch() { - -} diff --git a/go.mod b/go.mod index 623057d..5bd4269 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module b612.me/clipboard go 1.21.2 require ( - b612.me/win32api v0.0.0-20240326080749-ad19f5cd4247 + b612.me/win32api v0.0.0-20240328010943-f10bafb4e804 golang.org/x/image v0.15.0 ) diff --git a/go.sum b/go.sum index 8029e86..fa555c4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ 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= 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= diff --git a/listener_windows.go b/listener_windows.go index fd56200..cc2f8bd 100644 --- a/listener_windows.go +++ b/listener_windows.go @@ -1,5 +1,145 @@ package clipboard -func Listen() (<-chan Clipboard, error) - res := make(chan Clipboard) -} \ No newline at end of file +import ( + "b612.me/win32api" + "errors" + "fmt" + "sync/atomic" + "time" +) + +var stopSign chan struct{} +var isListening uint32 + +/* +func listenMethod2() (<-chan Clipboard, error) { + if atomic.LoadUint32(&isListening) == 1 { + return nil, errors.New("Already listening") + } + atomic.StoreUint32(&isListening, 1) + res := make(chan Clipboard, 3) + stopSign = make(chan struct{}) + hWnd, err := win32api.CreateWindowEx(0, + "Message", + "B612 Clipboard Listener", + 0, + 0, 0, 500, 500, + 0, 0, 0, nil) + if hWnd == 0 || err != nil { + return nil, fmt.Errorf("Failed to create window: %v,hWnd:%v", err, hWnd) + } + set, err := win32api.AddClipboardFormatListener(hWnd) + if !set || err != nil { + return nil, fmt.Errorf("Failed to set clipboard listener: %v", err) + } + fetcher := make(chan struct{}, 8) + go fetchListener(hWnd, fetcher) + go func() { + for { + select { + case <-stopSign: + fmt.Println("stopped") + atomic.StoreUint32(&isListening, 0) + close(res) + close(stopSign) + win32api.RemoveClipboardFormatListener(win32api.HWND(hWnd)) + win32api.DestoryWindow(hWnd) + return + case <-fetcher: + cb, err := Get() + if err != nil { + fmt.Println(err) + } + if atomic.LoadUint32(&isListening) == 1 { + res <- cb + continue + } + } + } + }() + return res, nil +} +func fetchListener(hWnd win32api.HWND, res chan struct{}) { + for { + var msg win32api.MSG + _, err := win32api.GetMessage(&msg, hWnd, 0, 0) + if msg.Message == 0x0012 { + return + } + if err == nil && win32api.DWORD(msg.Message) == win32api.WM_CLIPBOARDUPDATE { + res <- struct{}{} + } + } +} +*/ + +func PauseListen() error { + if atomic.LoadUint32(&isListening) == 0 { + return errors.New("Not listening") + } + atomic.StoreUint32(&isListening, 2) + return nil +} + +func RecoverListen() error { + if atomic.LoadUint32(&isListening) == 0 { + return errors.New("Not Listening") + } + atomic.StoreUint32(&isListening, 1) + return nil +} + +func StopListen() error { + defer func() { + if r := recover(); r != nil { + fmt.Println("StopListen panic:", r) + } + }() + if atomic.LoadUint32(&isListening) == 0 { + return nil + } + stopSign <- struct{}{} + return nil +} + +func Listen() (<-chan Clipboard, error) { + if atomic.LoadUint32(&isListening) != 0 { + return nil, errors.New("Already listening") + } + atomic.StoreUint32(&isListening, 1) + res := make(chan Clipboard, 3) + stopSign = make(chan struct{}) + go func() { + var storeSeq win32api.DWORD + for { + select { + case <-stopSign: + atomic.StoreUint32(&isListening, 0) + close(res) + close(stopSign) + return + case <-time.After(time.Millisecond * 900): + seq, err := win32api.GetClipboardSequenceNumber() + if err != nil { + continue + } + if seq != storeSeq { + 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 + } + } + } + } + + } + }() + return res, nil +} diff --git a/handle_windows.go b/readhandle_windows.go similarity index 96% rename from handle_windows.go rename to readhandle_windows.go index c854575..79ff3b4 100644 --- a/handle_windows.go +++ b/readhandle_windows.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" "golang.org/x/image/bmp" "image" "image/color" @@ -18,16 +19,16 @@ import ( 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, err + return nil, fmt.Errorf("GetClipboardData failed: %v", err) } p, err := win32api.GlobalLock(mem) if err != nil { - return nil, err + return nil, fmt.Errorf("GlobalLock failed: %v", err) } defer win32api.GlobalUnlock(mem) size, err := win32api.GlobalSize(mem) if err != nil { - return nil, err + return nil, fmt.Errorf("GlobalSize failed: %v", err) } if fn == nil { return defaultFetchFn(p, uint32(size)) diff --git a/writehandle_windows.go b/writehandle_windows.go new file mode 100644 index 0000000..0b1389c --- /dev/null +++ b/writehandle_windows.go @@ -0,0 +1 @@ +package clipboard