chore: vendor clipboard into go package

This commit is contained in:
adamdottv
2025-07-08 18:48:40 -05:00
parent da3df51316
commit 39bcba85a9
34 changed files with 2883 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021 The golang.design Initiative Authors.
All rights reserved. Use of this source code is governed
by a MIT license that can be found in the LICENSE file.
Written by Changkun Ou <changkun.de>
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="design.golang.clipboard.gclip"
android:versionCode="1"
android:versionName="1.0">
<!-- In order to access the clipboard, the application manifest must
specify the permission requirement. See the following page for
details.
http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms -->
<uses-permission android:name="android.permission.CLIPBOARD" />
<application android:label="gclip" android:debuggable="true">
<activity android:name="org.golang.app.GoNativeActivity"
android:label="Gclip"
android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name" android:value="Gclip" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,31 @@
# gclip-gui
This is a very basic example for verification purpose that demonstrates
how the [golang.design/x/clipboard](https://golang.design/x/clipboard)
can interact with macOS/Linux/Windows/Android/iOS system clipboard.
The gclip GUI application writes a string to the system clipboard
periodically then reads it back and renders it if possible.
Because of the system limitation, on mobile devices, only string data is
supported at the moment. Hence, one must use clipboard.FmtText. Other supplied
formats result in a panic.
This example is intentded as cross platform application. To build it, one
must use [gomobile](https://golang.org/x/mobile). You may follow the instructions
provided in the [GoMobile wiki](https://github.com/golang/go/wiki/Mobile) page.
- For desktop: `go build -o gclip-gui`
- For Android: `gomobile build -v -target=android -o gclip-gui.apk`
- For iOS: `gomobile build -v -target=ios -bundleid design.golang.gclip-gui.app`
## Screenshots
| macOS | iOS | Windows | Android | Linux |
|:-----:|:---:|:-------:|:-------:|:-----:|
|![](../../tests/testdata/darwin.png)|![](../../tests/testdata/ios.png)|![](../../tests/testdata/windows.png)|![](../../tests/testdata/android.png)|![](../../tests/testdata/linux.png)|
## License
MIT | &copy; 2021 The golang.design Initiative Authors, written by [Changkun Ou](https://changkun.de).

View File

@@ -0,0 +1,236 @@
// Copyright 2021 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>
//go:build android || ios || linux || darwin || windows
// This is a very basic example for verification purpose that
// demonstrates how the golang.design/x/clipboard can interact
// with macOS/Linux/Windows/Android/iOS system clipboard.
//
// The gclip GUI application writes a string to the system clipboard
// periodically then reads it back and renders it if possible.
//
// Because of the system limitation, on mobile devices, only string
// data is supported at the moment. Hence, one must use clipboard.FmtText.
// Other supplied formats result in a panic.
//
// This example is intentded as cross platform application.
// To build it, one must use gomobile (https://golang.org/x/mobile).
// You may follow the instructions provided in the GoMobile's wiki page:
// https://github.com/golang/go/wiki/Mobile.
//
// - For desktop:
//
// go build -o gclip-gui
//
// - For Android:
//
// gomobile build -v -target=android -o gclip-gui.apk
//
// - For iOS:
//
// gomobile build -v -target=ios -bundleid design.golang.gclip-gui.app
//
package main
import (
"fmt"
"image"
"image/color"
"log"
"os"
"sync"
"time"
"golang.design/x/clipboard"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/exp/gl/glutil"
"golang.org/x/mobile/geom"
"golang.org/x/mobile/gl"
)
type Label struct {
sz size.Event
images *glutil.Images
m *glutil.Image
drawer *font.Drawer
mu sync.Mutex
data string
}
func NewLabel(images *glutil.Images) *Label {
return &Label{
images: images,
data: "Hello! Gclip.",
drawer: nil,
}
}
func (l *Label) SetLabel(s string) {
l.mu.Lock()
defer l.mu.Unlock()
l.data = s
}
const (
lineWidth = 100
lineHeight = 120
)
func (l *Label) Draw(sz size.Event) {
l.mu.Lock()
s := l.data
l.mu.Unlock()
imgW, imgH := lineWidth*basicfont.Face7x13.Width, lineHeight*basicfont.Face7x13.Height
if sz.WidthPx == 0 && sz.HeightPx == 0 {
return
}
if imgW > sz.WidthPx {
imgW = sz.WidthPx
}
if l.sz != sz {
l.sz = sz
if l.m != nil {
l.m.Release()
}
l.m = l.images.NewImage(imgW, imgH)
}
// Clear the drawing image.
for i := 0; i < len(l.m.RGBA.Pix); i++ {
l.m.RGBA.Pix[i] = 0
}
l.drawer = &font.Drawer{
Dst: l.m.RGBA,
Src: image.NewUniform(color.RGBA{0, 100, 125, 255}),
Face: basicfont.Face7x13,
Dot: fixed.P(5, 10),
}
l.drawer.DrawString(s)
l.m.Upload()
l.m.Draw(
sz,
geom.Point{X: 0, Y: 50},
geom.Point{X: geom.Pt(imgW), Y: 50},
geom.Point{X: 0, Y: geom.Pt(imgH)},
l.m.RGBA.Bounds(),
)
}
func (l *Label) Release() {
if l.m != nil {
l.m.Release()
l.m = nil
l.images = nil
}
}
// GclipApp is the application instance.
type GclipApp struct {
app app.App
ctx gl.Context
siz size.Event
images *glutil.Images
l *Label
counter int
}
// WatchClipboard watches the system clipboard every seconds.
func (g *GclipApp) WatchClipboard() {
go func() {
tk := time.NewTicker(time.Second)
for range tk.C {
// Write something to the clipboard
w := fmt.Sprintf("(gclip: %d)", g.counter)
clipboard.Write(clipboard.FmtText, []byte(w))
g.counter++
log.Println(w)
// Read it back and render it, if possible.
data := clipboard.Read(clipboard.FmtText)
if len(data) == 0 {
continue
}
// Set the current clipboard data as label content and render on the screen.
r := fmt.Sprintf("clipboard: %s", string(data))
g.l.SetLabel(r)
g.app.Send(paint.Event{})
}
}()
}
func (g *GclipApp) OnStart(e lifecycle.Event) {
g.ctx, _ = e.DrawContext.(gl.Context)
g.images = glutil.NewImages(g.ctx)
g.l = NewLabel(g.images)
g.app.Send(paint.Event{})
}
func (g *GclipApp) OnStop() {
g.l.Release()
g.images.Release()
g.ctx = nil
}
func (g *GclipApp) OnSize(size size.Event) {
g.siz = size
}
func (g *GclipApp) OnDraw() {
if g.ctx == nil {
return
}
defer g.app.Send(paint.Event{})
defer g.app.Publish()
g.ctx.ClearColor(0, 0, 0, 1)
g.ctx.Clear(gl.COLOR_BUFFER_BIT)
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}
gclip.app.Send(size.Event{WidthPx: 800, HeightPx: 500})
gclip.WatchClipboard()
for e := range gclip.app.Events() {
switch e := gclip.app.Filter(e).(type) {
case lifecycle.Event:
switch e.Crosses(lifecycle.StageVisible) {
case lifecycle.CrossOn:
gclip.OnStart(e)
case lifecycle.CrossOff:
gclip.OnStop()
os.Exit(0)
}
case size.Event:
gclip.OnSize(e)
case paint.Event:
gclip.OnDraw()
}
}
})
}