mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 11:18:54 +01:00
driver: Add UI.SetWindowSize and UIContext.Layout
This is a preparation to introduce RunGame function. Updates # 943 (Fix this line before committing)
This commit is contained in:
parent
4341c5ff1a
commit
bda11b0e17
@ -165,6 +165,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ebiten.SetScreenSize(screenWidth, screenHeight)
|
ebiten.SetScreenSize(screenWidth, screenHeight)
|
||||||
|
// TODO: Add a flag for compatibility mode and call SetScreenScale only when the flag is on.
|
||||||
ebiten.SetScreenScale(screenScale)
|
ebiten.SetScreenScale(screenScale)
|
||||||
ebiten.SetFullscreen(fullscreen)
|
ebiten.SetFullscreen(fullscreen)
|
||||||
ebiten.SetRunnableInBackground(runnableInBackground)
|
ebiten.SetRunnableInBackground(runnableInBackground)
|
||||||
|
@ -20,8 +20,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UIContext interface {
|
type UIContext interface {
|
||||||
SetSize(width, height int, scale float64)
|
|
||||||
Update(afterFrameUpdate func()) error
|
Update(afterFrameUpdate func()) error
|
||||||
|
Layout(outsideWidth, outsideHeight float64)
|
||||||
|
AdjustPosition(x, y float64) (float64, float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegularTermination represents a regular termination.
|
// RegularTermination represents a regular termination.
|
||||||
@ -40,23 +41,21 @@ type UI interface {
|
|||||||
IsVsyncEnabled() bool
|
IsVsyncEnabled() bool
|
||||||
IsWindowDecorated() bool
|
IsWindowDecorated() bool
|
||||||
IsWindowResizable() bool
|
IsWindowResizable() bool
|
||||||
ScreenPadding() (x0, y0, x1, y1 float64)
|
|
||||||
ScreenScale() float64
|
|
||||||
ScreenSizeInFullscreen() (int, int)
|
ScreenSizeInFullscreen() (int, int)
|
||||||
WindowPosition() (int, int)
|
WindowPosition() (int, int)
|
||||||
IsScreenTransparent() bool
|
IsScreenTransparent() bool
|
||||||
|
CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
|
||||||
|
|
||||||
SetCursorMode(mode CursorMode)
|
SetCursorMode(mode CursorMode)
|
||||||
SetFullscreen(fullscreen bool)
|
SetFullscreen(fullscreen bool)
|
||||||
SetRunnableInBackground(runnableInBackground bool)
|
SetRunnableInBackground(runnableInBackground bool)
|
||||||
SetScreenScale(scale float64)
|
|
||||||
SetScreenSize(width, height int)
|
|
||||||
SetVsyncEnabled(enabled bool)
|
SetVsyncEnabled(enabled bool)
|
||||||
SetWindowDecorated(decorated bool)
|
SetWindowDecorated(decorated bool)
|
||||||
SetWindowIcon(iconImages []image.Image)
|
SetWindowIcon(iconImages []image.Image)
|
||||||
SetWindowResizable(resizable bool)
|
SetWindowResizable(resizable bool)
|
||||||
SetWindowTitle(title string)
|
SetWindowTitle(title string)
|
||||||
SetWindowPosition(x, y int)
|
SetWindowPosition(x, y int)
|
||||||
|
SetWindowSize(width, height int)
|
||||||
SetScreenTransparent(transparent bool)
|
SetScreenTransparent(transparent bool)
|
||||||
|
|
||||||
Input() Input
|
Input() Input
|
||||||
|
@ -63,7 +63,7 @@ func (i *Input) CursorPosition() (x, y int) {
|
|||||||
cx, cy = i.cursorX, i.cursorY
|
cx, cy = i.cursorX, i.cursorY
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return i.ui.adjustPosition(cx, cy)
|
return cx, cy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadIDs() []int {
|
func (i *Input) GamepadIDs() []int {
|
||||||
@ -181,7 +181,7 @@ func (i *Input) TouchPosition(id int) (x, y int) {
|
|||||||
if !found {
|
if !found {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
return i.ui.adjustPosition(p.X, p.Y)
|
return p.X, p.Y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) RuneBuffer() []rune {
|
func (i *Input) RuneBuffer() []rune {
|
||||||
@ -284,7 +284,8 @@ func (i *Input) setWheel(xoff, yoff float64) {
|
|||||||
i.scrollY = yoff
|
i.scrollY = yoff
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) update(window *glfw.Window) {
|
func (i *Input) update(window *glfw.Window, context driver.UIContext) {
|
||||||
|
var cx, cy float64
|
||||||
_ = i.ui.t.Call(func() error {
|
_ = i.ui.t.Call(func() error {
|
||||||
i.onceCallback.Do(func() {
|
i.onceCallback.Do(func() {
|
||||||
window.SetCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) {
|
window.SetCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) {
|
||||||
@ -306,9 +307,17 @@ func (i *Input) update(window *glfw.Window) {
|
|||||||
for gb := range glfwMouseButtonToMouseButton {
|
for gb := range glfwMouseButtonToMouseButton {
|
||||||
i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press
|
i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press
|
||||||
}
|
}
|
||||||
x, y := window.GetCursorPos()
|
cx, cy = window.GetCursorPos()
|
||||||
i.cursorX = int(i.ui.toDeviceIndependentPixel(x) / i.ui.getScale())
|
cx = i.ui.toDeviceIndependentPixel(cx)
|
||||||
i.cursorY = int(i.ui.toDeviceIndependentPixel(y) / i.ui.getScale())
|
cy = i.ui.toDeviceIndependentPixel(cy)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cx, cy = context.AdjustPosition(cx, cy)
|
||||||
|
|
||||||
|
_ = i.ui.t.Call(func() error {
|
||||||
|
i.cursorX, i.cursorY = int(cx), int(cy)
|
||||||
|
|
||||||
for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ {
|
for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ {
|
||||||
i.gamepads[id].valid = false
|
i.gamepads[id].valid = false
|
||||||
if !id.Present() {
|
if !id.Present() {
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
@ -38,12 +37,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserInterface struct {
|
type UserInterface struct {
|
||||||
title string
|
title string
|
||||||
window *glfw.Window
|
window *glfw.Window
|
||||||
screenWidthInDP int
|
|
||||||
screenHeightInDP int
|
// windowWidth and windowHeight represents a window size.
|
||||||
scale float64
|
// The unit is device-dependent pixels.
|
||||||
fullscreenScale float64
|
windowWidth int
|
||||||
|
windowHeight int
|
||||||
|
|
||||||
running bool
|
running bool
|
||||||
toChangeSize bool
|
toChangeSize bool
|
||||||
@ -329,32 +329,6 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
|||||||
return w, h
|
return w, h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetScreenSize(width, height int) {
|
|
||||||
if !u.isRunning() {
|
|
||||||
panic("glfw: SetScreenSize can't be called before the main loop starts")
|
|
||||||
}
|
|
||||||
u.setScreenSize(width, height, u.scale, u.isFullscreen(), u.vsync)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) SetScreenScale(scale float64) {
|
|
||||||
if !u.isRunning() {
|
|
||||||
panic("glfw: SetScreenScale can't be called before the main loop starts")
|
|
||||||
}
|
|
||||||
u.setScreenSize(u.screenWidthInDP, u.screenHeightInDP, scale, u.isFullscreen(), u.vsync)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) ScreenScale() float64 {
|
|
||||||
if !u.isRunning() {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
s := 0.0
|
|
||||||
_ = u.t.Call(func() error {
|
|
||||||
s = u.scale
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
||||||
@ -380,7 +354,22 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
|||||||
u.setInitFullscreen(fullscreen)
|
u.setInitFullscreen(fullscreen)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u.setScreenSize(u.screenWidthInDP, u.screenHeightInDP, u.scale, fullscreen, u.vsync)
|
|
||||||
|
var update bool
|
||||||
|
_ = u.t.Call(func() error {
|
||||||
|
update = u.isFullscreen() != fullscreen
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if !update {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var w, h int
|
||||||
|
_ = u.t.Call(func() error {
|
||||||
|
w, h = u.windowWidth, u.windowHeight
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
u.setWindowSize(w, h, fullscreen, u.vsync)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
|
func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
|
||||||
@ -394,15 +383,20 @@ func (u *UserInterface) IsRunnableInBackground() bool {
|
|||||||
func (u *UserInterface) SetVsyncEnabled(enabled bool) {
|
func (u *UserInterface) SetVsyncEnabled(enabled bool) {
|
||||||
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 setWindowSize so far, but
|
||||||
// it should be OK since any goroutines can't reach here when
|
// it should be OK since any goroutines can't reach here when
|
||||||
// the game already starts and setScreenSize can be called.
|
// the game already starts and setWindowSize can be called.
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
u.vsync = enabled
|
u.vsync = enabled
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u.setScreenSize(u.screenWidthInDP, u.screenHeightInDP, u.scale, u.isFullscreen(), enabled)
|
var w, h int
|
||||||
|
_ = u.t.Call(func() error {
|
||||||
|
w, h = u.windowWidth, u.windowHeight
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
u.setWindowSize(w, h, u.isFullscreen(), enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) IsVsyncEnabled() bool {
|
func (u *UserInterface) IsVsyncEnabled() bool {
|
||||||
@ -434,59 +428,6 @@ func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
|
|
||||||
if !u.isRunning() {
|
|
||||||
return 0, 0, 0, 0
|
|
||||||
}
|
|
||||||
if !u.IsFullscreen() {
|
|
||||||
w, _ := u.window.GetSize()
|
|
||||||
wf := u.toDeviceIndependentPixel(float64(w)) / u.getScale()
|
|
||||||
if u.screenWidthInDP == int(wf) {
|
|
||||||
return 0, 0, 0, 0
|
|
||||||
}
|
|
||||||
// The window width can be bigger than the game screen width (#444).
|
|
||||||
ox := 0.0
|
|
||||||
_ = u.t.Call(func() error {
|
|
||||||
ox = (wf*u.actualScreenScale() - float64(u.screenWidthInDP)*u.actualScreenScale()) / 2
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return ox, 0, ox, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
d := 0.0
|
|
||||||
sx := 0.0
|
|
||||||
sy := 0.0
|
|
||||||
mx := 0.0
|
|
||||||
my := 0.0
|
|
||||||
_ = u.t.Call(func() error {
|
|
||||||
sx = float64(u.screenWidthInDP) * u.actualScreenScale()
|
|
||||||
sy = float64(u.screenHeightInDP) * u.actualScreenScale()
|
|
||||||
|
|
||||||
v := u.currentMonitor().GetVideoMode()
|
|
||||||
d = u.deviceScaleFactor()
|
|
||||||
mx = u.toDeviceIndependentPixel(float64(v.Width)) * d
|
|
||||||
my = u.toDeviceIndependentPixel(float64(v.Height)) * d
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ox := (mx - sx) / 2
|
|
||||||
oy := (my - sy) / 2
|
|
||||||
return ox, oy, (mx - sx) - ox, (my - sy) - oy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
|
||||||
if !u.isRunning() {
|
|
||||||
return x, y
|
|
||||||
}
|
|
||||||
ox, oy, _, _ := u.ScreenPadding()
|
|
||||||
s := 0.0
|
|
||||||
_ = u.t.Call(func() error {
|
|
||||||
s = u.actualScreenScale()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return x - int(ox/s), y - int(oy/s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) CursorMode() driver.CursorMode {
|
func (u *UserInterface) CursorMode() driver.CursorMode {
|
||||||
if !u.isRunning() {
|
if !u.isRunning() {
|
||||||
return u.getInitCursorMode()
|
return u.getInitCursorMode()
|
||||||
@ -684,11 +625,8 @@ func (u *UserInterface) createWindow() error {
|
|||||||
if u.isFullscreen() {
|
if u.isFullscreen() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
u.reqWidth = width
|
||||||
w := int(u.toDeviceIndependentPixel(float64(width)) / u.scale)
|
u.reqHeight = height
|
||||||
h := int(u.toDeviceIndependentPixel(float64(height)) / u.scale)
|
|
||||||
u.reqWidth = w
|
|
||||||
u.reqHeight = h
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -699,6 +637,7 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
|||||||
m *glfw.Monitor
|
m *glfw.Monitor
|
||||||
mx, my int
|
mx, my int
|
||||||
v *glfw.VidMode
|
v *glfw.VidMode
|
||||||
|
ww, wh int
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := u.t.Call(func() error {
|
if err := u.t.Call(func() error {
|
||||||
@ -757,6 +696,9 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
|||||||
mx, my = m.GetPos()
|
mx, my = m.GetPos()
|
||||||
v = m.GetVideoMode()
|
v = m.GetVideoMode()
|
||||||
|
|
||||||
|
ww = int(u.toDeviceDependentPixel(float64(width) * scale))
|
||||||
|
wh = int(u.toDeviceDependentPixel(float64(height) * scale))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -764,7 +706,7 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
|||||||
|
|
||||||
// The game is in window mode (not fullscreen mode) at the first state.
|
// The game is in window mode (not fullscreen mode) at the first state.
|
||||||
// Don't refer u.initFullscreen here to avoid some GLFW problems.
|
// Don't refer u.initFullscreen here to avoid some GLFW problems.
|
||||||
u.setScreenSize(width, height, scale, false, u.vsync)
|
u.setWindowSize(ww, wh, false, u.vsync)
|
||||||
|
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
// Get the window size before showing it. Showing the window might change the current monitor which
|
// Get the window size before showing it. Showing the window might change the current monitor which
|
||||||
@ -798,31 +740,13 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
|||||||
return u.loop(context)
|
return u.loop(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getScale must be called from the main thread.
|
|
||||||
func (u *UserInterface) getScale() float64 {
|
|
||||||
if !u.isFullscreen() {
|
|
||||||
return u.scale
|
|
||||||
}
|
|
||||||
if u.fullscreenScale == 0 {
|
|
||||||
v := u.currentMonitor().GetVideoMode()
|
|
||||||
sw := u.toDeviceIndependentPixel(float64(v.Width)) / float64(u.screenWidthInDP)
|
|
||||||
sh := u.toDeviceIndependentPixel(float64(v.Height)) / float64(u.screenHeightInDP)
|
|
||||||
s := sw
|
|
||||||
if s > sh {
|
|
||||||
s = sh
|
|
||||||
}
|
|
||||||
u.fullscreenScale = s
|
|
||||||
}
|
|
||||||
return u.fullscreenScale
|
|
||||||
}
|
|
||||||
|
|
||||||
// actualScreenScale must be called from the main thread.
|
|
||||||
func (u *UserInterface) actualScreenScale() float64 {
|
|
||||||
return u.getScale() * u.deviceScaleFactor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) updateSize(context driver.UIContext) {
|
func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||||
u.setScreenSize(u.screenWidthInDP, u.screenHeightInDP, u.scale, u.isFullscreen(), u.vsync)
|
var w, h int
|
||||||
|
_ = u.t.Call(func() error {
|
||||||
|
w, h = u.windowWidth, u.windowHeight
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
|
||||||
|
|
||||||
sizeChanged := false
|
sizeChanged := false
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
@ -835,12 +759,21 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
actualScale := 0.0
|
var w, h float64
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
actualScale = u.actualScreenScale()
|
var ww, wh int
|
||||||
|
if u.isFullscreen() {
|
||||||
|
v := u.currentMonitor().GetVideoMode()
|
||||||
|
ww = v.Width
|
||||||
|
wh = v.Height
|
||||||
|
} else {
|
||||||
|
ww, wh = u.windowWidth, u.windowHeight
|
||||||
|
}
|
||||||
|
w = u.toDeviceIndependentPixel(float64(ww))
|
||||||
|
h = u.toDeviceIndependentPixel(float64(wh))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
context.SetSize(u.screenWidthInDP, u.screenHeightInDP, actualScale)
|
context.Layout(w, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,7 +788,12 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.isInitFullscreen() {
|
if u.isInitFullscreen() {
|
||||||
u.setScreenSize(u.screenWidthInDP, u.screenHeightInDP, u.scale, true, u.vsync)
|
var w, h int
|
||||||
|
_ = u.t.Call(func() error {
|
||||||
|
w, h = u.window.GetSize()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
u.setWindowSize(w, h, true, u.vsync)
|
||||||
u.setInitFullscreen(false)
|
u.setInitFullscreen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +804,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
|||||||
glfw.PollEvents()
|
glfw.PollEvents()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
u.input.update(u.window)
|
u.input.update(u.window, context)
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
defer hooks.ResumeAudio()
|
defer hooks.ResumeAudio()
|
||||||
|
|
||||||
@ -895,7 +833,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if w != 0 || h != 0 {
|
if w != 0 || h != 0 {
|
||||||
u.setScreenSize(w, h, u.scale, u.isFullscreen(), u.vsync)
|
u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
|
||||||
}
|
}
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
u.reqWidth = 0
|
u.reqWidth = 0
|
||||||
@ -958,11 +896,11 @@ func (u *UserInterface) swapBuffers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscreen bool, vsync bool) {
|
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync bool) {
|
||||||
windowRecreated := false
|
windowRecreated := false
|
||||||
|
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
if u.screenWidthInDP == width && u.screenHeightInDP == height && u.scale == scale && u.isFullscreen() == fullscreen && u.vsync == vsync && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
|
if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.vsync == vsync && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,10 +911,6 @@ func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscre
|
|||||||
height = 1
|
height = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
u.screenWidthInDP = width
|
|
||||||
u.screenHeightInDP = height
|
|
||||||
u.scale = scale
|
|
||||||
u.fullscreenScale = 0
|
|
||||||
u.vsync = vsync
|
u.vsync = vsync
|
||||||
u.lastDeviceScaleFactor = u.deviceScaleFactor()
|
u.lastDeviceScaleFactor = u.deviceScaleFactor()
|
||||||
|
|
||||||
@ -1025,20 +959,34 @@ func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscre
|
|||||||
|
|
||||||
// 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.
|
// 126 is an arbitrary number and I guess this is small enough.
|
||||||
minWindowWidth := 252
|
minWindowWidth := int(u.toDeviceDependentPixel(126))
|
||||||
if u.window.GetAttrib(glfw.Decorated) == glfw.False {
|
if u.window.GetAttrib(glfw.Decorated) == glfw.False {
|
||||||
minWindowWidth = 1
|
minWindowWidth = 1
|
||||||
}
|
}
|
||||||
windowWidthInDP := width
|
if width < minWindowWidth {
|
||||||
s := scale * u.deviceScaleFactor()
|
width = minWindowWidth
|
||||||
if int(float64(width)*s) < minWindowWidth {
|
|
||||||
windowWidthInDP = int(math.Ceil(float64(minWindowWidth) / s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.origPosX != invalidPos && u.origPosY != invalidPos {
|
||||||
|
x := u.origPosX
|
||||||
|
y := u.origPosY
|
||||||
|
u.window.SetPos(x, y)
|
||||||
|
// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
|
||||||
|
// work with two or more SetPos.
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
u.window.SetPos(x+1, y)
|
||||||
|
u.window.SetPos(x, y)
|
||||||
|
}
|
||||||
|
u.origPosX = invalidPos
|
||||||
|
u.origPosY = invalidPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the window size after the position. The order matters.
|
||||||
|
// In the opposite order, the window size might not be correct when going back from fullscreen with multi monitors.
|
||||||
oldW, oldH := u.window.GetSize()
|
oldW, oldH := u.window.GetSize()
|
||||||
newW := int(u.toDeviceDependentPixel(float64(windowWidthInDP) * u.getScale()))
|
newW := width
|
||||||
newH := int(u.toDeviceDependentPixel(float64(u.screenHeightInDP) * u.getScale()))
|
newH := height
|
||||||
if oldW != newW || oldH != newH {
|
if oldW != newW || oldH != newH {
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
u.window.SetFramebufferSizeCallback(func(_ *glfw.Window, _, _ int) {
|
u.window.SetFramebufferSizeCallback(func(_ *glfw.Window, _, _ int) {
|
||||||
@ -1057,24 +1005,14 @@ func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.origPosX != invalidPos && u.origPosY != invalidPos {
|
|
||||||
x := u.origPosX
|
|
||||||
y := u.origPosY
|
|
||||||
u.window.SetPos(x, y)
|
|
||||||
// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
|
|
||||||
// work with two or more SetPos.
|
|
||||||
if runtime.GOOS == "darwin" {
|
|
||||||
u.window.SetPos(x+1, y)
|
|
||||||
u.window.SetPos(x, y)
|
|
||||||
}
|
|
||||||
u.origPosX = invalidPos
|
|
||||||
u.origPosY = invalidPos
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window title might be lost on macOS after coming back from fullscreen.
|
// Window title might be lost on macOS after coming back from fullscreen.
|
||||||
u.window.SetTitle(u.title)
|
u.window.SetTitle(u.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As width might be updated, update windowWidth/Height here.
|
||||||
|
u.windowWidth = width
|
||||||
|
u.windowHeight = height
|
||||||
|
|
||||||
if u.graphics.IsGL() {
|
if u.graphics.IsGL() {
|
||||||
// SwapInterval is affected by the current monitor of the window.
|
// SwapInterval is affected by the current monitor of the window.
|
||||||
// This needs to be called at least after SetMonitor.
|
// This needs to be called at least after SetMonitor.
|
||||||
@ -1162,6 +1100,19 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||||
|
if !u.isRunning() {
|
||||||
|
panic("glfw: SetWindowSize can't be called before the main loop starts")
|
||||||
|
}
|
||||||
|
w := int(u.toDeviceDependentPixel(float64(width)))
|
||||||
|
h := int(u.toDeviceDependentPixel(float64(height)))
|
||||||
|
u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) CanHaveWindow() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) Input() driver.Input {
|
func (u *UserInterface) Input() driver.Input {
|
||||||
return &u.input
|
return &u.input
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ type Input struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) CursorPosition() (x, y int) {
|
func (i *Input) CursorPosition() (x, y int) {
|
||||||
return i.ui.adjustPosition(i.cursorX, i.cursorY)
|
xf, yf := i.ui.context.AdjustPosition(float64(i.cursorX), float64(i.cursorY))
|
||||||
|
return int(xf), int(yf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadIDs() []int {
|
func (i *Input) GamepadIDs() []int {
|
||||||
@ -110,7 +111,8 @@ func (i *Input) TouchIDs() []int {
|
|||||||
func (i *Input) TouchPosition(id int) (x, y int) {
|
func (i *Input) TouchPosition(id int) (x, y int) {
|
||||||
for tid, pos := range i.touches {
|
for tid, pos := range i.touches {
|
||||||
if id == tid {
|
if id == tid {
|
||||||
return i.ui.adjustPosition(pos.X, pos.Y)
|
x, y := i.ui.context.AdjustPosition(float64(pos.X), float64(pos.Y))
|
||||||
|
return int(x), int(y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"log"
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -30,9 +29,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserInterface struct {
|
type UserInterface struct {
|
||||||
width int
|
|
||||||
height int
|
|
||||||
scale float64
|
|
||||||
runnableInBackground bool
|
runnableInBackground bool
|
||||||
vsync bool
|
vsync bool
|
||||||
running bool
|
running bool
|
||||||
@ -40,14 +36,10 @@ type UserInterface struct {
|
|||||||
sizeChanged bool
|
sizeChanged bool
|
||||||
contextLost bool
|
contextLost bool
|
||||||
|
|
||||||
lastActualScale float64
|
lastDeviceScaleFactor float64
|
||||||
|
|
||||||
context driver.UIContext
|
context driver.UIContext
|
||||||
input Input
|
input Input
|
||||||
|
|
||||||
// pseudoScale is a value to store 'scale'. This doesn't affect actual rendering.
|
|
||||||
// This is for backward compatibility.
|
|
||||||
pseudoScale float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var theUI = &UserInterface{
|
var theUI = &UserInterface{
|
||||||
@ -75,18 +67,6 @@ 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 (u *UserInterface) SetScreenSize(width, height int) {
|
|
||||||
u.setScreenSize(width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) SetScreenScale(scale float64) {
|
|
||||||
u.pseudoScale = scale
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) ScreenScale() float64 {
|
|
||||||
return u.pseudoScale
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
@ -111,24 +91,11 @@ func (u *UserInterface) IsVsyncEnabled() bool {
|
|||||||
return u.vsync
|
return u.vsync
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
|
|
||||||
return 0, 0, 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
|
||||||
rect := canvas.Call("getBoundingClientRect")
|
|
||||||
x -= rect.Get("left").Int()
|
|
||||||
y -= rect.Get("top").Int()
|
|
||||||
s := u.scale
|
|
||||||
return int(float64(x) / s), int(float64(y) / s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) CursorMode() driver.CursorMode {
|
func (u *UserInterface) CursorMode() driver.CursorMode {
|
||||||
if canvas.Get("style").Get("cursor").String() != "none" {
|
if canvas.Get("style").Get("cursor").String() != "none" {
|
||||||
return driver.CursorModeVisible
|
return driver.CursorModeVisible
|
||||||
} else {
|
|
||||||
return driver.CursorModeHidden
|
|
||||||
}
|
}
|
||||||
|
return driver.CursorModeHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
||||||
@ -150,6 +117,7 @@ func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetWindowTitle(title string) {
|
func (u *UserInterface) SetWindowTitle(title string) {
|
||||||
|
// TODO: As the page should be in an iframe, this might be useless.
|
||||||
document.Set("title", title)
|
document.Set("title", title)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,34 +139,31 @@ func (u *UserInterface) IsWindowResizable() bool {
|
|||||||
|
|
||||||
func (u *UserInterface) SetWindowResizable(decorated bool) {
|
func (u *UserInterface) SetWindowResizable(decorated bool) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
if u.running {
|
}
|
||||||
panic("js: SetWindowResizable can't be called after the main loop starts")
|
|
||||||
}
|
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||||
|
// TODO: This is too tricky: Even though browsers don't have windows, SetWindowSize is called whenever the
|
||||||
|
// screen size is changed. Fix this hack.
|
||||||
|
u.sizeChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) DeviceScaleFactor() float64 {
|
func (u *UserInterface) DeviceScaleFactor() float64 {
|
||||||
return devicescale.GetAt(0, 0)
|
return devicescale.GetAt(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) actualScreenScale() float64 {
|
|
||||||
// CSS imageRendering property seems useful to enlarge the screen,
|
|
||||||
// but doesn't work in some cases (#306):
|
|
||||||
// * Chrome just after restoring the lost context
|
|
||||||
// * Safari
|
|
||||||
// Let's use the devicePixelRatio as it is here.
|
|
||||||
return u.scale * devicescale.GetAt(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) updateSize() {
|
func (u *UserInterface) updateSize() {
|
||||||
a := u.actualScreenScale()
|
a := u.DeviceScaleFactor()
|
||||||
if u.lastActualScale != a {
|
if u.lastDeviceScaleFactor != a {
|
||||||
u.updateScreenSize()
|
u.updateScreenSize()
|
||||||
}
|
}
|
||||||
u.lastActualScale = a
|
u.lastDeviceScaleFactor = a
|
||||||
|
|
||||||
if u.sizeChanged {
|
if u.sizeChanged {
|
||||||
u.sizeChanged = false
|
u.sizeChanged = false
|
||||||
u.context.SetSize(u.width, u.height, a)
|
body := document.Get("body")
|
||||||
|
bw := body.Get("clientWidth").Float()
|
||||||
|
bh := body.Get("clientHeight").Float()
|
||||||
|
u.context.Layout(bw, bh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +271,7 @@ func init() {
|
|||||||
canvas = document.Call("createElement", "canvas")
|
canvas = document.Call("createElement", "canvas")
|
||||||
canvas.Set("width", 16)
|
canvas.Set("width", 16)
|
||||||
canvas.Set("height", 16)
|
canvas.Set("height", 16)
|
||||||
|
|
||||||
document.Get("body").Call("appendChild", canvas)
|
document.Get("body").Call("appendChild", canvas)
|
||||||
|
|
||||||
htmlStyle := document.Get("documentElement").Get("style")
|
htmlStyle := document.Get("documentElement").Get("style")
|
||||||
@ -315,13 +281,9 @@ func init() {
|
|||||||
|
|
||||||
bodyStyle := document.Get("body").Get("style")
|
bodyStyle := document.Get("body").Get("style")
|
||||||
bodyStyle.Set("backgroundColor", "#000")
|
bodyStyle.Set("backgroundColor", "#000")
|
||||||
bodyStyle.Set("position", "relative")
|
|
||||||
bodyStyle.Set("height", "100%")
|
bodyStyle.Set("height", "100%")
|
||||||
bodyStyle.Set("margin", "0")
|
bodyStyle.Set("margin", "0")
|
||||||
bodyStyle.Set("padding", "0")
|
bodyStyle.Set("padding", "0")
|
||||||
bodyStyle.Set("display", "flex")
|
|
||||||
bodyStyle.Set("alignItems", "center")
|
|
||||||
bodyStyle.Set("justifyContent", "center")
|
|
||||||
|
|
||||||
// TODO: This is OK as long as the game is in an independent iframe.
|
// TODO: This is OK as long as the game is in an independent iframe.
|
||||||
// What if the canvas is embedded in a HTML directly?
|
// What if the canvas is embedded in a HTML directly?
|
||||||
@ -331,7 +293,10 @@ func init() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
canvasStyle := canvas.Get("style")
|
canvasStyle := canvas.Get("style")
|
||||||
canvasStyle.Set("position", "absolute")
|
canvasStyle.Set("width", "100%")
|
||||||
|
canvasStyle.Set("height", "100%")
|
||||||
|
canvasStyle.Set("margin", "0")
|
||||||
|
canvasStyle.Set("padding", "0")
|
||||||
|
|
||||||
// Make the canvas focusable.
|
// Make the canvas focusable.
|
||||||
canvas.Call("setAttribute", "tabindex", 1)
|
canvas.Call("setAttribute", "tabindex", 1)
|
||||||
@ -438,11 +403,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) Run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error {
|
func (u *UserInterface) Run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error {
|
||||||
// scale is ignored.
|
|
||||||
|
|
||||||
document.Set("title", title)
|
document.Set("title", title)
|
||||||
u.setScreenSize(width, height)
|
|
||||||
u.pseudoScale = scale
|
|
||||||
canvas.Call("focus")
|
canvas.Call("focus")
|
||||||
u.running = true
|
u.running = true
|
||||||
ch := u.loop(context)
|
ch := u.loop(context)
|
||||||
@ -467,37 +428,12 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
|
|||||||
panic("js: RunWithoutMainLoop is not implemented")
|
panic("js: RunWithoutMainLoop is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) setScreenSize(width, height int) bool {
|
|
||||||
if u.width == width && u.height == height {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
u.width = width
|
|
||||||
u.height = height
|
|
||||||
u.updateScreenSize()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) updateScreenSize() {
|
func (u *UserInterface) updateScreenSize() {
|
||||||
body := document.Get("body")
|
body := document.Get("body")
|
||||||
bw := body.Get("clientWidth").Float()
|
bw := int(body.Get("clientWidth").Float() * u.DeviceScaleFactor())
|
||||||
bh := body.Get("clientHeight").Float()
|
bh := int(body.Get("clientHeight").Float() * u.DeviceScaleFactor())
|
||||||
sw := bw / float64(u.width)
|
canvas.Set("width", bw)
|
||||||
sh := bh / float64(u.height)
|
canvas.Set("height", bh)
|
||||||
if sw > sh {
|
|
||||||
u.scale = sh
|
|
||||||
} else {
|
|
||||||
u.scale = sw
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.Set("width", int(float64(u.width)*u.actualScreenScale()))
|
|
||||||
canvas.Set("height", int(float64(u.height)*u.actualScreenScale()))
|
|
||||||
canvasStyle := canvas.Get("style")
|
|
||||||
|
|
||||||
cssWidth := int(float64(u.width) * u.scale)
|
|
||||||
cssHeight := int(float64(u.height) * u.scale)
|
|
||||||
canvasStyle.Set("width", strconv.Itoa(cssWidth)+"px")
|
|
||||||
canvasStyle.Set("height", strconv.Itoa(cssHeight)+"px")
|
|
||||||
|
|
||||||
u.sizeChanged = true
|
u.sizeChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,6 +466,10 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
|||||||
return bodyStyle.Get("backgroundColor").String() == "transparent"
|
return bodyStyle.Get("backgroundColor").String() == "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) CanHaveWindow() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) Input() driver.Input {
|
func (u *UserInterface) Input() driver.Input {
|
||||||
return &u.input
|
return &u.input
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ func (u *UserInterface) appMain(a app.App) {
|
|||||||
glctx = nil
|
glctx = nil
|
||||||
}
|
}
|
||||||
case size.Event:
|
case size.Event:
|
||||||
u.setGBuildImpl(e.WidthPx, e.HeightPx)
|
u.setGBuild(e.WidthPx, e.HeightPx)
|
||||||
case paint.Event:
|
case paint.Event:
|
||||||
if glctx == nil || e.External {
|
if glctx == nil || e.External {
|
||||||
continue
|
continue
|
||||||
@ -236,30 +236,37 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) updateSize(context driver.UIContext) {
|
func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||||
width, height := 0, 0
|
var width, height float64
|
||||||
actualScale := 0.0
|
|
||||||
|
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
sizeChanged := u.sizeChanged
|
sizeChanged := u.sizeChanged
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
width = u.width
|
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||||
height = u.height
|
s := u.scaleImpl()
|
||||||
actualScale = u.scaleImpl() * deviceScale()
|
width = float64(u.width) * s
|
||||||
|
height = float64(u.height) * s
|
||||||
|
} else {
|
||||||
|
// gomobile build
|
||||||
|
d := deviceScale()
|
||||||
|
width = float64(u.gbuildWidthPx) / d
|
||||||
|
height = float64(u.gbuildHeightPx) / d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
u.sizeChanged = false
|
u.sizeChanged = false
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
|
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
// Sizing also calls GL functions
|
// Dirty hack to set the offscreen size for gomobile-bind.
|
||||||
context.SetSize(width, height, actualScale)
|
// TODO: Remove this. The layouting logic must be in the package ebiten, not here.
|
||||||
}
|
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||||
}
|
context.(interface {
|
||||||
|
SetScreenSize(width, height int)
|
||||||
|
}).SetScreenSize(u.width, u.height)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) ActualScale() float64 {
|
// Sizing also calls GL functions
|
||||||
u.m.Lock()
|
context.Layout(width, height)
|
||||||
s := u.scaleImpl() * deviceScale()
|
}
|
||||||
u.m.Unlock()
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) scaleImpl() float64 {
|
func (u *UserInterface) scaleImpl() float64 {
|
||||||
@ -295,47 +302,26 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) ScreenSize() (int, int) {
|
|
||||||
u.m.Lock()
|
|
||||||
w, h := u.width, u.height
|
|
||||||
u.m.Unlock()
|
|
||||||
return w, h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
||||||
// TODO: This function should return gbuildWidthPx, gbuildHeightPx,
|
// TODO: This function should return gbuildWidthPx, gbuildHeightPx,
|
||||||
// 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 (u *UserInterface) SetScreenSize(width, height int) {
|
func (u *UserInterface) SetScreenSizeAndScale(width, height int, scale float64) {
|
||||||
|
// Called from ebitenmobileview.
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
if u.width != width || u.height != height {
|
if u.width != width || u.height != height || u.scale != scale {
|
||||||
u.width = width
|
u.width = width
|
||||||
u.height = height
|
u.height = height
|
||||||
|
u.scale = scale
|
||||||
u.updateGBuildScaleIfNeeded()
|
u.updateGBuildScaleIfNeeded()
|
||||||
u.sizeChanged = true
|
u.sizeChanged = true
|
||||||
}
|
}
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetScreenScale(scale float64) {
|
func (u *UserInterface) setGBuild(widthPx, heightPx int) {
|
||||||
u.m.Lock()
|
|
||||||
if u.scale != scale {
|
|
||||||
u.scale = scale
|
|
||||||
u.sizeChanged = true
|
|
||||||
}
|
|
||||||
u.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) ScreenScale() float64 {
|
|
||||||
u.m.RLock()
|
|
||||||
s := u.scale
|
|
||||||
u.m.RUnlock()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) setGBuildImpl(widthPx, heightPx int) {
|
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
u.gbuildWidthPx = widthPx
|
u.gbuildWidthPx = widthPx
|
||||||
u.gbuildHeightPx = heightPx
|
u.gbuildHeightPx = heightPx
|
||||||
@ -348,6 +334,7 @@ func (u *UserInterface) updateGBuildScaleIfNeeded() {
|
|||||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w, h := u.width, u.height
|
w, h := u.width, u.height
|
||||||
scaleX := float64(u.gbuildWidthPx) / float64(w)
|
scaleX := float64(u.gbuildWidthPx) / float64(w)
|
||||||
scaleY := float64(u.gbuildHeightPx) / float64(h)
|
scaleY := float64(u.gbuildHeightPx) / float64(h)
|
||||||
@ -359,14 +346,8 @@ func (u *UserInterface) updateGBuildScaleIfNeeded() {
|
|||||||
u.sizeChanged = true
|
u.sizeChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) ScreenPadding() (x0, y0, x1, y1 float64) {
|
|
||||||
u.m.Lock()
|
|
||||||
x0, y0, x1, y1 = u.screenPaddingImpl()
|
|
||||||
u.m.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
|
func (u *UserInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
|
||||||
|
// TODO: Replace this with UIContext's Layout.
|
||||||
if u.gbuildScale == 0 {
|
if u.gbuildScale == 0 {
|
||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
}
|
}
|
||||||
@ -377,6 +358,7 @@ func (u *UserInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
||||||
|
// TODO: Replace this with UIContext's AdjustPosition.
|
||||||
ox, oy, _, _ := u.screenPaddingImpl()
|
ox, oy, _, _ := u.screenPaddingImpl()
|
||||||
s := u.scaleImpl()
|
s := u.scaleImpl()
|
||||||
as := s * deviceScale()
|
as := s * deviceScale()
|
||||||
@ -459,6 +441,14 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) CanHaveWindow() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) Input() driver.Input {
|
func (u *UserInterface) Input() driver.Input {
|
||||||
return &u.input
|
return &u.input
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,7 @@ func layout(viewWidth, viewHeight int, viewRectSetter ViewRectSetter) {
|
|||||||
y := (viewHeight - height) / 2
|
y := (viewHeight - height) / 2
|
||||||
|
|
||||||
if theState.isRunning() {
|
if theState.isRunning() {
|
||||||
mobile.Get().SetScreenSize(w, h)
|
mobile.Get().SetScreenSizeAndScale(w, h, scale)
|
||||||
mobile.Get().SetScreenScale(scale)
|
|
||||||
} else {
|
} else {
|
||||||
// The last argument 'title' is not used on mobile platforms, so just pass an empty string.
|
// The last argument 'title' is not used on mobile platforms, so just pass an empty string.
|
||||||
theState.errorCh = ebiten.RunWithoutMainLoop(theState.game.Update, w, h, scale, "")
|
theState.errorCh = ebiten.RunWithoutMainLoop(theState.game.Update, w, h, scale, "")
|
||||||
|
31
run.go
31
run.go
@ -151,8 +151,8 @@ func IsRunningSlowly() bool {
|
|||||||
func Run(f func(*Image) error, width, height int, scale float64, title string) error {
|
func Run(f func(*Image) error, width, height int, scale float64, title string) error {
|
||||||
f = (&imageDumper{f: f}).update
|
f = (&imageDumper{f: f}).update
|
||||||
|
|
||||||
c := newUIContext(f)
|
theUIContext = newUIContext(f, width, height, scale)
|
||||||
if err := uiDriver().Run(width, height, scale, title, c, graphicsDriver()); err != nil {
|
if err := uiDriver().Run(width, height, scale, title, theUIContext, graphicsDriver()); err != nil {
|
||||||
if err == driver.RegularTermination {
|
if err == driver.RegularTermination {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -170,8 +170,8 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
|
|||||||
func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64, title string) <-chan error {
|
func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64, title string) <-chan error {
|
||||||
f = (&imageDumper{f: f}).update
|
f = (&imageDumper{f: f}).update
|
||||||
|
|
||||||
c := newUIContext(f)
|
theUIContext = newUIContext(f, width, height, scale)
|
||||||
return uiDriver().RunWithoutMainLoop(width, height, scale, title, c, graphicsDriver())
|
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext, graphicsDriver())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
|
// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
|
||||||
@ -221,7 +221,12 @@ 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")
|
||||||
}
|
}
|
||||||
uiDriver().SetScreenSize(width, height)
|
if theUIContext == nil {
|
||||||
|
panic("ebiten: SetScreenSize can't be called before the main loop starts")
|
||||||
|
}
|
||||||
|
theUIContext.SetScreenSize(width, height)
|
||||||
|
s := theUIContext.getScaleForWindow()
|
||||||
|
uiDriver().SetWindowSize(int(float64(width)*s), int(float64(height)*s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenScale changes the scale of the screen on desktops.
|
// SetScreenScale changes the scale of the screen on desktops.
|
||||||
@ -243,11 +248,18 @@ func SetScreenSize(width, height int) {
|
|||||||
// SetScreenScale panics if scale is not a positive number.
|
// SetScreenScale panics if scale is not a positive number.
|
||||||
//
|
//
|
||||||
// SetScreenScale is concurrent-safe.
|
// SetScreenScale is concurrent-safe.
|
||||||
|
//
|
||||||
|
// TODO: Deprecate this function.
|
||||||
func SetScreenScale(scale float64) {
|
func SetScreenScale(scale float64) {
|
||||||
if scale <= 0 {
|
if scale <= 0 {
|
||||||
panic("ebiten: scale must be positive")
|
panic("ebiten: scale must be positive")
|
||||||
}
|
}
|
||||||
uiDriver().SetScreenScale(scale)
|
if theUIContext == nil {
|
||||||
|
panic("ebiten: SetScreenScale can't be called before the main loop starts")
|
||||||
|
}
|
||||||
|
theUIContext.setScaleForWindow(scale)
|
||||||
|
w, h := theUIContext.size()
|
||||||
|
uiDriver().SetWindowSize(int(float64(w)*scale), int(float64(h)*scale))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScreenScale returns the current screen scale.
|
// ScreenScale returns the current screen scale.
|
||||||
@ -257,8 +269,13 @@ func SetScreenScale(scale float64) {
|
|||||||
// If Run is not called, this returns 0.
|
// If Run is not called, this returns 0.
|
||||||
//
|
//
|
||||||
// ScreenScale is concurrent-safe.
|
// ScreenScale is concurrent-safe.
|
||||||
|
//
|
||||||
|
// TODO: Deprecate this function.
|
||||||
func ScreenScale() float64 {
|
func ScreenScale() float64 {
|
||||||
return uiDriver().ScreenScale()
|
if theUIContext == nil {
|
||||||
|
panic("ebiten: ScreenScale can't be called before the main loop starts")
|
||||||
|
}
|
||||||
|
return theUIContext.getScaleForWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CursorMode returns the current cursor mode.
|
// CursorMode returns the current cursor mode.
|
||||||
|
116
uicontext.go
116
uicontext.go
@ -17,6 +17,7 @@ package ebiten
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/buffered"
|
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||||
"github.com/hajimehoshi/ebiten/internal/clock"
|
"github.com/hajimehoshi/ebiten/internal/clock"
|
||||||
@ -31,45 +32,99 @@ func init() {
|
|||||||
graphicscommand.SetGraphicsDriver(graphicsDriver())
|
graphicscommand.SetGraphicsDriver(graphicsDriver())
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUIContext(f func(*Image) error) *uiContext {
|
func newUIContext(f func(*Image) error, width, height int, scaleForWindow float64) *uiContext {
|
||||||
return &uiContext{
|
return &uiContext{
|
||||||
f: f,
|
f: f,
|
||||||
|
screenWidth: width,
|
||||||
|
screenHeight: height,
|
||||||
|
scaleForWindow: scaleForWindow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type uiContext struct {
|
type uiContext struct {
|
||||||
f func(*Image) error
|
f func(*Image) error
|
||||||
offscreen *Image
|
offscreen *Image
|
||||||
screen *Image
|
screen *Image
|
||||||
screenWidth int
|
screenWidth int
|
||||||
screenHeight int
|
screenHeight int
|
||||||
screenScale float64
|
screenScale float64
|
||||||
offsetX float64
|
scaleForWindow float64
|
||||||
offsetY float64
|
offsetX float64
|
||||||
|
offsetY float64
|
||||||
|
|
||||||
|
reqWidth int
|
||||||
|
reqHeight int
|
||||||
|
m sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *uiContext) SetSize(screenWidth, screenHeight int, screenScale float64) {
|
var theUIContext *uiContext
|
||||||
c.screenScale = screenScale
|
|
||||||
|
|
||||||
if c.screen != nil {
|
func (c *uiContext) resolveSize() (int, int) {
|
||||||
_ = c.screen.Dispose()
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
if c.reqWidth != 0 || c.reqHeight != 0 {
|
||||||
|
c.screenWidth = c.reqWidth
|
||||||
|
c.screenHeight = c.reqHeight
|
||||||
|
c.reqWidth = 0
|
||||||
|
c.reqHeight = 0
|
||||||
}
|
}
|
||||||
if c.offscreen != nil {
|
if c.offscreen != nil {
|
||||||
_ = c.offscreen.Dispose()
|
if w, h := c.offscreen.Size(); w != c.screenWidth || h != c.screenHeight {
|
||||||
|
// The offscreen might still be used somewhere. Do not Dispose it. Finalizer will do that.
|
||||||
|
c.offscreen = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.offscreen == nil {
|
||||||
|
c.offscreen = newImage(c.screenWidth, c.screenHeight, FilterDefault, true)
|
||||||
|
}
|
||||||
|
return c.screenWidth, c.screenHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) size() (int, int) {
|
||||||
|
return c.resolveSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) setScaleForWindow(scale float64) {
|
||||||
|
c.scaleForWindow = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) getScaleForWindow() float64 {
|
||||||
|
return c.scaleForWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) SetScreenSize(width, height int) {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
// TODO: Use the interface Game's Layout and then update screenWidth and screenHeight, then this function
|
||||||
|
// is no longer needed.
|
||||||
|
c.reqWidth = width
|
||||||
|
c.reqHeight = height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
|
||||||
|
if c.screen != nil {
|
||||||
|
_ = c.screen.Dispose()
|
||||||
|
c.screen = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.offscreen = newImage(screenWidth, screenHeight, FilterDefault, true)
|
// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
|
||||||
|
d := uiDriver().DeviceScaleFactor()
|
||||||
|
c.screen = newScreenFramebufferImage(int(outsideWidth*d), int(outsideHeight*d))
|
||||||
|
|
||||||
// Round up the screensize not to cause glitches e.g. on Xperia (#622)
|
sw, sh := c.resolveSize()
|
||||||
w := int(math.Ceil(float64(screenWidth) * screenScale))
|
scaleX := float64(outsideWidth) / float64(sw) * d
|
||||||
h := int(math.Ceil(float64(screenHeight) * screenScale))
|
scaleY := float64(outsideHeight) / float64(sh) * d
|
||||||
px0, py0, px1, py1 := uiDriver().ScreenPadding()
|
c.screenScale = math.Min(scaleX, scaleY)
|
||||||
c.screen = newScreenFramebufferImage(w+int(math.Ceil(px0+px1)), h+int(math.Ceil(py0+py1)))
|
if uiDriver().CanHaveWindow() && !uiDriver().IsFullscreen() {
|
||||||
c.screenWidth = w
|
// When the UI driver cannot have a window, scaleForWindow is updated only via setScaleFowWindow.
|
||||||
c.screenHeight = h
|
c.scaleForWindow = c.screenScale / d
|
||||||
|
}
|
||||||
|
|
||||||
c.offsetX = px0
|
width := float64(sw) * c.screenScale
|
||||||
c.offsetY = py0
|
height := float64(sh) * c.screenScale
|
||||||
|
c.offsetX = (float64(outsideWidth)*d - width) / 2
|
||||||
|
c.offsetY = (float64(outsideHeight)*d - height) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *uiContext) Update(afterFrameUpdate func()) error {
|
func (c *uiContext) Update(afterFrameUpdate func()) error {
|
||||||
@ -82,17 +137,17 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < updateCount; i++ {
|
for i := 0; i < updateCount; i++ {
|
||||||
|
// Mipmap images should be disposed by Clear.
|
||||||
c.offscreen.Clear()
|
c.offscreen.Clear()
|
||||||
// Mipmap images should be disposed by fill.
|
|
||||||
|
|
||||||
setDrawingSkipped(i < updateCount-1)
|
setDrawingSkipped(i < updateCount-1)
|
||||||
|
|
||||||
if err := hooks.RunBeforeUpdateHooks(); err != nil {
|
if err := hooks.RunBeforeUpdateHooks(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.f(c.offscreen); err != nil {
|
if err := c.f(c.offscreen); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
uiDriver().Input().ResetForFrame()
|
uiDriver().Input().ResetForFrame()
|
||||||
afterFrameUpdate()
|
afterFrameUpdate()
|
||||||
}
|
}
|
||||||
@ -107,7 +162,7 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
|||||||
// c.screen is special: its Y axis is down to up,
|
// c.screen is special: its Y axis is down to up,
|
||||||
// and the origin point is lower left.
|
// and the origin point is lower left.
|
||||||
op.GeoM.Scale(c.screenScale, -c.screenScale)
|
op.GeoM.Scale(c.screenScale, -c.screenScale)
|
||||||
op.GeoM.Translate(0, float64(c.screenHeight))
|
op.GeoM.Translate(0, float64(c.screenHeight)*c.screenScale)
|
||||||
case driver.VUpward:
|
case driver.VUpward:
|
||||||
op.GeoM.Scale(c.screenScale, c.screenScale)
|
op.GeoM.Scale(c.screenScale, c.screenScale)
|
||||||
default:
|
default:
|
||||||
@ -131,3 +186,8 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *uiContext) AdjustPosition(x, y float64) (float64, float64) {
|
||||||
|
d := uiDriver().DeviceScaleFactor()
|
||||||
|
return (x*d - c.offsetX) / c.screenScale, (y*d - c.offsetY) / c.screenScale
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user