From cafb71719ce7a0b42dbf10ca9bd917eddb6b6a9c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 7 Feb 2022 21:56:16 +0900 Subject: [PATCH] internal/ui: use the cursor position to choose the initial monitor Updates #1982 --- internal/ui/ui_glfw.go | 53 ++++++++++++++++++----------------- internal/ui/ui_glfw_darwin.go | 30 ++++++++++++++++++-- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 56e51a953..5799e8966 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -169,21 +169,32 @@ func initialize() error { glfw.WindowHint(glfw.Visible, glfw.False) glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI) - // Create a window to set the initial monitor. - // TODO: Instead of a dummy window, get a mouse cursor position and get a monitor from it (#1982). - w, err := glfw.CreateWindow(16, 16, "", nil, nil) - if err != nil { - return err + var m *glfw.Monitor + if runtime.GOOS == "darwin" { + m = initialMonitorByOS() + if m == nil { + m = glfw.GetPrimaryMonitor() + } + } else { + // Create a window to set the initial monitor. + // TODO: Instead of a dummy window, get a mouse cursor position and get a monitor from it (#1982). + w, err := glfw.CreateWindow(16, 16, "", nil, nil) + if err != nil { + return err + } + if w == nil { + // This can happen on Windows Remote Desktop (#903). + panic("ui: glfw.CreateWindow must not return nil") + } + defer w.Destroy() + initializeWindowAfterCreation(w) + theUI.waitForFramebufferSizeCallback(w, nil) + m = initialMonitorByOS() + if m == nil { + m = currentMonitorImpl(w) + } } - if w == nil { - // This can happen on Windows Remote Desktop (#903). - panic("ui: glfw.CreateWindow must not return nil") - } - defer w.Destroy() - initializeWindowAfterCreation(w) - theUI.waitForFramebufferSizeCallback(w, nil) - m := initialMonitor(w) theUI.initMonitor = m // GetVideoMode must be called from the main thread, then call this here and record // initFullscreen{Width,Height}InDIP. @@ -914,8 +925,10 @@ func (u *UserInterface) init() error { // 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. + // TODO: Set the position first even on X. setWindowSizeInDIP uses the monitor of the window, and + // if the window position is not reliable, the device scale factor is also not reliable (#1118, #1982). wx, wy := u.getInitWindowPositionInDIP() - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { u.setWindowPositionInDIP(wx, wy, u.initMonitor) setSize() } else { @@ -1345,18 +1358,6 @@ func (u *UserInterface) updateVsync() { Graphics().SetVsyncEnabled(u.fpsMode == 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. diff --git a/internal/ui/ui_glfw_darwin.go b/internal/ui/ui_glfw_darwin.go index af031f591..8cece67e8 100644 --- a/internal/ui/ui_glfw_darwin.go +++ b/internal/ui/ui_glfw_darwin.go @@ -120,6 +120,12 @@ package ui // } // [cursor push]; // } +// +// static void currentMouseLocation(int* x, int* y) { +// NSPoint location = [NSEvent mouseLocation]; +// *x = (int)(location.x); +// *y = (int)(location.y); +// } import "C" import ( @@ -153,12 +159,32 @@ func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) { } func initialMonitorByOS() *glfw.Monitor { + var cx, cy C.int + C.currentMouseLocation(&cx, &cy) + x, y := int(cx), int(cy) + + // Flip Y. + for _, m := range ensureMonitors() { + if m.x == 0 && m.y == 0 { + y = -y + y += m.vm.Height + break + } + } + + // Find the monitor including the cursor. + for _, m := range ensureMonitors() { + w, h := m.vm.Width, m.vm.Height + if x >= m.x && x < m.x+w && y >= m.y && y < m.y+h { + return m.m + } + } + return nil } func currentMonitorByOS(w *glfw.Window) *glfw.Monitor { - x := C.int(0) - y := C.int(0) + var x, y C.int // Note: [NSApp mainWindow] is nil when it doesn't have its border. Use w here. win := w.GetCocoaWindow() C.currentMonitorPos(C.uintptr_t(win), &x, &y)