diff --git a/internal/ui/mainloop_gomobilebuild.go b/internal/ui/mainloop_gomobilebuild.go deleted file mode 100644 index afeba8fc8..000000000 --- a/internal/ui/mainloop_gomobilebuild.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 The Ebiten Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build android ios -// +build gomobilebuild - -package ui - -import ( - "golang.org/x/mobile/app" - "golang.org/x/mobile/event/lifecycle" - "golang.org/x/mobile/event/paint" - "golang.org/x/mobile/event/size" - "golang.org/x/mobile/event/touch" - "golang.org/x/mobile/gl" - - "github.com/hajimehoshi/ebiten/internal/devicescale" - "github.com/hajimehoshi/ebiten/internal/input" - "github.com/hajimehoshi/ebiten/internal/opengl" -) - -var ( - glContextCh chan gl.Context -) - -func appMain(a app.App) { - var glctx gl.Context - touches := map[touch.Sequence]*input.Touch{} - for e := range a.Events() { - switch e := a.Filter(e).(type) { - case lifecycle.Event: - switch e.Crosses(lifecycle.StageVisible) { - case lifecycle.CrossOn: - glctx, _ = e.DrawContext.(gl.Context) - // Assume that glctx is always a same instance. - // Then, only once initializing should be enough. - if glContextCh != nil { - glContextCh <- glctx - glContextCh = nil - } - a.Send(paint.Event{}) - case lifecycle.CrossOff: - glctx = nil - } - case size.Event: - setFullscreen(e.WidthPx, e.HeightPx) - case paint.Event: - if glctx == nil || e.External { - continue - } - chRender <- struct{}{} - <-chRenderEnd - a.Publish() - a.Send(paint.Event{}) - case touch.Event: - switch e.Type { - case touch.TypeBegin, touch.TypeMove: - s := devicescale.DeviceScale() - x, y := float64(e.X)/s, float64(e.Y)/s - // TODO: Is it ok to cast from int64 to int here? - t := input.NewTouch(int(e.Sequence), int(x), int(y)) - touches[e.Sequence] = t - case touch.TypeEnd: - delete(touches, e.Sequence) - } - ts := []*input.Touch{} - for _, t := range touches { - ts = append(ts, t) - } - UpdateTouches(ts) - } - } -} - -func RunMainThreadLoop(ch <-chan error) error { - glContextCh = make(chan gl.Context) - app.Main(appMain) - return nil -} - -func initOpenGL() { - ctx := <-glContextCh - opengl.InitWithContext(ctx) -} diff --git a/internal/ui/mainloop_notgomobilebuild.go b/internal/ui/mainloop_notgomobilebuild.go deleted file mode 100644 index e258f013e..000000000 --- a/internal/ui/mainloop_notgomobilebuild.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 The Ebiten Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build android ios -// +build !gomobilebuild - -package ui - -import ( - "errors" - - "github.com/hajimehoshi/ebiten/internal/opengl" -) - -func RunMainThreadLoop(ch <-chan error) error { - return errors.New("ui: don't call this: use RunWithoutMainLoop instead of Run") -} - -func initOpenGL() { - opengl.Init() -} diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 1474acf10..c475380ac 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -434,7 +434,7 @@ func SetWindowDecorated(decorated bool) { // return nil } -func Run(width, height int, scale float64, title string, g GraphicsContext) error { +func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error { <-currentUIInitialized u := currentUI diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index e97041baf..7c545a29d 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -302,7 +302,7 @@ func RunMainThreadLoop(ch <-chan error) error { return <-ch } -func Run(width, height int, scale float64, title string, g GraphicsContext) error { +func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error { u := currentUI doc := js.Global.Get("document") doc.Set("title", title) diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 01c2e390c..b97718614 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -23,11 +23,25 @@ import ( "sync" "time" + "golang.org/x/mobile/app" + "golang.org/x/mobile/event/lifecycle" + "golang.org/x/mobile/event/paint" + "golang.org/x/mobile/event/size" + "golang.org/x/mobile/event/touch" + "golang.org/x/mobile/gl" + "github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/input" "github.com/hajimehoshi/ebiten/internal/opengl" ) +var ( + glContextCh = make(chan gl.Context) + renderCh = make(chan struct{}) + renderChEnd = make(chan struct{}) + currentUI = &userInterface{} +) + func Render(chError <-chan error) error { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -37,8 +51,8 @@ func Render(chError <-chan error) error { } // TODO: Check this is called on the rendering thread select { - case chRender <- struct{}{}: - return opengl.GetContext().DoWork(chError, chRenderEnd) + case renderCh <- struct{}{}: + return opengl.GetContext().DoWork(chError, renderChEnd) case <-time.After(500 * time.Millisecond): // This function must not be blocked. We need to break for timeout. return nil @@ -59,13 +73,57 @@ type userInterface struct { m sync.RWMutex } -var ( - chRender = make(chan struct{}) - chRenderEnd = make(chan struct{}) - currentUI = &userInterface{} -) +// appMain is the main routine for gomobile-build mode. +func appMain(a app.App) { + var glctx gl.Context + touches := map[touch.Sequence]*input.Touch{} + for e := range a.Events() { + switch e := a.Filter(e).(type) { + case lifecycle.Event: + switch e.Crosses(lifecycle.StageVisible) { + case lifecycle.CrossOn: + glctx, _ = e.DrawContext.(gl.Context) + // Assume that glctx is always a same instance. + // Then, only once initializing should be enough. + if glContextCh != nil { + glContextCh <- glctx + glContextCh = nil + } + a.Send(paint.Event{}) + case lifecycle.CrossOff: + glctx = nil + } + case size.Event: + setFullscreen(e.WidthPx, e.HeightPx) + case paint.Event: + if glctx == nil || e.External { + continue + } + renderCh <- struct{}{} + <-renderChEnd + a.Publish() + a.Send(paint.Event{}) + case touch.Event: + switch e.Type { + case touch.TypeBegin, touch.TypeMove: + s := devicescale.DeviceScale() + x, y := float64(e.X)/s, float64(e.Y)/s + // TODO: Is it ok to cast from int64 to int here? + t := input.NewTouch(int(e.Sequence), int(x), int(y)) + touches[e.Sequence] = t + case touch.TypeEnd: + delete(touches, e.Sequence) + } + ts := []*input.Touch{} + for _, t := range touches { + ts = append(ts, t) + } + UpdateTouches(ts) + } + } +} -func Run(width, height int, scale float64, title string, g GraphicsContext) error { +func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error { u := currentUI u.m.Lock() @@ -76,7 +134,12 @@ func Run(width, height int, scale float64, title string, g GraphicsContext) erro u.m.Unlock() // title is ignored? - initOpenGL() + if mainloop { + ctx := <-glContextCh + opengl.InitWithContext(ctx) + } else { + opengl.Init() + } for { if err := u.update(g); err != nil { @@ -85,6 +148,13 @@ func Run(width, height int, scale float64, title string, g GraphicsContext) erro } } +// RunMainThreadLoop runs the main routine for gomobile-build. +func RunMainThreadLoop(ch <-chan error) error { + // TODO: ch should be used + app.Main(appMain) + return nil +} + func (u *userInterface) updateGraphicsContext(g GraphicsContext) { sizeChanged := false width, height := 0, 0 @@ -126,9 +196,9 @@ func (u *userInterface) scaleImpl() float64 { } func (u *userInterface) update(g GraphicsContext) error { - <-chRender + <-renderCh defer func() { - chRenderEnd <- struct{}{} + renderChEnd <- struct{}{} }() u.updateGraphicsContext(g) diff --git a/run.go b/run.go index 6158dd2b9..338c3b10f 100644 --- a/run.go +++ b/run.go @@ -78,8 +78,8 @@ func IsRunningSlowly() bool { var theGraphicsContext atomic.Value -func run(width, height int, scale float64, title string, g *graphicsContext) error { - if err := ui.Run(width, height, scale, title, g); err != nil { +func run(width, height int, scale float64, title string, g *graphicsContext, mainloop bool) error { + if err := ui.Run(width, height, scale, title, g, mainloop); err != nil { if err == ui.RegularTermination { return nil } @@ -124,7 +124,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e g := newGraphicsContext(f) theGraphicsContext.Store(g) - if err := run(width, height, scale, title, g); err != nil { + if err := run(width, height, scale, title, g, true); err != nil { ch <- err return } @@ -148,7 +148,7 @@ func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64, g := newGraphicsContext(f) theGraphicsContext.Store(g) - if err := run(width, height, scale, title, g); err != nil { + if err := run(width, height, scale, title, g, false); err != nil { ch <- err return }