ui: Bug fix: wrong scaling when a window move across monitors

Fixes #701
This commit is contained in:
Hajime Hoshi 2018-10-06 22:42:28 +09:00
parent 0e7dab3707
commit e0d2d5e753
5 changed files with 157 additions and 24 deletions

View File

@ -24,7 +24,6 @@ import (
const ( const (
logPixelsX = 88 logPixelsX = 88
monitorDefaultToNull = 0
monitorDefaultToNearest = 2 monitorDefaultToNearest = 2
mdtEffectiveDpi = 0 mdtEffectiveDpi = 0
) )
@ -178,7 +177,7 @@ func impl(x, y int) float64 {
// MonitorFromPoint requires to pass a POINT value, and there seems no portable way to // MonitorFromPoint requires to pass a POINT value, and there seems no portable way to
// do this with Cgo. Use MonitorFromRect instead. // do this with Cgo. Use MonitorFromRect instead.
m, err := monitorFromRect(uintptr(unsafe.Pointer(&lprc)), monitorDefaultToNull) m, err := monitorFromRect(uintptr(unsafe.Pointer(&lprc)), monitorDefaultToNearest)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -774,14 +774,6 @@ func (u *userInterface) currentMonitor() *glfw.Monitor {
if m := w.GetMonitor(); m != nil { if m := w.GetMonitor(); m != nil {
return m return m
} }
// Get the monitor which the current window belongs to. This requires OS API.
wx, wy := w.GetPos() return currentMonitor()
for _, m := range glfw.GetMonitors() {
mx, my := m.GetPos()
v := m.GetVideoMode()
if mx <= wx && wx < mx+v.Width && my <= wy && wy < my+v.Height {
return m
}
}
return glfw.GetPrimaryMonitor()
} }

View File

@ -18,6 +18,25 @@
package ui package ui
// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework AppKit
//
// #import <AppKit/AppKit.h>
//
// static void currentMonitorPos(int* x, int* y) {
// NSDictionary* screenDictionary = [[NSScreen mainScreen] deviceDescription];
// NSNumber* screenID = [screenDictionary objectForKey:@"NSScreenNumber"];
// CGDirectDisplayID aID = [screenID unsignedIntValue];
// const CGRect bounds = CGDisplayBounds(aID);
// *x = bounds.origin.x;
// *y = bounds.origin.y;
// }
import "C"
import (
"github.com/go-gl/glfw/v3.2/glfw"
)
func glfwScale() float64 { func glfwScale() float64 {
return 1 return 1
} }
@ -25,3 +44,16 @@ func glfwScale() float64 {
func adjustWindowPosition(x, y int) (int, int) { func adjustWindowPosition(x, y int) (int, int) {
return x, y return x, y
} }
func currentMonitor() *glfw.Monitor {
x := C.int(0)
y := C.int(0)
C.currentMonitorPos(&x, &y)
for _, m := range glfw.GetMonitors() {
mx, my := m.GetPos()
if int(x) == mx && int(y) == my {
return m
}
}
return nil
}

View File

@ -19,6 +19,8 @@
package ui package ui
import ( import (
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
) )
@ -30,3 +32,17 @@ func glfwScale() float64 {
func adjustWindowPosition(x, y int) (int, int) { func adjustWindowPosition(x, y int) (int, int) {
return x, y return x, y
} }
func currentMonitor() *glfw.Monitor {
// TODO: Return more appropriate display.
w := glfw.GetCurrentContext()
wx, wy := w.GetPos()
for _, m := range glfw.GetMonitors() {
mx, my := m.GetPos()
v := m.GetVideoMode()
if mx <= wx && wx < mx+v.Width && my <= wy && wy < my+v.Height {
return m
}
}
return nil
}

View File

@ -16,21 +16,81 @@
package ui package ui
// TODO: Use golang.org/x/sys/windows (NewLazyDLL) instead of cgo.
// #cgo LDFLAGS: -lgdi32
//
// #include <windows.h>
//
// static int getCaptionHeight() {
// return GetSystemMetrics(SM_CYCAPTION);
// }
import "C"
import ( import (
"fmt"
"syscall"
"unsafe"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
) )
const (
smCyCaption = 4
monitorDefaultToNearest = 2
)
type rect struct {
left int32
top int32
right int32
bottom int32
}
type monitorInfo struct {
cbSize uint32
rcMonitor rect
rcWork rect
dwFlags uint32
}
var (
// user32 is defined at hideconsole_windows.go
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
procGetActiveWindow = user32.NewProc("GetActiveWindow")
procMonitorFromWindow = user32.NewProc("MonitorFromWindow")
procGetMonitorInfoW = user32.NewProc("GetMonitorInfoW")
)
func getSystemMetrics(nIndex int) (int, error) {
r, _, e := syscall.Syscall(procGetSystemMetrics.Addr(), 1, uintptr(nIndex), 0, 0)
if e != 0 {
return 0, fmt.Errorf("ui: GetSystemMetrics failed: error code: %d", e)
}
return int(r), nil
}
func getActiveWindow() (uintptr, error) {
r, _, e := syscall.Syscall(procGetActiveWindow.Addr(), 0, 0, 0, 0)
if e != 0 {
return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e)
}
return r, nil
}
func monitorFromWindow(hwnd uintptr, dwFlags uint32) (uintptr, error) {
r, _, e := syscall.Syscall(procMonitorFromWindow.Addr(), 2, hwnd, uintptr(dwFlags), 0)
if e != 0 {
return 0, fmt.Errorf("ui: MonitorFromWindow failed: error code: %d", e)
}
if r == 0 {
return 0, fmt.Errorf("ui: MonitorFromWindow failed: returned value: %d", r)
}
return r, nil
}
func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
r, _, e := syscall.Syscall(procGetMonitorInfoW.Addr(), 2, hMonitor, uintptr(unsafe.Pointer(lpmi)), 0)
if e != 0 {
return fmt.Errorf("ui: GetMonitorInfoW failed: error code: %d", e)
}
if r == 0 {
return fmt.Errorf("ui: GetMonitorInfoW failed: returned value: %d", r)
}
return nil
}
func glfwScale() float64 { func glfwScale() float64 {
// This function must be called on the main thread. // This function must be called on the main thread.
return devicescale.GetAt(currentUI.currentMonitor().GetPos()) return devicescale.GetAt(currentUI.currentMonitor().GetPos())
@ -42,9 +102,43 @@ func adjustWindowPosition(x, y int) (int, int) {
if x < 0 { if x < 0 {
x = 0 x = 0
} }
t := int(C.getCaptionHeight()) t, err := getSystemMetrics(smCyCaption)
if err != nil {
panic(err)
}
if y < t { if y < t {
y = t y = t
} }
return x, y return x, y
} }
func currentMonitor() *glfw.Monitor {
w, err := getActiveWindow()
if err != nil {
panic(err)
}
if w == 0 {
// There is no window at launching.
return glfw.GetPrimaryMonitor()
}
m, err := monitorFromWindow(w, monitorDefaultToNearest)
if err != nil {
panic(err)
}
mi := monitorInfo{}
mi.cbSize = uint32(unsafe.Sizeof(mi))
if err := getMonitorInfoW(m, &mi); err != nil {
panic(err)
}
x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
for _, m := range glfw.GetMonitors() {
mx, my := m.GetPos()
if mx == x && my == y {
return m
}
}
return nil
}