uidriver/js: Bug fix: Watch the state regularly instead of events

visibilitystate is sometimes not called and in this case the app
does not come back.

This fix creates another goroutine to watch the state, and remove
event handlers.

Fixes #961
This commit is contained in:
Hajime Hoshi 2019-10-21 00:02:35 +09:00
parent 016a9a6923
commit e7611139c3

View File

@ -22,13 +22,12 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"syscall/js" "syscall/js"
"time"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
) )
var canvas js.Value
type UserInterface struct { type UserInterface struct {
width int width int
height int height int
@ -37,8 +36,6 @@ type UserInterface struct {
vsync bool vsync bool
sizeChanged bool sizeChanged bool
windowFocus bool
pageVisible bool
contextLost bool contextLost bool
lastActualScale float64 lastActualScale float64
@ -53,8 +50,6 @@ type UserInterface struct {
var theUI = &UserInterface{ var theUI = &UserInterface{
sizeChanged: true, sizeChanged: true,
windowFocus: true,
pageVisible: true,
vsync: true, vsync: true,
} }
@ -69,6 +64,7 @@ func Get() *UserInterface {
var ( var (
window = js.Global().Get("window") window = js.Global().Get("window")
document = js.Global().Get("document") document = js.Global().Get("document")
canvas js.Value
requestAnimationFrame = window.Get("requestAnimationFrame") requestAnimationFrame = window.Get("requestAnimationFrame")
setTimeout = window.Get("setTimeout") setTimeout = window.Get("setTimeout")
) )
@ -189,7 +185,17 @@ func (u *UserInterface) updateSize() {
} }
func (u *UserInterface) suspended() bool { func (u *UserInterface) suspended() bool {
return !u.runnableInBackground && (!u.windowFocus || !u.pageVisible) if u.runnableInBackground {
return false
}
if !document.Call("hasFocus").Bool() {
return true
}
if document.Get("hidden").Bool() {
return true
}
return false
} }
func (u *UserInterface) update() error { func (u *UserInterface) update() error {
@ -210,7 +216,7 @@ func (u *UserInterface) update() error {
} }
func (u *UserInterface) loop(context driver.UIContext) <-chan error { func (u *UserInterface) loop(context driver.UIContext) <-chan error {
u.init(context) u.context = context
ch := make(chan error) ch := make(chan error)
var cf js.Func var cf js.Func
@ -238,38 +244,23 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
go func() { go func() {
f(js.Value{}, nil) f(js.Value{}, nil)
}() }()
return ch
}
func (u *UserInterface) init(context driver.UIContext) { // Run another loop to watch suspended() as the above update function is never called when the tab is hidden.
u.context = context // To check the document's visiblity, visibilitychange event should usually be used. However, this event is
window.Call("addEventListener", "focus", js.FuncOf(func(this js.Value, args []js.Value) interface{} { // not reliable and sometimes it is not fired (#961). Then, watch the state regularly instead.
u.windowFocus = true go func() {
if u.suspended() { t := time.NewTicker(100 * time.Millisecond)
u.context.SuspendAudio() defer t.Stop()
} else { for range t.C {
u.context.ResumeAudio() if u.suspended() {
u.context.SuspendAudio()
} else {
u.context.ResumeAudio()
}
} }
return nil }()
}))
window.Call("addEventListener", "blur", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return ch
u.windowFocus = false
if u.suspended() {
u.context.SuspendAudio()
} else {
u.context.ResumeAudio()
}
return nil
}))
document.Call("addEventListener", "visibilitychange", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
u.pageVisible = !document.Get("hidden").Bool()
if u.suspended() {
u.context.SuspendAudio()
} else {
u.context.ResumeAudio()
}
return nil
}))
} }
func init() { func init() {