internal/ui: refactoring: replace *glfw.Monitor with *Monitor

This commit is contained in:
Hajime Hoshi 2023-09-24 02:05:42 +09:00
parent f72b8a4ced
commit 5d5e3c7c0b
6 changed files with 94 additions and 131 deletions

View File

@ -26,13 +26,14 @@ import (
// Monitor is a wrapper around glfw.Monitor.
type Monitor struct {
m *glfw.Monitor
vm *glfw.VidMode
m *glfw.Monitor
videoMode *glfw.VidMode
id int
name string
x int
y int
id int
name string
x int
y int
videoModeScale float64
}
// Name returns the monitor's name.
@ -90,12 +91,13 @@ func (m *monitors) update() {
for i, m := range glfwMonitors {
x, y := m.GetPos()
newMonitors = append(newMonitors, &Monitor{
m: m,
vm: m.GetVideoMode(),
id: i,
name: m.GetName(),
x: x,
y: y,
m: m,
videoMode: m.GetVideoMode(),
id: i,
name: m.GetName(),
x: x,
y: y,
videoModeScale: videoModeScale(m),
})
}
@ -103,7 +105,6 @@ func (m *monitors) update() {
m.monitors = newMonitors
m.m.Unlock()
clearVideoModeScaleCache()
devicescale.ClearCache()
atomic.StoreInt32(&m.updateCalled, 1)

View File

@ -179,7 +179,7 @@ func initialize() error {
return err
}
if m == nil {
m = glfw.GetPrimaryMonitor()
m = theMonitors.monitorFromGLFWMonitor(glfw.GetPrimaryMonitor())
}
// GetPrimaryMonitor might return nil in theory (#1887).
@ -187,7 +187,7 @@ func initialize() error {
return errors.New("ui: no monitor was found at initialize")
}
theUI.setInitMonitor(theMonitors.monitorFromGLFWMonitor(m))
theUI.setInitMonitor(m)
// Create system cursors. These cursors are destroyed at glfw.Terminate().
glfwSystemCursors[CursorShapeDefault] = nil
@ -211,10 +211,10 @@ func (u *userInterfaceImpl) setInitMonitor(m *Monitor) {
u.initMonitor = m
// TODO: Remove these members. These can be calculated anytime from initMonitor.
u.initDeviceScaleFactor = u.deviceScaleFactor(m.m)
v := m.vm
u.initFullscreenWidthInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Width), m.m))
u.initFullscreenHeightInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Height), m.m))
u.initDeviceScaleFactor = u.deviceScaleFactor(m)
v := m.videoMode
u.initFullscreenWidthInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Width), m))
u.initFullscreenHeightInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Height), m))
}
// AppendMonitors appends the current monitors to the passed in mons slice and returns it.
@ -232,11 +232,7 @@ func (u *userInterfaceImpl) Monitor() *Monitor {
if u.isTerminated() {
return
}
glfwMonitor := u.currentMonitor()
if glfwMonitor == nil {
return
}
monitor = theMonitors.monitorFromGLFWMonitor(glfwMonitor)
monitor = u.currentMonitor()
})
return monitor
}
@ -249,7 +245,7 @@ func getMonitorFromPosition(wx, wy int) *Monitor {
for _, m := range theMonitors.append(nil) {
// TODO: Fix incorrectness in the cases of https://github.com/glfw/glfw/issues/1961.
// See also internal/devicescale/impl_desktop.go for a maybe better way of doing this.
if m.x <= wx && wx < m.x+m.vm.Width && m.y <= wy && wy < m.y+m.vm.Height {
if m.x <= wx && wx < m.x+m.videoMode.Width && m.y <= wy && wy < m.y+m.videoMode.Height {
return m
}
}
@ -283,7 +279,7 @@ func (u *userInterfaceImpl) setWindowMonitor(monitor *Monitor) {
}
// Ignore if it is the same monitor.
if monitor.m == u.currentMonitor() {
if monitor == u.currentMonitor() {
return
}
@ -304,15 +300,14 @@ func (u *userInterfaceImpl) setWindowMonitor(monitor *Monitor) {
}
}
w := u.dipToGLFWPixel(float64(ww), monitor.m)
h := u.dipToGLFWPixel(float64(wh), monitor.m)
x, y := monitor.x, monitor.y
mw := u.dipFromGLFWMonitorPixel(float64(monitor.vm.Width), monitor.m)
mh := u.dipFromGLFWMonitorPixel(float64(monitor.vm.Height), monitor.m)
mw = u.dipToGLFWPixel(mw, monitor.m)
mh = u.dipToGLFWPixel(mh, monitor.m)
w := u.dipToGLFWPixel(float64(ww), monitor)
h := u.dipToGLFWPixel(float64(wh), monitor)
mw := u.dipFromGLFWMonitorPixel(float64(monitor.videoMode.Width), monitor)
mh := u.dipFromGLFWMonitorPixel(float64(monitor.videoMode.Height), monitor)
mw = u.dipToGLFWPixel(mw, monitor)
mh = u.dipToGLFWPixel(mh, monitor)
px, py := InitialWindowPosition(int(mw), int(mh), int(w), int(h))
u.window.SetPos(x+px, y+py)
u.window.SetPos(monitor.x+px, monitor.y+py)
if fullscreen {
// Calling setFullscreen immediately might not work well, especially on Linux (#2778).
@ -559,9 +554,9 @@ func (u *userInterfaceImpl) ScreenSizeInFullscreen() (int, int) {
if m == nil {
return
}
v := m.GetVideoMode()
w = int(u.dipFromGLFWMonitorPixel(float64(v.Width), m))
h = int(u.dipFromGLFWMonitorPixel(float64(v.Height), m))
vm := m.videoMode
w = int(u.dipFromGLFWMonitorPixel(float64(vm.Width), m))
h = int(u.dipFromGLFWMonitorPixel(float64(vm.Height), m))
})
return w, h
}
@ -765,15 +760,14 @@ func (u *userInterfaceImpl) DeviceScaleFactor() float64 {
}
// deviceScaleFactor must be called from the main thread.
func (u *userInterfaceImpl) deviceScaleFactor(monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) deviceScaleFactor(monitor *Monitor) float64 {
// It is rare, but monitor can be nil when glfw.GetPrimaryMonitor returns nil.
// In this case, return 1 as a tentative scale (#1878).
if monitor == nil {
return 1
}
mx, my := monitor.GetPos()
return devicescale.GetAt(mx, my)
return devicescale.GetAt(monitor.x, monitor.y)
}
func init() {
@ -788,7 +782,7 @@ func init() {
// createWindow must be called from the main thread.
//
// createWindow does not set the position or size so far.
func (u *userInterfaceImpl) createWindow(width, height int, monitor *glfw.Monitor) error {
func (u *userInterfaceImpl) createWindow(width, height int, monitor *Monitor) error {
if u.window != nil {
panic("ui: u.window must not exist at createWindow")
}
@ -800,14 +794,13 @@ func (u *userInterfaceImpl) createWindow(width, height int, monitor *glfw.Monito
}
// Set our target monitor if provided. This is required to prevent an initial window flash on the default monitor.
x, y := monitor.GetPos()
vm := monitor.GetVideoMode()
vm := monitor.videoMode
mw := u.dipFromGLFWMonitorPixel(float64(vm.Width), monitor)
mh := u.dipFromGLFWMonitorPixel(float64(vm.Height), monitor)
mw = u.dipToGLFWPixel(mw, monitor)
mh = u.dipToGLFWPixel(mh, monitor)
px, py := InitialWindowPosition(int(mw), int(mh), width, height)
window.SetPos(x+px, y+py)
window.SetPos(monitor.x+px, monitor.y+py)
initializeWindowAfterCreation(window)
@ -1003,9 +996,9 @@ func (u *userInterfaceImpl) initOnMainThread(options *RunOptions) error {
}
ww, wh := u.getInitWindowSizeInDIP()
initW := int(u.dipToGLFWPixel(float64(ww), u.initMonitor.m))
initH := int(u.dipToGLFWPixel(float64(wh), u.initMonitor.m))
if err := u.createWindow(initW, initH, u.initMonitor.m); err != nil {
initW := int(u.dipToGLFWPixel(float64(ww), u.initMonitor))
initH := int(u.dipToGLFWPixel(float64(wh), u.initMonitor))
if err := u.createWindow(initW, initH, u.initMonitor); err != nil {
return err
}
@ -1026,7 +1019,7 @@ func (u *userInterfaceImpl) initOnMainThread(options *RunOptions) error {
if max := u.initFullscreenHeightInDIP - wh; wy >= max {
wy = max
}
u.setWindowPositionInDIP(wx, wy, u.initMonitor.m)
u.setWindowPositionInDIP(wx, wy, u.initMonitor)
u.setWindowSizeInDIP(ww, wh, true)
// Maximizing a window requires a proper size and position. Call Maximize here (#1117).
@ -1070,8 +1063,8 @@ func (u *userInterfaceImpl) outsideSize() (float64, float64) {
// reflecting the adjustment of the view size (#1745).
var w, h float64
if m := u.currentMonitor(); m != nil {
v := m.GetVideoMode()
ww, wh := v.Width, v.Height
vm := m.videoMode
ww, wh := vm.Width, vm.Height
w = u.dipFromGLFWMonitorPixel(float64(ww), m)
h = u.dipFromGLFWMonitorPixel(float64(wh), m)
}
@ -1434,8 +1427,8 @@ func (u *userInterfaceImpl) setFullscreen(fullscreen bool) {
return
}
v := m.GetVideoMode()
u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
vm := m.videoMode
u.window.SetMonitor(m.m, 0, 0, vm.Width, vm.Height, vm.RefreshRate)
}
u.adjustViewSizeAfterFullscreen()
return
@ -1519,20 +1512,20 @@ func (u *userInterfaceImpl) updateVsyncOnRenderThread() {
// currentMonitor returns the current active monitor.
//
// currentMonitor must be called on the main thread.
func (u *userInterfaceImpl) currentMonitor() *glfw.Monitor {
func (u *userInterfaceImpl) currentMonitor() *Monitor {
if u.window == nil {
return u.initMonitor.m
return u.initMonitor
}
if m := monitorFromWindow(u.window); m != nil {
return m
}
return glfw.GetPrimaryMonitor()
return theMonitors.monitorFromGLFWMonitor(glfw.GetPrimaryMonitor())
}
// monitorFromWindow returns the monitor from the given window.
//
// monitorFromWindow must be called on the main thread.
func monitorFromWindow(window *glfw.Window) *glfw.Monitor {
func monitorFromWindow(window *glfw.Window) *Monitor {
// Getting a monitor from a window position is not reliable in general (e.g., when a window is put across
// multiple monitors, or, before SetWindowPosition is called.).
// Get the monitor which the current window belongs to. This requires OS API.
@ -1542,7 +1535,7 @@ func monitorFromWindow(window *glfw.Window) *glfw.Monitor {
// As the fallback, detect the monitor from the window.
if m := getMonitorFromPosition(window.GetPos()); m != nil {
return m.m
return m
}
return nil
@ -1669,13 +1662,13 @@ func (u *userInterfaceImpl) setWindowResizingMode(mode WindowResizingMode) {
// x and y are the position in device-independent pixels.
//
// setWindowPositionInDIP must be called from the main thread.
func (u *userInterfaceImpl) setWindowPositionInDIP(x, y int, monitor *glfw.Monitor) {
func (u *userInterfaceImpl) setWindowPositionInDIP(x, y int, monitor *Monitor) {
if microsoftgdk.IsXbox() {
// Do nothing. The position is always fixed.
return
}
mx, my := monitor.GetPos()
mx, my := monitor.x, monitor.y
xf := u.dipToGLFWPixel(float64(x), monitor)
yf := u.dipToGLFWPixel(float64(y), monitor)
if x, y := u.adjustWindowPosition(mx+int(xf), my+int(yf), monitor); u.isFullscreen() {

View File

@ -184,16 +184,16 @@ func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return metal.NewGraphics()
}
// clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {}
// videoModeScale must be called from the main thread.
func videoModeScale(monitor *glfw.Monitor) float64 {
return 1
}
// dipFromGLFWMonitorPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *Monitor) float64 {
return x
}
// dipFromGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *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
@ -201,12 +201,11 @@ func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *glfw.Monitor) f
return x
}
// dipToGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *Monitor) float64 {
return x
}
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *glfw.Monitor) (int, int) {
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *Monitor) (int, int) {
return x, y
}
@ -254,21 +253,21 @@ func currentMouseLocation() (x, y int) {
return int(point.X), int(point.Y)
}
func initialMonitorByOS() (*glfw.Monitor, error) {
func initialMonitorByOS() (*Monitor, error) {
x, y := currentMouseLocation()
// Find the monitor including the cursor.
for _, m := range theMonitors.append(nil) {
w, h := m.vm.Width, m.vm.Height
w, h := m.videoMode.Width, m.videoMode.Height
if x >= m.x && x < m.x+w && y >= m.y && y < m.y+h {
return m.m, nil
return m, nil
}
}
return nil, nil
}
func monitorFromWindowByOS(w *glfw.Window) *glfw.Monitor {
func monitorFromWindowByOS(w *glfw.Window) *Monitor {
window := cocoa.NSWindow{ID: objc.ID(w.GetCocoaWindow())}
pool := cocoa.NSAutoreleasePool_new()
screen := cocoa.NSScreen_mainScreen()
@ -283,7 +282,7 @@ func monitorFromWindowByOS(w *glfw.Window) *glfw.Monitor {
pool.Release()
for _, m := range theMonitors.append(nil) {
if m.m.GetCocoaMonitor() == aID {
return m.m
return m
}
}
return nil

View File

@ -51,39 +51,8 @@ func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil, nil
}
type videoModeScaleCacheKey struct{ X, Y int }
var videoModeScaleCache = map[videoModeScaleCacheKey]float64{}
// clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {
for k := range videoModeScaleCache {
delete(videoModeScaleCache, k)
}
}
// videoModeScale must be called from the main thread.
func videoModeScale(m *glfw.Monitor) float64 {
if m == nil {
return 1
}
// Caching wrapper for videoModeScaleUncached as
// videoModeScaleUncached may be expensive (uses blocking calls on X connection)
// and public ScreenSizeInFullscreen API needs the videoModeScale.
monitorX, monitorY := m.GetPos()
cacheKey := videoModeScaleCacheKey{X: monitorX, Y: monitorY}
if cached, ok := videoModeScaleCache[cacheKey]; ok {
return cached
}
scale := videoModeScaleUncached(m)
videoModeScaleCache[cacheKey] = scale
return scale
}
// videoModeScaleUncached must be called from the main thread.
func videoModeScaleUncached(m *glfw.Monitor) float64 {
// TODO: if glfw/glfw#1961 gets fixed, this function may need revising.
// In case GLFW decides to switch to returning logical pixels, we can just return 1.
@ -142,25 +111,25 @@ func videoModeScaleUncached(m *glfw.Monitor) float64 {
}
// dipFromGLFWMonitorPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 {
return x / (videoModeScale(monitor) * u.deviceScaleFactor(monitor))
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *Monitor) float64 {
return x / (monitor.videoModeScale * u.deviceScaleFactor(monitor))
}
// dipFromGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *Monitor) float64 {
return x / u.deviceScaleFactor(monitor)
}
// dipToGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *Monitor) float64 {
return x * u.deviceScaleFactor(monitor)
}
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *glfw.Monitor) (int, int) {
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *Monitor) (int, int) {
return x, y
}
func initialMonitorByOS() (*glfw.Monitor, error) {
func initialMonitorByOS() (*Monitor, error) {
xconn, err := xgb.NewConn()
if err != nil {
// Assume we're on pure Wayland then.
@ -177,16 +146,16 @@ func initialMonitorByOS() (*glfw.Monitor, error) {
// Find the monitor including the cursor.
for _, m := range theMonitors.append(nil) {
w, h := m.vm.Width, m.vm.Height
w, h := m.videoMode.Width, m.videoMode.Height
if x >= m.x && x < m.x+w && y >= m.y && y < m.y+h {
return m.m, nil
return m, nil
}
}
return nil, nil
}
func monitorFromWindowByOS(_ *glfw.Window) *glfw.Monitor {
func monitorFromWindowByOS(_ *glfw.Window) *Monitor {
// TODO: Implement this correctly. (#1119).
return nil
}

View File

@ -86,30 +86,32 @@ func (*graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error) {
return nil, nil
}
// clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {}
// videoModeScale must be called from the main thread.
func videoModeScale(monitor *glfw.Monitor) float64 {
return 1
}
// dipFromGLFWMonitorPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipFromGLFWMonitorPixel(x float64, monitor *Monitor) float64 {
return x / u.deviceScaleFactor(monitor)
}
// dipFromGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipFromGLFWPixel(x float64, monitor *Monitor) float64 {
return x / u.deviceScaleFactor(monitor)
}
// dipToGLFWPixel must be called from the main thread.
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *glfw.Monitor) float64 {
func (u *userInterfaceImpl) dipToGLFWPixel(x float64, monitor *Monitor) float64 {
return x * u.deviceScaleFactor(monitor)
}
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *glfw.Monitor) (int, int) {
func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *Monitor) (int, int) {
if microsoftgdk.IsXbox() {
return x, y
}
mx, my := monitor.GetPos()
mx, my := monitor.x, monitor.y
// As the video width/height might be wrong,
// adjust x/y at least to enable to handle the window (#328)
if x < mx {
@ -125,9 +127,9 @@ func (u *userInterfaceImpl) adjustWindowPosition(x, y int, monitor *glfw.Monitor
return x, y
}
func initialMonitorByOS() (*glfw.Monitor, error) {
func initialMonitorByOS() (*Monitor, error) {
if microsoftgdk.IsXbox() {
return glfw.GetPrimaryMonitor(), nil
return theMonitors.monitorFromGLFWMonitor(glfw.GetPrimaryMonitor()), nil
}
px, py, err := _GetCursorPos()
@ -141,23 +143,23 @@ func initialMonitorByOS() (*glfw.Monitor, error) {
// Find the monitor including the cursor.
for _, m := range theMonitors.append(nil) {
w, h := m.vm.Width, m.vm.Height
w, h := m.videoMode.Width, m.videoMode.Height
if x >= m.x && x < m.x+w && y >= m.y && y < m.y+h {
return m.m, nil
return m, nil
}
}
return nil, nil
}
func monitorFromWindowByOS(w *glfw.Window) *glfw.Monitor {
func monitorFromWindowByOS(w *glfw.Window) *Monitor {
if microsoftgdk.IsXbox() {
return glfw.GetPrimaryMonitor()
return theMonitors.monitorFromGLFWMonitor(glfw.GetPrimaryMonitor())
}
return monitorFromWin32Window(windows.HWND(w.GetWin32Window()))
}
func monitorFromWin32Window(w windows.HWND) *glfw.Monitor {
func monitorFromWin32Window(w windows.HWND) *Monitor {
// Get the current monitor by the window handle instead of the window position. It is because the window
// position is not reliable in some cases e.g. when the window is put across multiple monitors.
@ -175,7 +177,7 @@ func monitorFromWin32Window(w windows.HWND) *glfw.Monitor {
x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
for _, m := range theMonitors.append(nil) {
if m.x == x && m.y == y {
return m.m
return m
}
}
return nil

View File

@ -264,9 +264,8 @@ func (w *glfwWindow) Position() (int, int) {
wx, wy = w.ui.window.GetPos()
}
m := w.ui.currentMonitor()
mx, my := m.GetPos()
wx -= mx
wy -= my
wx -= m.x
wy -= m.y
xf := w.ui.dipFromGLFWPixel(float64(wx), m)
yf := w.ui.dipFromGLFWPixel(float64(wy), m)
x, y = int(xf), int(yf)