mirror of
https://git.sr.ht/~eliasnaur/gio-cmd
synced 2026-07-01 07:35:37 +00:00
gogio: implement custom rendering test
This commit adds an end to end test for the custom rendering use-case. I confirmed that the new test failed when custom rendering frame lifecycle was broken, and succeeds now. However, the old X11 tests started failing when the new one started passing. I'm not sure how they interfere with one another, but I'm out of time to investigate. Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
@@ -3,22 +3,22 @@ module gioui.org/cmd
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
gioui.org v0.0.0-20220328154813-a3f147541fd0
|
||||
gioui.org v0.0.0-20220628163331-e21c665e70ae
|
||||
github.com/akavel/rsrc v0.10.1
|
||||
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4
|
||||
github.com/chromedp/chromedp v0.5.2
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
golang.org/x/text v0.3.6
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/tools v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect
|
||||
gioui.org/shader v1.0.6 // indirect
|
||||
github.com/benoitkugler/textlayout v0.0.10 // indirect
|
||||
github.com/benoitkugler/textlayout v0.1.1 // indirect
|
||||
github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12 // indirect
|
||||
github.com/go-text/typesetting v0.0.0-20220112121102-58fe93c84506 // indirect
|
||||
github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b // indirect
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
|
||||
github.com/gobwas/pool v0.2.0 // indirect
|
||||
github.com/gobwas/ws v1.0.2 // indirect
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
gioui.org v0.0.0-20220328154813-a3f147541fd0 h1:n4FUiCT6P4a2wF6hwX4a5R8TpjAhu/d+3nhwZW16MAI=
|
||||
gioui.org v0.0.0-20220328154813-a3f147541fd0/go.mod h1:b8vBukexG6eYuXZa14asjLAWJ+JjbZ/ophEnS2FjYUg=
|
||||
eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3 h1:djFprmHZgrSepsHAIRMp5UJn3PzsoTg9drI+BDmif5Q=
|
||||
gioui.org v0.0.0-20220628163331-e21c665e70ae h1:s8Erm0/zVvi3Fbq0ijjPkRT04XxcGZWTxkxDwUBsxuQ=
|
||||
gioui.org v0.0.0-20220628163331-e21c665e70ae/go.mod h1:WHoHbUjH91BJS2xkfps2AhKxji+9o3xwfsphGsCBfnM=
|
||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
@@ -9,8 +10,9 @@ github.com/akavel/rsrc v0.10.1 h1:hCCPImjmFKVNGpeLZyTDRHEFC283DzyTXTo0cO0Rq9o=
|
||||
github.com/akavel/rsrc v0.10.1/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE=
|
||||
github.com/benoitkugler/textlayout v0.0.5/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8=
|
||||
github.com/benoitkugler/textlayout v0.0.10 h1:uIaQgH4pBFw1LQ0tPkfjgxo94WYcckzzQaB41L2X84w=
|
||||
github.com/benoitkugler/textlayout v0.0.10/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8=
|
||||
github.com/benoitkugler/textlayout v0.1.1 h1:hizE/085xAeY8q7gwV00uHR2Q27KYB2g1HW+UacXl68=
|
||||
github.com/benoitkugler/textlayout v0.1.1/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w=
|
||||
github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQjtD6K2SFe9/HvnIbJk=
|
||||
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4 h1:QD3KxSJ59L2lxG6MXBjNHxiQO2RmxTQ3XcK+wO44WOg=
|
||||
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
|
||||
github.com/chromedp/chromedp v0.5.2 h1:W8xBXQuUnd2dZK0SN/lyVwsQM7KgW+kY5HGnntms194=
|
||||
@@ -20,8 +22,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12 h1:1bjaB/5IIicfKpP4k0s30T2WEw//Kh00zULa8DQ0cxA=
|
||||
github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12/go.mod h1:kDhBRTA/i3H46PVdhqcw26TdGSIj42TOKNWKY+Kipnw=
|
||||
github.com/go-text/typesetting v0.0.0-20220112121102-58fe93c84506 h1:1TPz/Gn/MsXwJ6bEtI9wdkPcQYr2X3V9I+wz4wPYUdY=
|
||||
github.com/go-text/typesetting v0.0.0-20220112121102-58fe93c84506/go.mod h1:R0mlTNeyszZ/tKQhbZA7SRGjx+OHsmNzgN2jTV7yZcs=
|
||||
github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b h1:WINlj3ANt+CVrO2B4NGDHRlPvEWZPxjhb7z+JKypwXI=
|
||||
github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b/go.mod h1:ZNYu5saGoMOqtkVH5T8onTwhzenDUVszI+5WFHJRaxQ=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
@@ -60,7 +62,6 @@ golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+o
|
||||
golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -93,8 +94,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
||||
+17
-10
@@ -13,6 +13,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -69,27 +70,33 @@ func TestEndToEnd(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
testdataWithGoImportPkgPath = "gioui.org/cmd/gogio/testdata"
|
||||
testdataWithRelativePkgPath = "testdata/testdata.go"
|
||||
testdataWithGoImportPkgPath = "gioui.org/cmd/gogio/internal/normal"
|
||||
testdataWithRelativePkgPath = "internal/normal/testdata.go"
|
||||
customRenderTestdataWithRelativePkgPath = "internal/custom/testdata.go"
|
||||
)
|
||||
// Keep this list local, to not reuse TestDriver objects.
|
||||
subtests := []struct {
|
||||
name string
|
||||
driver TestDriver
|
||||
pkgPath string
|
||||
name string
|
||||
driver TestDriver
|
||||
pkgPath string
|
||||
skipGeese string
|
||||
}{
|
||||
{"X11 using go import path", &X11TestDriver{}, testdataWithGoImportPkgPath},
|
||||
{"X11", &X11TestDriver{}, testdataWithRelativePkgPath},
|
||||
{"X11 using go import path", &X11TestDriver{}, testdataWithGoImportPkgPath, ""},
|
||||
{"X11", &X11TestDriver{}, testdataWithRelativePkgPath, ""},
|
||||
{"X11 with custom rendering", &X11TestDriver{}, customRenderTestdataWithRelativePkgPath, "openbsd"},
|
||||
// Doesn't work on the builders.
|
||||
//{"Wayland", &WaylandTestDriver{}, testdataWithRelativePkgPath},
|
||||
{"JS", &JSTestDriver{}, testdataWithRelativePkgPath},
|
||||
{"Android", &AndroidTestDriver{}, testdataWithRelativePkgPath},
|
||||
{"Windows", &WineTestDriver{}, testdataWithRelativePkgPath},
|
||||
{"JS", &JSTestDriver{}, testdataWithRelativePkgPath, ""},
|
||||
{"Android", &AndroidTestDriver{}, testdataWithRelativePkgPath, ""},
|
||||
{"Windows", &WineTestDriver{}, testdataWithRelativePkgPath, ""},
|
||||
}
|
||||
|
||||
for _, subtest := range subtests {
|
||||
t.Run(subtest.name, func(t *testing.T) {
|
||||
subtest := subtest // copy the changing loop variable
|
||||
if strings.Contains(subtest.skipGeese, runtime.GOOS) {
|
||||
t.Skipf("not supported on %s", runtime.GOOS)
|
||||
}
|
||||
t.Parallel()
|
||||
runEndToEndTest(t, subtest.driver, subtest.pkgPath)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
// This program demonstrates the use of a custom OpenGL ES context with
|
||||
// app.Window.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/app"
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: egl wayland-egl
|
||||
#cgo freebsd openbsd CFLAGS: -I/usr/local/include
|
||||
#cgo openbsd CFLAGS: -I/usr/X11R6/include
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/lib
|
||||
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
|
||||
#cgo freebsd openbsd LDFLAGS: -lwayland-egl
|
||||
#cgo CFLAGS: -DEGL_NO_X11
|
||||
#cgo LDFLAGS: -lEGL -lGLESv2
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func getDisplay(ve app.ViewEvent) C.EGLDisplay {
|
||||
switch ve := ve.(type) {
|
||||
case app.X11ViewEvent:
|
||||
return C.eglGetDisplay(C.EGLNativeDisplayType(ve.Display))
|
||||
case app.WaylandViewEvent:
|
||||
return C.eglGetDisplay(C.EGLNativeDisplayType(ve.Display))
|
||||
}
|
||||
panic("no display available")
|
||||
}
|
||||
|
||||
func nativeViewFor(e app.ViewEvent, size image.Point) (C.EGLNativeWindowType, func()) {
|
||||
switch e := e.(type) {
|
||||
case app.X11ViewEvent:
|
||||
return C.EGLNativeWindowType(uintptr(e.Window)), func() {}
|
||||
case app.WaylandViewEvent:
|
||||
eglWin := C.wl_egl_window_create((*C.struct_wl_surface)(e.Surface), C.int(size.X), C.int(size.Y))
|
||||
return C.EGLNativeWindowType(uintptr(unsafe.Pointer(eglWin))), func() {
|
||||
C.wl_egl_window_destroy(eglWin)
|
||||
}
|
||||
}
|
||||
panic("no native view available")
|
||||
}
|
||||
|
||||
type (
|
||||
C = layout.Context
|
||||
D = layout.Dimensions
|
||||
)
|
||||
|
||||
type notifyFrame int
|
||||
|
||||
const (
|
||||
notifyNone notifyFrame = iota
|
||||
notifyInvalidate
|
||||
notifyPrint
|
||||
)
|
||||
|
||||
// notify keeps track of whether we want to print to stdout to notify the user
|
||||
// when a frame is ready. Initially we want to notify about the first frame.
|
||||
var notify = notifyInvalidate
|
||||
|
||||
type eglContext struct {
|
||||
disp C.EGLDisplay
|
||||
ctx C.EGLContext
|
||||
surf C.EGLSurface
|
||||
cleanup func()
|
||||
}
|
||||
|
||||
func main() {
|
||||
go func() {
|
||||
// Set CustomRenderer so we can provide our own rendering context.
|
||||
w := app.NewWindow(app.CustomRenderer(true))
|
||||
if err := loop(w); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
}()
|
||||
app.Main()
|
||||
}
|
||||
|
||||
func loop(w *app.Window) error {
|
||||
var ops op.Ops
|
||||
var (
|
||||
ctx *eglContext
|
||||
gioCtx gpu.GPU
|
||||
ve app.ViewEvent
|
||||
init bool
|
||||
size image.Point
|
||||
)
|
||||
|
||||
recreateContext := func() {
|
||||
w.Run(func() {
|
||||
if gioCtx != nil {
|
||||
gioCtx.Release()
|
||||
gioCtx = nil
|
||||
}
|
||||
if ctx != nil {
|
||||
C.eglMakeCurrent(ctx.disp, nil, nil, nil)
|
||||
ctx.Release()
|
||||
ctx = nil
|
||||
}
|
||||
c, err := createContext(ve, size)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ctx = c
|
||||
})
|
||||
if ok := C.eglMakeCurrent(ctx.disp, ctx.surf, ctx.surf, ctx.ctx); ok != C.EGL_TRUE {
|
||||
err := fmt.Errorf("eglMakeCurrent failed (%#x)", C.eglGetError())
|
||||
log.Fatal(err)
|
||||
}
|
||||
glGetString := func(e C.GLenum) string {
|
||||
return C.GoString((*C.char)(unsafe.Pointer(C.glGetString(e))))
|
||||
}
|
||||
fmt.Printf("GL_VERSION: %s\nGL_RENDERER: %s\n", glGetString(C.GL_VERSION), glGetString(C.GL_RENDERER))
|
||||
var err error
|
||||
gioCtx, err = gpu.New(gpu.OpenGL{ES: true, Shared: true})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
topLeft := quarterWidget{
|
||||
color: color.NRGBA{R: 0xde, G: 0xad, B: 0xbe, A: 0xff},
|
||||
}
|
||||
topRight := quarterWidget{
|
||||
color: color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff},
|
||||
}
|
||||
botLeft := quarterWidget{
|
||||
color: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff},
|
||||
}
|
||||
botRight := quarterWidget{
|
||||
color: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0x80},
|
||||
}
|
||||
|
||||
// eglMakeCurrent binds a context to an operating system thread. Prevent Go from switching thread.
|
||||
runtime.LockOSThread()
|
||||
for e := range w.Events() {
|
||||
switch e := e.(type) {
|
||||
case app.ViewEvent:
|
||||
ve = e
|
||||
init = true
|
||||
if size != (image.Point{}) {
|
||||
recreateContext()
|
||||
}
|
||||
case system.DestroyEvent:
|
||||
return e.Err
|
||||
case system.FrameEvent:
|
||||
if init && size != e.Size {
|
||||
size = e.Size
|
||||
recreateContext()
|
||||
}
|
||||
if gioCtx == nil || !init {
|
||||
break
|
||||
}
|
||||
// Build ops.
|
||||
gtx := layout.NewContext(&ops, e)
|
||||
|
||||
// Clear background to white, even on embedded platforms such as webassembly.
|
||||
paint.Fill(gtx.Ops, color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff})
|
||||
layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
// r1c1
|
||||
layout.Flexed(1, func(gtx C) D { return topLeft.Layout(gtx) }),
|
||||
// r1c2
|
||||
layout.Flexed(1, func(gtx C) D { return topRight.Layout(gtx) }),
|
||||
)
|
||||
}),
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
// r2c1
|
||||
layout.Flexed(1, func(gtx C) D { return botLeft.Layout(gtx) }),
|
||||
// r2c2
|
||||
layout.Flexed(1, func(gtx C) D { return botRight.Layout(gtx) }),
|
||||
)
|
||||
}),
|
||||
)
|
||||
op.InvalidateOp{}.Add(gtx.Ops)
|
||||
log.Println("frame")
|
||||
|
||||
// Trigger window resize detection in ANGLE.
|
||||
C.eglWaitClient()
|
||||
// Draw custom OpenGL content.
|
||||
drawGL()
|
||||
|
||||
// Render drawing ops.
|
||||
if err := gioCtx.Frame(gtx.Ops, gpu.OpenGLRenderTarget{}, e.Size); err != nil {
|
||||
log.Fatal(fmt.Errorf("render failed: %v", err))
|
||||
}
|
||||
|
||||
// Process non-drawing ops.
|
||||
e.Frame(gtx.Ops)
|
||||
switch notify {
|
||||
case notifyInvalidate:
|
||||
notify = notifyPrint
|
||||
w.Invalidate()
|
||||
case notifyPrint:
|
||||
notify = notifyNone
|
||||
fmt.Println("gio frame ready")
|
||||
}
|
||||
|
||||
if ok := C.eglSwapBuffers(ctx.disp, ctx.surf); ok != C.EGL_TRUE {
|
||||
log.Fatal(fmt.Errorf("swap failed: %v", C.eglGetError()))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func drawGL() {
|
||||
C.glClearColor(0, 0, 0, 1)
|
||||
C.glClear(C.GL_COLOR_BUFFER_BIT | C.GL_DEPTH_BUFFER_BIT)
|
||||
}
|
||||
|
||||
func createContext(ve app.ViewEvent, size image.Point) (*eglContext, error) {
|
||||
view, cleanup := nativeViewFor(ve, size)
|
||||
var nilv C.EGLNativeWindowType
|
||||
if view == nilv {
|
||||
return nil, fmt.Errorf("failed creating native view")
|
||||
}
|
||||
disp := getDisplay(ve)
|
||||
if disp == 0 {
|
||||
return nil, fmt.Errorf("eglGetPlatformDisplay failed: 0x%x", C.eglGetError())
|
||||
}
|
||||
var major, minor C.EGLint
|
||||
if ok := C.eglInitialize(disp, &major, &minor); ok != C.EGL_TRUE {
|
||||
return nil, fmt.Errorf("eglInitialize failed: 0x%x", C.eglGetError())
|
||||
}
|
||||
exts := strings.Split(C.GoString(C.eglQueryString(disp, C.EGL_EXTENSIONS)), " ")
|
||||
srgb := hasExtension(exts, "EGL_KHR_gl_colorspace")
|
||||
attribs := []C.EGLint{
|
||||
C.EGL_RENDERABLE_TYPE, C.EGL_OPENGL_ES2_BIT,
|
||||
C.EGL_SURFACE_TYPE, C.EGL_WINDOW_BIT,
|
||||
C.EGL_BLUE_SIZE, 8,
|
||||
C.EGL_GREEN_SIZE, 8,
|
||||
C.EGL_RED_SIZE, 8,
|
||||
C.EGL_CONFIG_CAVEAT, C.EGL_NONE,
|
||||
}
|
||||
if srgb {
|
||||
// Some drivers need alpha for sRGB framebuffers to work.
|
||||
attribs = append(attribs, C.EGL_ALPHA_SIZE, 8)
|
||||
}
|
||||
attribs = append(attribs, C.EGL_NONE)
|
||||
var (
|
||||
cfg C.EGLConfig
|
||||
numCfgs C.EGLint
|
||||
)
|
||||
if ok := C.eglChooseConfig(disp, &attribs[0], &cfg, 1, &numCfgs); ok != C.EGL_TRUE {
|
||||
return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", C.eglGetError())
|
||||
}
|
||||
if numCfgs == 0 {
|
||||
supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
|
||||
if !supportsNoCfg {
|
||||
return nil, errors.New("eglChooseConfig returned no configs")
|
||||
}
|
||||
}
|
||||
ctxAttribs := []C.EGLint{
|
||||
C.EGL_CONTEXT_CLIENT_VERSION, 3,
|
||||
C.EGL_NONE,
|
||||
}
|
||||
ctx := C.eglCreateContext(disp, cfg, nil, &ctxAttribs[0])
|
||||
if ctx == nil {
|
||||
return nil, fmt.Errorf("eglCreateContext failed: 0x%x", C.eglGetError())
|
||||
}
|
||||
var surfAttribs []C.EGLint
|
||||
if srgb {
|
||||
surfAttribs = append(surfAttribs, C.EGL_GL_COLORSPACE, C.EGL_GL_COLORSPACE_SRGB)
|
||||
}
|
||||
surfAttribs = append(surfAttribs, C.EGL_NONE)
|
||||
surf := C.eglCreateWindowSurface(disp, cfg, view, &surfAttribs[0])
|
||||
if surf == nil {
|
||||
return nil, fmt.Errorf("eglCreateWindowSurface failed (0x%x)", C.eglGetError())
|
||||
}
|
||||
return &eglContext{disp: disp, ctx: ctx, surf: surf, cleanup: cleanup}, nil
|
||||
}
|
||||
|
||||
func (c *eglContext) Release() {
|
||||
if c.ctx != nil {
|
||||
C.eglDestroyContext(c.disp, c.ctx)
|
||||
}
|
||||
if c.surf != nil {
|
||||
C.eglDestroySurface(c.disp, c.surf)
|
||||
}
|
||||
if c.cleanup != nil {
|
||||
c.cleanup()
|
||||
}
|
||||
*c = eglContext{}
|
||||
}
|
||||
|
||||
func hasExtension(exts []string, ext string) bool {
|
||||
for _, e := range exts {
|
||||
if ext == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// quarterWidget paints a quarter of the screen with one color. When clicked, it
|
||||
// turns red, going back to its normal color when clicked again.
|
||||
type quarterWidget struct {
|
||||
color color.NRGBA
|
||||
|
||||
clicked bool
|
||||
}
|
||||
|
||||
var red = color.NRGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}
|
||||
|
||||
func (w *quarterWidget) Layout(gtx layout.Context) layout.Dimensions {
|
||||
var color color.NRGBA
|
||||
if w.clicked {
|
||||
color = red
|
||||
} else {
|
||||
color = w.color
|
||||
}
|
||||
|
||||
r := image.Rectangle{Max: gtx.Constraints.Max}
|
||||
paint.FillShape(gtx.Ops, color, clip.Rect(r).Op())
|
||||
|
||||
defer clip.Rect(image.Rectangle{
|
||||
Max: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y),
|
||||
}).Push(gtx.Ops).Pop()
|
||||
pointer.InputOp{
|
||||
Tag: w,
|
||||
Types: pointer.Press,
|
||||
}.Add(gtx.Ops)
|
||||
|
||||
for _, e := range gtx.Events(w) {
|
||||
if e, ok := e.(pointer.Event); ok && e.Type == pointer.Press {
|
||||
w.clicked = !w.clicked
|
||||
// notify when we're done updating the frame.
|
||||
notify = notifyInvalidate
|
||||
}
|
||||
}
|
||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
||||
}
|
||||
Reference in New Issue
Block a user