diff --git a/app/android.go b/app/android.go index 30cc6f1..0da19cb 100644 --- a/app/android.go +++ b/app/android.go @@ -278,21 +278,6 @@ func mainUI(vm, jniEnv, ctx uintptr) error { var pixelsPerPt float32 var orientation size.Orientation - // Android can send a windowRedrawNeeded event any time, including - // in the middle of a paint cycle. The redraw event may have changed - // the size of the screen, so any partial painting is now invalidated. - // We must also not return to Android (via sending on windowRedrawDone) - // until a complete paint with the new configuration is complete. - // - // When a windowRedrawNeeded request comes in, we increment redrawGen - // (Gen is short for generation number), and do not make a paint cycle - // visible on <-endPaint unless Generation agrees. If possible, - // windowRedrawDone is signalled, allowing onNativeWindowRedrawNeeded - // to return. - // - // TODO: is this still needed? - var redrawGen uint32 - for { select { case <-donec: @@ -317,8 +302,7 @@ func mainUI(vm, jniEnv, ctx uintptr) error { PixelsPerPt: pixelsPerPt, Orientation: orientation, } - redrawGen++ - theApp.eventsIn <- paint.Event{redrawGen} + theApp.eventsIn <- paint.Event{External: true} case <-windowDestroyed: if C.surface != nil { if errStr := C.destroyEGLSurface(); errStr != nil { diff --git a/app/darwin_amd64.go b/app/darwin_amd64.go index 6935769..09de408 100644 --- a/app/darwin_amd64.go +++ b/app/darwin_amd64.go @@ -113,7 +113,9 @@ var drawDone = make(chan struct{}) func drawgl() { switch theApp.lifecycleStage { case lifecycle.StageFocused, lifecycle.StageVisible: - theApp.Send(paint.Event{}) + theApp.Send(paint.Event{ + External: true, + }) <-drawDone } } @@ -218,7 +220,6 @@ func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) } //export lifecycleVisible func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) - theApp.eventsIn <- paint.Event{} } //export lifecycleFocused diff --git a/event/paint/paint.go b/event/paint/paint.go index 1c9c557..b7b4113 100644 --- a/event/paint/paint.go +++ b/event/paint/paint.go @@ -7,11 +7,18 @@ // See the golang.org/x/mobile/app package for details on the event model. package paint // import "golang.org/x/mobile/event/paint" -// Event indicates that the app is ready to paint the next frame of the GUI. A -// frame is completed by calling the App's EndPaint method. +// Event indicates that the app is ready to paint the next frame of the GUI. +// +//A frame is completed by calling the App's Publish method. type Event struct { - // Generation is a monotonically increasing generation number. + // External is true for paint events sent by the screen driver. // - // TODO: is a generation number the right model for stale paints? - Generation uint32 + // An external event may be sent at any time in response to an + // operating system event, for example the window opened, was + // resized, or the screen memory was lost. + // + // Programs actively drawing to the screen as fast as vsync allows + // should ignore external paint events to avoid a backlog of paint + // events building up. + External bool } diff --git a/example/audio/main.go b/example/audio/main.go index e632bcb..93daaae 100644 --- a/example/audio/main.go +++ b/example/audio/main.go @@ -73,28 +73,27 @@ var ( func main() { app.Main(func(a app.App) { var glctx gl.Context - visible := false for e := range a.Events() { switch e := a.Filter(e).(type) { case lifecycle.Event: switch e.Crosses(lifecycle.StageVisible) { case lifecycle.CrossOn: - visible = true glctx, _ = e.DrawContext.(gl.Context) onStart(glctx) + a.Send(paint.Event{}) case lifecycle.CrossOff: - visible = false onStop() + glctx = nil } case size.Event: sz = e case paint.Event: + if glctx == nil || e.External { + continue + } onPaint(glctx) a.Publish() - if visible { - // Keep animating. - a.Send(paint.Event{}) - } + a.Send(paint.Event{}) // keep animating } } }) diff --git a/example/basic/main.go b/example/basic/main.go index 1edc066..2b2e0c9 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -60,35 +60,36 @@ var ( func main() { app.Main(func(a app.App) { var glctx gl.Context - visible, sz := false, size.Event{} + var sz size.Event for e := range a.Events() { switch e := a.Filter(e).(type) { case lifecycle.Event: switch e.Crosses(lifecycle.StageVisible) { case lifecycle.CrossOn: - visible = true glctx, _ = e.DrawContext.(gl.Context) onStart(glctx) + a.Send(paint.Event{}) case lifecycle.CrossOff: - visible = false onStop(glctx) + glctx = nil } case size.Event: sz = e touchX = float32(sz.WidthPx / 2) touchY = float32(sz.HeightPx / 2) case paint.Event: + if glctx == nil || e.External { + // As we are actively painting as fast as + // we can (usually 60 FPS), skip any paint + // events sent by the system. + continue + } + onPaint(glctx, 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{}) - } + // Drive the animation by preparing to paint the next frame + // after this one is shown. + a.Send(paint.Event{}) case touch.Event: touchX = e.X touchY = e.Y diff --git a/example/network/main.go b/example/network/main.go index 6e29a2d..8fba730 100644 --- a/example/network/main.go +++ b/example/network/main.go @@ -46,8 +46,6 @@ import ( "golang.org/x/mobile/event/lifecycle" "golang.org/x/mobile/event/paint" "golang.org/x/mobile/event/size" - "golang.org/x/mobile/exp/app/debug" - "golang.org/x/mobile/exp/gl/glutil" "golang.org/x/mobile/gl" ) @@ -68,14 +66,12 @@ func main() { switch e := a.Filter(e).(type) { case lifecycle.Event: glctx, _ = e.DrawContext.(gl.Context) - if glctx != nil { - glctx = e.DrawContext.(gl.Context) - images = glutil.NewImages(glctx) - fps = debug.NewFPS(images) - } case size.Event: sz = e case paint.Event: + if glctx == nil { + continue + } onDraw(glctx, sz) a.Publish() } @@ -85,8 +81,6 @@ func main() { } var ( - images *glutil.Images - fps *debug.FPS determined = make(chan struct{}) ok = false ) @@ -113,6 +107,4 @@ func onDraw(glctx gl.Context, sz size.Event) { glctx.ClearColor(0, 0, 0, 1) } glctx.Clear(gl.COLOR_BUFFER_BIT) - - fps.Draw(sz) } diff --git a/example/sprite/main.go b/example/sprite/main.go index 38a6043..1436ab9 100644 --- a/example/sprite/main.go +++ b/example/sprite/main.go @@ -61,28 +61,28 @@ var ( func main() { app.Main(func(a app.App) { var glctx gl.Context - visible, sz := false, size.Event{} + var sz size.Event for e := range a.Events() { switch e := a.Filter(e).(type) { case lifecycle.Event: switch e.Crosses(lifecycle.StageVisible) { case lifecycle.CrossOn: - visible = true glctx, _ = e.DrawContext.(gl.Context) onStart(glctx) + a.Send(paint.Event{}) case lifecycle.CrossOff: - visible = false onStop() + glctx = nil } case size.Event: sz = e case paint.Event: - if visible { - onPaint(glctx, sz) - a.Publish() - // Keep animating. - a.Send(paint.Event{}) + if glctx == nil || e.External { + continue } + onPaint(glctx, sz) + a.Publish() + a.Send(paint.Event{}) // keep animating } } })