From 3029507430f3b9d22fca32b69151077d65ef4340 Mon Sep 17 00:00:00 2001 From: 502647092 Date: Thu, 30 Aug 2018 20:36:03 +0800 Subject: [PATCH] Init: Create & Init Project... --- .gitignore | 29 +++++++ Gopkg.lock | 17 ++++ Gopkg.toml | 34 ++++++++ lib/clipboard/clipboard.go | 22 +++++ lib/clipboard/clipboard_darwin.go | 52 ++++++++++++ lib/clipboard/clipboard_unix.go | 98 +++++++++++++++++++++ lib/clipboard/clipboard_windows.go | 132 +++++++++++++++++++++++++++++ lib/event.go | 45 ++++++++++ lib/windows.go | 80 +++++++++++++++++ main.go | 79 +++++++++++++++++ 10 files changed, 588 insertions(+) create mode 100644 .gitignore create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 lib/clipboard/clipboard.go create mode 100644 lib/clipboard/clipboard_darwin.go create mode 100644 lib/clipboard/clipboard_unix.go create mode 100644 lib/clipboard/clipboard_windows.go create mode 100644 lib/event.go create mode 100644 lib/windows.go create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1fbaac --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# ---> Go +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +/vendor +debug +*.exe \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..464b1f7 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,17 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + digest = "1:256554af8321e28c57fab65ec53c3497d6e5d03c8b9eab1530875530030b68ef" + name = "github.com/lxn/win" + packages = ["."] + pruneopts = "UT" + revision = "785c4956069227e430929ac27bfa26af2ff25dfb" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/lxn/win"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..7ca4460 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "github.com/lxn/win" + +[prune] + go-tests = true + unused-packages = true diff --git a/lib/clipboard/clipboard.go b/lib/clipboard/clipboard.go new file mode 100644 index 0000000..5b68b33 --- /dev/null +++ b/lib/clipboard/clipboard.go @@ -0,0 +1,22 @@ +// Copyright 2013 @atotto. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package clipboard read/write on clipboard +package clipboard + +// import () + +// ReadAll read string from clipboard +func ReadAll() (string, error) { + return readAll() +} + +// WriteAll write string to clipboard +func WriteAll(text string) error { + return writeAll(text) +} + +// Unsupported might be set true during clipboard init, to help callers decide +// whether or not to offer clipboard options. +var Unsupported bool diff --git a/lib/clipboard/clipboard_darwin.go b/lib/clipboard/clipboard_darwin.go new file mode 100644 index 0000000..144100d --- /dev/null +++ b/lib/clipboard/clipboard_darwin.go @@ -0,0 +1,52 @@ +// Copyright 2013 @atotto. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package clipboard + +import ( + "os/exec" +) + +var ( + pasteCmdArgs = "pbpaste" + copyCmdArgs = "pbcopy" +) + +func getPasteCommand() *exec.Cmd { + return exec.Command(pasteCmdArgs) +} + +func getCopyCommand() *exec.Cmd { + return exec.Command(copyCmdArgs) +} + +func readAll() (string, error) { + pasteCmd := getPasteCommand() + out, err := pasteCmd.Output() + if err != nil { + return "", err + } + return string(out), nil +} + +func writeAll(text string) error { + copyCmd := getCopyCommand() + in, err := copyCmd.StdinPipe() + if err != nil { + return err + } + + if err := copyCmd.Start(); err != nil { + return err + } + if _, err := in.Write([]byte(text)); err != nil { + return err + } + if err := in.Close(); err != nil { + return err + } + return copyCmd.Wait() +} \ No newline at end of file diff --git a/lib/clipboard/clipboard_unix.go b/lib/clipboard/clipboard_unix.go new file mode 100644 index 0000000..76d7611 --- /dev/null +++ b/lib/clipboard/clipboard_unix.go @@ -0,0 +1,98 @@ +// Copyright 2013 @atotto. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd linux netbsd openbsd solaris dragonfly + +package clipboard + +import ( + "errors" + "os/exec" +) + +const ( + xsel = "xsel" + xclip = "xclip" +) + +var ( + // Primary choose primary mode on unix + Primary bool + + pasteCmdArgs, copyCmdArgs []string + + xselPasteArgs = []string{xsel, "--output", "--clipboard"} + xselCopyArgs = []string{xsel, "--input", "--clipboard"} + + xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"} + xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"} + + errMissingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip") +) + +func init() { + pasteCmdArgs = xclipPasteArgs + copyCmdArgs = xclipCopyArgs + + if _, err := exec.LookPath(xclip); err == nil { + return + } + + pasteCmdArgs = xselPasteArgs + copyCmdArgs = xselCopyArgs + + if _, err := exec.LookPath(xsel); err == nil { + return + } + + Unsupported = true +} + +func getPasteCommand() *exec.Cmd { + if Primary { + pasteCmdArgs = pasteCmdArgs[:1] + } + return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...) +} + +func getCopyCommand() *exec.Cmd { + if Primary { + copyCmdArgs = copyCmdArgs[:1] + } + return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...) +} + +func readAll() (string, error) { + if Unsupported { + return "", errMissingCommands + } + pasteCmd := getPasteCommand() + out, err := pasteCmd.Output() + if err != nil { + return "", err + } + return string(out), nil +} + +func writeAll(text string) error { + if Unsupported { + return errMissingCommands + } + copyCmd := getCopyCommand() + in, err := copyCmd.StdinPipe() + if err != nil { + return err + } + + if err := copyCmd.Start(); err != nil { + return err + } + if _, err := in.Write([]byte(text)); err != nil { + return err + } + if err := in.Close(); err != nil { + return err + } + return copyCmd.Wait() +} diff --git a/lib/clipboard/clipboard_windows.go b/lib/clipboard/clipboard_windows.go new file mode 100644 index 0000000..93285dd --- /dev/null +++ b/lib/clipboard/clipboard_windows.go @@ -0,0 +1,132 @@ +// Copyright 2013 @atotto. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package clipboard + +import ( + "syscall" + "time" + "unsafe" +) + +const ( + cfUnicodetext = 13 + // gmemFixed = 0x0000 + gmemMoveable = 0x0002 +) + +var ( + user32 = syscall.MustLoadDLL("user32") + openClipboard = user32.MustFindProc("OpenClipboard") + closeClipboard = user32.MustFindProc("CloseClipboard") + emptyClipboard = user32.MustFindProc("EmptyClipboard") + getClipboardData = user32.MustFindProc("GetClipboardData") + setClipboardData = user32.MustFindProc("SetClipboardData") + + kernel32 = syscall.NewLazyDLL("kernel32") + globalAlloc = kernel32.NewProc("GlobalAlloc") + globalFree = kernel32.NewProc("GlobalFree") + globalLock = kernel32.NewProc("GlobalLock") + globalUnlock = kernel32.NewProc("GlobalUnlock") + lstrcpy = kernel32.NewProc("lstrcpyW") +) + +// waitOpenClipboard opens the clipboard, waiting for up to a second to do so. +func waitOpenClipboard() error { + started := time.Now() + limit := started.Add(time.Second) + var r uintptr + var err error + for time.Now().Before(limit) { + r, _, err = openClipboard.Call(0) + if r != 0 { + return nil + } + time.Sleep(time.Millisecond) + } + return err +} + +func readAll() (string, error) { + // r, _, err := openClipboard.Call(0) + err := waitOpenClipboard() + if err != nil { + return "", err + } + defer closeClipboard.Call() + + h, _, err := getClipboardData.Call(cfUnicodetext) + if h == 0 { + return "", err + } + + l, _, err := globalLock.Call(h) + if l == 0 { + return "", err + } + + text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) + + r, _, err := globalUnlock.Call(h) + if r == 0 { + return "", err + } + + return text, nil +} + +func writeAll(text string) error { + err := waitOpenClipboard() + if err != nil { + return err + } + defer closeClipboard.Call() + + r, _, err := emptyClipboard.Call(0) + if r == 0 { + return err + } + + data := syscall.StringToUTF16(text) + + // "If the hMem parameter identifies a memory object, the object must have + // been allocated using the function with the GMEM_MOVEABLE flag." + h, _, err := globalAlloc.Call(gmemMoveable, + uintptr(len(data)*int(unsafe.Sizeof(data[0])))) + if h == 0 { + return err + } + defer func() { + if h != 0 { + globalFree.Call(h) + } + }() + + l, _, err := globalLock.Call(h) + if l == 0 { + return err + } + + r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) + if r == 0 { + return err + } + + r, _, err = globalUnlock.Call(h) + if r == 0 { + return err + } + + r, _, err = setClipboardData.Call(cfUnicodetext, h) + if r == 0 { + if err.(syscall.Errno) != 0 { + return err + } + } + + h = 0 // suppress deferred cleanup + return nil +} diff --git a/lib/event.go b/lib/event.go new file mode 100644 index 0000000..b5aace7 --- /dev/null +++ b/lib/event.go @@ -0,0 +1,45 @@ +// Copyright 2011 The Walk Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package lib + +type EventHandler func() + +type Event struct { + handlers []EventHandler +} + +func (e *Event) Attach(handler EventHandler) int { + for i, h := range e.handlers { + if h == nil { + e.handlers[i] = handler + return i + } + } + + e.handlers = append(e.handlers, handler) + return len(e.handlers) - 1 +} + +func (e *Event) Detach(handle int) { + e.handlers[handle] = nil +} + +type EventPublisher struct { + event Event +} + +func (p *EventPublisher) Event() *Event { + return &p.event +} + +func (p *EventPublisher) Publish() { + for _, handler := range p.event.handlers { + if handler != nil { + handler() + } + } +} diff --git a/lib/windows.go b/lib/windows.go new file mode 100644 index 0000000..0c27eec --- /dev/null +++ b/lib/windows.go @@ -0,0 +1,80 @@ +// Copyright 2010 The Walk Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package lib + +import ( + "syscall" + "unsafe" + + "github.com/lxn/win" +) + +var ( + registeredWindowClasses = make(map[string]bool) + defaultWndProcPtr = syscall.NewCallback(defaultWndProc) +) + +// MustRegisterWindowClass registers the specified window class. +// +// MustRegisterWindowClass must be called once for every window type that is not +// based on any system provided control, before calling InitChildWidget or +// InitWidget. Calling MustRegisterWindowClass twice with the same className +// results in a panic. +func MustRegisterWindowClass(className string) { + MustRegisterWindowClassWithWndProcPtr(className, defaultWndProcPtr) +} + +func MustRegisterWindowClassWithStyle(className string, style uint32) { + MustRegisterWindowClassWithWndProcPtrAndStyle(className, defaultWndProcPtr, style) +} + +func MustRegisterWindowClassWithWndProcPtr(className string, wndProcPtr uintptr) { + MustRegisterWindowClassWithWndProcPtrAndStyle(className, wndProcPtr, 0) +} + +func MustRegisterWindowClassWithWndProcPtrAndStyle(className string, wndProcPtr uintptr, style uint32) { + if registeredWindowClasses[className] { + panic("window class already registered") + } + + hInst := win.GetModuleHandle(nil) + if hInst == 0 { + panic("GetModuleHandle") + } + + hIcon := win.LoadIcon(hInst, win.MAKEINTRESOURCE(7)) // rsrc uses 7 for app icon + if hIcon == 0 { + hIcon = win.LoadIcon(0, win.MAKEINTRESOURCE(win.IDI_APPLICATION)) + } + if hIcon == 0 { + panic("LoadIcon") + } + + hCursor := win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW)) + if hCursor == 0 { + panic("LoadCursor") + } + + var wc win.WNDCLASSEX + wc.CbSize = uint32(unsafe.Sizeof(wc)) + wc.LpfnWndProc = wndProcPtr + wc.HInstance = hInst + wc.HIcon = hIcon + wc.HCursor = hCursor + wc.HbrBackground = win.COLOR_BTNFACE + 1 + wc.LpszClassName = syscall.StringToUTF16Ptr(className) + wc.Style = style + + if atom := win.RegisterClassEx(&wc); atom == 0 { + panic("RegisterClassEx") + } + registeredWindowClasses[className] = true +} + +func defaultWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintptr) { + return win.DefWindowProc(hwnd, msg, wParam, lParam) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0eab17c --- /dev/null +++ b/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" + "yumc.pw/cloud/clipboard-sync/lib/clipboard" +) + +func main() { + t := flag.String("t", "c", "Type c(client) or s(server)") + a := flag.String("a", "http://127.0.0.1:8080", "Server Address") + b := flag.String("b", ":8080", "Server Listen Port") + flag.Parse() + if *t == "c" { + if clipboard.Unsupported { + panic("Unsupported On This Machine") + } + readClipboard(*a) + } else { + startServer(*b) + } +} + +const ( + TextHeader = "Go-Clipboard-Text" + TimeHeader = "Go-Clipboard-Time" +) + +func startServer(b string) { + text := "" + time := time.Now().Unix() + var err error + g := gin.Default() + g.GET("/", func(c *gin.Context) { + c.Header(TextHeader, text) + c.Header(TimeHeader, strconv.FormatInt(time, 10)) + }) + g.POST("/", func(c *gin.Context) { + text = c.Query("text") + time, err = strconv.ParseInt(c.Query("time"), 10, 64) + }) + g.Run(b) +} + +func readClipboard(a string) { + old, _ := clipboard.ReadAll() + updateTime := time.Now().Unix() + client := &http.Client{} + address := a + "?text=%s&time=%s" + for { + time.Sleep(100 * time.Millisecond) + text, _ := clipboard.ReadAll() + if old != text { + old = text + updateTime = time.Now().Unix() + request, _ := http.NewRequest("POST", + fmt.Sprintf(address, text, strconv.FormatInt(updateTime, 10)), nil) + client.Do(request) + } else { + response, _ := http.Get(a) + texts, ok := response.Header[TextHeader] + if ok { + sText := texts[0] + sTime, _ := strconv.ParseInt(response.Header[TimeHeader][0], 10, 64) + if sTime > updateTime { + old = sText + updateTime = time.Now().Unix() + clipboard.WriteAll(sText) + fmt.Printf("Update Clipboard: %s\n", sText) + } + } + } + } +}