uidriver: Remove width/height/scale arguments from run

The initial window position is determined on ebiten package side.

Updates #943
This commit is contained in:
Hajime Hoshi 2019-12-17 12:28:52 +09:00
parent d07028735f
commit dabaf66b81
5 changed files with 108 additions and 61 deletions

View File

@ -44,6 +44,7 @@ type UI interface {
ScreenSizeInFullscreen() (int, int) ScreenSizeInFullscreen() (int, int)
WindowPosition() (int, int) WindowPosition() (int, int)
IsScreenTransparent() bool IsScreenTransparent() bool
MonitorPosition() (int, int)
CanHaveWindow() bool // TODO: Create a 'Widnow' interface. CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
SetCursorMode(mode CursorMode) SetCursorMode(mode CursorMode)

View File

@ -129,14 +129,12 @@ func initialize() error {
panic("glfw: glfw.CreateWindow must not return nil") panic("glfw: glfw.CreateWindow must not return nil")
} }
// TODO: Fix this hack. currentMonitor now requires u.window on POSIX. // Create a window and leave it as it is: this affects the result of currentMonitorFromPosition.
theUI.window = w theUI.window = w
theUI.initMonitor = theUI.currentMonitor() theUI.initMonitor = theUI.currentMonitor()
v := theUI.initMonitor.GetVideoMode() v := theUI.initMonitor.GetVideoMode()
theUI.initFullscreenWidthInDP = int(theUI.toDeviceIndependentPixel(float64(v.Width))) theUI.initFullscreenWidthInDP = int(theUI.toDeviceIndependentPixel(float64(v.Width)))
theUI.initFullscreenHeightInDP = int(theUI.toDeviceIndependentPixel(float64(v.Height))) theUI.initFullscreenHeightInDP = int(theUI.toDeviceIndependentPixel(float64(v.Height)))
theUI.window.Destroy()
theUI.window = nil
return nil return nil
} }
@ -568,7 +566,7 @@ func (u *UserInterface) Run(width, height int, scale float64, title string, uico
go func() { go func() {
defer cancel() defer cancel()
defer close(ch) defer close(ch)
if err := u.run(width, height, scale, title, uicontext); err != nil { if err := u.run(title, uicontext); err != nil {
ch <- err ch <- err
} }
}() }()
@ -632,15 +630,12 @@ func (u *UserInterface) createWindow() error {
return nil return nil
} }
func (u *UserInterface) run(width, height int, scale float64, title string, context driver.UIContext) error { func (u *UserInterface) run(title string, context driver.UIContext) error {
var (
m *glfw.Monitor
mx, my int
v *glfw.VidMode
ww, wh int
)
if err := u.t.Call(func() error { if err := u.t.Call(func() error {
// The window is created at initialize().
u.window.Destroy()
u.window = nil
if u.graphics.IsGL() { if u.graphics.IsGL() {
glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1) glfw.WindowHint(glfw.ContextVersionMinor, 1)
@ -679,53 +674,16 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
if i := u.getInitIconImages(); i != nil { if i := u.getInitIconImages(); i != nil {
u.window.SetIcon(i) u.window.SetIcon(i)
} }
// Get the monitor before showing the window.
//
// On Windows, there are two types of windows:
//
// active window: The window that has input-focus and attached to the calling thread.
// foreground window: The window that has input-focus: this can be in another process
//
// currentMonitor returns the monitor for the active window when possible and then the monitor for
// the foreground window as fallback. In the current situation, the current window is hidden and
// there is not the active window but the foreground window. After showing the current window, the
// current window will be the active window. Thus, currentMonitor result varies before and after
// showing the window.
m = u.currentMonitor()
mx, my = m.GetPos()
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
} }
// The game is in window mode (not fullscreen mode) at the first state. u.SetWindowPosition(u.getInitWindowPosition())
// Don't refer u.initFullscreen here to avoid some GLFW problems.
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
// affects deviceDependentWindowSize result.
w, h := u.window.GetSize()
u.title = title u.title = title
u.window.SetTitle(title) u.window.SetTitle(title)
x, y := u.getInitWindowPosition()
if x == invalidPos || y == invalidPos {
x = mx + (v.Width-w)/2
y = my + (v.Height-h)/3
}
// Adjusting the position is needed only when the monitor is primary. (#829)
if mx == 0 && my == 0 {
x, y = adjustWindowPosition(x, y)
}
u.window.SetPos(x, y)
u.window.Show() u.window.Show()
return nil return nil
@ -1042,9 +1000,11 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
// //
// currentMonitor must be called on the main thread. // currentMonitor must be called on the main thread.
func (u *UserInterface) currentMonitor() *glfw.Monitor { func (u *UserInterface) currentMonitor() *glfw.Monitor {
w := u.window if w := u.window; w != nil {
if m := w.GetMonitor(); m != nil { // TODO: When is the monitor nil?
return m if m := w.GetMonitor(); m != nil {
return m
}
} }
// Get the monitor which the current window belongs to. This requires OS API. // Get the monitor which the current window belongs to. This requires OS API.
return u.currentMonitorFromPosition() return u.currentMonitorFromPosition()
@ -1058,10 +1018,11 @@ func (u *UserInterface) SetWindowPosition(x, y int) {
_ = u.t.Call(func() error { _ = u.t.Call(func() error {
xf := u.toDeviceDependentPixel(float64(x)) xf := u.toDeviceDependentPixel(float64(x))
yf := u.toDeviceDependentPixel(float64(y)) yf := u.toDeviceDependentPixel(float64(y))
x, y := adjustWindowPosition(int(xf), int(yf))
if u.isFullscreen() { if u.isFullscreen() {
u.origPosX, u.origPosY = int(xf), int(yf) u.origPosX, u.origPosY = x, y
} else { } else {
u.window.SetPos(int(xf), int(yf)) u.window.SetPos(x, y)
} }
return nil return nil
}) })
@ -1116,6 +1077,23 @@ func (u *UserInterface) SetWindowSize(width, height int) {
u.setWindowSize(w, h, u.isFullscreen(), u.vsync) u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
} }
func (u *UserInterface) MonitorPosition() (int, int) {
if !u.isRunning() {
return u.monitorPosition()
}
var mx, my int
_ = u.t.Call(func() error {
mx, my = u.monitorPosition()
return nil
})
return mx, my
}
func (u *UserInterface) monitorPosition() (int, int) {
// TODO: toDeviceIndependentPixel might be required.
return u.currentMonitor().GetPos()
}
func (u *UserInterface) CanHaveWindow() bool { func (u *UserInterface) CanHaveWindow() bool {
return true return true
} }

View File

@ -59,12 +59,14 @@ func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
x := C.int(0) x := C.int(0)
y := C.int(0) y := C.int(0)
// Note: [NSApp mainWindow] is nil when it doesn't have its border. Use u.window here. // Note: [NSApp mainWindow] is nil when it doesn't have its border. Use u.window here.
win := u.window.GetCocoaWindow() if u.window != nil {
C.currentMonitorPos(win, &x, &y) win := u.window.GetCocoaWindow()
for _, m := range glfw.GetMonitors() { C.currentMonitorPos(win, &x, &y)
mx, my := m.GetPos() for _, m := range glfw.GetMonitors() {
if int(x) == mx && int(y) == my { mx, my := m.GetPos()
return m if int(x) == mx && int(y) == my {
return m
}
} }
} }
return glfw.GetPrimaryMonitor() return glfw.GetPrimaryMonitor()

1
run.go
View File

@ -156,6 +156,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
} }
theUIContext = newUIContext(game, scale) theUIContext = newUIContext(game, scale)
fixWindowPosition(int(float64(width)*scale), int(float64(height)*scale))
if err := uiDriver().Run(width, height, scale, title, theUIContext, 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

View File

@ -16,6 +16,13 @@ package ebiten
import ( import (
"image" "image"
"sync"
)
const (
maxInt = int(^uint(0) >> 1)
minInt = -maxInt - 1
invalidPos = minInt
) )
// SetWindowDecorated sets the state if the window is decorated. // SetWindowDecorated sets the state if the window is decorated.
@ -105,6 +112,9 @@ func SetWindowIcon(iconImages []image.Image) {
// //
// WindowPosition is concurrent-safe. // WindowPosition is concurrent-safe.
func WindowPosition() (x, y int) { func WindowPosition() (x, y int) {
if x, y, ok := initWindowPosition(); ok {
return x, y
}
return uiDriver().WindowPosition() return uiDriver().WindowPosition()
} }
@ -118,5 +128,60 @@ func WindowPosition() (x, y int) {
// //
// SetWindowPosition is concurrent-safe. // SetWindowPosition is concurrent-safe.
func SetWindowPosition(x, y int) { func SetWindowPosition(x, y int) {
if setInitWindowPosition(x, y) {
return
}
uiDriver().SetWindowPosition(x, y) uiDriver().SetWindowPosition(x, y)
} }
var (
windowM sync.Mutex
mainLoopStarted bool
initWindowPositionX = invalidPos
initWindowPositionY = invalidPos
)
func initWindowPosition() (x, y int, ok bool) {
windowM.Lock()
defer windowM.Unlock()
if mainLoopStarted {
return 0, 0, false
}
if initWindowPositionX == invalidPos || initWindowPositionY == invalidPos {
return 0, 0, false
}
return initWindowPositionX, initWindowPositionY, true
}
func setInitWindowPosition(x, y int) bool {
windowM.Lock()
defer windowM.Unlock()
if mainLoopStarted {
return false
}
initWindowPositionX, initWindowPositionY = x, y
return true
}
func fixWindowPosition(width, height int) {
windowM.Lock()
defer windowM.Unlock()
defer func() {
mainLoopStarted = true
}()
if !uiDriver().CanHaveWindow() {
return
}
if initWindowPositionX == invalidPos || initWindowPositionY == invalidPos {
mx, my := uiDriver().MonitorPosition()
sw, sh := uiDriver().ScreenSizeInFullscreen()
x := mx + (sw-width)/2
y := my + (sh-height)/3
uiDriver().SetWindowPosition(x, y)
} else {
uiDriver().SetWindowPosition(initWindowPositionX, initWindowPositionY)
}
}