Add uidriver package and move UI implementation to this packaage

This commit is contained in:
Hajime Hoshi 2019-04-07 10:42:55 +09:00
parent a4a129e3af
commit 7e5085f15b
16 changed files with 325 additions and 264 deletions

View File

@ -24,7 +24,6 @@ import (
"github.com/hajimehoshi/ebiten/internal/hooks" "github.com/hajimehoshi/ebiten/internal/hooks"
"github.com/hajimehoshi/ebiten/internal/input" "github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/shareable" "github.com/hajimehoshi/ebiten/internal/shareable"
"github.com/hajimehoshi/ebiten/internal/ui"
) )
func init() { func init() {
@ -64,7 +63,7 @@ func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale flo
// Round up the screensize not to cause glitches e.g. on Xperia (#622) // Round up the screensize not to cause glitches e.g. on Xperia (#622)
w := int(math.Ceil(float64(screenWidth) * screenScale)) w := int(math.Ceil(float64(screenWidth) * screenScale))
h := int(math.Ceil(float64(screenHeight) * screenScale)) h := int(math.Ceil(float64(screenHeight) * screenScale))
px0, py0, px1, py1 := ui.ScreenPadding() px0, py0, px1, py1 := uiDriver().ScreenPadding()
c.screen = newImageWithScreenFramebuffer(w+int(math.Ceil(px0+px1)), h+int(math.Ceil(py0+py1))) c.screen = newImageWithScreenFramebuffer(w+int(math.Ceil(px0+px1)), h+int(math.Ceil(py0+py1)))
c.screenWidth = w c.screenWidth = w
c.screenHeight = h c.screenHeight = h

View File

@ -17,7 +17,6 @@ package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/input" "github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/ui"
) )
// InputChars return "printable" runes read from the keyboard at the time update is called. // InputChars return "printable" runes read from the keyboard at the time update is called.
@ -50,7 +49,7 @@ func IsKeyPressed(key Key) bool {
// //
// CursorPosition is concurrent-safe. // CursorPosition is concurrent-safe.
func CursorPosition() (x, y int) { func CursorPosition() (x, y int) {
return ui.AdjustPosition(input.Get().CursorPosition()) return uiDriver().AdjustPosition(input.Get().CursorPosition())
} }
// Wheel returns the x and y offset of the mouse wheel or touchpad scroll. // Wheel returns the x and y offset of the mouse wheel or touchpad scroll.
@ -146,7 +145,7 @@ func TouchPosition(id int) (int, int) {
return 0, 0 return 0, 0
} }
return ui.AdjustPosition(input.Get().TouchPosition(id)) return uiDriver().AdjustPosition(input.Get().TouchPosition(id))
} }
// Touch is deprecated as of 1.7.0. Use TouchPosition instead. // Touch is deprecated as of 1.7.0. Use TouchPosition instead.

View File

@ -16,6 +16,7 @@ package driver
import ( import (
"errors" "errors"
"image"
) )
type GraphicsContext interface { type GraphicsContext interface {
@ -27,3 +28,29 @@ type GraphicsContext interface {
// Run can return this error, and if this error is received, // Run can return this error, and if this error is received,
// the game loop should be terminated as soon as possible. // the game loop should be terminated as soon as possible.
var RegularTermination = errors.New("regular termination") var RegularTermination = errors.New("regular termination")
type UI interface {
AdjustPosition(x, y int) (int, int)
DeviceScaleFactor() float64
IsCursorVisible() bool
IsFullscreen() bool
IsRunnableInBackground() bool
IsVsyncEnabled() bool
IsWindowDecorated() bool
IsWindowResizable() bool
Loop(ch <-chan error) error
Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool, graphics Graphics, input Input) error
ScreenPadding() (x0, y0, x1, y1 float64)
ScreenScale() float64
ScreenSizeInFullscreen() (int, int)
SetCursorVisible(visible bool)
SetFullscreen(fullscreen bool)
SetRunnableInBackground(runnableInBackground bool)
SetScreenScale(scale float64)
SetScreenSize(width, height int)
SetVsyncEnabled(enabled bool)
SetWindowDecorated(decorated bool)
SetWindowIcon(iconImages []image.Image)
SetWindowResizable(resizable bool)
SetWindowTitle(title string)
}

View File

@ -14,7 +14,7 @@
// +build !windows js // +build !windows js
package ui package glfw
// hideConsoleWindowOnWindows does nothing on non-Windows systems. // hideConsoleWindowOnWindows does nothing on non-Windows systems.
func hideConsoleWindowOnWindows() {} func hideConsoleWindowOnWindows() {}

View File

@ -14,7 +14,7 @@
// +build !js // +build !js
package ui package glfw
import ( import (
"fmt" "fmt"

View File

@ -17,7 +17,7 @@
// +build !android // +build !android
// +build !ios // +build !ios
package ui package glfw
import ( import (
"image" "image"
@ -33,7 +33,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/mainthread" "github.com/hajimehoshi/ebiten/internal/mainthread"
) )
type userInterface struct { type UserInterface struct {
title string title string
window *glfw.Window window *glfw.Window
width int width int
@ -77,7 +77,7 @@ const (
) )
var ( var (
theUI = &userInterface{ theUI = &UserInterface{
origPosX: invalidPos, origPosX: invalidPos,
origPosY: invalidPos, origPosY: invalidPos,
initCursorVisible: true, initCursorVisible: true,
@ -86,6 +86,10 @@ var (
} }
) )
func Get() *UserInterface {
return theUI
}
func init() { func init() {
hideConsoleWindowOnWindows() hideConsoleWindowOnWindows()
if err := initialize(); err != nil { if err := initialize(); err != nil {
@ -112,7 +116,7 @@ func initialize() error {
theUI.window = w theUI.window = w
theUI.initMonitor = theUI.currentMonitorFromPosition() theUI.initMonitor = theUI.currentMonitorFromPosition()
v := theUI.initMonitor.GetVideoMode() v := theUI.initMonitor.GetVideoMode()
s := glfwScale() s := theUI.glfwScale()
theUI.initFullscreenWidth = int(float64(v.Width) / s) theUI.initFullscreenWidth = int(float64(v.Width) / s)
theUI.initFullscreenHeight = int(float64(v.Height) / s) theUI.initFullscreenHeight = int(float64(v.Height) / s)
theUI.window.Destroy() theUI.window.Destroy()
@ -163,108 +167,107 @@ func getCachedMonitor(wx, wy int) (*cachedMonitor, bool) {
return nil, false return nil, false
} }
func Loop(ch <-chan error) error { func (u *UserInterface) Loop(ch <-chan error) error {
theUI.setRunning(true) u.setRunning(true)
if err := mainthread.Loop(ch); err != nil { if err := mainthread.Loop(ch); err != nil {
return err return err
} }
theUI.setRunning(false) u.setRunning(false)
return nil return nil
} }
func (u *userInterface) isRunning() bool { func (u *UserInterface) isRunning() bool {
u.m.Lock() u.m.Lock()
v := u.running v := u.running
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setRunning(running bool) { func (u *UserInterface) setRunning(running bool) {
u.m.Lock() u.m.Lock()
u.running = running u.running = running
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isInitFullscreen() bool { func (u *UserInterface) isInitFullscreen() bool {
u.m.Lock() u.m.Lock()
v := u.initFullscreen v := u.initFullscreen
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setInitFullscreen(initFullscreen bool) { func (u *UserInterface) setInitFullscreen(initFullscreen bool) {
u.m.Lock() u.m.Lock()
u.initFullscreen = initFullscreen u.initFullscreen = initFullscreen
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isInitCursorVisible() bool { func (u *UserInterface) isInitCursorVisible() bool {
u.m.Lock() u.m.Lock()
v := u.initCursorVisible v := u.initCursorVisible
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setInitCursorVisible(visible bool) { func (u *UserInterface) setInitCursorVisible(visible bool) {
u.m.Lock() u.m.Lock()
u.initCursorVisible = visible u.initCursorVisible = visible
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isInitWindowDecorated() bool { func (u *UserInterface) isInitWindowDecorated() bool {
u.m.Lock() u.m.Lock()
v := u.initWindowDecorated v := u.initWindowDecorated
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setInitWindowDecorated(decorated bool) { func (u *UserInterface) setInitWindowDecorated(decorated bool) {
u.m.Lock() u.m.Lock()
u.initWindowDecorated = decorated u.initWindowDecorated = decorated
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isRunnableInBackground() bool { func (u *UserInterface) isRunnableInBackground() bool {
u.m.Lock() u.m.Lock()
v := u.runnableInBackground v := u.runnableInBackground
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setRunnableInBackground(runnableInBackground bool) { func (u *UserInterface) setRunnableInBackground(runnableInBackground bool) {
u.m.Lock() u.m.Lock()
u.runnableInBackground = runnableInBackground u.runnableInBackground = runnableInBackground
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isInitWindowResizable() bool { func (u *UserInterface) isInitWindowResizable() bool {
u.m.Lock() u.m.Lock()
v := u.initWindowResizable v := u.initWindowResizable
u.m.Unlock() u.m.Unlock()
return v return v
} }
func (u *userInterface) setInitWindowResizable(resizable bool) { func (u *UserInterface) setInitWindowResizable(resizable bool) {
u.m.Lock() u.m.Lock()
u.initWindowResizable = resizable u.initWindowResizable = resizable
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) getInitIconImages() []image.Image { func (u *UserInterface) getInitIconImages() []image.Image {
u.m.Lock() u.m.Lock()
i := u.initIconImages i := u.initIconImages
u.m.Unlock() u.m.Unlock()
return i return i
} }
func (u *userInterface) setInitIconImages(iconImages []image.Image) { func (u *UserInterface) setInitIconImages(iconImages []image.Image) {
u.m.Lock() u.m.Lock()
u.initIconImages = iconImages u.initIconImages = iconImages
u.m.Unlock() u.m.Unlock()
} }
func ScreenSizeInFullscreen() (int, int) { func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return u.initFullscreenWidth, u.initFullscreenHeight return u.initFullscreenWidth, u.initFullscreenHeight
} }
@ -273,14 +276,13 @@ func ScreenSizeInFullscreen() (int, int) {
s := 0.0 s := 0.0
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
v = u.currentMonitor().GetVideoMode() v = u.currentMonitor().GetVideoMode()
s = glfwScale() s = u.glfwScale()
return nil return nil
}) })
return int(float64(v.Width) / s), int(float64(v.Height) / s) return int(float64(v.Width) / s), int(float64(v.Height) / s)
} }
func SetScreenSize(width, height int) { func (u *UserInterface) SetScreenSize(width, height int) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
panic("ui: Run is not called yet") panic("ui: Run is not called yet")
} }
@ -291,22 +293,18 @@ func SetScreenSize(width, height int) {
}) })
} }
func SetScreenScale(scale float64) bool { func (u *UserInterface) SetScreenScale(scale float64) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
panic("ui: Run is not called yet") panic("ui: Run is not called yet")
} }
r := false
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
// TODO: What if the window is maximized? (#320) // TODO: What if the window is maximized? (#320)
r = u.setScreenSize(u.width, u.height, scale, u.isFullscreen(), u.vsync) u.setScreenSize(u.width, u.height, scale, u.isFullscreen(), u.vsync)
return nil return nil
}) })
return r
} }
func ScreenScale() float64 { func (u *UserInterface) ScreenScale() float64 {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return 0 return 0
} }
@ -319,15 +317,14 @@ func ScreenScale() float64 {
} }
// isFullscreen must be called from the main thread. // isFullscreen must be called from the main thread.
func (u *userInterface) isFullscreen() bool { func (u *UserInterface) isFullscreen() bool {
if !u.isRunning() { if !u.isRunning() {
panic("ui: the game must be running at isFullscreen") panic("ui: the game must be running at isFullscreen")
} }
return u.window.GetMonitor() != nil return u.window.GetMonitor() != nil
} }
func IsFullscreen() bool { func (u *UserInterface) IsFullscreen() bool {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return u.isInitFullscreen() return u.isInitFullscreen()
} }
@ -339,29 +336,26 @@ func IsFullscreen() bool {
return b return b
} }
func SetFullscreen(fullscreen bool) { func (u *UserInterface) SetFullscreen(fullscreen bool) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
u.setInitFullscreen(fullscreen) u.setInitFullscreen(fullscreen)
return return
} }
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
u := theUI
u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync) u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync)
return nil return nil
}) })
} }
func SetRunnableInBackground(runnableInBackground bool) { func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
theUI.setRunnableInBackground(runnableInBackground) u.setRunnableInBackground(runnableInBackground)
} }
func IsRunnableInBackground() bool { func (u *UserInterface) IsRunnableInBackground() bool {
return theUI.isRunnableInBackground() return u.isRunnableInBackground()
} }
func SetVsyncEnabled(enabled bool) { func (u *UserInterface) SetVsyncEnabled(enabled bool) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
// In general, m is used for locking init* values. // In general, m is used for locking init* values.
// m is not used for updating vsync in setScreenSize so far, but // m is not used for updating vsync in setScreenSize so far, but
@ -373,47 +367,44 @@ func SetVsyncEnabled(enabled bool) {
return return
} }
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
u := theUI
u.setScreenSize(u.width, u.height, u.scale, u.isFullscreen(), enabled) u.setScreenSize(u.width, u.height, u.scale, u.isFullscreen(), enabled)
return nil return nil
}) })
} }
func IsVsyncEnabled() bool { func (u *UserInterface) IsVsyncEnabled() bool {
u := theUI
u.m.Lock() u.m.Lock()
r := u.vsync r := u.vsync
u.m.Unlock() u.m.Unlock()
return r return r
} }
func SetWindowTitle(title string) { func (u *UserInterface) SetWindowTitle(title string) {
if !theUI.isRunning() { if !u.isRunning() {
return return
} }
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
theUI.window.SetTitle(title) u.window.SetTitle(title)
return nil return nil
}) })
} }
func SetWindowIcon(iconImages []image.Image) { func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
if !theUI.isRunning() { if !u.isRunning() {
theUI.setInitIconImages(iconImages) u.setInitIconImages(iconImages)
return return
} }
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
theUI.window.SetIcon(iconImages) u.window.SetIcon(iconImages)
return nil return nil
}) })
} }
func ScreenPadding() (x0, y0, x1, y1 float64) { func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
if !IsFullscreen() { if !u.IsFullscreen() {
if u.width == u.windowWidth { if u.width == u.windowWidth {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
@ -437,7 +428,7 @@ func ScreenPadding() (x0, y0, x1, y1 float64) {
d = devicescale.GetAt(m.GetPos()) d = devicescale.GetAt(m.GetPos())
sx = float64(u.width) * u.actualScreenScale() sx = float64(u.width) * u.actualScreenScale()
sy = float64(u.height) * u.actualScreenScale() sy = float64(u.height) * u.actualScreenScale()
gs = glfwScale() gs = u.glfwScale()
v := m.GetVideoMode() v := m.GetVideoMode()
vw, vh = float64(v.Width), float64(v.Height) vw, vh = float64(v.Width), float64(v.Height)
@ -451,35 +442,32 @@ func ScreenPadding() (x0, y0, x1, y1 float64) {
return ox, oy, (mx - sx) - ox, (my - sy) - oy return ox, oy, (mx - sx) - ox, (my - sy) - oy
} }
func AdjustPosition(x, y int) (int, int) { func (u *UserInterface) AdjustPosition(x, y int) (int, int) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return x, y return x, y
} }
ox, oy, _, _ := ScreenPadding() ox, oy, _, _ := u.ScreenPadding()
s := 0.0 s := 0.0
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
s = theUI.actualScreenScale() s = u.actualScreenScale()
return nil return nil
}) })
return x - int(ox/s), y - int(oy/s) return x - int(ox/s), y - int(oy/s)
} }
func IsCursorVisible() bool { func (u *UserInterface) IsCursorVisible() bool {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return u.isInitCursorVisible() return u.isInitCursorVisible()
} }
v := false v := false
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
v = theUI.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal v = u.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal
return nil return nil
}) })
return v return v
} }
func SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorVisible(visible bool) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
u.setInitCursorVisible(visible) u.setInitCursorVisible(visible)
return return
@ -489,26 +477,24 @@ func SetCursorVisible(visible bool) {
if !visible { if !visible {
c = glfw.CursorHidden c = glfw.CursorHidden
} }
theUI.window.SetInputMode(glfw.CursorMode, c) u.window.SetInputMode(glfw.CursorMode, c)
return nil return nil
}) })
} }
func IsWindowDecorated() bool { func (u *UserInterface) IsWindowDecorated() bool {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return u.isInitWindowDecorated() return u.isInitWindowDecorated()
} }
v := false v := false
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
v = theUI.window.GetAttrib(glfw.Decorated) == glfw.True v = u.window.GetAttrib(glfw.Decorated) == glfw.True
return nil return nil
}) })
return v return v
} }
func SetWindowDecorated(decorated bool) { func (u *UserInterface) SetWindowDecorated(decorated bool) {
u := theUI
if !u.isRunning() { if !u.isRunning() {
u.setInitWindowDecorated(decorated) u.setInitWindowDecorated(decorated)
return return
@ -525,26 +511,25 @@ func SetWindowDecorated(decorated bool) {
// v = glfw.True // v = glfw.True
// } // }
// }) // })
// theUI.window.SetAttrib(glfw.Decorated, v) // u.window.SetAttrib(glfw.Decorated, v)
// return nil // return nil
} }
func IsWindowResizable() bool { func (u *UserInterface) IsWindowResizable() bool {
u := theUI
if !u.isRunning() { if !u.isRunning() {
return u.isInitWindowResizable() return u.isInitWindowResizable()
} }
v := false v := false
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
v = theUI.window.GetAttrib(glfw.Resizable) == glfw.True v = u.window.GetAttrib(glfw.Resizable) == glfw.True
return nil return nil
}) })
return v return v
} }
func SetWindowResizable(resizable bool) { func (u *UserInterface) SetWindowResizable(resizable bool) {
if !theUI.isRunning() { if !u.isRunning() {
theUI.setInitWindowResizable(resizable) u.setInitWindowResizable(resizable)
return return
} }
@ -553,9 +538,8 @@ func SetWindowResizable(resizable bool) {
// TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556). // TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556).
} }
func DeviceScaleFactor() float64 { func (u *UserInterface) DeviceScaleFactor() float64 {
f := 0.0 f := 0.0
u := theUI
if !u.isRunning() { if !u.isRunning() {
return devicescale.GetAt(u.initMonitor.GetPos()) return devicescale.GetAt(u.initMonitor.GetPos())
} }
@ -568,8 +552,7 @@ func DeviceScaleFactor() float64 {
return f return f
} }
func Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error { func (u *UserInterface) Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error {
u := theUI
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
u.graphics = graphics u.graphics = graphics
u.input = input u.input = input
@ -662,7 +645,7 @@ func Run(width, height int, scale float64, title string, g driver.GraphicsContex
return return
} }
s := glfwScale() s := u.glfwScale()
w := int(float64(width) / u.scale / s) w := int(float64(width) / u.scale / s)
h := int(float64(height) / u.scale / s) h := int(float64(height) / u.scale / s)
u.reqWidth = w u.reqWidth = w
@ -681,21 +664,21 @@ func Run(width, height int, scale float64, title string, g driver.GraphicsContex
} }
// getSize must be called from the main thread. // getSize must be called from the main thread.
func (u *userInterface) glfwSize() (int, int) { func (u *UserInterface) glfwSize() (int, int) {
w := int(float64(u.windowWidth) * u.getScale() * glfwScale()) w := int(float64(u.windowWidth) * u.getScale() * u.glfwScale())
h := int(float64(u.height) * u.getScale() * glfwScale()) h := int(float64(u.height) * u.getScale() * u.glfwScale())
return w, h return w, h
} }
// getScale must be called from the main thread. // getScale must be called from the main thread.
func (u *userInterface) getScale() float64 { func (u *UserInterface) getScale() float64 {
if !u.isFullscreen() { if !u.isFullscreen() {
return u.scale return u.scale
} }
if u.fullscreenScale == 0 { if u.fullscreenScale == 0 {
v := u.window.GetMonitor().GetVideoMode() v := u.window.GetMonitor().GetVideoMode()
sw := float64(v.Width) / glfwScale() / float64(u.width) sw := float64(v.Width) / u.glfwScale() / float64(u.width)
sh := float64(v.Height) / glfwScale() / float64(u.height) sh := float64(v.Height) / u.glfwScale() / float64(u.height)
s := sw s := sw
if s > sh { if s > sh {
s = sh s = sh
@ -706,7 +689,7 @@ func (u *userInterface) getScale() float64 {
} }
// actualScreenScale must be called from the main thread. // actualScreenScale must be called from the main thread.
func (u *userInterface) actualScreenScale() float64 { func (u *UserInterface) actualScreenScale() float64 {
// Avoid calling monitor.GetPos if we have the monitor position cached already. // Avoid calling monitor.GetPos if we have the monitor position cached already.
if cm, ok := getCachedMonitor(u.window.GetPos()); ok { if cm, ok := getCachedMonitor(u.window.GetPos()); ok {
return u.getScale() * devicescale.GetAt(cm.x, cm.y) return u.getScale() * devicescale.GetAt(cm.x, cm.y)
@ -714,7 +697,7 @@ func (u *userInterface) actualScreenScale() float64 {
return u.getScale() * devicescale.GetAt(u.currentMonitor().GetPos()) return u.getScale() * devicescale.GetAt(u.currentMonitor().GetPos())
} }
func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) { func (u *UserInterface) updateGraphicsContext(g driver.GraphicsContext) {
actualScale := 0.0 actualScale := 0.0
sizeChanged := false sizeChanged := false
// TODO: Is it possible to reduce 'runOnMainThread' calls? // TODO: Is it possible to reduce 'runOnMainThread' calls?
@ -738,7 +721,7 @@ func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) {
} }
} }
func (u *userInterface) update(g driver.GraphicsContext) error { func (u *UserInterface) update(g driver.GraphicsContext) error {
shouldClose := false shouldClose := false
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
shouldClose = u.window.ShouldClose() shouldClose = u.window.ShouldClose()
@ -750,7 +733,6 @@ func (u *userInterface) update(g driver.GraphicsContext) error {
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
if u.isInitFullscreen() { if u.isInitFullscreen() {
u := theUI
u.setScreenSize(u.width, u.height, u.scale, true, u.vsync) u.setScreenSize(u.width, u.height, u.scale, true, u.vsync)
u.setInitFullscreen(false) u.setInitFullscreen(false)
} }
@ -767,7 +749,7 @@ func (u *userInterface) update(g driver.GraphicsContext) error {
Update(window *glfw.Window, scale float64) Update(window *glfw.Window, scale float64)
} }
u.input.(updater).Update(u.window, u.getScale()*glfwScale()) u.input.(updater).Update(u.window, u.getScale()*u.glfwScale())
defer hooks.ResumeAudio() defer hooks.ResumeAudio()
@ -802,7 +784,7 @@ func (u *userInterface) update(g driver.GraphicsContext) error {
return nil return nil
} }
func (u *userInterface) loop(g driver.GraphicsContext) error { func (u *UserInterface) loop(g driver.GraphicsContext) error {
defer func() { defer func() {
_ = mainthread.Run(func() error { _ = mainthread.Run(func() error {
glfw.Terminate() glfw.Terminate()
@ -830,14 +812,14 @@ func (u *userInterface) loop(g driver.GraphicsContext) error {
} }
// swapBuffers must be called from the main thread. // swapBuffers must be called from the main thread.
func (u *userInterface) swapBuffers() { func (u *UserInterface) swapBuffers() {
if u.graphics.IsGL() { if u.graphics.IsGL() {
u.window.SwapBuffers() u.window.SwapBuffers()
} }
} }
// setScreenSize must be called from the main thread. // setScreenSize must be called from the main thread.
func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) bool { func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) bool {
if u.width == width && u.height == height && u.scale == scale && u.isFullscreen() == fullscreen && u.vsync == vsync { if u.width == width && u.height == height && u.scale == scale && u.isFullscreen() == fullscreen && u.vsync == vsync {
return false return false
} }
@ -846,12 +828,12 @@ func (u *userInterface) setScreenSize(width, height int, scale float64, fullscre
} }
// forceSetScreenSize must be called from the main thread. // forceSetScreenSize must be called from the main thread.
func (u *userInterface) forceSetScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) { func (u *UserInterface) forceSetScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) {
// On Windows, giving a too small width doesn't call a callback (#165). // On Windows, giving a too small width doesn't call a callback (#165).
// To prevent hanging up, return asap if the width is too small. // To prevent hanging up, return asap if the width is too small.
// 252 is an arbitrary number and I guess this is small enough. // 252 is an arbitrary number and I guess this is small enough.
minWindowWidth := 252 minWindowWidth := 252
if theUI.window.GetAttrib(glfw.Decorated) == glfw.False { if u.window.GetAttrib(glfw.Decorated) == glfw.False {
minWindowWidth = 1 minWindowWidth = 1
} }
@ -951,7 +933,7 @@ func (u *userInterface) forceSetScreenSize(width, height int, scale float64, ful
// currentMonitor returns the monitor most suitable with the current window. // currentMonitor returns the monitor most suitable with the current window.
// //
// currentMonitor must be called on the main thread. // currentMonitor must be called on the main thread.
func (u *userInterface) currentMonitor() *glfw.Monitor { func (u *UserInterface) currentMonitor() *glfw.Monitor {
w := u.window w := u.window
if m := w.GetMonitor(); m != nil { if m := w.GetMonitor(); m != nil {
return m return m

View File

@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// +build darwin
// +build !js // +build !js
// +build !ios // +build !ios
package ui package glfw
// #cgo CFLAGS: -x objective-c // #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework AppKit // #cgo LDFLAGS: -framework AppKit
@ -48,7 +47,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/glfw" "github.com/hajimehoshi/ebiten/internal/glfw"
) )
func glfwScale() float64 { func (u *UserInterface) glfwScale() float64 {
return 1 return 1
} }
@ -56,7 +55,7 @@ func adjustWindowPosition(x, y int) (int, int) {
return x, y return x, y
} }
func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor { func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
x := C.int(0) x := C.int(0)
y := C.int(0) y := C.int(0)
// Note: [NSApp mainWindow] is nil when it doesn't have its border. Use u.window here. // Note: [NSApp mainWindow] is nil when it doesn't have its border. Use u.window here.
@ -71,6 +70,6 @@ func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor {
return glfw.GetPrimaryMonitor() return glfw.GetPrimaryMonitor()
} }
func (u *userInterface) nativeWindow() uintptr { func (u *UserInterface) nativeWindow() uintptr {
return u.window.GetCocoaWindow() return u.window.GetCocoaWindow()
} }

View File

@ -16,18 +16,18 @@
// +build !js // +build !js
// +build !android // +build !android
package ui package glfw
import ( import (
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/glfw" "github.com/hajimehoshi/ebiten/internal/glfw"
) )
func glfwScale() float64 { func (u *UserInterface) glfwScale() float64 {
// This function must be called on the main thread. // This function must be called on the main thread.
cm, ok := getCachedMonitor(theUI.window.GetPos()) cm, ok := getCachedMonitor(u.window.GetPos())
if !ok { if !ok {
return devicescale.GetAt(theUI.currentMonitor().GetPos()) return devicescale.GetAt(u.currentMonitor().GetPos())
} }
return devicescale.GetAt(cm.x, cm.y) return devicescale.GetAt(cm.x, cm.y)
} }
@ -36,7 +36,7 @@ func adjustWindowPosition(x, y int) (int, int) {
return x, y return x, y
} }
func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor { func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
// TODO: Return more appropriate display. // TODO: Return more appropriate display.
if cm, ok := getCachedMonitor(u.window.GetPos()); ok { if cm, ok := getCachedMonitor(u.window.GetPos()); ok {
return cm.m return cm.m
@ -44,7 +44,7 @@ func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor {
return glfw.GetPrimaryMonitor() return glfw.GetPrimaryMonitor()
} }
func (u *userInterface) nativeWindow() uintptr { func (u *UserInterface) nativeWindow() uintptr {
// TODO: Implement this. // TODO: Implement this.
return 0 return 0
} }

View File

@ -14,7 +14,7 @@
// +build !js // +build !js
package ui package glfw
import ( import (
"fmt" "fmt"
@ -100,9 +100,9 @@ func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
return nil return nil
} }
func glfwScale() float64 { func (u *UserInterface) glfwScale() float64 {
// This function must be called on the main thread. // This function must be called on the main thread.
return devicescale.GetAt(theUI.currentMonitor().GetPos()) return devicescale.GetAt(u.currentMonitor().GetPos())
} }
func adjustWindowPosition(x, y int) (int, int) { func adjustWindowPosition(x, y int) (int, int) {
@ -121,7 +121,7 @@ func adjustWindowPosition(x, y int) (int, int) {
return x, y return x, y
} }
func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor { func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
// TODO: Should we use u.window.GetWin32Window() here? // TODO: Should we use u.window.GetWin32Window() here?
w, err := getActiveWindow() w, err := getActiveWindow()
if err != nil { if err != nil {
@ -165,6 +165,6 @@ func (u *userInterface) currentMonitorFromPosition() *glfw.Monitor {
return glfw.GetPrimaryMonitor() return glfw.GetPrimaryMonitor()
} }
func (u *userInterface) nativeWindow() uintptr { func (u *UserInterface) nativeWindow() uintptr {
return u.window.GetWin32Window() return u.window.GetWin32Window()
} }

View File

@ -14,7 +14,7 @@
// +build js // +build js
package ui package js
import ( import (
"image" "image"
@ -38,7 +38,7 @@ type inputDriver interface {
UpdateGamepads() UpdateGamepads()
} }
type userInterface struct { type UserInterface struct {
width int width int
height int height int
scale float64 scale float64
@ -56,13 +56,17 @@ type userInterface struct {
input inputDriver input inputDriver
} }
var theUI = &userInterface{ var theUI = &UserInterface{
sizeChanged: true, sizeChanged: true,
windowFocus: true, windowFocus: true,
pageVisible: true, pageVisible: true,
vsync: true, vsync: true,
} }
func Get() *UserInterface {
return theUI
}
var ( var (
window = js.Global().Get("window") window = js.Global().Get("window")
document = js.Global().Get("document") document = js.Global().Get("document")
@ -70,64 +74,64 @@ var (
setTimeout = window.Get("setTimeout") setTimeout = window.Get("setTimeout")
) )
func ScreenSizeInFullscreen() (int, int) { func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
return window.Get("innerWidth").Int(), window.Get("innerHeight").Int() return window.Get("innerWidth").Int(), window.Get("innerHeight").Int()
} }
func SetScreenSize(width, height int) bool { func (u *UserInterface) SetScreenSize(width, height int) {
return theUI.setScreenSize(width, height, theUI.scale, theUI.fullscreen) u.setScreenSize(width, height, u.scale, u.fullscreen)
} }
func SetScreenScale(scale float64) bool { func (u *UserInterface) SetScreenScale(scale float64) {
return theUI.setScreenSize(theUI.width, theUI.height, scale, theUI.fullscreen) u.setScreenSize(u.width, u.height, scale, u.fullscreen)
} }
func ScreenScale() float64 { func (u *UserInterface) ScreenScale() float64 {
return theUI.scale return u.scale
} }
func SetFullscreen(fullscreen bool) { func (u *UserInterface) SetFullscreen(fullscreen bool) {
theUI.setScreenSize(theUI.width, theUI.height, theUI.scale, fullscreen) u.setScreenSize(u.width, u.height, u.scale, fullscreen)
} }
func IsFullscreen() bool { func (u *UserInterface) IsFullscreen() bool {
return theUI.fullscreen return u.fullscreen
} }
func SetRunnableInBackground(runnableInBackground bool) { func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
theUI.runnableInBackground = runnableInBackground u.runnableInBackground = runnableInBackground
} }
func IsRunnableInBackground() bool { func (u *UserInterface) IsRunnableInBackground() bool {
return theUI.runnableInBackground return u.runnableInBackground
} }
func SetVsyncEnabled(enabled bool) { func (u *UserInterface) SetVsyncEnabled(enabled bool) {
theUI.vsync = enabled u.vsync = enabled
} }
func IsVsyncEnabled() bool { func (u *UserInterface) IsVsyncEnabled() bool {
return theUI.vsync return u.vsync
} }
func ScreenPadding() (x0, y0, x1, y1 float64) { func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
func AdjustPosition(x, y int) (int, int) { func (u *UserInterface) AdjustPosition(x, y int) (int, int) {
rect := canvas.Call("getBoundingClientRect") rect := canvas.Call("getBoundingClientRect")
x -= rect.Get("left").Int() x -= rect.Get("left").Int()
y -= rect.Get("top").Int() y -= rect.Get("top").Int()
scale := theUI.getScale() scale := u.getScale()
return int(float64(x) / scale), int(float64(y) / scale) return int(float64(x) / scale), int(float64(y) / scale)
} }
func IsCursorVisible() bool { func (u *UserInterface) IsCursorVisible() bool {
// The initial value is an empty string, so don't compare with "auto" here. // The initial value is an empty string, so don't compare with "auto" here.
return canvas.Get("style").Get("cursor").String() != "none" return canvas.Get("style").Get("cursor").String() != "none"
} }
func SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorVisible(visible bool) {
if visible { if visible {
canvas.Get("style").Set("cursor", "auto") canvas.Get("style").Set("cursor", "auto")
} else { } else {
@ -135,35 +139,35 @@ func SetCursorVisible(visible bool) {
} }
} }
func SetWindowTitle(title string) { func (u *UserInterface) SetWindowTitle(title string) {
document.Set("title", title) document.Set("title", title)
} }
func SetWindowIcon(iconImages []image.Image) { func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
// Do nothing // Do nothing
} }
func IsWindowDecorated() bool { func (u *UserInterface) IsWindowDecorated() bool {
return false return false
} }
func SetWindowDecorated(decorated bool) { func (u *UserInterface) SetWindowDecorated(decorated bool) {
// Do nothing // Do nothing
} }
func IsWindowResizable() bool { func (u *UserInterface) IsWindowResizable() bool {
return false return false
} }
func SetWindowResizable(decorated bool) { func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing // Do nothing
} }
func DeviceScaleFactor() float64 { func (u *UserInterface) DeviceScaleFactor() float64 {
return devicescale.GetAt(0, 0) return devicescale.GetAt(0, 0)
} }
func (u *userInterface) getScale() float64 { func (u *UserInterface) getScale() float64 {
if !u.fullscreen { if !u.fullscreen {
return u.scale return u.scale
} }
@ -178,7 +182,7 @@ func (u *userInterface) getScale() float64 {
return sw return sw
} }
func (u *userInterface) actualScreenScale() float64 { func (u *UserInterface) actualScreenScale() float64 {
// CSS imageRendering property seems useful to enlarge the screen, // CSS imageRendering property seems useful to enlarge the screen,
// but doesn't work in some cases (#306): // but doesn't work in some cases (#306):
// * Chrome just after restoring the lost context // * Chrome just after restoring the lost context
@ -187,7 +191,7 @@ func (u *userInterface) actualScreenScale() float64 {
return u.getScale() * devicescale.GetAt(0, 0) return u.getScale() * devicescale.GetAt(0, 0)
} }
func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) { func (u *UserInterface) updateGraphicsContext(g driver.GraphicsContext) {
a := u.actualScreenScale() a := u.actualScreenScale()
if u.lastActualScale != a { if u.lastActualScale != a {
u.updateScreenSize() u.updateScreenSize()
@ -200,11 +204,11 @@ func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) {
} }
} }
func (u *userInterface) suspended() bool { func (u *UserInterface) suspended() bool {
return !u.runnableInBackground && (!u.windowFocus || !u.pageVisible) return !u.runnableInBackground && (!u.windowFocus || !u.pageVisible)
} }
func (u *userInterface) update(g driver.GraphicsContext) error { func (u *UserInterface) update(g driver.GraphicsContext) error {
if u.suspended() { if u.suspended() {
hooks.SuspendAudio() hooks.SuspendAudio()
return nil return nil
@ -221,7 +225,7 @@ func (u *userInterface) update(g driver.GraphicsContext) error {
return nil return nil
} }
func (u *userInterface) loop(g driver.GraphicsContext) <-chan error { func (u *UserInterface) loop(g driver.GraphicsContext) <-chan error {
ch := make(chan error) ch := make(chan error)
var cf js.Callback var cf js.Callback
f := func([]js.Value) { f := func([]js.Value) {
@ -381,12 +385,11 @@ func init() {
})) }))
} }
func Loop(ch <-chan error) error { func (u *UserInterface) Loop(ch <-chan error) error {
return <-ch return <-ch
} }
func Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error { func (u *UserInterface) Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error {
u := theUI
u.input = input.(inputDriver) u.input = input.(inputDriver)
document.Set("title", title) document.Set("title", title)
@ -407,7 +410,7 @@ func Run(width, height int, scale float64, title string, g driver.GraphicsContex
return nil return nil
} }
func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool { func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
if u.width == width && u.height == height && if u.width == width && u.height == height &&
u.scale == scale && fullscreen == u.fullscreen { u.scale == scale && fullscreen == u.fullscreen {
return false return false
@ -420,7 +423,7 @@ func (u *userInterface) setScreenSize(width, height int, scale float64, fullscre
return true return true
} }
func (u *userInterface) updateScreenSize() { func (u *UserInterface) updateScreenSize() {
canvas.Set("width", int(float64(u.width)*u.actualScreenScale())) canvas.Set("width", int(float64(u.width)*u.actualScreenScale()))
canvas.Set("height", int(float64(u.height)*u.actualScreenScale())) canvas.Set("height", int(float64(u.height)*u.actualScreenScale()))
canvasStyle := canvas.Get("style") canvasStyle := canvas.Get("style")

View File

@ -14,7 +14,7 @@
// +build android ios // +build android ios
package ui package mobile
import ( import (
"errors" "errors"
@ -41,10 +41,14 @@ var (
glContextCh = make(chan gl.Context) glContextCh = make(chan gl.Context)
renderCh = make(chan struct{}) renderCh = make(chan struct{})
renderChEnd = make(chan struct{}) renderChEnd = make(chan struct{})
theUI = &userInterface{} theUI = &UserInterface{}
) )
func Render(chError <-chan error) error { func Get() *UserInterface {
return theUI
}
func (u *UserInterface) Render(chError <-chan error) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -63,7 +67,7 @@ func Render(chError <-chan error) error {
} }
} }
type userInterface struct { type UserInterface struct {
width int width int
height int height int
scale float64 scale float64
@ -113,7 +117,7 @@ func appMain(a app.App) {
glctx = nil glctx = nil
} }
case size.Event: case size.Event:
setFullscreen(e.WidthPx, e.HeightPx) theUI.setFullscreenImpl(e.WidthPx, e.HeightPx)
case paint.Event: case paint.Event:
if glctx == nil || e.External { if glctx == nil || e.External {
continue continue
@ -148,13 +152,11 @@ func appMain(a app.App) {
} }
} }
func Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error { func (u *UserInterface) Run(width, height int, scale float64, title string, g driver.GraphicsContext, mainloop bool, graphics driver.Graphics, input driver.Input) error {
if graphics != opengl.Get() { if graphics != opengl.Get() {
panic("ui: graphics driver must be OpenGL") panic("ui: graphics driver must be OpenGL")
} }
u := theUI
u.m.Lock() u.m.Lock()
u.width = width u.width = width
u.height = height u.height = height
@ -180,7 +182,7 @@ func Run(width, height int, scale float64, title string, g driver.GraphicsContex
} }
// Loop runs the main routine for gomobile-build. // Loop runs the main routine for gomobile-build.
func Loop(ch <-chan error) error { func (u *UserInterface) Loop(ch <-chan error) error {
go func() { go func() {
// As mobile apps never ends, Loop can't return. Just panic here. // As mobile apps never ends, Loop can't return. Just panic here.
err := <-ch err := <-ch
@ -190,7 +192,7 @@ func Loop(ch <-chan error) error {
return nil return nil
} }
func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) { func (u *UserInterface) updateGraphicsContext(g driver.GraphicsContext) {
width, height := 0, 0 width, height := 0, 0
actualScale := 0.0 actualScale := 0.0
@ -210,18 +212,14 @@ func (u *userInterface) updateGraphicsContext(g driver.GraphicsContext) {
} }
} }
func actualScale() float64 { func (u *UserInterface) ActualScale() float64 {
return theUI.actualScale()
}
func (u *userInterface) actualScale() float64 {
u.m.Lock() u.m.Lock()
s := u.scaleImpl() * getDeviceScale() s := u.scaleImpl() * getDeviceScale()
u.m.Unlock() u.m.Unlock()
return s return s
} }
func (u *userInterface) scaleImpl() float64 { func (u *UserInterface) scaleImpl() float64 {
scale := u.scale scale := u.scale
if u.fullscreenScale != 0 { if u.fullscreenScale != 0 {
scale = u.fullscreenScale scale = u.fullscreenScale
@ -229,7 +227,7 @@ func (u *userInterface) scaleImpl() float64 {
return scale return scale
} }
func (u *userInterface) update(g driver.GraphicsContext) error { func (u *UserInterface) update(g driver.GraphicsContext) error {
render: render:
for { for {
select { select {
@ -254,29 +252,20 @@ render:
return nil return nil
} }
func screenSize() (int, int) { func (u *UserInterface) ScreenSize() (int, int) {
return theUI.screenSize()
}
func (u *userInterface) screenSize() (int, int) {
u.m.Lock() u.m.Lock()
w, h := u.width, u.height w, h := u.width, u.height
u.m.Unlock() u.m.Unlock()
return w, h return w, h
} }
func ScreenSizeInFullscreen() (int, int) { func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
// TODO: This function should return fullscreenWidthPx, fullscreenHeightPx, // TODO: This function should return fullscreenWidthPx, fullscreenHeightPx,
// but these values are not initialized until the main loop starts. // but these values are not initialized until the main loop starts.
return 0, 0 return 0, 0
} }
func SetScreenSize(width, height int) bool { func (u *UserInterface) SetScreenSize(width, height int) {
theUI.setScreenSize(width, height)
return true
}
func (u *userInterface) setScreenSize(width, height int) {
u.m.Lock() u.m.Lock()
if u.width != width || u.height != height { if u.width != width || u.height != height {
u.width = width u.width = width
@ -287,12 +276,7 @@ func (u *userInterface) setScreenSize(width, height int) {
u.m.Unlock() u.m.Unlock()
} }
func SetScreenScale(scale float64) bool { func (u *UserInterface) SetScreenScale(scale float64) {
theUI.setScreenScale(scale)
return false
}
func (u *userInterface) setScreenScale(scale float64) {
u.m.Lock() u.m.Lock()
if u.scale != scale { if u.scale != scale {
u.scale = scale u.scale = scale
@ -301,19 +285,15 @@ func (u *userInterface) setScreenScale(scale float64) {
u.m.Unlock() u.m.Unlock()
} }
func ScreenScale() float64 { func (u *UserInterface) ScreenScale() float64 {
u := theUI
u.m.RLock() u.m.RLock()
s := u.scale s := u.scale
u.m.RUnlock() u.m.RUnlock()
return s return s
} }
func setFullscreen(widthPx, heightPx int) { func (u *UserInterface) setFullscreenImpl(widthPx, heightPx int) {
theUI.setFullscreen(widthPx, heightPx) // This implementation is only for gomobile-build so far.
}
func (u *userInterface) setFullscreen(widthPx, heightPx int) {
u.m.Lock() u.m.Lock()
u.fullscreenWidthPx = widthPx u.fullscreenWidthPx = widthPx
u.fullscreenHeightPx = heightPx u.fullscreenHeightPx = heightPx
@ -322,7 +302,7 @@ func (u *userInterface) setFullscreen(widthPx, heightPx int) {
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) updateFullscreenScaleIfNeeded() { func (u *UserInterface) updateFullscreenScaleIfNeeded() {
if u.fullscreenWidthPx == 0 || u.fullscreenHeightPx == 0 { if u.fullscreenWidthPx == 0 || u.fullscreenHeightPx == 0 {
return return
} }
@ -337,18 +317,14 @@ func (u *userInterface) updateFullscreenScaleIfNeeded() {
u.sizeChanged = true u.sizeChanged = true
} }
func ScreenPadding() (x0, y0, x1, y1 float64) { func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
return theUI.screenPadding()
}
func (u *userInterface) screenPadding() (x0, y0, x1, y1 float64) {
u.m.Lock() u.m.Lock()
x0, y0, x1, y1 = u.screenPaddingImpl() x0, y0, x1, y1 = u.screenPaddingImpl()
u.m.Unlock() u.m.Unlock()
return return
} }
func (u *userInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) { func (u *UserInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
if u.fullscreenScale == 0 { if u.fullscreenScale == 0 {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
@ -358,11 +334,7 @@ func (u *userInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
return ox, oy, ox, oy return ox, oy, ox, oy
} }
func AdjustPosition(x, y int) (int, int) { func (u *UserInterface) AdjustPosition(x, y int) (int, int) {
return theUI.adjustPosition(x, y)
}
func (u *userInterface) adjustPosition(x, y int) (int, int) {
u.m.Lock() u.m.Lock()
ox, oy, _, _ := u.screenPaddingImpl() ox, oy, _, _ := u.screenPaddingImpl()
s := u.scaleImpl() s := u.scaleImpl()
@ -371,62 +343,62 @@ func (u *userInterface) adjustPosition(x, y int) (int, int) {
return int(float64(x)/s - ox/as), int(float64(y)/s - oy/as) return int(float64(x)/s - ox/as), int(float64(y)/s - oy/as)
} }
func IsCursorVisible() bool { func (u *UserInterface) IsCursorVisible() bool {
return false return false
} }
func SetCursorVisible(visible bool) { func (u *UserInterface) SetCursorVisible(visible bool) {
// Do nothing // Do nothing
} }
func IsFullscreen() bool { func (u *UserInterface) IsFullscreen() bool {
return false return false
} }
func SetFullscreen(fullscreen bool) { func (u *UserInterface) SetFullscreen(fullscreen bool) {
// Do nothing // Do nothing
} }
func IsRunnableInBackground() bool { func (u *UserInterface) IsRunnableInBackground() bool {
return false return false
} }
func SetRunnableInBackground(runnableInBackground bool) { func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
// Do nothing // Do nothing
} }
func SetWindowTitle(title string) { func (u *UserInterface) SetWindowTitle(title string) {
// Do nothing // Do nothing
} }
func SetWindowIcon(iconImages []image.Image) { func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
// Do nothing // Do nothing
} }
func IsWindowDecorated() bool { func (u *UserInterface) IsWindowDecorated() bool {
return false return false
} }
func SetWindowDecorated(decorated bool) { func (u *UserInterface) SetWindowDecorated(decorated bool) {
// Do nothing // Do nothing
} }
func IsWindowResizable() bool { func (u *UserInterface) IsWindowResizable() bool {
return false return false
} }
func SetWindowResizable(decorated bool) { func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing // Do nothing
} }
func IsVsyncEnabled() bool { func (u *UserInterface) IsVsyncEnabled() bool {
return true return true
} }
func SetVsyncEnabled(enabled bool) { func (u *UserInterface) SetVsyncEnabled(enabled bool) {
// Do nothing // Do nothing
} }
func DeviceScaleFactor() float64 { func (u *UserInterface) DeviceScaleFactor() float64 {
return getDeviceScale() return getDeviceScale()
} }

View File

@ -20,7 +20,7 @@ import (
"errors" "errors"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/uidriver/mobile"
) )
var ( var (
@ -35,7 +35,7 @@ func update() error {
if !running { if !running {
return errors.New("mobile: start must be called ahead of update") return errors.New("mobile: start must be called ahead of update")
} }
return ui.Render(chError) return mobile.Get().Render(chError)
} }
func start(f func(*ebiten.Image) error, width, height int, scale float64, title string) { func start(f func(*ebiten.Image) error, width, height int, scale float64, title string) {

43
run.go
View File

@ -21,7 +21,6 @@ import (
"github.com/hajimehoshi/ebiten/internal/clock" "github.com/hajimehoshi/ebiten/internal/clock"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/input" "github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/ui"
"github.com/hajimehoshi/ebiten/internal/web" "github.com/hajimehoshi/ebiten/internal/web"
) )
@ -98,7 +97,7 @@ func run(width, height int, scale float64, title string, g *graphicsContext, mai
if !web.IsGopherJS() { if !web.IsGopherJS() {
defer atomic.StoreInt32(&isRunning, 0) defer atomic.StoreInt32(&isRunning, 0)
} }
if err := ui.Run(width, height, scale, title, g, mainloop, graphicsDriver(), input.Get()); err != nil { if err := uiDriver().Run(width, height, scale, title, g, mainloop, graphicsDriver(), input.Get()); err != nil {
if err == driver.RegularTermination { if err == driver.RegularTermination {
return nil return nil
} }
@ -157,7 +156,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
} }
}() }()
// TODO: Use context in Go 1.7? // TODO: Use context in Go 1.7?
if err := ui.Loop(ch); err != nil { if err := uiDriver().Loop(ch); err != nil {
return err return err
} }
return nil return nil
@ -214,7 +213,7 @@ func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64,
// //
// ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run. // ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
func ScreenSizeInFullscreen() (int, int) { func ScreenSizeInFullscreen() (int, int) {
return ui.ScreenSizeInFullscreen() return uiDriver().ScreenSizeInFullscreen()
} }
// MonitorSize is deprecated as of 1.8.0-alpha. Use ScreenSizeInFullscreen instead. // MonitorSize is deprecated as of 1.8.0-alpha. Use ScreenSizeInFullscreen instead.
@ -232,7 +231,7 @@ func SetScreenSize(width, height int) {
if width <= 0 || height <= 0 { if width <= 0 || height <= 0 {
panic("ebiten: width and height must be positive") panic("ebiten: width and height must be positive")
} }
ui.SetScreenSize(width, height) uiDriver().SetScreenSize(width, height)
} }
// SetScreenScale changes the scale of the screen. // SetScreenScale changes the scale of the screen.
@ -248,7 +247,7 @@ func SetScreenScale(scale float64) {
if scale <= 0 { if scale <= 0 {
panic("ebiten: scale must be positive") panic("ebiten: scale must be positive")
} }
ui.SetScreenScale(scale) uiDriver().SetScreenScale(scale)
} }
// ScreenScale returns the current screen scale. // ScreenScale returns the current screen scale.
@ -257,7 +256,7 @@ func SetScreenScale(scale float64) {
// //
// ScreenScale is concurrent-safe. // ScreenScale is concurrent-safe.
func ScreenScale() float64 { func ScreenScale() float64 {
return ui.ScreenScale() return uiDriver().ScreenScale()
} }
// IsCursorVisible returns a boolean value indicating whether // IsCursorVisible returns a boolean value indicating whether
@ -267,7 +266,7 @@ func ScreenScale() float64 {
// //
// IsCursorVisible is concurrent-safe. // IsCursorVisible is concurrent-safe.
func IsCursorVisible() bool { func IsCursorVisible() bool {
return ui.IsCursorVisible() return uiDriver().IsCursorVisible()
} }
// SetCursorVisible changes the state of cursor visiblity. // SetCursorVisible changes the state of cursor visiblity.
@ -276,7 +275,7 @@ func IsCursorVisible() bool {
// //
// SetCursorVisible is concurrent-safe. // SetCursorVisible is concurrent-safe.
func SetCursorVisible(visible bool) { func SetCursorVisible(visible bool) {
ui.SetCursorVisible(visible) uiDriver().SetCursorVisible(visible)
} }
// SetCursorVisibility is deprecated as of 1.6.0-alpha. Use SetCursorVisible instead. // SetCursorVisibility is deprecated as of 1.6.0-alpha. Use SetCursorVisible instead.
@ -291,7 +290,7 @@ func SetCursorVisibility(visible bool) {
// //
// IsFullscreen is concurrent-safe. // IsFullscreen is concurrent-safe.
func IsFullscreen() bool { func IsFullscreen() bool {
return ui.IsFullscreen() return uiDriver().IsFullscreen()
} }
// SetFullscreen changes the current mode to fullscreen or not. // SetFullscreen changes the current mode to fullscreen or not.
@ -312,7 +311,7 @@ func IsFullscreen() bool {
// //
// SetFullscreen is concurrent-safe. // SetFullscreen is concurrent-safe.
func SetFullscreen(fullscreen bool) { func SetFullscreen(fullscreen bool) {
ui.SetFullscreen(fullscreen) uiDriver().SetFullscreen(fullscreen)
} }
// IsRunnableInBackground returns a boolean value indicating whether // IsRunnableInBackground returns a boolean value indicating whether
@ -320,7 +319,7 @@ func SetFullscreen(fullscreen bool) {
// //
// IsRunnableInBackground is concurrent-safe. // IsRunnableInBackground is concurrent-safe.
func IsRunnableInBackground() bool { func IsRunnableInBackground() bool {
return ui.IsRunnableInBackground() return uiDriver().IsRunnableInBackground()
} }
// SetWindowDecorated sets the state if the window is decorated. // SetWindowDecorated sets the state if the window is decorated.
@ -334,14 +333,14 @@ func IsRunnableInBackground() bool {
// //
// SetWindowDecorated is concurrent-safe. // SetWindowDecorated is concurrent-safe.
func SetWindowDecorated(decorated bool) { func SetWindowDecorated(decorated bool) {
ui.SetWindowDecorated(decorated) uiDriver().SetWindowDecorated(decorated)
} }
// IsWindowDecorated reports whether the window is decorated. // IsWindowDecorated reports whether the window is decorated.
// //
// IsWindowDecorated is concurrent-safe. // IsWindowDecorated is concurrent-safe.
func IsWindowDecorated() bool { func IsWindowDecorated() bool {
return ui.IsWindowDecorated() return uiDriver().IsWindowDecorated()
} }
// setWindowResizable is unexported until specification is determined (#320) // setWindowResizable is unexported until specification is determined (#320)
@ -359,14 +358,14 @@ func IsWindowDecorated() bool {
// //
// setWindowResizable is concurrent-safe. // setWindowResizable is concurrent-safe.
func setWindowResizable(resizable bool) { func setWindowResizable(resizable bool) {
ui.SetWindowResizable(resizable) uiDriver().SetWindowResizable(resizable)
} }
// IsWindowResizable reports whether the window is resizable. // IsWindowResizable reports whether the window is resizable.
// //
// IsWindowResizable is concurrent-safe. // IsWindowResizable is concurrent-safe.
func IsWindowResizable() bool { func IsWindowResizable() bool {
return ui.IsWindowResizable() return uiDriver().IsWindowResizable()
} }
// SetRunnableInBackground sets the state if the game runs even in background. // SetRunnableInBackground sets the state if the game runs even in background.
@ -381,7 +380,7 @@ func IsWindowResizable() bool {
// //
// SetRunnableInBackground is concurrent-safe. // SetRunnableInBackground is concurrent-safe.
func SetRunnableInBackground(runnableInBackground bool) { func SetRunnableInBackground(runnableInBackground bool) {
ui.SetRunnableInBackground(runnableInBackground) uiDriver().SetRunnableInBackground(runnableInBackground)
} }
// SetWindowTitle sets the title of the window. // SetWindowTitle sets the title of the window.
@ -390,7 +389,7 @@ func SetRunnableInBackground(runnableInBackground bool) {
// //
// SetWindowTitle is concurrent-safe. // SetWindowTitle is concurrent-safe.
func SetWindowTitle(title string) { func SetWindowTitle(title string) {
ui.SetWindowTitle(title) uiDriver().SetWindowTitle(title)
} }
// SetWindowIcon sets the icon of the game window. // SetWindowIcon sets the icon of the game window.
@ -414,7 +413,7 @@ func SetWindowTitle(title string) {
// //
// SetWindowIcon is concurrent-safe. // SetWindowIcon is concurrent-safe.
func SetWindowIcon(iconImages []image.Image) { func SetWindowIcon(iconImages []image.Image) {
ui.SetWindowIcon(iconImages) uiDriver().SetWindowIcon(iconImages)
} }
// DeviceScaleFactor returns a device scale factor value of the current monitor which the window belongs to. // DeviceScaleFactor returns a device scale factor value of the current monitor which the window belongs to.
@ -427,7 +426,7 @@ func SetWindowIcon(iconImages []image.Image) {
// //
// DeviceScaleFactor must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run. // DeviceScaleFactor must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
func DeviceScaleFactor() float64 { func DeviceScaleFactor() float64 {
return ui.DeviceScaleFactor() return uiDriver().DeviceScaleFactor()
} }
// IsVsyncEnabled returns a boolean value indicating whether // IsVsyncEnabled returns a boolean value indicating whether
@ -435,7 +434,7 @@ func DeviceScaleFactor() float64 {
// //
// IsVsyncEnabled is concurrent-safe. // IsVsyncEnabled is concurrent-safe.
func IsVsyncEnabled() bool { func IsVsyncEnabled() bool {
return ui.IsVsyncEnabled() return uiDriver().IsVsyncEnabled()
} }
// SetVsyncEnabled sets a boolean value indicating whether // SetVsyncEnabled sets a boolean value indicating whether
@ -453,7 +452,7 @@ func IsVsyncEnabled() bool {
// //
// SetVsyncEnabled is concurrent-safe. // SetVsyncEnabled is concurrent-safe.
func SetVsyncEnabled(enabled bool) { func SetVsyncEnabled(enabled bool) {
ui.SetVsyncEnabled(enabled) uiDriver().SetVsyncEnabled(enabled)
} }
// MaxTPS returns the current maximum TPS. // MaxTPS returns the current maximum TPS.

29
ui_glfw.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2019 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 darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/uidriver/glfw"
)
func uiDriver() driver.UI {
return glfw.Get()
}

26
ui_js.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2019 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 js
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/uidriver/js"
)
func uiDriver() driver.UI {
return js.Get()
}

26
ui_mobile.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2019 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
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/uidriver/mobile"
)
func uiDriver() driver.UI {
return mobile.Get()
}