diff --git a/internal/uidriver/glfw/input.go b/internal/uidriver/glfw/input.go index c53cf074c..f9873bfbe 100644 --- a/internal/uidriver/glfw/input.go +++ b/internal/uidriver/glfw/input.go @@ -304,9 +304,10 @@ func (i *Input) update(window *glfw.Window, context driver.UIContext) { } cx, cy := window.GetCursorPos() // TODO: This is tricky. Rename the function? - s := i.ui.deviceScaleFactor() - cx = i.ui.fromGLFWPixel(cx) - cy = i.ui.fromGLFWPixel(cy) + m := i.ui.currentMonitor() + s := i.ui.deviceScaleFactor(m) + cx = i.ui.fromGLFWPixel(cx, m) + cy = i.ui.fromGLFWPixel(cy, m) cx, cy = context.AdjustPosition(cx, cy, s) // AdjustPosition can return NaN at the initialization. diff --git a/internal/uidriver/glfw/ui.go b/internal/uidriver/glfw/ui.go index f1988c05b..480105f9e 100644 --- a/internal/uidriver/glfw/ui.go +++ b/internal/uidriver/glfw/ui.go @@ -187,12 +187,11 @@ func initialize() error { defer w.Destroy() initializeWindowAfterCreation(w) - m := currentMonitor(w) + m := initialMonitor(w) theUI.initMonitor = m v := m.GetVideoMode() - scale := videoModeScale(m) - theUI.initFullscreenWidthInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Width), scale)) - theUI.initFullscreenHeightInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Height), scale)) + theUI.initFullscreenWidthInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Width), m)) + theUI.initFullscreenHeightInDP = int(theUI.fromGLFWMonitorPixel(float64(v.Height), m)) // Create system cursors. These cursors are destroyed at glfw.Terminate(). glfwSystemCursors[driver.CursorShapeDefault] = nil @@ -275,17 +274,18 @@ func (u *UserInterface) getWindowSizeLimits() (minw, minh, maxw, maxh int) { defer u.m.RUnlock() minw, minh, maxw, maxh = -1, -1, -1, -1 + m := u.currentMonitor() if u.minWindowWidthInDP >= 0 { - minw = int(u.toGLFWPixel(float64(u.minWindowWidthInDP))) + minw = int(u.toGLFWPixel(float64(u.minWindowWidthInDP), m)) } if u.minWindowHeightInDP >= 0 { - minh = int(u.toGLFWPixel(float64(u.minWindowHeightInDP))) + minh = int(u.toGLFWPixel(float64(u.minWindowHeightInDP), m)) } if u.maxWindowWidthInDP >= 0 { - maxw = int(u.toGLFWPixel(float64(u.maxWindowWidthInDP))) + maxw = int(u.toGLFWPixel(float64(u.maxWindowWidthInDP), m)) } if u.maxWindowHeightInDP >= 0 { - maxh = int(u.toGLFWPixel(float64(u.maxWindowHeightInDP))) + maxh = int(u.toGLFWPixel(float64(u.maxWindowHeightInDP), m)) } return } @@ -531,11 +531,10 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) { var w, h int _ = u.t.Call(func() error { - m := currentMonitor(u.window) + m := u.currentMonitor() v := m.GetVideoMode() - s := videoModeScale(m) - w = int(u.fromGLFWMonitorPixel(float64(v.Width), s)) - h = int(u.fromGLFWMonitorPixel(float64(v.Height), s)) + w = int(u.fromGLFWMonitorPixel(float64(v.Width), m)) + h = int(u.fromGLFWMonitorPixel(float64(v.Height), m)) return nil }) return w, h @@ -707,24 +706,20 @@ func (u *UserInterface) SetCursorShape(shape driver.CursorShape) { func (u *UserInterface) DeviceScaleFactor() float64 { if !u.isRunning() { // TODO: Use the initWindowPosition. This requires to convert the units correctly (#1575). - return u.deviceScaleFactor() + return u.deviceScaleFactor(u.currentMonitor()) } f := 0.0 _ = u.t.Call(func() error { - f = u.deviceScaleFactor() + f = u.deviceScaleFactor(u.currentMonitor()) return nil }) return f } // deviceScaleFactor must be called from the main thread. -func (u *UserInterface) deviceScaleFactor() float64 { - m := u.initMonitor - if u.window != nil { - m = currentMonitor(u.window) - } - mx, my := m.GetPos() +func (u *UserInterface) deviceScaleFactor(monitor *glfw.Monitor) float64 { + mx, my := monitor.GetPos() return devicescale.GetAt(mx, my) } @@ -896,20 +891,21 @@ func (u *UserInterface) init() error { setSize := func() { ww, wh := u.getInitWindowSize() - ww = int(u.toGLFWPixel(float64(ww))) - wh = int(u.toGLFWPixel(float64(wh))) + ww = int(u.toGLFWPixel(float64(ww), u.initMonitor)) + wh = int(u.toGLFWPixel(float64(wh), u.initMonitor)) u.setWindowSize(ww, wh, u.isFullscreen()) } // Set the window size and the window position in this order on Linux or other UNIX using X (#1118), // but this should be inverted on Windows. This is very tricky, but there is no obvious way to solve // this. This doesn't matter on macOS. + wx, wy := u.getInitWindowPosition() if runtime.GOOS == "windows" { - u.setWindowPosition(u.getInitWindowPosition()) + u.setWindowPosition(wx, wy, u.initMonitor) setSize() } else { setSize() - u.setWindowPosition(u.getInitWindowPosition()) + u.setWindowPosition(wx, wy, u.initMonitor) } u.updateWindowSizeLimits() @@ -945,19 +941,18 @@ func (u *UserInterface) updateSize() (float64, float64, bool) { // fullscreened. Use the monitor size. // On macOS's native fullscreen, the window's size returns a more precise size // reflecting the adjustment of the view size (#1745). - m := currentMonitor(u.window) + m := u.currentMonitor() v := m.GetVideoMode() ww, wh := v.Width, v.Height - s := videoModeScale(m) - w = u.fromGLFWMonitorPixel(float64(ww), s) - h = u.fromGLFWMonitorPixel(float64(wh), s) + w = u.fromGLFWMonitorPixel(float64(ww), m) + h = u.fromGLFWMonitorPixel(float64(wh), m) } else { // Instead of u.windowWidth and u.windowHeight, use the actual window size here. // On Windows, the specified size at SetSize and the actual window size might not // match (#1163). ww, wh := u.window.GetSize() - w = u.fromGLFWPixel(float64(ww)) - h = u.fromGLFWPixel(float64(wh)) + w = u.fromGLFWPixel(float64(ww), u.currentMonitor()) + h = u.fromGLFWPixel(float64(wh), u.currentMonitor()) } return w, h, true @@ -1140,26 +1135,27 @@ func (u *UserInterface) swapBuffers() { // updateWindowSizeLimits must be called from the main thread. func (u *UserInterface) updateWindowSizeLimits() { + m := u.currentMonitor() minw, minh, maxw, maxh := u.getWindowSizeLimitsInDP() if minw < 0 { minw = glfw.DontCare } else { - minw = int(u.toGLFWPixel(float64(minw))) + minw = int(u.toGLFWPixel(float64(minw), m)) } if minh < 0 { minh = glfw.DontCare } else { - minh = int(u.toGLFWPixel(float64(minh))) + minh = int(u.toGLFWPixel(float64(minh), m)) } if maxw < 0 { maxw = glfw.DontCare } else { - maxw = int(u.toGLFWPixel(float64(maxw))) + maxw = int(u.toGLFWPixel(float64(maxw), m)) } if maxh < 0 { maxh = glfw.DontCare } else { - maxh = int(u.toGLFWPixel(float64(maxh))) + maxh = int(u.toGLFWPixel(float64(maxh), m)) } u.window.SetSizeLimits(minw, minh, maxw, maxh) } @@ -1208,7 +1204,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) { u.Graphics().SetFullscreen(fullscreen) - if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() { + if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor(u.currentMonitor()) { return } @@ -1219,7 +1215,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) { height = 1 } - u.lastDeviceScaleFactor = u.deviceScaleFactor() + u.lastDeviceScaleFactor = u.deviceScaleFactor(u.currentMonitor()) // To make sure the current existing framebuffers are rendered, // swap buffers here before SetSize is called. @@ -1264,7 +1260,7 @@ func (u *UserInterface) setWindowSizeImpl(width, height int, fullscreen bool) bo if u.isNativeFullscreenAvailable() { u.setNativeFullscreen(fullscreen) } else { - m := currentMonitor(u.window) + m := u.currentMonitor() v := m.GetVideoMode() u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate) @@ -1279,7 +1275,7 @@ func (u *UserInterface) setWindowSizeImpl(width, height int, fullscreen bool) bo // 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. // 126 is an arbitrary number and I guess this is small enough. - minWindowWidth := int(u.toGLFWPixel(126)) + minWindowWidth := int(u.toGLFWPixel(126, u.currentMonitor())) if u.window.GetAttrib(glfw.Decorated) == glfw.False { minWindowWidth = 1 } @@ -1395,12 +1391,34 @@ func (u *UserInterface) updateVsync() { u.Graphics().SetVsyncEnabled(u.fpsMode == driver.FPSModeVsyncOn) } +// initialMonitor returns the initial monitor to show the window. +// +// The given window is just a hint and might not be used to determine the initial monitor. +// +// initialMonitor must be called on the main thread. +func initialMonitor(window *glfw.Window) *glfw.Monitor { + if m := initialMonitorByOS(); m != nil { + return m + } + return currentMonitorImpl(window) +} + // currentMonitor returns the current active monitor. // +// currentMonitor must be called on the main thread. +func (u *UserInterface) currentMonitor() *glfw.Monitor { + if !u.isRunning() { + return u.initMonitor + } + return currentMonitorImpl(u.window) +} + +// currentMonitorImpl returns the current active monitor. +// // The given window might or might not be used to detect the monitor. // -// currentMonitor must be called on the main thread. -func currentMonitor(window *glfw.Window) *glfw.Monitor { +// currentMonitorImpl must be called on the main thread. +func currentMonitorImpl(window *glfw.Window) *glfw.Monitor { // GetMonitor is available only in fullscreen. if m := window.GetMonitor(); m != nil { return m @@ -1479,7 +1497,7 @@ func (u *UserInterface) SetInitFocused(focused bool) { func (u *UserInterface) monitorPosition() (int, int) { // TODO: fromGLFWMonitorPixel might be required. - return currentMonitor(u.window).GetPos() + return u.currentMonitor().GetPos() } func (u *UserInterface) Input() driver.Input { @@ -1626,7 +1644,7 @@ func (u *UserInterface) setWindowResizable(resizable bool) { } // setWindowPosition must be called from the main thread. -func (u *UserInterface) setWindowPosition(x, y int) { +func (u *UserInterface) setWindowPosition(x, y int, monitor *glfw.Monitor) { if u.setSizeCallbackEnabled { u.setSizeCallbackEnabled = false defer func() { @@ -1634,9 +1652,9 @@ func (u *UserInterface) setWindowPosition(x, y int) { }() } - mx, my := currentMonitor(u.window).GetPos() - xf := u.toGLFWPixel(float64(x)) - yf := u.toGLFWPixel(float64(y)) + mx, my := monitor.GetPos() + xf := u.toGLFWPixel(float64(x), monitor) + yf := u.toGLFWPixel(float64(y), monitor) if x, y := u.adjustWindowPosition(mx+int(xf), my+int(yf)); u.isFullscreen() { u.setOrigPos(x, y) } else { diff --git a/internal/uidriver/glfw/ui_darwin.go b/internal/uidriver/glfw/ui_darwin.go index 3f4513fb8..56de5f4d6 100644 --- a/internal/uidriver/glfw/ui_darwin.go +++ b/internal/uidriver/glfw/ui_darwin.go @@ -127,14 +127,13 @@ func videoModeScale(m *glfw.Monitor) float64 { } // fromGLFWMonitorPixel must be called from the main thread. -func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 { - // videoModeScale is always 1 on macOS, - // however leaving the divison in place for consistency. - return x / videoModeScale +func (u *UserInterface) fromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 { + // videoModeScale is always 1 on macOS. + return x } // fromGLFWPixel must be called from the main thread. -func (u *UserInterface) fromGLFWPixel(x float64) float64 { +func (u *UserInterface) fromGLFWPixel(x float64, monitor *glfw.Monitor) float64 { // NOTE: On macOS, GLFW exposes the device independent coordinate system. // Thus, the conversion functions are unnecessary, // however we still need the deviceScaleFactor internally @@ -143,7 +142,7 @@ func (u *UserInterface) fromGLFWPixel(x float64) float64 { } // toGLFWPixel must be called from the main thread. -func (u *UserInterface) toGLFWPixel(x float64) float64 { +func (u *UserInterface) toGLFWPixel(x float64, monitor *glfw.Monitor) float64 { return x } @@ -151,6 +150,10 @@ func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) { return x, y } +func initialMonitorByOS() *glfw.Monitor { + return nil +} + func currentMonitorByOS(w *glfw.Window) *glfw.Monitor { x := C.int(0) y := C.int(0) diff --git a/internal/uidriver/glfw/ui_unix.go b/internal/uidriver/glfw/ui_unix.go index 740a209c1..1c6e4dd41 100644 --- a/internal/uidriver/glfw/ui_unix.go +++ b/internal/uidriver/glfw/ui_unix.go @@ -117,24 +117,28 @@ func videoModeScaleUncached(m *glfw.Monitor) float64 { } // fromGLFWMonitorPixel must be called from the main thread. -func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 { - return x / (videoModeScale * u.deviceScaleFactor()) +func (u *UserInterface) fromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 { + return x / (videoModeScale(monitor) * u.deviceScaleFactor(monitor)) } // fromGLFWPixel must be called from the main thread. -func (u *UserInterface) fromGLFWPixel(x float64) float64 { - return x / u.deviceScaleFactor() +func (u *UserInterface) fromGLFWPixel(x float64, monitor *glfw.Monitor) float64 { + return x / u.deviceScaleFactor(monitor) } // toGLFWPixel must be called from the main thread. -func (u *UserInterface) toGLFWPixel(x float64) float64 { - return x * u.deviceScaleFactor() +func (u *UserInterface) toGLFWPixel(x float64, monitor *glfw.Monitor) float64 { + return x * u.deviceScaleFactor(monitor) } func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) { return x, y } +func initialMonitorByOS() *glfw.Monitor { + return nil +} + func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor { // TODO: Implement this correctly. (#1119). return nil diff --git a/internal/uidriver/glfw/ui_windows.go b/internal/uidriver/glfw/ui_windows.go index 3e4ecd777..0f3c937a7 100644 --- a/internal/uidriver/glfw/ui_windows.go +++ b/internal/uidriver/glfw/ui_windows.go @@ -47,7 +47,6 @@ type monitorInfo struct { var ( // user32 is defined at hideconsole_windows.go procGetSystemMetrics = user32.NewProc("GetSystemMetrics") - procGetActiveWindow = user32.NewProc("GetActiveWindow") procGetForegroundWindow = user32.NewProc("GetForegroundWindow") procMonitorFromWindow = user32.NewProc("MonitorFromWindow") procGetMonitorInfoW = user32.NewProc("GetMonitorInfoW") @@ -61,14 +60,6 @@ func getSystemMetrics(nIndex int) (int, error) { return int(r), nil } -func getActiveWindow() (uintptr, error) { - r, _, e := procGetActiveWindow.Call() - if e != nil && e.(windows.Errno) != 0 { - return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e) - } - return r, nil -} - func getForegroundWindow() (uintptr, error) { r, _, e := procGetForegroundWindow.Call() if e != nil && e.(windows.Errno) != 0 { @@ -102,28 +93,23 @@ func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error { // clearVideoModeScaleCache must be called from the main thread. func clearVideoModeScaleCache() {} -// videoModeScale must be called from the main thread. -func videoModeScale(m *glfw.Monitor) float64 { - return 1 -} - // fromGLFWMonitorPixel must be called from the main thread. -func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 { - return x / (videoModeScale * u.deviceScaleFactor()) +func (u *UserInterface) fromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 { + return x / u.deviceScaleFactor(monitor) } // fromGLFWPixel must be called from the main thread. -func (u *UserInterface) fromGLFWPixel(x float64) float64 { - return x / u.deviceScaleFactor() +func (u *UserInterface) fromGLFWPixel(x float64, monitor *glfw.Monitor) float64 { + return x / u.deviceScaleFactor(monitor) } // toGLFWPixel must be called from the main thread. -func (u *UserInterface) toGLFWPixel(x float64) float64 { - return x * u.deviceScaleFactor() +func (u *UserInterface) toGLFWPixel(x float64, monitor *glfw.Monitor) float64 { + return x * u.deviceScaleFactor(monitor) } func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) { - mx, my := currentMonitor(u.window).GetPos() + mx, my := u.currentMonitor().GetPos() // As the video width/height might be wrong, // adjust x/y at least to enable to handle the window (#328) if x < mx { @@ -139,28 +125,24 @@ func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) { return x, y } -func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor { - // TODO: Why not using the given window? - - // TODO: Should we return nil here? - w, err := getActiveWindow() +func initialMonitorByOS() *glfw.Monitor { + // Get the foreground window, that is common among multiple processes. + w, err := getForegroundWindow() if err != nil { panic(err) } - if w == 0 { - // The active window doesn't exist when launching, or the application is runnable on unfocused. - // Get the foreground window, that is common among multiple processes. - w, err = getForegroundWindow() - if err != nil { - panic(err) - } - if w == 0 { - // GetForegroundWindow can return null according to the document. - return nil - } + // GetForegroundWindow can return null according to the document. + return nil } + return monitorFromWin32Window(w) +} +func currentMonitorByOS(w *glfw.Window) *glfw.Monitor { + return monitorFromWin32Window(w.GetWin32Window()) +} + +func monitorFromWin32Window(w uintptr) *glfw.Monitor { // Get the current monitor by the window handle instead of the window position. It is because the window // position is not relaiable in some cases e.g. when the window is put across multiple monitors. diff --git a/internal/uidriver/glfw/window.go b/internal/uidriver/glfw/window.go index 3b094afdd..b9b03e6ad 100644 --- a/internal/uidriver/glfw/window.go +++ b/internal/uidriver/glfw/window.go @@ -181,11 +181,12 @@ func (w *window) Position() (int, int) { } else { wx, wy = w.ui.window.GetPos() } - mx, my := currentMonitor(w.ui.window).GetPos() + m := w.ui.currentMonitor() + mx, my := m.GetPos() wx -= mx wy -= my - xf := w.ui.fromGLFWPixel(float64(wx)) - yf := w.ui.fromGLFWPixel(float64(wy)) + xf := w.ui.fromGLFWPixel(float64(wx), m) + yf := w.ui.fromGLFWPixel(float64(wy), m) x, y = int(xf), int(yf) return nil }) @@ -198,7 +199,7 @@ func (w *window) SetPosition(x, y int) { return } _ = w.ui.t.Call(func() error { - w.ui.setWindowPosition(x, y) + w.ui.setWindowPosition(x, y, w.ui.currentMonitor()) return nil }) } @@ -210,8 +211,8 @@ func (w *window) Size() (int, int) { } ww, wh := 0, 0 _ = w.ui.t.Call(func() error { - ww = int(w.ui.fromGLFWPixel(float64(w.ui.windowWidth))) - wh = int(w.ui.fromGLFWPixel(float64(w.ui.windowHeight))) + ww = int(w.ui.fromGLFWPixel(float64(w.ui.windowWidth), w.ui.currentMonitor())) + wh = int(w.ui.fromGLFWPixel(float64(w.ui.windowHeight), w.ui.currentMonitor())) return nil }) return ww, wh @@ -229,8 +230,8 @@ func (w *window) SetSize(width, height int) { return nil } - ww := int(w.ui.toGLFWPixel(float64(width))) - wh := int(w.ui.toGLFWPixel(float64(height))) + ww := int(w.ui.toGLFWPixel(float64(width), w.ui.currentMonitor())) + wh := int(w.ui.toGLFWPixel(float64(height), w.ui.currentMonitor())) w.ui.setWindowSize(ww, wh, w.ui.isFullscreen()) return nil })