event/paint: mark paint events sent by the system
A paint.Event now has an External field. Whenever a paint event is sent by the x/mobile/app package, it is marked as external so users with an active paint loop can ignore them. Implemented on OS X and Android, with examples updated. Change-Id: Ibee8d65625c8818ff954936be48257ad30daa147 Reviewed-on: https://go-review.googlesource.com/15480 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
parent
0d45604e75
commit
7fe809f8e0
@ -278,21 +278,6 @@ func mainUI(vm, jniEnv, ctx uintptr) error {
|
|||||||
var pixelsPerPt float32
|
var pixelsPerPt float32
|
||||||
var orientation size.Orientation
|
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 {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-donec:
|
case <-donec:
|
||||||
@ -317,8 +302,7 @@ func mainUI(vm, jniEnv, ctx uintptr) error {
|
|||||||
PixelsPerPt: pixelsPerPt,
|
PixelsPerPt: pixelsPerPt,
|
||||||
Orientation: orientation,
|
Orientation: orientation,
|
||||||
}
|
}
|
||||||
redrawGen++
|
theApp.eventsIn <- paint.Event{External: true}
|
||||||
theApp.eventsIn <- paint.Event{redrawGen}
|
|
||||||
case <-windowDestroyed:
|
case <-windowDestroyed:
|
||||||
if C.surface != nil {
|
if C.surface != nil {
|
||||||
if errStr := C.destroyEGLSurface(); errStr != nil {
|
if errStr := C.destroyEGLSurface(); errStr != nil {
|
||||||
|
@ -113,7 +113,9 @@ var drawDone = make(chan struct{})
|
|||||||
func drawgl() {
|
func drawgl() {
|
||||||
switch theApp.lifecycleStage {
|
switch theApp.lifecycleStage {
|
||||||
case lifecycle.StageFocused, lifecycle.StageVisible:
|
case lifecycle.StageFocused, lifecycle.StageVisible:
|
||||||
theApp.Send(paint.Event{})
|
theApp.Send(paint.Event{
|
||||||
|
External: true,
|
||||||
|
})
|
||||||
<-drawDone
|
<-drawDone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +220,6 @@ func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
|
|||||||
//export lifecycleVisible
|
//export lifecycleVisible
|
||||||
func lifecycleVisible() {
|
func lifecycleVisible() {
|
||||||
theApp.sendLifecycle(lifecycle.StageVisible)
|
theApp.sendLifecycle(lifecycle.StageVisible)
|
||||||
theApp.eventsIn <- paint.Event{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export lifecycleFocused
|
//export lifecycleFocused
|
||||||
|
@ -7,11 +7,18 @@
|
|||||||
// See the golang.org/x/mobile/app package for details on the event model.
|
// See the golang.org/x/mobile/app package for details on the event model.
|
||||||
package paint // import "golang.org/x/mobile/event/paint"
|
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
|
// Event indicates that the app is ready to paint the next frame of the GUI.
|
||||||
// frame is completed by calling the App's EndPaint method.
|
//
|
||||||
|
//A frame is completed by calling the App's Publish method.
|
||||||
type Event struct {
|
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?
|
// An external event may be sent at any time in response to an
|
||||||
Generation uint32
|
// 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
|
||||||
}
|
}
|
||||||
|
@ -73,28 +73,27 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
app.Main(func(a app.App) {
|
app.Main(func(a app.App) {
|
||||||
var glctx gl.Context
|
var glctx gl.Context
|
||||||
visible := false
|
|
||||||
for e := range a.Events() {
|
for e := range a.Events() {
|
||||||
switch e := a.Filter(e).(type) {
|
switch e := a.Filter(e).(type) {
|
||||||
case lifecycle.Event:
|
case lifecycle.Event:
|
||||||
switch e.Crosses(lifecycle.StageVisible) {
|
switch e.Crosses(lifecycle.StageVisible) {
|
||||||
case lifecycle.CrossOn:
|
case lifecycle.CrossOn:
|
||||||
visible = true
|
|
||||||
glctx, _ = e.DrawContext.(gl.Context)
|
glctx, _ = e.DrawContext.(gl.Context)
|
||||||
onStart(glctx)
|
onStart(glctx)
|
||||||
|
a.Send(paint.Event{})
|
||||||
case lifecycle.CrossOff:
|
case lifecycle.CrossOff:
|
||||||
visible = false
|
|
||||||
onStop()
|
onStop()
|
||||||
|
glctx = nil
|
||||||
}
|
}
|
||||||
case size.Event:
|
case size.Event:
|
||||||
sz = e
|
sz = e
|
||||||
case paint.Event:
|
case paint.Event:
|
||||||
|
if glctx == nil || e.External {
|
||||||
|
continue
|
||||||
|
}
|
||||||
onPaint(glctx)
|
onPaint(glctx)
|
||||||
a.Publish()
|
a.Publish()
|
||||||
if visible {
|
a.Send(paint.Event{}) // keep animating
|
||||||
// Keep animating.
|
|
||||||
a.Send(paint.Event{})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -60,35 +60,36 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
app.Main(func(a app.App) {
|
app.Main(func(a app.App) {
|
||||||
var glctx gl.Context
|
var glctx gl.Context
|
||||||
visible, sz := false, size.Event{}
|
var sz size.Event
|
||||||
for e := range a.Events() {
|
for e := range a.Events() {
|
||||||
switch e := a.Filter(e).(type) {
|
switch e := a.Filter(e).(type) {
|
||||||
case lifecycle.Event:
|
case lifecycle.Event:
|
||||||
switch e.Crosses(lifecycle.StageVisible) {
|
switch e.Crosses(lifecycle.StageVisible) {
|
||||||
case lifecycle.CrossOn:
|
case lifecycle.CrossOn:
|
||||||
visible = true
|
|
||||||
glctx, _ = e.DrawContext.(gl.Context)
|
glctx, _ = e.DrawContext.(gl.Context)
|
||||||
onStart(glctx)
|
onStart(glctx)
|
||||||
|
a.Send(paint.Event{})
|
||||||
case lifecycle.CrossOff:
|
case lifecycle.CrossOff:
|
||||||
visible = false
|
|
||||||
onStop(glctx)
|
onStop(glctx)
|
||||||
|
glctx = nil
|
||||||
}
|
}
|
||||||
case size.Event:
|
case size.Event:
|
||||||
sz = e
|
sz = e
|
||||||
touchX = float32(sz.WidthPx / 2)
|
touchX = float32(sz.WidthPx / 2)
|
||||||
touchY = float32(sz.HeightPx / 2)
|
touchY = float32(sz.HeightPx / 2)
|
||||||
case paint.Event:
|
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)
|
onPaint(glctx, sz)
|
||||||
a.Publish()
|
a.Publish()
|
||||||
if visible {
|
// Drive the animation by preparing to paint the next frame
|
||||||
// Drive the animation by preparing to paint the next frame
|
// after this one is shown.
|
||||||
// after this one is shown.
|
a.Send(paint.Event{})
|
||||||
//
|
|
||||||
// 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:
|
case touch.Event:
|
||||||
touchX = e.X
|
touchX = e.X
|
||||||
touchY = e.Y
|
touchY = e.Y
|
||||||
|
@ -46,8 +46,6 @@ import (
|
|||||||
"golang.org/x/mobile/event/lifecycle"
|
"golang.org/x/mobile/event/lifecycle"
|
||||||
"golang.org/x/mobile/event/paint"
|
"golang.org/x/mobile/event/paint"
|
||||||
"golang.org/x/mobile/event/size"
|
"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"
|
"golang.org/x/mobile/gl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,14 +66,12 @@ func main() {
|
|||||||
switch e := a.Filter(e).(type) {
|
switch e := a.Filter(e).(type) {
|
||||||
case lifecycle.Event:
|
case lifecycle.Event:
|
||||||
glctx, _ = e.DrawContext.(gl.Context)
|
glctx, _ = e.DrawContext.(gl.Context)
|
||||||
if glctx != nil {
|
|
||||||
glctx = e.DrawContext.(gl.Context)
|
|
||||||
images = glutil.NewImages(glctx)
|
|
||||||
fps = debug.NewFPS(images)
|
|
||||||
}
|
|
||||||
case size.Event:
|
case size.Event:
|
||||||
sz = e
|
sz = e
|
||||||
case paint.Event:
|
case paint.Event:
|
||||||
|
if glctx == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
onDraw(glctx, sz)
|
onDraw(glctx, sz)
|
||||||
a.Publish()
|
a.Publish()
|
||||||
}
|
}
|
||||||
@ -85,8 +81,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
images *glutil.Images
|
|
||||||
fps *debug.FPS
|
|
||||||
determined = make(chan struct{})
|
determined = make(chan struct{})
|
||||||
ok = false
|
ok = false
|
||||||
)
|
)
|
||||||
@ -113,6 +107,4 @@ func onDraw(glctx gl.Context, sz size.Event) {
|
|||||||
glctx.ClearColor(0, 0, 0, 1)
|
glctx.ClearColor(0, 0, 0, 1)
|
||||||
}
|
}
|
||||||
glctx.Clear(gl.COLOR_BUFFER_BIT)
|
glctx.Clear(gl.COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
fps.Draw(sz)
|
|
||||||
}
|
}
|
||||||
|
@ -61,28 +61,28 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
app.Main(func(a app.App) {
|
app.Main(func(a app.App) {
|
||||||
var glctx gl.Context
|
var glctx gl.Context
|
||||||
visible, sz := false, size.Event{}
|
var sz size.Event
|
||||||
for e := range a.Events() {
|
for e := range a.Events() {
|
||||||
switch e := a.Filter(e).(type) {
|
switch e := a.Filter(e).(type) {
|
||||||
case lifecycle.Event:
|
case lifecycle.Event:
|
||||||
switch e.Crosses(lifecycle.StageVisible) {
|
switch e.Crosses(lifecycle.StageVisible) {
|
||||||
case lifecycle.CrossOn:
|
case lifecycle.CrossOn:
|
||||||
visible = true
|
|
||||||
glctx, _ = e.DrawContext.(gl.Context)
|
glctx, _ = e.DrawContext.(gl.Context)
|
||||||
onStart(glctx)
|
onStart(glctx)
|
||||||
|
a.Send(paint.Event{})
|
||||||
case lifecycle.CrossOff:
|
case lifecycle.CrossOff:
|
||||||
visible = false
|
|
||||||
onStop()
|
onStop()
|
||||||
|
glctx = nil
|
||||||
}
|
}
|
||||||
case size.Event:
|
case size.Event:
|
||||||
sz = e
|
sz = e
|
||||||
case paint.Event:
|
case paint.Event:
|
||||||
if visible {
|
if glctx == nil || e.External {
|
||||||
onPaint(glctx, sz)
|
continue
|
||||||
a.Publish()
|
|
||||||
// Keep animating.
|
|
||||||
a.Send(paint.Event{})
|
|
||||||
}
|
}
|
||||||
|
onPaint(glctx, sz)
|
||||||
|
a.Publish()
|
||||||
|
a.Send(paint.Event{}) // keep animating
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user