ui: Add Main for the OS main thread

This commit is contained in:
Hajime Hoshi 2016-05-06 12:23:48 +09:00
parent f7211f3391
commit 209b4e6864
3 changed files with 114 additions and 59 deletions

View File

@ -19,7 +19,6 @@ package ui
import ( import (
"errors" "errors"
"runtime" "runtime"
"sync"
"time" "time"
"github.com/go-gl/glfw/v3.1/glfw" "github.com/go-gl/glfw/v3.1/glfw"
@ -38,7 +37,7 @@ type UserInterface struct {
deviceScale float64 deviceScale float64
framebufferScale int framebufferScale int
context *opengl.Context context *opengl.Context
m sync.RWMutex funcs chan func()
} }
var currentUI *UserInterface var currentUI *UserInterface
@ -67,6 +66,7 @@ func Init() (*opengl.Context, error) {
u := &UserInterface{ u := &UserInterface{
window: window, window: window,
funcs: make(chan func()),
} }
ch := make(chan error) ch := make(chan error)
go func() { go func() {
@ -92,57 +92,91 @@ func Init() (*opengl.Context, error) {
return u.context, nil return u.context, nil
} }
func Main() error {
return CurrentUI().main()
}
func (u *UserInterface) main() error {
// TODO: Check this is done on the main thread.
for f := range u.funcs {
f()
}
return nil
}
func (u *UserInterface) runOnMainThread(f func()) {
if u.funcs == nil {
// already closed
return
}
ch := make(chan struct{})
u.funcs <- func() {
f()
close(ch)
}
<-ch
}
func (u *UserInterface) SetScreenSize(width, height int) bool { func (u *UserInterface) SetScreenSize(width, height int) bool {
u.m.Lock() r := false
defer u.m.Unlock() u.runOnMainThread(func() {
return u.setScreenSize(width, height, u.scale) r = u.setScreenSize(width, height, u.scale)
})
return r
} }
func (u *UserInterface) SetScreenScale(scale int) bool { func (u *UserInterface) SetScreenScale(scale int) bool {
u.m.Lock() r := false
defer u.m.Unlock() u.runOnMainThread(func() {
return u.setScreenSize(u.width, u.height, scale) r = u.setScreenSize(u.width, u.height, scale)
})
return r
} }
func (u *UserInterface) ScreenScale() int { func (u *UserInterface) ScreenScale() int {
u.m.RLock() s := 0
defer u.m.RUnlock() u.runOnMainThread(func() {
return u.scale s = u.scale
})
return s
} }
func (u *UserInterface) ActualScreenScale() int { func (u *UserInterface) ActualScreenScale() int {
u.m.RLock() s := 0
defer u.m.RUnlock() u.runOnMainThread(func() {
return u.actualScreenScale() 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 {
u.m.Lock() var ferr error
defer u.m.Unlock() u.runOnMainThread(func() {
m := glfw.GetPrimaryMonitor() m := glfw.GetPrimaryMonitor()
v := m.GetVideoMode() v := m.GetVideoMode()
mw, _ := m.GetPhysicalSize() mw, _ := m.GetPhysicalSize()
u.deviceScale = 1 u.deviceScale = 1
u.framebufferScale = 1 u.framebufferScale = 1
// mw can be 0 on some environment like Linux VM // mw can be 0 on some environment like Linux VM
if 0 < mw { if 0 < mw {
dpi := float64(v.Width) * 25.4 / float64(mw) dpi := float64(v.Width) * 25.4 / float64(mw)
u.deviceScale = dpi / 96 u.deviceScale = dpi / 96
if u.deviceScale < 1 { if u.deviceScale < 1 {
u.deviceScale = 1 u.deviceScale = 1
}
} }
}
if !u.setScreenSize(width, height, scale) { if !u.setScreenSize(width, height, scale) {
return errors.New("ui: Fail to set the screen size") ferr = errors.New("ui: Fail to set the screen size")
} return
u.window.SetTitle(title) }
u.window.Show() u.window.SetTitle(title)
u.window.Show()
x := (v.Width - width*u.windowScale()) / 2
y := (v.Height - height*u.windowScale()) / 3
u.window.SetPos(x, y)
x := (v.Width - width*u.windowScale()) / 2
y := (v.Height - height*u.windowScale()) / 3
u.window.SetPos(x, y)
})
return nil return nil
} }
@ -160,40 +194,47 @@ func (u *UserInterface) pollEvents() error {
} }
func (u *UserInterface) DoEvents() error { func (u *UserInterface) DoEvents() error {
u.m.Lock() var ferr error
defer u.m.Unlock() u.runOnMainThread(func() {
if err := u.pollEvents(); err != nil {
return err
}
for u.window.GetAttrib(glfw.Focused) == 0 {
// Wait for an arbitrary period to avoid busy loop.
time.Sleep(time.Second / 60)
if err := u.pollEvents(); err != nil { if err := u.pollEvents(); err != nil {
return err ferr = err
return
} }
if u.window.ShouldClose() { for u.window.GetAttrib(glfw.Focused) == 0 {
return nil // Wait for an arbitrary period to avoid busy loop.
time.Sleep(time.Second / 60)
if err := u.pollEvents(); err != nil {
ferr = err
return
}
if u.window.ShouldClose() {
return
}
} }
} })
return nil return ferr
} }
func (u *UserInterface) Terminate() { func (u *UserInterface) Terminate() {
u.m.Lock() u.runOnMainThread(func() {
defer u.m.Unlock() glfw.Terminate()
glfw.Terminate() })
close(u.funcs)
u.funcs = nil
} }
func (u *UserInterface) IsClosed() bool { func (u *UserInterface) IsClosed() bool {
u.m.RLock() r := false
defer u.m.RUnlock() u.runOnMainThread(func() {
return u.window.ShouldClose() r = u.window.ShouldClose()
})
return r
} }
func (u *UserInterface) SwapBuffers() { func (u *UserInterface) SwapBuffers() {
u.m.Lock() u.runOnMainThread(func() {
defer u.m.Unlock() u.swapBuffers()
u.swapBuffers() })
} }
func (u *UserInterface) swapBuffers() { func (u *UserInterface) swapBuffers() {

View File

@ -228,6 +228,11 @@ func devicePixelRatio() float64 {
return ratio return ratio
} }
func Main() error {
// Do nothing
return nil
}
func (u *UserInterface) Start(width, height, scale int, title string) error { func (u *UserInterface) Start(width, height, scale int, title string) error {
doc := js.Global.Get("document") doc := js.Global.Get("document")
doc.Set("title", title) doc.Set("title", title)

9
run.go
View File

@ -167,6 +167,15 @@ func IsRunningSlowly() bool {
// even if a rendering frame is skipped. // even if a rendering frame is skipped.
// f is not called when the screen is not shown. // f is not called when the screen is not shown.
func Run(f func(*Image) error, width, height, scale int, title string) error { func Run(f func(*Image) error, width, height, scale int, title string) error {
ch := make(chan error)
go func() {
ch <- run(f, width, height, scale, title)
}()
ui.Main()
return <-ch
}
func run(f func(*Image) error, width, height, scale int, title string) error {
currentRunContext.startRunning() currentRunContext.startRunning()
defer currentRunContext.endRunning() defer currentRunContext.endRunning()