From f0d47312c40d23657c7684293d054d2b45d69201 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 2 Aug 2017 23:37:50 +0900 Subject: [PATCH] ui: Add IsRunnableInBackground / SetRunnableInBackground (#272) --- examples/audio/main.go | 7 ++++- examples/windowsize/main.go | 14 ++++++++++ internal/ui/ui_glfw.go | 56 ++++++++++++++++++++++++++----------- internal/ui/ui_js.go | 19 +++++++++---- internal/ui/ui_mobile.go | 12 ++++++-- run.go | 22 +++++++++++++++ 6 files changed, 105 insertions(+), 25 deletions(-) diff --git a/examples/audio/main.go b/examples/audio/main.go index 248f77b8a..b72e8ea4a 100644 --- a/examples/audio/main.go +++ b/examples/audio/main.go @@ -58,7 +58,7 @@ type Input struct { } func (i *Input) update() { - for _, key := range []ebiten.Key{ebiten.KeyP, ebiten.KeyS, ebiten.KeyX, ebiten.KeyZ} { + for _, key := range []ebiten.Key{ebiten.KeyP, ebiten.KeyS, ebiten.KeyX, ebiten.KeyZ, ebiten.KeyB} { if !ebiten.IsKeyPressed(key) { i.keyStates[key] = 0 } else { @@ -170,6 +170,10 @@ func (p *Player) update() error { p.updatePlayPause() p.updateSE() p.updateVolume() + if p.input.isKeyTriggered(ebiten.KeyB) { + b := ebiten.IsRunnableInBackground() + ebiten.SetRunnableInBackground(!b) + } if err := p.audioContext.Update(); err != nil { return err } @@ -262,6 +266,7 @@ func (p *Player) draw(screen *ebiten.Image) { Press S to toggle Play/Pause Press P to play SE Press Z or X to change volume of the music +Press B to switch the run-in-background state %s`, ebiten.CurrentFPS(), currentTimeStr) ebitenutil.DebugPrint(screen, msg) } diff --git a/examples/windowsize/main.go b/examples/windowsize/main.go index e5b4d9655..ff8821414 100644 --- a/examples/windowsize/main.go +++ b/examples/windowsize/main.go @@ -21,6 +21,7 @@ import ( "image/color" _ "image/jpeg" "log" + "math" "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/ebitenutil" @@ -41,7 +42,9 @@ var ( ebiten.KeyRight: 0, ebiten.KeyS: 0, ebiten.KeyF: 0, + ebiten.KeyB: 0, } + count = 0 ) func update(screen *ebiten.Image) error { @@ -56,6 +59,7 @@ func update(screen *ebiten.Image) error { d := int(32 / screenScale) screenWidth, screenHeight := screen.Size() fullscreen := ebiten.IsFullscreen() + runnableInBackground := ebiten.IsRunnableInBackground() if keyStates[ebiten.KeyUp] == 1 { screenHeight += d @@ -88,9 +92,15 @@ func update(screen *ebiten.Image) error { if keyStates[ebiten.KeyF] == 1 { fullscreen = !fullscreen } + if keyStates[ebiten.KeyB] == 1 { + runnableInBackground = !runnableInBackground + } ebiten.SetScreenSize(screenWidth, screenHeight) ebiten.SetScreenScale(screenScale) ebiten.SetFullscreen(fullscreen) + ebiten.SetRunnableInBackground(runnableInBackground) + + count++ if ebiten.IsRunningSlowly() { return nil @@ -101,12 +111,16 @@ func update(screen *ebiten.Image) error { w2, h2 := screen.Size() op := &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(-w+w2)/2, float64(-h+h2)/2) + dx := math.Cos(2*math.Pi*float64(count)/360) * 10 + dy := math.Sin(2*math.Pi*float64(count)/360) * 10 + op.GeoM.Translate(dx, dy) screen.DrawImage(gophersImage, op) x, y := ebiten.CursorPosition() msg := fmt.Sprintf(`Press arrow keys to change the window size Press S key to change the window scale Press F key to change the fullscreen state +Press B key to change the run-in-background state Cursor: (%d, %d) FPS: %0.2f`, x, y, ebiten.CurrentFPS()) ebitenutil.DebugPrint(screen, msg) diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 20ba44dfe..7ed6d19ae 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -30,22 +30,23 @@ import ( ) type userInterface struct { - title string - window *glfw.Window - width int - height int - scale float64 - deviceScale float64 - glfwScale float64 - fullscreen bool - fullscreenScale float64 - funcs chan func() - running bool - sizeChanged bool - origPosX int - origPosY int - initFullscreen bool - m sync.Mutex + title string + window *glfw.Window + width int + height int + scale float64 + deviceScale float64 + glfwScale float64 + fullscreen bool + fullscreenScale float64 + funcs chan func() + running bool + sizeChanged bool + origPosX int + origPosY int + initFullscreen bool + runnableInBackground bool + m sync.Mutex } var ( @@ -133,6 +134,19 @@ func (u *userInterface) setInitFullscreen(initFullscreen bool) { u.m.Unlock() } +func (u *userInterface) isRunnableInBackground() bool { + u.m.Lock() + v := u.runnableInBackground + u.m.Unlock() + return v +} + +func (u *userInterface) setRunnableInBackground(runnableInBackground bool) { + u.m.Lock() + u.runnableInBackground = runnableInBackground + u.m.Unlock() +} + func (u *userInterface) runOnMainThread(f func() error) error { if u.funcs == nil { // already closed @@ -213,6 +227,14 @@ func SetFullscreen(fullscreen bool) { }) } +func SetRunnableInBackground(runnableInBackground bool) { + currentUI.setRunnableInBackground(runnableInBackground) +} + +func IsRunnableInBackground() bool { + return currentUI.isRunnableInBackground() +} + func ScreenOffset() (float64, float64) { u := currentUI if !u.isRunning() { @@ -372,7 +394,7 @@ func (u *userInterface) update(g GraphicsContext) error { _ = u.runOnMainThread(func() error { u.pollEvents() - for u.window.GetAttrib(glfw.Focused) == 0 { + for !u.isRunnableInBackground() && u.window.GetAttrib(glfw.Focused) == 0 { // Wait for an arbitrary period to avoid busy loop. time.Sleep(time.Second / 60) u.pollEvents() diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index d8946c240..e76a8d549 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -27,10 +27,11 @@ import ( var canvas *js.Object type userInterface struct { - width int - height int - scale float64 - fullscreen bool + width int + height int + scale float64 + fullscreen bool + runnableInBackground bool deviceScale float64 sizeChanged bool @@ -67,6 +68,14 @@ func IsFullscreen() bool { return currentUI.fullscreen } +func SetRunnableInBackground(runnableInBackground bool) { + currentUI.runnableInBackground = runnableInBackground +} + +func IsRunnableInBackground() bool { + return currentUI.runnableInBackground +} + func ScreenOffset() (float64, float64) { return 0, 0 } @@ -104,7 +113,7 @@ func (u *userInterface) actualScreenScale() float64 { } func (u *userInterface) update(g GraphicsContext) error { - if !u.windowFocus { + if !u.runnableInBackground && !u.windowFocus { return nil } if opengl.GetContext().IsContextLost() { diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 454e939ea..a943f04dd 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -118,12 +118,20 @@ func SetCursorVisibility(visibility bool) { // Do nothing } -func SetFullscreen(fullscreen bool) bool { +func SetFullscreen(fullscreen bool) { + // Do nothing +} + +func IsFullscreen() bool { // Do nothing return false } -func IsFullscreen() bool { +func SetRunnableInBackground(runnableInBackground bool) { + // Do nothing +} + +func IsRunnableInBackground() bool { // Do nothing return false } diff --git a/run.go b/run.go index 4ac482c68..197c92139 100644 --- a/run.go +++ b/run.go @@ -178,3 +178,25 @@ func IsFullscreen() bool { func SetFullscreen(fullscreen bool) { ui.SetFullscreen(fullscreen) } + +// IsRunnableInBackground returns a boolean value indicating whether the game runs even in background. +// +// This function is concurrent-safe. +func IsRunnableInBackground() bool { + return ui.IsRunnableInBackground() +} + +// SetRunnableInBackground sets the state if the game runs even in background. +// +// If the given value is true, the game runs in background e.g. when losing focus. +// The initial state is false. +// +// Known issue: On browsers, even if the state is on, the game doesn't run in background tabs. +// This is because browsers throttles background tabs not to often update. +// +// SetRunnableInBackground doesn't work on mobiles so far. +// +// This function is concurrent-safe. +func SetRunnableInBackground(runnableInBackground bool) { + ui.SetRunnableInBackground(runnableInBackground) +}