ui: Introduce ScreenSizeEvent to simplify the run loop

This commit is contained in:
Hajime Hoshi 2016-05-18 11:56:43 +09:00
parent faff188574
commit 335781759c
5 changed files with 64 additions and 103 deletions

View File

@ -15,7 +15,6 @@
package loop package loop
import ( import (
"errors"
"sync" "sync"
"time" "time"
@ -24,10 +23,6 @@ import (
const FPS = 60 const FPS = 60
func Main() {
ui.Main()
}
func CurrentFPS() float64 { func CurrentFPS() float64 {
return currentRunContext.currentFPS() return currentRunContext.currentFPS()
} }
@ -40,24 +35,9 @@ func IsRunningSlowly() bool {
return currentRunContext.isRunningSlowly() return currentRunContext.isRunningSlowly()
} }
func SetScreenSize(width, height int) error {
return currentRunContext.setScreenSize(width, height)
}
func SetScreenScale(scale int) error {
return currentRunContext.setScreenScale(scale)
}
func ScreenScale() int {
return ui.CurrentUI().ScreenScale()
}
type runContext struct { type runContext struct {
running bool running bool
fps float64 fps float64
newScreenWidth int
newScreenHeight int
newScreenScale int
runningSlowly bool runningSlowly bool
m sync.RWMutex m sync.RWMutex
} }
@ -114,60 +94,6 @@ func (c *runContext) setRunningSlowly(isRunningSlowly bool) {
c.runningSlowly = isRunningSlowly c.runningSlowly = isRunningSlowly
} }
func (c *runContext) updateScreenSize(g GraphicsContext) error {
c.m.Lock()
defer c.m.Unlock()
if c.newScreenWidth == 0 && c.newScreenHeight == 0 && c.newScreenScale == 0 {
return nil
}
changed := false
if 0 < c.newScreenWidth || 0 < c.newScreenHeight {
c := ui.CurrentUI().SetScreenSize(c.newScreenWidth, c.newScreenHeight)
changed = changed || c
}
if 0 < c.newScreenScale {
c := ui.CurrentUI().SetScreenScale(c.newScreenScale)
changed = changed || c
}
if changed {
w, h := c.newScreenWidth, c.newScreenHeight
if err := g.SetSize(w, h, ui.CurrentUI().ActualScreenScale()); err != nil {
return err
}
}
c.newScreenWidth = 0
c.newScreenHeight = 0
c.newScreenScale = 0
return nil
}
func (c *runContext) setScreenSize(width, height int) error {
c.m.Lock()
defer c.m.Unlock()
if !c.running {
return errors.New("ebiten: SetScreenSize must be called during Run")
}
if width <= 0 || height <= 0 {
return errors.New("ebiten: width and height must be positive")
}
c.newScreenWidth = width
c.newScreenHeight = height
return nil
}
func (c *runContext) setScreenScale(scale int) error {
c.m.Lock()
defer c.m.Unlock()
if !c.running {
return errors.New("ebiten: SetScreenScale must be called during Run")
}
if scale <= 0 {
return errors.New("ebiten: scale must be positive")
}
c.newScreenScale = scale
return nil
}
type GraphicsContext interface { type GraphicsContext interface {
SetSize(width, height, scale int) error SetSize(width, height, scale int) error
Update() error Update() error
@ -182,23 +108,20 @@ func Run(g GraphicsContext, width, height, scale int, title string) error {
} }
defer ui.CurrentUI().Terminate() defer ui.CurrentUI().Terminate()
if err := g.SetSize(width, height, ui.CurrentUI().ActualScreenScale()); err != nil {
return err
}
frames := 0 frames := 0
n := now() n := now()
beforeForUpdate := n beforeForUpdate := n
beforeForFPS := n beforeForFPS := n
for { for {
if err := currentRunContext.updateScreenSize(g); err != nil {
return err
}
e, err := ui.CurrentUI().Update() e, err := ui.CurrentUI().Update()
if err != nil { if err != nil {
return err return err
} }
switch e.(type) { switch e := e.(type) {
case ui.ScreenSizeEvent:
if err := g.SetSize(e.Width, e.Height, e.ActualScale); err != nil {
return err
}
case ui.CloseEvent: case ui.CloseEvent:
return nil return nil
case ui.RenderEvent: case ui.RenderEvent:

View File

@ -19,3 +19,10 @@ type CloseEvent struct {
type RenderEvent struct { type RenderEvent struct {
} }
type ScreenSizeEvent struct {
Width int
Height int
Scale int
ActualScale int
}

View File

@ -35,6 +35,7 @@ type UserInterface struct {
framebufferScale int framebufferScale int
context *opengl.Context context *opengl.Context
funcs chan func() funcs chan func()
sizeChanged bool
} }
var currentUI *UserInterface var currentUI *UserInterface
@ -63,6 +64,7 @@ func initialize() (*opengl.Context, error) {
u := &UserInterface{ u := &UserInterface{
window: window, window: window,
funcs: make(chan func()), funcs: make(chan func()),
sizeChanged: true,
} }
ch := make(chan error) ch := make(chan error)
go func() { go func() {
@ -140,14 +142,6 @@ func (u *UserInterface) ScreenScale() int {
return s return s
} }
func (u *UserInterface) ActualScreenScale() int {
s := 0
u.runOnMainThread(func() {
s = u.actualScreenScale()
})
return s
}
func (u *UserInterface) Start(width, height, scale int, title string) error { func (u *UserInterface) Start(width, height, scale int, title string) error {
var err error var err error
u.runOnMainThread(func() { u.runOnMainThread(func() {
@ -200,6 +194,24 @@ func (u *UserInterface) Update() (interface{}, error) {
if shouldClose { if shouldClose {
return CloseEvent{}, nil return CloseEvent{}, nil
} }
var screenSizeEvent *ScreenSizeEvent
u.runOnMainThread(func() {
if !u.sizeChanged {
return
}
u.sizeChanged = false
screenSizeEvent = &ScreenSizeEvent{
Width: u.width,
Height: u.height,
Scale: u.scale,
ActualScale: u.actualScreenScale(),
}
})
if screenSizeEvent != nil {
return *screenSizeEvent, nil
}
var ferr error var ferr error
u.runOnMainThread(func() { u.runOnMainThread(func() {
if err := u.pollEvents(); err != nil { if err := u.pollEvents(); err != nil {
@ -291,5 +303,6 @@ event:
// This is usually 1, but sometimes more than 1 (e.g. Retina Mac) // This is usually 1, but sometimes more than 1 (e.g. Retina Mac)
fw, _ := window.GetFramebufferSize() fw, _ := window.GetFramebufferSize()
u.framebufferScale = fw / width / u.windowScale() u.framebufferScale = fw / width / u.windowScale()
u.sizeChanged = true
return true return true
} }

View File

@ -46,9 +46,12 @@ var canvas *js.Object
type UserInterface struct { type UserInterface struct {
scale int scale int
deviceScale float64 deviceScale float64
sizeChanged bool
} }
var currentUI = &UserInterface{} var currentUI = &UserInterface{
sizeChanged: true,
}
func CurrentUI() *UserInterface { func CurrentUI() *UserInterface {
return currentUI return currentUI
@ -79,6 +82,17 @@ func vsync() {
func (u *UserInterface) Update() (interface{}, error) { func (u *UserInterface) Update() (interface{}, error) {
currentInput.UpdateGamepads() currentInput.UpdateGamepads()
if u.sizeChanged {
u.sizeChanged = false
w, h := u.size()
e := ScreenSizeEvent{
Width: w,
Height: h,
Scale: u.ScreenScale(),
ActualScale: u.ActualScreenScale(),
}
return e, nil
}
return RenderEvent{}, nil return RenderEvent{}, nil
} }
@ -276,5 +290,6 @@ func (u *UserInterface) setScreenSize(width, height, scale int) bool {
// CSS calc requires space chars. // CSS calc requires space chars.
canvasStyle.Set("left", "calc((100% - "+strconv.Itoa(cssWidth)+"px) / 2)") canvasStyle.Set("left", "calc((100% - "+strconv.Itoa(cssWidth)+"px) / 2)")
canvasStyle.Set("top", "calc((100% - "+strconv.Itoa(cssHeight)+"px) / 2)") canvasStyle.Set("top", "calc((100% - "+strconv.Itoa(cssHeight)+"px) / 2)")
u.sizeChanged = true
return true return true
} }

15
run.go
View File

@ -16,6 +16,7 @@ package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/loop" "github.com/hajimehoshi/ebiten/internal/loop"
"github.com/hajimehoshi/ebiten/internal/ui"
) )
// FPS represents how many times game updating happens in a second. // FPS represents how many times game updating happens in a second.
@ -58,7 +59,7 @@ func Run(f func(*Image) error, width, height, scale int, title string) error {
g := newGraphicsContext(f) g := newGraphicsContext(f)
ch <- loop.Run(g, width, height, scale, title) ch <- loop.Run(g, width, height, scale, title)
}() }()
loop.Main() ui.Main()
return <-ch return <-ch
} }
@ -67,23 +68,25 @@ func Run(f func(*Image) error, width, height, scale int, title string) error {
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func SetScreenSize(width, height int) { func SetScreenSize(width, height int) {
if err := loop.SetScreenSize(width, height); err != nil { if width <= 0 || height <= 0 {
panic(err) panic("ebiten: width and height must be positive")
} }
ui.CurrentUI().SetScreenSize(width, height)
} }
// SetScreenScale changes the scale of the screen. // SetScreenScale changes the scale of the screen.
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func SetScreenScale(scale int) { func SetScreenScale(scale int) {
if err := loop.SetScreenScale(scale); err != nil { if scale <= 0 {
panic(err) panic("ebiten: scale must be positive")
} }
ui.CurrentUI().SetScreenScale(scale)
} }
// ScreenScale returns the current screen scale. // ScreenScale returns the current screen scale.
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func ScreenScale() int { func ScreenScale() int {
return loop.ScreenScale() return ui.CurrentUI().ScreenScale()
} }