uidriver/mobile: Implement IsForeground

This adds hooks on resuming/suspending the application, and
switches the foreground state there. This change also updates
the logic to suspend the game loop to be clearer.

Fixes #1037
This commit is contained in:
Hajime Hoshi 2020-01-22 01:58:59 +09:00
parent 38f49c3768
commit 802693fa20
4 changed files with 43 additions and 17 deletions

View File

@ -313,6 +313,7 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
@synchronized(self) {
active_ = false;
EbitenmobileviewSuspend();
}
}
@ -321,6 +322,7 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
@synchronized(self) {
active_ = true;
EbitenmobileviewResume();
}
}
@ -402,6 +404,7 @@ public class EbitenView extends ViewGroup {
// Activity's onPause is called.
public void suspendGame() {
ebitenSurfaceView_.onPause();
Ebitenmobileview.suspend();
}
// resumeGame resumes the game.
@ -409,6 +412,7 @@ public class EbitenView extends ViewGroup {
// Activity's onResume is called.
public void resumeGame() {
ebitenSurfaceView_.onResume();
Ebitenmobileview.resume();
}
// onErrorOnGameUpdate is called on the main thread when an error happens when updating a game.

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,6 @@ import (
"fmt"
"runtime/debug"
"sync"
"time"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/lifecycle"
@ -46,7 +45,9 @@ var (
// renderEndCh receives when updating finishes.
renderEndCh = make(chan struct{})
theUI = &UserInterface{}
theUI = &UserInterface{
foreground: true,
}
)
func init() {
@ -58,6 +59,13 @@ func Get() *UserInterface {
}
func (u *UserInterface) Update() {
u.m.Lock()
fg := u.foreground
u.m.Unlock()
if !fg {
return
}
renderCh <- struct{}{}
ctx, cancel := context.WithCancel(context.Background())
go func() {
@ -99,6 +107,7 @@ type UserInterface struct {
scale float64
sizeChanged bool
foreground bool
// Used for gomobile-build
gbuildWidthPx int
@ -130,6 +139,7 @@ func (u *UserInterface) appMain(a app.App) {
case lifecycle.Event:
switch e.Crosses(lifecycle.StageVisible) {
case lifecycle.CrossOn:
u.SetForeground(true)
glctx, _ = e.DrawContext.(gl.Context)
// Assume that glctx is always a same instance.
// Then, only once initializing should be enough.
@ -139,6 +149,7 @@ func (u *UserInterface) appMain(a app.App) {
}
a.Send(paint.Event{})
case lifecycle.CrossOff:
u.SetForeground(false)
glctx = nil
}
case size.Event:
@ -182,6 +193,18 @@ func (u *UserInterface) appMain(a app.App) {
}
}
func (u *UserInterface) SetForeground(foreground bool) {
u.m.Lock()
u.foreground = foreground
u.m.Unlock()
if foreground {
hooks.ResumeAudio()
} else {
hooks.SuspendAudio()
}
}
func (u *UserInterface) Run(context driver.UIContext) error {
// TODO: Remove width/height/scale arguments. They are not used from gomobile-build.
@ -290,18 +313,7 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
}
func (u *UserInterface) update(context driver.UIContext) error {
t := time.NewTimer(500 * time.Millisecond)
defer t.Stop()
select {
case <-renderCh:
case <-t.C:
hooks.SuspendAudio()
<-renderCh
}
hooks.ResumeAudio()
<-renderCh
defer func() {
renderEndCh <- struct{}{}
}()
@ -370,8 +382,10 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
}
func (u *UserInterface) IsForeground() bool {
// TODO: implement this
return true
u.m.Lock()
fg := u.foreground
u.m.Unlock()
return fg
}
func (u *UserInterface) IsRunnableInBackground() bool {

View File

@ -80,3 +80,11 @@ func Update() error {
return update()
}
func Suspend() {
mobile.Get().SetForeground(false)
}
func Resume() {
mobile.Get().SetForeground(true)
}