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)
|
||||
// TODO: Add a flag for compatibility mode and call SetScreenScale only when the flag is on.
|
||||
ebiten.SetScreenScale(screenScale)
|
||||
ebiten.SetFullscreen(fullscreen)
|
||||
ebiten.SetRunnableInBackground(runnableInBackground)
|
||||
|
@ -20,8 +20,9 @@ import (
|
||||
)
|
||||
|
||||
type UIContext interface {
|
||||
SetSize(width, height int, scale float64)
|
||||
Update(afterFrameUpdate func()) error
|
||||
Layout(outsideWidth, outsideHeight float64)
|
||||
AdjustPosition(x, y float64) (float64, float64)
|
||||
}
|
||||
|
||||
// RegularTermination represents a regular termination.
|
||||
@ -40,23 +41,21 @@ type UI interface {
|
||||
IsVsyncEnabled() bool
|
||||
IsWindowDecorated() bool
|
||||
IsWindowResizable() bool
|
||||
ScreenPadding() (x0, y0, x1, y1 float64)
|
||||
ScreenScale() float64
|
||||
ScreenSizeInFullscreen() (int, int)
|
||||
WindowPosition() (int, int)
|
||||
IsScreenTransparent() bool
|
||||
CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
|
||||
|
||||
SetCursorMode(mode CursorMode)
|
||||
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)
|
||||
SetWindowPosition(x, y int)
|
||||
SetWindowSize(width, height int)
|
||||
SetScreenTransparent(transparent bool)
|
||||
|
||||
Input() Input
|
||||
|
@ -63,7 +63,7 @@ func (i *Input) CursorPosition() (x, y int) {
|
||||
cx, cy = i.cursorX, i.cursorY
|
||||
return nil
|
||||
})
|
||||
return i.ui.adjustPosition(cx, cy)
|
||||
return cx, cy
|
||||
}
|
||||
|
||||
func (i *Input) GamepadIDs() []int {
|
||||
@ -181,7 +181,7 @@ func (i *Input) TouchPosition(id int) (x, y int) {
|
||||
if !found {
|
||||
return 0, 0
|
||||
}
|
||||
return i.ui.adjustPosition(p.X, p.Y)
|
||||
return p.X, p.Y
|
||||
}
|
||||
|
||||
func (i *Input) RuneBuffer() []rune {
|
||||
@ -284,7 +284,8 @@ func (i *Input) setWheel(xoff, yoff float64) {
|
||||
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.onceCallback.Do(func() {
|
||||
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 {
|
||||
i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press
|
||||
}
|
||||
x, y := window.GetCursorPos()
|
||||
i.cursorX = int(i.ui.toDeviceIndependentPixel(x) / i.ui.getScale())
|
||||
i.cursorY = int(i.ui.toDeviceIndependentPixel(y) / i.ui.getScale())
|
||||
cx, cy = window.GetCursorPos()
|
||||
cx = i.ui.toDeviceIndependentPixel(cx)
|
||||
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++ {
|
||||
i.gamepads[id].valid = false
|
||||
if !id.Present() {
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
@ -38,12 +37,13 @@ import (
|
||||
)
|
||||
|
||||
type UserInterface struct {
|
||||
title string
|
||||
window *glfw.Window
|
||||
screenWidthInDP int
|
||||
screenHeightInDP int
|
||||
scale float64
|
||||
fullscreenScale float64
|
||||
title string
|
||||
window *glfw.Window
|
||||
|
||||
// windowWidth and windowHeight represents a window size.
|
||||
// The unit is device-dependent pixels.
|
||||
windowWidth int
|
||||
windowHeight int
|
||||
|
||||
running bool
|
||||
toChangeSize bool
|
||||
@ -329,32 +329,6 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
||||
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.
|
||||
func (u *UserInterface) isFullscreen() bool {
|
||||
if !u.isRunning() {
|
||||
@ -380,7 +354,22 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
||||
u.setInitFullscreen(fullscreen)
|
||||
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) {
|
||||
@ -394,15 +383,20 @@ func (u *UserInterface) IsRunnableInBackground() bool {
|
||||
func (u *UserInterface) SetVsyncEnabled(enabled bool) {
|
||||
if !u.isRunning() {
|
||||
// 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
|
||||
// the game already starts and setScreenSize can be called.
|
||||
// the game already starts and setWindowSize can be called.
|
||||
u.m.Lock()
|
||||
u.vsync = enabled
|
||||
u.m.Unlock()
|
||||
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 {
|
||||
@ -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 {
|
||||
if !u.isRunning() {
|
||||
return u.getInitCursorMode()
|
||||
@ -684,11 +625,8 @@ func (u *UserInterface) createWindow() error {
|
||||
if u.isFullscreen() {
|
||||
return
|
||||
}
|
||||
|
||||
w := int(u.toDeviceIndependentPixel(float64(width)) / u.scale)
|
||||
h := int(u.toDeviceIndependentPixel(float64(height)) / u.scale)
|
||||
u.reqWidth = w
|
||||
u.reqHeight = h
|
||||
u.reqWidth = width
|
||||
u.reqHeight = height
|
||||
})
|
||||
|
||||
return nil
|
||||
@ -699,6 +637,7 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
||||
m *glfw.Monitor
|
||||
mx, my int
|
||||
v *glfw.VidMode
|
||||
ww, wh int
|
||||
)
|
||||
|
||||
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()
|
||||
v = m.GetVideoMode()
|
||||
|
||||
ww = int(u.toDeviceDependentPixel(float64(width) * scale))
|
||||
wh = int(u.toDeviceDependentPixel(float64(height) * scale))
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
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.
|
||||
// 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 {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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
|
||||
_ = u.t.Call(func() error {
|
||||
@ -835,12 +759,21 @@ func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||
return nil
|
||||
})
|
||||
if sizeChanged {
|
||||
actualScale := 0.0
|
||||
var w, h float64
|
||||
_ = 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
|
||||
})
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -866,7 +804,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
glfw.PollEvents()
|
||||
return nil
|
||||
})
|
||||
u.input.update(u.window)
|
||||
u.input.update(u.window, context)
|
||||
_ = u.t.Call(func() error {
|
||||
defer hooks.ResumeAudio()
|
||||
|
||||
@ -895,7 +833,7 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
return nil
|
||||
})
|
||||
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.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
|
||||
|
||||
_ = 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
|
||||
}
|
||||
|
||||
@ -973,10 +911,6 @@ func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscre
|
||||
height = 1
|
||||
}
|
||||
|
||||
u.screenWidthInDP = width
|
||||
u.screenHeightInDP = height
|
||||
u.scale = scale
|
||||
u.fullscreenScale = 0
|
||||
u.vsync = vsync
|
||||
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).
|
||||
// To prevent hanging up, return asap if the width is too small.
|
||||
// 252 is an arbitrary number and I guess this is small enough.
|
||||
minWindowWidth := 252
|
||||
// 126 is an arbitrary number and I guess this is small enough.
|
||||
minWindowWidth := int(u.toDeviceDependentPixel(126))
|
||||
if u.window.GetAttrib(glfw.Decorated) == glfw.False {
|
||||
minWindowWidth = 1
|
||||
}
|
||||
windowWidthInDP := width
|
||||
s := scale * u.deviceScaleFactor()
|
||||
if int(float64(width)*s) < minWindowWidth {
|
||||
windowWidthInDP = int(math.Ceil(float64(minWindowWidth) / s))
|
||||
if width < minWindowWidth {
|
||||
width = minWindowWidth
|
||||
}
|
||||
|
||||
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()
|
||||
newW := int(u.toDeviceDependentPixel(float64(windowWidthInDP) * u.getScale()))
|
||||
newH := int(u.toDeviceDependentPixel(float64(u.screenHeightInDP) * u.getScale()))
|
||||
newW := width
|
||||
newH := height
|
||||
if oldW != newW || oldH != newH {
|
||||
ch := make(chan struct{})
|
||||
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.
|
||||
u.window.SetTitle(u.title)
|
||||
}
|
||||
|
||||
// As width might be updated, update windowWidth/Height here.
|
||||
u.windowWidth = width
|
||||
u.windowHeight = height
|
||||
|
||||
if u.graphics.IsGL() {
|
||||
// SwapInterval is affected by the current monitor of the window.
|
||||
// This needs to be called at least after SetMonitor.
|
||||
@ -1162,6 +1100,19 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
||||
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 {
|
||||
return &u.input
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ type Input struct {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -110,7 +111,8 @@ func (i *Input) TouchIDs() []int {
|
||||
func (i *Input) TouchPosition(id int) (x, y int) {
|
||||
for tid, pos := range i.touches {
|
||||
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
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"image"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall/js"
|
||||
"time"
|
||||
|
||||
@ -30,9 +29,6 @@ import (
|
||||
)
|
||||
|
||||
type UserInterface struct {
|
||||
width int
|
||||
height int
|
||||
scale float64
|
||||
runnableInBackground bool
|
||||
vsync bool
|
||||
running bool
|
||||
@ -40,14 +36,10 @@ type UserInterface struct {
|
||||
sizeChanged bool
|
||||
contextLost bool
|
||||
|
||||
lastActualScale float64
|
||||
lastDeviceScaleFactor float64
|
||||
|
||||
context driver.UIContext
|
||||
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{
|
||||
@ -75,18 +67,6 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, 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) {
|
||||
// Do nothing
|
||||
}
|
||||
@ -111,24 +91,11 @@ func (u *UserInterface) IsVsyncEnabled() bool {
|
||||
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 {
|
||||
if canvas.Get("style").Get("cursor").String() != "none" {
|
||||
return driver.CursorModeVisible
|
||||
} else {
|
||||
return driver.CursorModeHidden
|
||||
}
|
||||
return driver.CursorModeHidden
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
||||
@ -150,6 +117,7 @@ func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetWindowTitle(title string) {
|
||||
// TODO: As the page should be in an iframe, this might be useless.
|
||||
document.Set("title", title)
|
||||
}
|
||||
|
||||
@ -171,34 +139,31 @@ func (u *UserInterface) IsWindowResizable() bool {
|
||||
|
||||
func (u *UserInterface) SetWindowResizable(decorated bool) {
|
||||
// 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 {
|
||||
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() {
|
||||
a := u.actualScreenScale()
|
||||
if u.lastActualScale != a {
|
||||
a := u.DeviceScaleFactor()
|
||||
if u.lastDeviceScaleFactor != a {
|
||||
u.updateScreenSize()
|
||||
}
|
||||
u.lastActualScale = a
|
||||
u.lastDeviceScaleFactor = a
|
||||
|
||||
if u.sizeChanged {
|
||||
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.Set("width", 16)
|
||||
canvas.Set("height", 16)
|
||||
|
||||
document.Get("body").Call("appendChild", canvas)
|
||||
|
||||
htmlStyle := document.Get("documentElement").Get("style")
|
||||
@ -315,13 +281,9 @@ func init() {
|
||||
|
||||
bodyStyle := document.Get("body").Get("style")
|
||||
bodyStyle.Set("backgroundColor", "#000")
|
||||
bodyStyle.Set("position", "relative")
|
||||
bodyStyle.Set("height", "100%")
|
||||
bodyStyle.Set("margin", "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.
|
||||
// What if the canvas is embedded in a HTML directly?
|
||||
@ -331,7 +293,10 @@ func init() {
|
||||
}))
|
||||
|
||||
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.
|
||||
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 {
|
||||
// scale is ignored.
|
||||
|
||||
document.Set("title", title)
|
||||
u.setScreenSize(width, height)
|
||||
u.pseudoScale = scale
|
||||
canvas.Call("focus")
|
||||
u.running = true
|
||||
ch := u.loop(context)
|
||||
@ -467,37 +428,12 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
|
||||
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() {
|
||||
body := document.Get("body")
|
||||
bw := body.Get("clientWidth").Float()
|
||||
bh := body.Get("clientHeight").Float()
|
||||
sw := bw / float64(u.width)
|
||||
sh := bh / float64(u.height)
|
||||
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")
|
||||
|
||||
bw := int(body.Get("clientWidth").Float() * u.DeviceScaleFactor())
|
||||
bh := int(body.Get("clientHeight").Float() * u.DeviceScaleFactor())
|
||||
canvas.Set("width", bw)
|
||||
canvas.Set("height", bh)
|
||||
u.sizeChanged = true
|
||||
}
|
||||
|
||||
@ -530,6 +466,10 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
||||
return bodyStyle.Get("backgroundColor").String() == "transparent"
|
||||
}
|
||||
|
||||
func (u *UserInterface) CanHaveWindow() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) Input() driver.Input {
|
||||
return &u.input
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func (u *UserInterface) appMain(a app.App) {
|
||||
glctx = nil
|
||||
}
|
||||
case size.Event:
|
||||
u.setGBuildImpl(e.WidthPx, e.HeightPx)
|
||||
u.setGBuild(e.WidthPx, e.HeightPx)
|
||||
case paint.Event:
|
||||
if glctx == nil || e.External {
|
||||
continue
|
||||
@ -236,30 +236,37 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
|
||||
}
|
||||
|
||||
func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||
width, height := 0, 0
|
||||
actualScale := 0.0
|
||||
var width, height float64
|
||||
|
||||
u.m.Lock()
|
||||
sizeChanged := u.sizeChanged
|
||||
if sizeChanged {
|
||||
width = u.width
|
||||
height = u.height
|
||||
actualScale = u.scaleImpl() * deviceScale()
|
||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||
s := u.scaleImpl()
|
||||
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.m.Unlock()
|
||||
|
||||
if sizeChanged {
|
||||
// Sizing also calls GL functions
|
||||
context.SetSize(width, height, actualScale)
|
||||
}
|
||||
}
|
||||
// Dirty hack to set the offscreen size for gomobile-bind.
|
||||
// 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 {
|
||||
u.m.Lock()
|
||||
s := u.scaleImpl() * deviceScale()
|
||||
u.m.Unlock()
|
||||
return s
|
||||
// Sizing also calls GL functions
|
||||
context.Layout(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserInterface) scaleImpl() float64 {
|
||||
@ -295,47 +302,26 @@ func (u *UserInterface) update(context driver.UIContext) error {
|
||||
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) {
|
||||
// TODO: This function should return gbuildWidthPx, gbuildHeightPx,
|
||||
// but these values are not initialized until the main loop starts.
|
||||
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()
|
||||
if u.width != width || u.height != height {
|
||||
if u.width != width || u.height != height || u.scale != scale {
|
||||
u.width = width
|
||||
u.height = height
|
||||
u.scale = scale
|
||||
u.updateGBuildScaleIfNeeded()
|
||||
u.sizeChanged = true
|
||||
}
|
||||
u.m.Unlock()
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetScreenScale(scale float64) {
|
||||
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) {
|
||||
func (u *UserInterface) setGBuild(widthPx, heightPx int) {
|
||||
u.m.Lock()
|
||||
u.gbuildWidthPx = widthPx
|
||||
u.gbuildHeightPx = heightPx
|
||||
@ -348,6 +334,7 @@ func (u *UserInterface) updateGBuildScaleIfNeeded() {
|
||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
w, h := u.width, u.height
|
||||
scaleX := float64(u.gbuildWidthPx) / float64(w)
|
||||
scaleY := float64(u.gbuildHeightPx) / float64(h)
|
||||
@ -359,14 +346,8 @@ func (u *UserInterface) updateGBuildScaleIfNeeded() {
|
||||
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) {
|
||||
// TODO: Replace this with UIContext's Layout.
|
||||
if u.gbuildScale == 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) {
|
||||
// TODO: Replace this with UIContext's AdjustPosition.
|
||||
ox, oy, _, _ := u.screenPaddingImpl()
|
||||
s := u.scaleImpl()
|
||||
as := s * deviceScale()
|
||||
@ -459,6 +441,14 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
func (u *UserInterface) CanHaveWindow() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) Input() driver.Input {
|
||||
return &u.input
|
||||
}
|
||||
|
@ -60,8 +60,7 @@ func layout(viewWidth, viewHeight int, viewRectSetter ViewRectSetter) {
|
||||
y := (viewHeight - height) / 2
|
||||
|
||||
if theState.isRunning() {
|
||||
mobile.Get().SetScreenSize(w, h)
|
||||
mobile.Get().SetScreenScale(scale)
|
||||
mobile.Get().SetScreenSizeAndScale(w, h, scale)
|
||||
} else {
|
||||
// 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, "")
|
||||
|
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 {
|
||||
f = (&imageDumper{f: f}).update
|
||||
|
||||
c := newUIContext(f)
|
||||
if err := uiDriver().Run(width, height, scale, title, c, graphicsDriver()); err != nil {
|
||||
theUIContext = newUIContext(f, width, height, scale)
|
||||
if err := uiDriver().Run(width, height, scale, title, theUIContext, graphicsDriver()); err != nil {
|
||||
if err == driver.RegularTermination {
|
||||
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 {
|
||||
f = (&imageDumper{f: f}).update
|
||||
|
||||
c := newUIContext(f)
|
||||
return uiDriver().RunWithoutMainLoop(width, height, scale, title, c, graphicsDriver())
|
||||
theUIContext = newUIContext(f, width, height, scale)
|
||||
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext, graphicsDriver())
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
@ -243,11 +248,18 @@ func SetScreenSize(width, height int) {
|
||||
// SetScreenScale panics if scale is not a positive number.
|
||||
//
|
||||
// SetScreenScale is concurrent-safe.
|
||||
//
|
||||
// TODO: Deprecate this function.
|
||||
func SetScreenScale(scale float64) {
|
||||
if scale <= 0 {
|
||||
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.
|
||||
@ -257,8 +269,13 @@ func SetScreenScale(scale float64) {
|
||||
// If Run is not called, this returns 0.
|
||||
//
|
||||
// ScreenScale is concurrent-safe.
|
||||
//
|
||||
// TODO: Deprecate this function.
|
||||
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.
|
||||
|
116
uicontext.go
116
uicontext.go
@ -17,6 +17,7 @@ package ebiten
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||
"github.com/hajimehoshi/ebiten/internal/clock"
|
||||
@ -31,45 +32,99 @@ func init() {
|
||||
graphicscommand.SetGraphicsDriver(graphicsDriver())
|
||||
}
|
||||
|
||||
func newUIContext(f func(*Image) error) *uiContext {
|
||||
func newUIContext(f func(*Image) error, width, height int, scaleForWindow float64) *uiContext {
|
||||
return &uiContext{
|
||||
f: f,
|
||||
f: f,
|
||||
screenWidth: width,
|
||||
screenHeight: height,
|
||||
scaleForWindow: scaleForWindow,
|
||||
}
|
||||
}
|
||||
|
||||
type uiContext struct {
|
||||
f func(*Image) error
|
||||
offscreen *Image
|
||||
screen *Image
|
||||
screenWidth int
|
||||
screenHeight int
|
||||
screenScale float64
|
||||
offsetX float64
|
||||
offsetY float64
|
||||
f func(*Image) error
|
||||
offscreen *Image
|
||||
screen *Image
|
||||
screenWidth int
|
||||
screenHeight int
|
||||
screenScale float64
|
||||
scaleForWindow float64
|
||||
offsetX float64
|
||||
offsetY float64
|
||||
|
||||
reqWidth int
|
||||
reqHeight int
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (c *uiContext) SetSize(screenWidth, screenHeight int, screenScale float64) {
|
||||
c.screenScale = screenScale
|
||||
var theUIContext *uiContext
|
||||
|
||||
if c.screen != nil {
|
||||
_ = c.screen.Dispose()
|
||||
func (c *uiContext) resolveSize() (int, int) {
|
||||
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 {
|
||||
_ = 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)
|
||||
w := int(math.Ceil(float64(screenWidth) * screenScale))
|
||||
h := int(math.Ceil(float64(screenHeight) * screenScale))
|
||||
px0, py0, px1, py1 := uiDriver().ScreenPadding()
|
||||
c.screen = newScreenFramebufferImage(w+int(math.Ceil(px0+px1)), h+int(math.Ceil(py0+py1)))
|
||||
c.screenWidth = w
|
||||
c.screenHeight = h
|
||||
sw, sh := c.resolveSize()
|
||||
scaleX := float64(outsideWidth) / float64(sw) * d
|
||||
scaleY := float64(outsideHeight) / float64(sh) * d
|
||||
c.screenScale = math.Min(scaleX, scaleY)
|
||||
if uiDriver().CanHaveWindow() && !uiDriver().IsFullscreen() {
|
||||
// When the UI driver cannot have a window, scaleForWindow is updated only via setScaleFowWindow.
|
||||
c.scaleForWindow = c.screenScale / d
|
||||
}
|
||||
|
||||
c.offsetX = px0
|
||||
c.offsetY = py0
|
||||
width := float64(sw) * c.screenScale
|
||||
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 {
|
||||
@ -82,17 +137,17 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
||||
}
|
||||
|
||||
for i := 0; i < updateCount; i++ {
|
||||
// Mipmap images should be disposed by Clear.
|
||||
c.offscreen.Clear()
|
||||
// Mipmap images should be disposed by fill.
|
||||
|
||||
setDrawingSkipped(i < updateCount-1)
|
||||
|
||||
if err := hooks.RunBeforeUpdateHooks(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.f(c.offscreen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uiDriver().Input().ResetForFrame()
|
||||
afterFrameUpdate()
|
||||
}
|
||||
@ -107,7 +162,7 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
||||
// c.screen is special: its Y axis is down to up,
|
||||
// and the origin point is lower left.
|
||||
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:
|
||||
op.GeoM.Scale(c.screenScale, c.screenScale)
|
||||
default:
|
||||
@ -131,3 +186,8 @@ func (c *uiContext) Update(afterFrameUpdate func()) error {
|
||||
}
|
||||
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