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:
Chris Waldon
2022-05-10 19:53:50 -04:00
committed by Elias Naur
parent 35e56c5af9
commit ecebd405a7
5 changed files with 398 additions and 22 deletions
+4 -4
View File
@@ -3,22 +3,22 @@ module gioui.org/cmd
go 1.17 go 1.17
require ( require (
gioui.org v0.0.0-20220328154813-a3f147541fd0 gioui.org v0.0.0-20220628163331-e21c665e70ae
github.com/akavel/rsrc v0.10.1 github.com/akavel/rsrc v0.10.1
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4 github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4
github.com/chromedp/chromedp v0.5.2 github.com/chromedp/chromedp v0.5.2
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 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 golang.org/x/tools v0.1.0
) )
require ( require (
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect
gioui.org/shader v1.0.6 // 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/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/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
github.com/gobwas/pool v0.2.0 // indirect github.com/gobwas/pool v0.2.0 // indirect
github.com/gobwas/ws v1.0.2 // indirect github.com/gobwas/ws v1.0.2 // indirect
+10 -8
View File
@@ -1,5 +1,6 @@
gioui.org v0.0.0-20220328154813-a3f147541fd0 h1:n4FUiCT6P4a2wF6hwX4a5R8TpjAhu/d+3nhwZW16MAI= eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3 h1:djFprmHZgrSepsHAIRMp5UJn3PzsoTg9drI+BDmif5Q=
gioui.org v0.0.0-20220328154813-a3f147541fd0/go.mod h1:b8vBukexG6eYuXZa14asjLAWJ+JjbZ/ophEnS2FjYUg= 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-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 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 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/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/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.5/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8=
github.com/benoitkugler/textlayout v0.0.10 h1:uIaQgH4pBFw1LQ0tPkfjgxo94WYcckzzQaB41L2X84w= github.com/benoitkugler/textlayout v0.1.1 h1:hizE/085xAeY8q7gwV00uHR2Q27KYB2g1HW+UacXl68=
github.com/benoitkugler/textlayout v0.0.10/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8= 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 h1:QD3KxSJ59L2lxG6MXBjNHxiQO2RmxTQ3XcK+wO44WOg=
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/chromedp v0.5.2 h1:W8xBXQuUnd2dZK0SN/lyVwsQM7KgW+kY5HGnntms194= 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/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 h1:1bjaB/5IIicfKpP4k0s30T2WEw//Kh00zULa8DQ0cxA=
github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12/go.mod h1:kDhBRTA/i3H46PVdhqcw26TdGSIj42TOKNWKY+Kipnw= 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-20220411150340-35994bc27a7b h1:WINlj3ANt+CVrO2B4NGDHRlPvEWZPxjhb7z+JKypwXI=
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/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 h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= 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-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 h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 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.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 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/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.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.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.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-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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+14 -7
View File
@@ -13,6 +13,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -69,27 +70,33 @@ func TestEndToEnd(t *testing.T) {
t.Parallel() t.Parallel()
const ( const (
testdataWithGoImportPkgPath = "gioui.org/cmd/gogio/testdata" testdataWithGoImportPkgPath = "gioui.org/cmd/gogio/internal/normal"
testdataWithRelativePkgPath = "testdata/testdata.go" testdataWithRelativePkgPath = "internal/normal/testdata.go"
customRenderTestdataWithRelativePkgPath = "internal/custom/testdata.go"
) )
// Keep this list local, to not reuse TestDriver objects. // Keep this list local, to not reuse TestDriver objects.
subtests := []struct { subtests := []struct {
name string name string
driver TestDriver driver TestDriver
pkgPath string pkgPath string
skipGeese string
}{ }{
{"X11 using go import path", &X11TestDriver{}, testdataWithGoImportPkgPath}, {"X11 using go import path", &X11TestDriver{}, testdataWithGoImportPkgPath, ""},
{"X11", &X11TestDriver{}, testdataWithRelativePkgPath}, {"X11", &X11TestDriver{}, testdataWithRelativePkgPath, ""},
{"X11 with custom rendering", &X11TestDriver{}, customRenderTestdataWithRelativePkgPath, "openbsd"},
// Doesn't work on the builders. // Doesn't work on the builders.
//{"Wayland", &WaylandTestDriver{}, testdataWithRelativePkgPath}, //{"Wayland", &WaylandTestDriver{}, testdataWithRelativePkgPath},
{"JS", &JSTestDriver{}, testdataWithRelativePkgPath}, {"JS", &JSTestDriver{}, testdataWithRelativePkgPath, ""},
{"Android", &AndroidTestDriver{}, testdataWithRelativePkgPath}, {"Android", &AndroidTestDriver{}, testdataWithRelativePkgPath, ""},
{"Windows", &WineTestDriver{}, testdataWithRelativePkgPath}, {"Windows", &WineTestDriver{}, testdataWithRelativePkgPath, ""},
} }
for _, subtest := range subtests { for _, subtest := range subtests {
t.Run(subtest.name, func(t *testing.T) { t.Run(subtest.name, func(t *testing.T) {
subtest := subtest // copy the changing loop variable subtest := subtest // copy the changing loop variable
if strings.Contains(subtest.skipGeese, runtime.GOOS) {
t.Skipf("not supported on %s", runtime.GOOS)
}
t.Parallel() t.Parallel()
runEndToEndTest(t, subtest.driver, subtest.pkgPath) runEndToEndTest(t, subtest.driver, subtest.pkgPath)
}) })
+367
View File
@@ -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}
}