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 (
logPixelsX = 88
monitorDefaultToNull = 0
monitorDefaultToNearest = 2
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
// 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 {
panic(err)
}

View File

@ -774,14 +774,6 @@ func (u *userInterface) currentMonitor() *glfw.Monitor {
if m := w.GetMonitor(); m != nil {
return m
}
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 glfw.GetPrimaryMonitor()
// Get the monitor which the current window belongs to. This requires OS API.
return currentMonitor()
}

View File

@ -18,6 +18,25 @@
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 {
return 1
}
@ -25,3 +44,16 @@ func glfwScale() float64 {
func adjustWindowPosition(x, y int) (int, int) {
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
import (
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/hajimehoshi/ebiten/internal/devicescale"
)
@ -30,3 +32,17 @@ func glfwScale() float64 {
func adjustWindowPosition(x, y int) (int, int) {
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
// 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 (
"fmt"
"syscall"
"unsafe"
"github.com/go-gl/glfw/v3.2/glfw"
"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 {
// This function must be called on the main thread.
return devicescale.GetAt(currentUI.currentMonitor().GetPos())
@ -42,9 +102,43 @@ func adjustWindowPosition(x, y int) (int, int) {
if x < 0 {
x = 0
}
t := int(C.getCaptionHeight())
t, err := getSystemMetrics(smCyCaption)
if err != nil {
panic(err)
}
if y < t {
y = t
}
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
}