2015-05-04 21:05:59 -04:00
|
|
|
// Copyright 2015 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.
|
|
|
|
|
2015-07-28 12:29:19 -04:00
|
|
|
// +build darwin linux
|
|
|
|
|
2015-05-04 21:05:59 -04:00
|
|
|
// An app that makes a sound as the gopher hits the walls of the screen.
|
|
|
|
//
|
|
|
|
// 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 audio example and use gomobile to build or install it on your device.
|
|
|
|
//
|
|
|
|
// $ go get -d golang.org/x/mobile/example/audio
|
|
|
|
// $ gomobile build golang.org/x/mobile/example/audio # 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/audio
|
|
|
|
//
|
|
|
|
// Additionally, you can run the sample on your desktop environment
|
|
|
|
// by using the go tool.
|
|
|
|
//
|
|
|
|
// $ go install golang.org/x/mobile/example/audio && audio
|
|
|
|
//
|
|
|
|
// On Linux, you need to install OpenAL developer library by
|
|
|
|
// running the command below.
|
|
|
|
//
|
|
|
|
// $ apt-get install libopenal-dev
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
_ "image/jpeg"
|
|
|
|
|
|
|
|
"golang.org/x/mobile/app"
|
2015-06-29 07:21:27 -04:00
|
|
|
"golang.org/x/mobile/asset"
|
2015-07-17 13:25:29 -04:00
|
|
|
"golang.org/x/mobile/event/lifecycle"
|
|
|
|
"golang.org/x/mobile/event/paint"
|
2015-08-13 19:59:18 +10:00
|
|
|
"golang.org/x/mobile/event/size"
|
2015-06-20 16:44:00 -07:00
|
|
|
"golang.org/x/mobile/exp/audio"
|
2015-07-02 17:20:49 +10:00
|
|
|
"golang.org/x/mobile/exp/f32"
|
2015-09-22 17:29:00 -04:00
|
|
|
"golang.org/x/mobile/exp/gl/glutil"
|
2015-06-18 15:29:31 +10:00
|
|
|
"golang.org/x/mobile/exp/sprite"
|
|
|
|
"golang.org/x/mobile/exp/sprite/clock"
|
|
|
|
"golang.org/x/mobile/exp/sprite/glsprite"
|
2015-05-04 21:05:59 -04:00
|
|
|
"golang.org/x/mobile/gl"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
width = 72
|
|
|
|
height = 60
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
app: use one thread for both GL and other UI C code.
This change will break Darwin. I have only built and tested this on
desktop linux and Android linux. A follow-up CL will fix Darwin.
Currently, OpenGL gets its own thread, and UI C code (e.g. the Android
event loop, or the X11 event loop) gets its own thread. This relies on
multiple system-provided UI-related C libraries working nicely together,
even when running on different threads. Keeping all the C code on the
one thread seems more sound.
As side-effects:
- In package app/debug, DrawFPS now takes an explicit Config.
- In package app, some callbacks now take an explicit Config.
- In package exp/sprite, Render now takes an explicit Config.
- In package event, there are new events (Config, Draw, Lifecycle),
and an event filter mechanism to replace multiple app Callbacks.
- In package geom, the deprecated Width, Height and PixelsPerPt global
variables were removed in favor of an event.Config that is
explicitly passed around (and does not require mutex-locking).
Converting a geom.Pt to pixels now requires passing a pixelsPerPt.
- In package gl, the Do, Start and Stop functions are removed, as well
as the need to call Start in its own goroutine. There is no longer a
separate GL thread. Instead, package app explicitly performs any GL
work (gl.DoWork) when some is available (gl.WorkAvailable).
- In package gl/glutil, Image.Draw now takes an explicit Config.
Callbacks are no longer executed on 'the UI thread'.
Changing the app programming model from callbacks to events (since a
channel of events works with select) will be a follow-up change.
Change-Id: Id9865cd9ee1c45a98c613e9021a63c17226a64b1
Reviewed-on: https://go-review.googlesource.com/11351
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-23 16:41:48 +10:00
|
|
|
startTime = time.Now()
|
2015-05-04 21:05:59 -04:00
|
|
|
|
2015-09-22 17:29:00 -04:00
|
|
|
images *glutil.Images
|
|
|
|
eng sprite.Engine
|
|
|
|
scene *sprite.Node
|
2015-05-04 21:05:59 -04:00
|
|
|
|
|
|
|
player *audio.Player
|
2015-07-17 14:13:37 -04:00
|
|
|
|
2015-08-13 19:59:18 +10:00
|
|
|
sz size.Event
|
2015-05-04 21:05:59 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2015-07-17 13:25:29 -04:00
|
|
|
app.Main(func(a app.App) {
|
2015-08-08 10:18:15 -04:00
|
|
|
var glctx gl.Context
|
2015-07-17 13:25:29 -04:00
|
|
|
for e := range a.Events() {
|
2015-09-22 13:19:05 -04:00
|
|
|
switch e := a.Filter(e).(type) {
|
2015-07-17 13:25:29 -04:00
|
|
|
case lifecycle.Event:
|
|
|
|
switch e.Crosses(lifecycle.StageVisible) {
|
|
|
|
case lifecycle.CrossOn:
|
2015-08-08 10:18:15 -04:00
|
|
|
glctx, _ = e.DrawContext.(gl.Context)
|
|
|
|
onStart(glctx)
|
2015-10-06 15:57:53 -04:00
|
|
|
a.Send(paint.Event{})
|
2015-07-17 13:25:29 -04:00
|
|
|
case lifecycle.CrossOff:
|
|
|
|
onStop()
|
2015-10-06 15:57:53 -04:00
|
|
|
glctx = nil
|
2015-07-17 13:25:29 -04:00
|
|
|
}
|
2015-08-13 19:59:18 +10:00
|
|
|
case size.Event:
|
|
|
|
sz = e
|
2015-07-17 13:25:29 -04:00
|
|
|
case paint.Event:
|
2015-10-06 15:57:53 -04:00
|
|
|
if glctx == nil || e.External {
|
|
|
|
continue
|
|
|
|
}
|
2015-08-08 10:18:15 -04:00
|
|
|
onPaint(glctx)
|
app: change EndPaint to Publish.
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>
2015-09-04 17:13:09 +10:00
|
|
|
a.Publish()
|
2015-10-06 15:57:53 -04:00
|
|
|
a.Send(paint.Event{}) // keep animating
|
2015-07-17 13:25:29 -04:00
|
|
|
}
|
|
|
|
}
|
2015-05-04 21:05:59 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-08-08 10:18:15 -04:00
|
|
|
func onStart(glctx gl.Context) {
|
|
|
|
images = glutil.NewImages(glctx)
|
2015-09-22 17:29:00 -04:00
|
|
|
eng = glsprite.Engine(images)
|
|
|
|
loadScene()
|
|
|
|
|
2015-06-29 07:21:27 -04:00
|
|
|
rc, err := asset.Open("boing.wav")
|
2015-05-04 21:05:59 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2015-05-07 19:37:01 -04:00
|
|
|
player, err = audio.NewPlayer(rc, 0, 0)
|
2015-05-04 21:05:59 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-15 17:17:08 +10:00
|
|
|
func onStop() {
|
2015-09-22 17:29:00 -04:00
|
|
|
eng.Release()
|
|
|
|
images.Release()
|
2015-05-07 19:54:46 -04:00
|
|
|
player.Close()
|
2015-05-04 21:05:59 -04:00
|
|
|
}
|
|
|
|
|
2015-08-08 10:18:15 -04:00
|
|
|
func onPaint(glctx gl.Context) {
|
|
|
|
glctx.ClearColor(1, 1, 1, 1)
|
|
|
|
glctx.Clear(gl.COLOR_BUFFER_BIT)
|
app: use one thread for both GL and other UI C code.
This change will break Darwin. I have only built and tested this on
desktop linux and Android linux. A follow-up CL will fix Darwin.
Currently, OpenGL gets its own thread, and UI C code (e.g. the Android
event loop, or the X11 event loop) gets its own thread. This relies on
multiple system-provided UI-related C libraries working nicely together,
even when running on different threads. Keeping all the C code on the
one thread seems more sound.
As side-effects:
- In package app/debug, DrawFPS now takes an explicit Config.
- In package app, some callbacks now take an explicit Config.
- In package exp/sprite, Render now takes an explicit Config.
- In package event, there are new events (Config, Draw, Lifecycle),
and an event filter mechanism to replace multiple app Callbacks.
- In package geom, the deprecated Width, Height and PixelsPerPt global
variables were removed in favor of an event.Config that is
explicitly passed around (and does not require mutex-locking).
Converting a geom.Pt to pixels now requires passing a pixelsPerPt.
- In package gl, the Do, Start and Stop functions are removed, as well
as the need to call Start in its own goroutine. There is no longer a
separate GL thread. Instead, package app explicitly performs any GL
work (gl.DoWork) when some is available (gl.WorkAvailable).
- In package gl/glutil, Image.Draw now takes an explicit Config.
Callbacks are no longer executed on 'the UI thread'.
Changing the app programming model from callbacks to events (since a
channel of events works with select) will be a follow-up change.
Change-Id: Id9865cd9ee1c45a98c613e9021a63c17226a64b1
Reviewed-on: https://go-review.googlesource.com/11351
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-23 16:41:48 +10:00
|
|
|
now := clock.Time(time.Since(startTime) * 60 / time.Second)
|
2015-08-13 19:59:18 +10:00
|
|
|
eng.Render(scene, now, sz)
|
2015-05-04 21:05:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func newNode() *sprite.Node {
|
|
|
|
n := &sprite.Node{}
|
|
|
|
eng.Register(n)
|
|
|
|
scene.AppendChild(n)
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2015-07-17 14:13:37 -04:00
|
|
|
func loadScene() {
|
2015-05-04 21:05:59 -04:00
|
|
|
gopher := loadGopher()
|
|
|
|
scene = &sprite.Node{}
|
|
|
|
eng.Register(scene)
|
|
|
|
eng.SetTransform(scene, f32.Affine{
|
|
|
|
{1, 0, 0},
|
|
|
|
{0, 1, 0},
|
|
|
|
})
|
|
|
|
|
|
|
|
var x, y float32
|
|
|
|
dx, dy := float32(1), float32(1)
|
|
|
|
|
|
|
|
n := newNode()
|
2015-08-13 19:59:18 +10:00
|
|
|
// TODO: Shouldn't arranger pass the size.Event?
|
2015-05-04 21:05:59 -04:00
|
|
|
n.Arranger = arrangerFunc(func(eng sprite.Engine, n *sprite.Node, t clock.Time) {
|
|
|
|
eng.SetSubTex(n, gopher)
|
|
|
|
|
|
|
|
if x < 0 {
|
|
|
|
dx = 1
|
|
|
|
boing()
|
|
|
|
}
|
|
|
|
if y < 0 {
|
|
|
|
dy = 1
|
|
|
|
boing()
|
|
|
|
}
|
2015-08-13 19:59:18 +10:00
|
|
|
if x+width > float32(sz.WidthPt) {
|
2015-05-04 21:05:59 -04:00
|
|
|
dx = -1
|
|
|
|
boing()
|
|
|
|
}
|
2015-08-13 19:59:18 +10:00
|
|
|
if y+height > float32(sz.HeightPt) {
|
2015-05-04 21:05:59 -04:00
|
|
|
dy = -1
|
|
|
|
boing()
|
|
|
|
}
|
|
|
|
|
|
|
|
x += dx
|
|
|
|
y += dy
|
|
|
|
|
|
|
|
eng.SetTransform(n, f32.Affine{
|
|
|
|
{width, 0, x},
|
|
|
|
{0, height, y},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func boing() {
|
|
|
|
player.Seek(0)
|
|
|
|
player.Play()
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadGopher() sprite.SubTex {
|
2015-06-29 07:21:27 -04:00
|
|
|
a, err := asset.Open("gopher.jpeg")
|
2015-05-04 21:05:59 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer a.Close()
|
|
|
|
|
|
|
|
img, _, err := image.Decode(a)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
t, err := eng.LoadTexture(img)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
return sprite.SubTex{t, image.Rect(0, 0, 360, 300)}
|
|
|
|
}
|
|
|
|
|
|
|
|
type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)
|
|
|
|
|
|
|
|
func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }
|