More than a name change, the painting model changes so that the app, not the library, is responsible for driving painting. If the app is animating and wants paint events at 60 Hz, it has to ask for that. If the app is not animating and doesn't need to update its screen, it shouldn't get any paint events. Plenty of TODOs, and this CL doesn't get us to a perfect place, but it is a checkpoint along the way. The darwin_*.go code changes were minimal. I don't even have a Mac or iOS device to test that this even builds. Even so, the TODOs about not sending paint.Events unconditionally are important TODOs. That's the whole point of switching to this model. I'll leave the actual implementation to you (crawshaw). Out of all the example apps, the change to example/network/main.go is probably the most interesting. It seems like there ought to be some way to reduce the copy/paste between all of the example app code, but I'll leave that for future CLs. Change-Id: I17e11c06174110c68e17f7183b2d8af19b6a170e Reviewed-on: https://go-review.googlesource.com/14300 Reviewed-by: David Crawshaw <crawshaw@golang.org>
172 lines
4.3 KiB
Go
172 lines
4.3 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build darwin linux
|
|
|
|
// An app that draws a green triangle on a red background.
|
|
//
|
|
// Note: This demo is an early preview of Go 1.5. In order to build this
|
|
// program as an Android APK using the gomobile tool.
|
|
//
|
|
// See http://godoc.org/golang.org/x/mobile/cmd/gomobile to install gomobile.
|
|
//
|
|
// Get the basic example and use gomobile to build or install it on your device.
|
|
//
|
|
// $ go get -d golang.org/x/mobile/example/basic
|
|
// $ gomobile build golang.org/x/mobile/example/basic # will build an APK
|
|
//
|
|
// # plug your Android device to your computer or start an Android emulator.
|
|
// # if you have adb installed on your machine, use gomobile install to
|
|
// # build and deploy the APK to an Android target.
|
|
// $ gomobile install golang.org/x/mobile/example/basic
|
|
//
|
|
// Switch to your device or emulator to start the Basic application from
|
|
// the launcher.
|
|
// You can also run the application on your desktop by running the command
|
|
// below. (Note: It currently doesn't work on Windows.)
|
|
// $ go install golang.org/x/mobile/example/basic && basic
|
|
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"log"
|
|
|
|
"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/event/touch"
|
|
"golang.org/x/mobile/exp/app/debug"
|
|
"golang.org/x/mobile/exp/f32"
|
|
"golang.org/x/mobile/exp/gl/glutil"
|
|
"golang.org/x/mobile/gl"
|
|
)
|
|
|
|
var (
|
|
program gl.Program
|
|
position gl.Attrib
|
|
offset gl.Uniform
|
|
color gl.Uniform
|
|
buf gl.Buffer
|
|
|
|
green float32
|
|
touchX float32
|
|
touchY float32
|
|
)
|
|
|
|
func main() {
|
|
app.Main(func(a app.App) {
|
|
visible, sz := false, size.Event{}
|
|
for e := range a.Events() {
|
|
switch e := app.Filter(e).(type) {
|
|
case lifecycle.Event:
|
|
switch e.Crosses(lifecycle.StageVisible) {
|
|
case lifecycle.CrossOn:
|
|
visible = true
|
|
onStart()
|
|
case lifecycle.CrossOff:
|
|
visible = false
|
|
onStop()
|
|
}
|
|
case size.Event:
|
|
sz = e
|
|
touchX = float32(sz.WidthPx / 2)
|
|
touchY = float32(sz.HeightPx / 2)
|
|
case paint.Event:
|
|
onPaint(sz)
|
|
a.Publish()
|
|
if visible {
|
|
// Drive the animation by preparing to paint the next frame
|
|
// after this one is shown.
|
|
//
|
|
// TODO: is paint.Event the right thing to send? Should we
|
|
// have a dedicated publish.Event type? Should App.Publish
|
|
// take an optional event sender and send a publish.Event?
|
|
a.Send(paint.Event{})
|
|
}
|
|
case touch.Event:
|
|
touchX = e.X
|
|
touchY = e.Y
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func onStart() {
|
|
var err error
|
|
program, err = glutil.CreateProgram(vertexShader, fragmentShader)
|
|
if err != nil {
|
|
log.Printf("error creating GL program: %v", err)
|
|
return
|
|
}
|
|
|
|
buf = gl.CreateBuffer()
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, buf)
|
|
gl.BufferData(gl.ARRAY_BUFFER, triangleData, gl.STATIC_DRAW)
|
|
|
|
position = gl.GetAttribLocation(program, "position")
|
|
color = gl.GetUniformLocation(program, "color")
|
|
offset = gl.GetUniformLocation(program, "offset")
|
|
|
|
// TODO(crawshaw): the debug package needs to put GL state init here
|
|
// Can this be an app.RegisterFilter call now??
|
|
}
|
|
|
|
func onStop() {
|
|
gl.DeleteProgram(program)
|
|
gl.DeleteBuffer(buf)
|
|
}
|
|
|
|
func onPaint(sz size.Event) {
|
|
gl.ClearColor(1, 0, 0, 1)
|
|
gl.Clear(gl.COLOR_BUFFER_BIT)
|
|
|
|
gl.UseProgram(program)
|
|
|
|
green += 0.01
|
|
if green > 1 {
|
|
green = 0
|
|
}
|
|
gl.Uniform4f(color, 0, green, 0, 1)
|
|
|
|
gl.Uniform2f(offset, touchX/float32(sz.WidthPx), touchY/float32(sz.HeightPx))
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, buf)
|
|
gl.EnableVertexAttribArray(position)
|
|
gl.VertexAttribPointer(position, coordsPerVertex, gl.FLOAT, false, 0, 0)
|
|
gl.DrawArrays(gl.TRIANGLES, 0, vertexCount)
|
|
gl.DisableVertexAttribArray(position)
|
|
|
|
debug.DrawFPS(sz)
|
|
}
|
|
|
|
var triangleData = f32.Bytes(binary.LittleEndian,
|
|
0.0, 0.4, 0.0, // top left
|
|
0.0, 0.0, 0.0, // bottom left
|
|
0.4, 0.0, 0.0, // bottom right
|
|
)
|
|
|
|
const (
|
|
coordsPerVertex = 3
|
|
vertexCount = 3
|
|
)
|
|
|
|
const vertexShader = `#version 100
|
|
uniform vec2 offset;
|
|
|
|
attribute vec4 position;
|
|
void main() {
|
|
// offset comes in with x/y values between 0 and 1.
|
|
// position bounds are -1 to 1.
|
|
vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
|
|
gl_Position = position + offset4;
|
|
}`
|
|
|
|
const fragmentShader = `#version 100
|
|
precision mediump float;
|
|
uniform vec4 color;
|
|
void main() {
|
|
gl_FragColor = color;
|
|
}`
|