internal/hooks: Enable to return error at suspend/resume audio

This commit is contained in:
Hajime Hoshi 2021-05-04 22:24:31 +09:00
parent bcf8ef85b6
commit 58843b68f9
9 changed files with 77 additions and 41 deletions

View File

@ -125,17 +125,19 @@ func NewContext(sampleRate int) *Context {
theContext = c theContext = c
h := getHook() h := getHook()
h.OnSuspendAudio(func() { h.OnSuspendAudio(func() error {
c.semaphore <- struct{}{} c.semaphore <- struct{}{}
if s, ok := np.(interface{ suspend() }); ok { if s, ok := np.(interface{ suspend() }); ok {
s.suspend() s.suspend()
} }
return nil
}) })
h.OnResumeAudio(func() { h.OnResumeAudio(func() error {
<-c.semaphore <-c.semaphore
if s, ok := np.(interface{ resume() }); ok { if s, ok := np.(interface{ resume() }); ok {
s.resume() s.resume()
} }
return nil
}) })
h.AppendHookOnBeforeUpdate(func() error { h.AppendHookOnBeforeUpdate(func() error {
@ -423,8 +425,8 @@ func (p *Player) SetVolume(volume float64) {
} }
type hook interface { type hook interface {
OnSuspendAudio(f func()) OnSuspendAudio(f func() error)
OnResumeAudio(f func()) OnResumeAudio(f func() error)
AppendHookOnBeforeUpdate(f func() error) AppendHookOnBeforeUpdate(f func() error)
} }
@ -439,11 +441,11 @@ func getHook() hook {
type hookImpl struct{} type hookImpl struct{}
func (h *hookImpl) OnSuspendAudio(f func()) { func (h *hookImpl) OnSuspendAudio(f func() error) {
hooks.OnSuspendAudio(f) hooks.OnSuspendAudio(f)
} }
func (h *hookImpl) OnResumeAudio(f func()) { func (h *hookImpl) OnResumeAudio(f func() error) {
hooks.OnResumeAudio(f) hooks.OnResumeAudio(f)
} }

View File

@ -141,10 +141,10 @@ type dummyHook struct {
updates []func() error updates []func() error
} }
func (h *dummyHook) OnSuspendAudio(f func()) { func (h *dummyHook) OnSuspendAudio(f func() error) {
} }
func (h *dummyHook) OnResumeAudio(f func()) { func (h *dummyHook) OnResumeAudio(f func() error) {
} }
func (h *dummyHook) AppendHookOnBeforeUpdate(f func() error) { func (h *dummyHook) AppendHookOnBeforeUpdate(f func() error) {

View File

@ -313,7 +313,11 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
@synchronized(self) { @synchronized(self) {
active_ = false; active_ = false;
EbitenmobileviewSuspend(); NSError* err = nil;
EbitenmobileviewSuspend(&err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
}
} }
} }
@ -322,7 +326,11 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
@synchronized(self) { @synchronized(self) {
active_ = true; active_ = true;
EbitenmobileviewResume(); NSError* err = nil;
EbitenmobileviewResume(&err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
}
} }
} }
@ -689,7 +697,11 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
public void suspendGame() { public void suspendGame() {
this.inputManager.unregisterInputDeviceListener(this); this.inputManager.unregisterInputDeviceListener(this);
this.ebitenSurfaceView.onPause(); this.ebitenSurfaceView.onPause();
try {
Ebitenmobileview.suspend(); Ebitenmobileview.suspend();
} catch (final Exception e) {
onErrorOnGameUpdate(e);
}
} }
// resumeGame resumes the game. // resumeGame resumes the game.
@ -698,7 +710,11 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
public void resumeGame() { public void resumeGame() {
this.inputManager.registerInputDeviceListener(this, null); this.inputManager.registerInputDeviceListener(this, null);
this.ebitenSurfaceView.onResume(); this.ebitenSurfaceView.onResume();
try {
Ebitenmobileview.resume(); Ebitenmobileview.resume();
} catch (final Exception e) {
onErrorOnGameUpdate(e);
}
} }
// onErrorOnGameUpdate is called on the main thread when an error happens when updating a game. // 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

@ -44,42 +44,44 @@ func RunBeforeUpdateHooks() error {
var ( var (
audioSuspended bool audioSuspended bool
onSuspendAudio func() onSuspendAudio func() error
onResumeAudio func() onResumeAudio func() error
) )
func OnSuspendAudio(f func()) { func OnSuspendAudio(f func() error) {
m.Lock() m.Lock()
onSuspendAudio = f onSuspendAudio = f
m.Unlock() m.Unlock()
} }
func OnResumeAudio(f func()) { func OnResumeAudio(f func() error) {
m.Lock() m.Lock()
onResumeAudio = f onResumeAudio = f
m.Unlock() m.Unlock()
} }
func SuspendAudio() { func SuspendAudio() error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
if audioSuspended { if audioSuspended {
return return nil
} }
audioSuspended = true audioSuspended = true
if onSuspendAudio != nil { if onSuspendAudio != nil {
onSuspendAudio() return onSuspendAudio()
} }
return nil
} }
func ResumeAudio() { func ResumeAudio() error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
if !audioSuspended { if !audioSuspended {
return return nil
} }
audioSuspended = false audioSuspended = false
if onResumeAudio != nil { if onResumeAudio != nil {
onResumeAudio() return onResumeAudio()
} }
return nil
} }

View File

@ -916,12 +916,16 @@ func (u *UserInterface) update() (float64, float64, bool, error) {
u.input.update(u.window, u.context) u.input.update(u.window, u.context)
for !u.isRunnableOnUnfocused() && u.window.GetAttrib(glfw.Focused) == 0 && !u.window.ShouldClose() { for !u.isRunnableOnUnfocused() && u.window.GetAttrib(glfw.Focused) == 0 && !u.window.ShouldClose() {
hooks.SuspendAudio() if err := hooks.SuspendAudio(); err != nil {
return 0, 0, false, err
}
// Wait for an arbitrary period to avoid busy loop. // Wait for an arbitrary period to avoid busy loop.
time.Sleep(time.Second / 60) time.Sleep(time.Second / 60)
glfw.PollEvents() glfw.PollEvents()
} }
hooks.ResumeAudio() if err := hooks.ResumeAudio(); err != nil {
return 0, 0, false, err
}
return outsideWidth, outsideHeight, outsideSizeChanged, nil return outsideWidth, outsideHeight, outsideSizeChanged, nil
} }

View File

@ -258,10 +258,11 @@ func (u *UserInterface) isFocused() bool {
func (u *UserInterface) update() error { func (u *UserInterface) update() error {
if u.suspended() { if u.suspended() {
hooks.SuspendAudio() return hooks.SuspendAudio()
return nil }
if err := hooks.ResumeAudio(); err != nil {
return err
} }
hooks.ResumeAudio()
return u.updateImpl(false) return u.updateImpl(false)
} }
@ -284,7 +285,7 @@ func (u *UserInterface) updateImpl(force bool) error {
func (u *UserInterface) loop(context driver.UIContext) <-chan error { func (u *UserInterface) loop(context driver.UIContext) <-chan error {
u.context = context u.context = context
errCh := make(chan error) errCh := make(chan error, 1)
reqStopAudioCh := make(chan struct{}) reqStopAudioCh := make(chan struct{})
resStopAudioCh := make(chan struct{}) resStopAudioCh := make(chan struct{})
@ -300,7 +301,6 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
<-resStopAudioCh <-resStopAudioCh
errCh <- err errCh <- err
close(errCh)
return return
} }
if u.vsync { if u.vsync {
@ -344,9 +344,15 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
select { select {
case <-t.C: case <-t.C:
if u.suspended() { if u.suspended() {
hooks.SuspendAudio() if err := hooks.SuspendAudio(); err != nil {
errCh <- err
return
}
} else { } else {
hooks.ResumeAudio() if err := hooks.ResumeAudio(); err != nil {
errCh <- err
return
}
} }
case <-reqStopAudioCh: case <-reqStopAudioCh:
return return

View File

@ -136,7 +136,10 @@ func (u *UserInterface) appMain(a app.App) {
case lifecycle.Event: case lifecycle.Event:
switch e.Crosses(lifecycle.StageVisible) { switch e.Crosses(lifecycle.StageVisible) {
case lifecycle.CrossOn: case lifecycle.CrossOn:
u.SetForeground(true) if err := u.SetForeground(true); err != nil {
// There are no other ways than panicking here.
panic(err)
}
restorable.OnContextLost() restorable.OnContextLost()
glctx, _ = e.DrawContext.(gl.Context) glctx, _ = e.DrawContext.(gl.Context)
// Assume that glctx is always a same instance. // Assume that glctx is always a same instance.
@ -147,7 +150,10 @@ func (u *UserInterface) appMain(a app.App) {
} }
a.Send(paint.Event{}) a.Send(paint.Event{})
case lifecycle.CrossOff: case lifecycle.CrossOff:
u.SetForeground(false) if err := u.SetForeground(false); err != nil {
// There are no other ways than panicking here.
panic(err)
}
glctx = nil glctx = nil
} }
case size.Event: case size.Event:
@ -213,7 +219,7 @@ func (u *UserInterface) appMain(a app.App) {
} }
} }
func (u *UserInterface) SetForeground(foreground bool) { func (u *UserInterface) SetForeground(foreground bool) error {
var v int32 var v int32
if foreground { if foreground {
v = 1 v = 1
@ -221,9 +227,9 @@ func (u *UserInterface) SetForeground(foreground bool) {
atomic.StoreInt32(&u.foreground, v) atomic.StoreInt32(&u.foreground, v)
if foreground { if foreground {
hooks.ResumeAudio() return hooks.ResumeAudio()
} else { } else {
hooks.SuspendAudio() return hooks.SuspendAudio()
} }
} }

View File

@ -75,12 +75,12 @@ func Update() error {
return mobile.Get().Update() return mobile.Get().Update()
} }
func Suspend() { func Suspend() error {
mobile.Get().SetForeground(false) return mobile.Get().SetForeground(false)
} }
func Resume() { func Resume() error {
mobile.Get().SetForeground(true) return mobile.Get().SetForeground(true)
} }
func OnContextLost() { func OnContextLost() {