Skip to content

Commit

Permalink
all: add Init() method (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
hrntknr authored Jan 22, 2022
1 parent 10ba29a commit 076d92f
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 30 deletions.
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,20 @@ import "golang.design/x/clipboard"

## API Usage

Package `clipboard` provides three major APIs for manipulating the
clipboard: `Read`, `Write`, and `Watch`. The most common operations are
`Read` and `Write`. To use them, you can:
Package clipboard provides cross platform clipboard access and supports
macOS/Linux/Windows/Android/iOS platform. Before interacting with the
clipboard, one must call Init to assert if it is possible to use this
package:

```go
// Init returns an error if the package is not ready for use.
err := clipboard.Init()
if err != nil {
panic(err)
}
```

The most common operations are `Read` and `Write`. To use them:

```go
// write/read text format data of the clipboard, and
Expand Down
33 changes: 27 additions & 6 deletions clipboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@

/*
Package clipboard provides cross platform clipboard access and supports
macOS/Linux/Windows/Android/iOS platform. There are three major APIs
to interact with the clipboard: `Read`, `Write`, and `Watch`.
macOS/Linux/Windows/Android/iOS platform. Before interacting with the
clipboard, one must call Init to assert if it is possible to use this
package:
err := clipboard.Init()
if err != nil {
panic(err)
}
The most common operations are `Read` and `Write`. To use them:
Expand Down Expand Up @@ -59,10 +65,9 @@ import (

var (
// activate only for running tests.
debug = false
errUnavailable = errors.New("clipboard unavailable")
errUnsupported = errors.New("unsupported format")
errInvalidOperation = errors.New("invalid operation")
debug = false
errUnavailable = errors.New("clipboard unavailable")
errUnsupported = errors.New("unsupported format")
)

// Format represents the format of clipboard data.
Expand All @@ -81,6 +86,22 @@ const (
// guarantee one read at a time.
var lock = sync.Mutex{}

// Init initializes the clipboard package. It returns an error
// if the clipboard is not available to use. This may happen if the
// target system lacks required dependency, such as libx11-dev in X11
// environment. For example,
//
// err := clipboard.Init()
// if err != nil {
// panic(err)
// }
//
// If Init returns an error, any subsequent Read/Write/Watch call
// may result in an unrecoverable panic.
func Init() error {
return initialize()
}

// Read returns a chunk of bytes of the clipboard data if it presents
// in the desired format t presents. Otherwise, it returns nil.
func Read(t Format) []byte {
Expand Down
10 changes: 6 additions & 4 deletions clipboard_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"golang.org/x/mobile/app"
)

func initialize() error { return nil }

func read(t Format) (buf []byte, err error) {
switch t {
case FmtText:
Expand All @@ -44,9 +46,9 @@ func read(t Format) (buf []byte, err error) {
})
return []byte(s), nil
case FmtImage:
return nil, errors.New("unsupported")
return nil, errUnsupported
default:
return nil, errors.New("unsupported")
return nil, errUnsupported
}
}

Expand All @@ -66,9 +68,9 @@ func write(t Format, buf []byte) (<-chan struct{}, error) {
})
return done, nil
case FmtImage:
return nil, errors.New("unsupported")
return nil, errUnsupported
default:
return nil, errors.New("unsupported")
return nil, errUnsupported
}
}

Expand Down
6 changes: 5 additions & 1 deletion clipboard_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"unsafe"
)

func initialize() error { return nil }

func read(t Format) (buf []byte, err error) {
var (
data unsafe.Pointer
Expand Down Expand Up @@ -68,9 +70,11 @@ func write(t Format, buf []byte) (<-chan struct{}, error) {
ok = C.clipboard_write_image(unsafe.Pointer(&buf[0]),
C.NSInteger(len(buf)))
}
default:
return nil, errUnsupported
}
if ok != 0 {
return nil, errInvalidOperation
return nil, errUnavailable
}

// use unbuffered data to prevent goroutine leak
Expand Down
10 changes: 6 additions & 4 deletions clipboard_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ import (
"unsafe"
)

func initialize() error { return nil }

func read(t Format) (buf []byte, err error) {
switch t {
case FmtText:
return []byte(C.GoString(C.clipboard_read_string())), nil
case FmtImage:
return nil, errors.New("unimplemented")
return nil, errUnsupported
default:
return nil, errors.New("unimplemented")
return nil, errUnsupported
}
}

Expand All @@ -48,9 +50,9 @@ func write(t Format, buf []byte) (<-chan struct{}, error) {
C.clipboard_write_string(cs)
return done, nil
case FmtImage:
return nil, errors.New("unimplemented")
return nil, errUnsupported
default:
return nil, errors.New("unimplemented")
return nil, errUnsupported
}
}

Expand Down
12 changes: 6 additions & 6 deletions clipboard_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import (
"golang.design/x/clipboard/internal/cgo"
)

const errmsg = `Failed to initialize the X11 display, and the clipboard package
var helpmsg = `%w: Failed to initialize the X11 display, and the clipboard package
will not work properly. Install the following dependency may help:
apt install -y libx11-dev
Expand All @@ -59,11 +59,12 @@ and initialize a virtual frame buffer:
Then this package should be ready to use.
`

func init() {
func initialize() error {
ok := C.clipboard_test()
if ok < 0 {
panic(errmsg)
if ok != 0 {
return fmt.Errorf(helpmsg, errUnavailable)
}
return nil
}

func read(t Format) (buf []byte, err error) {
Expand Down Expand Up @@ -97,7 +98,6 @@ func readc(t string) ([]byte, error) {
// write writes the given data to clipboard and
// returns true if success or false if failed.
func write(t Format, buf []byte) (<-chan struct{}, error) {

var s string
switch t {
case FmtText:
Expand Down Expand Up @@ -132,7 +132,7 @@ func write(t Format, buf []byte) (<-chan struct{}, error) {

status := <-start
if status < 0 {
return nil, errInvalidOperation
return nil, errUnavailable
}
// wait until enter event loop
return done, nil
Expand Down
4 changes: 4 additions & 0 deletions clipboard_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ package clipboard

import "context"

func initialize() error {
panic("clipboard: cannot use when CGO_ENABLED=0")
}

func read(t Format) (buf []byte, err error) {
panic("clipboard: cannot use when CGO_ENABLED=0")
}
Expand Down
35 changes: 34 additions & 1 deletion clipboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package clipboard_test
import (
"bytes"
"context"
"errors"
"image/png"
"os"
"reflect"
Expand All @@ -23,6 +24,38 @@ func init() {
clipboard.Debug = true
}

func TestClipboardInit(t *testing.T) {
t.Run("no-cgo", func(t *testing.T) {
if val, ok := os.LookupEnv("CGO_ENABLED"); !ok || val != "0" {
t.Skip("CGO_ENABLED is set to 1")
}
if runtime.GOOS == "windows" {
t.Skip("Windows does not need to check for cgo")
}

defer func() {
if r := recover(); r != nil {
return
}
t.Fatalf("expect to fail when CGO_ENABLED=0")
}()

clipboard.Init()
})
t.Run("with-cgo", func(t *testing.T) {
if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
t.Skip("CGO_ENABLED is set to 0")
}
if runtime.GOOS != "linux" {
t.Skip("Only Linux may return error at the moment.")
}

if err := clipboard.Init(); err != nil && !errors.Is(err, clipboard.ErrUnavailable) {
t.Fatalf("expect ErrUnavailable, but got: %v", err)
}
})
}

func TestClipboard(t *testing.T) {
if runtime.GOOS != "windows" {
if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
Expand Down Expand Up @@ -255,7 +288,7 @@ func BenchmarkClipboard(b *testing.B) {
}

func TestClipboardNoCgo(t *testing.T) {
if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "1" {
if val, ok := os.LookupEnv("CGO_ENABLED"); !ok || val != "0" {
t.Skip("CGO_ENABLED is set to 1")
}
if runtime.GOOS == "windows" {
Expand Down
2 changes: 2 additions & 0 deletions clipboard_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"unsafe"
)

func initialize() error { return nil }

// readText reads the clipboard and returns the text data if presents.
// The caller is responsible for opening/closing the clipboard before
// calling this function.
Expand Down
7 changes: 7 additions & 0 deletions cmd/gclip-gui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ func (g *GclipApp) OnDraw() {
g.l.Draw(g.siz)
}

func init() {
err := clipboard.Init()
if err != nil {
panic(err)
}
}

func main() {
app.Main(func(a app.App) {
gclip := GclipApp{app: a}
Expand Down
10 changes: 6 additions & 4 deletions cmd/gclip/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package main // go install golang.design/x/clipboard/cmd/gclip@latest

import (
"errors"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -44,9 +43,12 @@ var (
file = flag.String("f", "", "source or destination to a given file path")
)

var (
errUnsupported = errors.New("unsupported data format")
)
func init() {
err := clipboard.Init()
if err != nil {
panic(err)
}
}

func main() {
flag.Usage = usage
Expand Down
15 changes: 15 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,32 @@ import (
)

func ExampleWrite() {
err := clipboard.Init()
if err != nil {
panic(err)
}

clipboard.Write(clipboard.FmtText, []byte("Hello, 世界"))
// Output:
}

func ExampleRead() {
err := clipboard.Init()
if err != nil {
panic(err)
}

fmt.Println(string(clipboard.Read(clipboard.FmtText)))
// Output:
// Hello, 世界
}

func ExampleWatch() {
err := clipboard.Init()
if err != nil {
panic(err)
}

ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()

Expand Down
5 changes: 4 additions & 1 deletion export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
package clipboard

// for debugging errors
var Debug = debug
var (
Debug = debug
ErrUnavailable = errUnavailable
)

0 comments on commit 076d92f

Please sign in to comment.