From dabaf66b8128696015d0fa4e2a61aa6e591ec9da Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 17 Dec 2019 12:28:52 +0900 Subject: [PATCH] uidriver: Remove width/height/scale arguments from run The initial window position is determined on ebiten package side. Updates #943 --- internal/driver/ui.go | 1 + internal/uidriver/glfw/ui.go | 88 +++++++++++------------------ internal/uidriver/glfw/ui_darwin.go | 14 +++-- run.go | 1 + window.go | 65 +++++++++++++++++++++ 5 files changed, 108 insertions(+), 61 deletions(-) diff --git a/internal/driver/ui.go b/internal/driver/ui.go index 1e2f1cb6d..87b22e93b 100644 --- a/internal/driver/ui.go +++ b/internal/driver/ui.go @@ -44,6 +44,7 @@ type UI interface { ScreenSizeInFullscreen() (int, int) WindowPosition() (int, int) IsScreenTransparent() bool + MonitorPosition() (int, int) CanHaveWindow() bool // TODO: Create a 'Widnow' interface. SetCursorMode(mode CursorMode) diff --git a/internal/uidriver/glfw/ui.go b/internal/uidriver/glfw/ui.go index 8401f701f..b56a12991 100644 --- a/internal/uidriver/glfw/ui.go +++ b/internal/uidriver/glfw/ui.go @@ -129,14 +129,12 @@ func initialize() error { 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.initMonitor = theUI.currentMonitor() v := theUI.initMonitor.GetVideoMode() theUI.initFullscreenWidthInDP = int(theUI.toDeviceIndependentPixel(float64(v.Width))) theUI.initFullscreenHeightInDP = int(theUI.toDeviceIndependentPixel(float64(v.Height))) - theUI.window.Destroy() - theUI.window = nil return nil } @@ -568,7 +566,7 @@ func (u *UserInterface) Run(width, height int, scale float64, title string, uico go func() { defer cancel() defer close(ch) - if err := u.run(width, height, scale, title, uicontext); err != nil { + if err := u.run(title, uicontext); err != nil { ch <- err } }() @@ -632,15 +630,12 @@ func (u *UserInterface) createWindow() error { return nil } -func (u *UserInterface) run(width, height int, scale float64, title string, context driver.UIContext) error { - var ( - m *glfw.Monitor - mx, my int - v *glfw.VidMode - ww, wh int - ) - +func (u *UserInterface) run(title string, context driver.UIContext) error { if err := u.t.Call(func() error { + // The window is created at initialize(). + u.window.Destroy() + u.window = nil + if u.graphics.IsGL() { glfw.WindowHint(glfw.ContextVersionMajor, 2) 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 { 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 }); err != nil { return err } - // 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.setWindowSize(ww, wh, false, u.vsync) + u.SetWindowPosition(u.getInitWindowPosition()) _ = 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.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() return nil @@ -1042,9 +1000,11 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync // // currentMonitor must be called on the main thread. func (u *UserInterface) currentMonitor() *glfw.Monitor { - w := u.window - if m := w.GetMonitor(); m != nil { - return m + if w := u.window; w != nil { + // TODO: When is the monitor nil? + if m := w.GetMonitor(); m != nil { + return m + } } // Get the monitor which the current window belongs to. This requires OS API. return u.currentMonitorFromPosition() @@ -1058,10 +1018,11 @@ func (u *UserInterface) SetWindowPosition(x, y int) { _ = u.t.Call(func() error { xf := u.toDeviceDependentPixel(float64(x)) yf := u.toDeviceDependentPixel(float64(y)) + x, y := adjustWindowPosition(int(xf), int(yf)) if u.isFullscreen() { - u.origPosX, u.origPosY = int(xf), int(yf) + u.origPosX, u.origPosY = x, y } else { - u.window.SetPos(int(xf), int(yf)) + u.window.SetPos(x, y) } return nil }) @@ -1116,6 +1077,23 @@ func (u *UserInterface) SetWindowSize(width, height int) { 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 { return true } diff --git a/internal/uidriver/glfw/ui_darwin.go b/internal/uidriver/glfw/ui_darwin.go index e0e236f23..c500f4c6f 100644 --- a/internal/uidriver/glfw/ui_darwin.go +++ b/internal/uidriver/glfw/ui_darwin.go @@ -59,12 +59,14 @@ func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor { x := C.int(0) y := C.int(0) // Note: [NSApp mainWindow] is nil when it doesn't have its border. Use u.window here. - win := u.window.GetCocoaWindow() - C.currentMonitorPos(win, &x, &y) - for _, m := range glfw.GetMonitors() { - mx, my := m.GetPos() - if int(x) == mx && int(y) == my { - return m + if u.window != nil { + win := u.window.GetCocoaWindow() + C.currentMonitorPos(win, &x, &y) + for _, m := range glfw.GetMonitors() { + mx, my := m.GetPos() + if int(x) == mx && int(y) == my { + return m + } } } return glfw.GetPrimaryMonitor() diff --git a/run.go b/run.go index 6e7fc3f84..fb81f9a8e 100644 --- a/run.go +++ b/run.go @@ -156,6 +156,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e } 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 == driver.RegularTermination { return nil diff --git a/window.go b/window.go index 0d3abfcc4..7246657dc 100644 --- a/window.go +++ b/window.go @@ -16,6 +16,13 @@ package ebiten import ( "image" + "sync" +) + +const ( + maxInt = int(^uint(0) >> 1) + minInt = -maxInt - 1 + invalidPos = minInt ) // SetWindowDecorated sets the state if the window is decorated. @@ -105,6 +112,9 @@ func SetWindowIcon(iconImages []image.Image) { // // WindowPosition is concurrent-safe. func WindowPosition() (x, y int) { + if x, y, ok := initWindowPosition(); ok { + return x, y + } return uiDriver().WindowPosition() } @@ -118,5 +128,60 @@ func WindowPosition() (x, y int) { // // SetWindowPosition is concurrent-safe. func SetWindowPosition(x, y int) { + if setInitWindowPosition(x, y) { + return + } 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) + } +}