clipboard.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Copyright 2021 The golang.design Initiative Authors.
  2. // All rights reserved. Use of this source code is governed
  3. // by a MIT license that can be found in the LICENSE file.
  4. //
  5. // Written by Changkun Ou <changkun.de>
  6. /*
  7. Package clipboard provides cross platform clipboard access and supports
  8. macOS/Linux/Windows/Android/iOS platform. Before interacting with the
  9. clipboard, one must call Init to assert if it is possible to use this
  10. package:
  11. err := clipboard.Init()
  12. if err != nil {
  13. panic(err)
  14. }
  15. The most common operations are `Read` and `Write`. To use them:
  16. // write/read text format data of the clipboard, and
  17. // the byte buffer regarding the text are UTF8 encoded.
  18. clipboard.Write(clipboard.FmtText, []byte("text data"))
  19. clipboard.Read(clipboard.FmtText)
  20. // write/read image format data of the clipboard, and
  21. // the byte buffer regarding the image are PNG encoded.
  22. clipboard.Write(clipboard.FmtImage, []byte("image data"))
  23. clipboard.Read(clipboard.FmtImage)
  24. Note that read/write regarding image format assumes that the bytes are
  25. PNG encoded since it serves the alpha blending purpose that might be
  26. used in other graphical software.
  27. In addition, `clipboard.Write` returns a channel that can receive an
  28. empty struct as a signal, which indicates the corresponding write call
  29. to the clipboard is outdated, meaning the clipboard has been overwritten
  30. by others and the previously written data is lost. For instance:
  31. changed := clipboard.Write(clipboard.FmtText, []byte("text data"))
  32. select {
  33. case <-changed:
  34. println(`"text data" is no longer available from clipboard.`)
  35. }
  36. You can ignore the returning channel if you don't need this type of
  37. notification. Furthermore, when you need more than just knowing whether
  38. clipboard data is changed, use the watcher API:
  39. ch := clipboard.Watch(context.TODO(), clipboard.FmtText)
  40. for data := range ch {
  41. // print out clipboard data whenever it is changed
  42. println(string(data))
  43. }
  44. */
  45. package clipboard
  46. import (
  47. "context"
  48. "errors"
  49. "fmt"
  50. "os"
  51. "sync"
  52. )
  53. var (
  54. // activate only for running tests.
  55. debug = false
  56. errUnavailable = errors.New("clipboard unavailable")
  57. errUnsupported = errors.New("unsupported format")
  58. errNoCgo = errors.New("clipboard: cannot use when CGO_ENABLED=0")
  59. )
  60. // Format represents the format of clipboard data.
  61. type Format int
  62. // All sorts of supported clipboard data
  63. const (
  64. // FmtText indicates plain text clipboard format
  65. FmtText Format = iota
  66. // FmtImage indicates image/png clipboard format
  67. FmtImage
  68. )
  69. var (
  70. // Due to the limitation on operating systems (such as darwin),
  71. // concurrent read can even cause panic, use a global lock to
  72. // guarantee one read at a time.
  73. lock = sync.Mutex{}
  74. initOnce sync.Once
  75. initError error
  76. )
  77. // Init initializes the clipboard package. It returns an error
  78. // if the clipboard is not available to use. This may happen if the
  79. // target system lacks required dependency, such as libx11-dev in X11
  80. // environment. For example,
  81. //
  82. // err := clipboard.Init()
  83. // if err != nil {
  84. // panic(err)
  85. // }
  86. //
  87. // If Init returns an error, any subsequent Read/Write/Watch call
  88. // may result in an unrecoverable panic.
  89. func Init() error {
  90. initOnce.Do(func() {
  91. initError = initialize()
  92. })
  93. return initError
  94. }
  95. // Read returns a chunk of bytes of the clipboard data if it presents
  96. // in the desired format t presents. Otherwise, it returns nil.
  97. func Read(t Format) []byte {
  98. lock.Lock()
  99. defer lock.Unlock()
  100. buf, err := read(t)
  101. if err != nil {
  102. if debug {
  103. fmt.Fprintf(os.Stderr, "read clipboard err: %v\n", err)
  104. }
  105. return nil
  106. }
  107. return buf
  108. }
  109. // Write writes a given buffer to the clipboard in a specified format.
  110. // Write returned a receive-only channel can receive an empty struct
  111. // as a signal, which indicates the clipboard has been overwritten from
  112. // this write.
  113. // If format t indicates an image, then the given buf assumes
  114. // the image data is PNG encoded.
  115. func Write(t Format, buf []byte) <-chan struct{} {
  116. lock.Lock()
  117. defer lock.Unlock()
  118. changed, err := write(t, buf)
  119. if err != nil {
  120. if debug {
  121. fmt.Fprintf(os.Stderr, "write to clipboard err: %v\n", err)
  122. }
  123. return nil
  124. }
  125. return changed
  126. }
  127. // Watch returns a receive-only channel that received the clipboard data
  128. // whenever any change of clipboard data in the desired format happens.
  129. //
  130. // The returned channel will be closed if the given context is canceled.
  131. func Watch(ctx context.Context, t Format) <-chan []byte {
  132. return watch(ctx, t)
  133. }