internal/uidriver/glfw: Bug fix: Disable the SetSize callback for most of GLFW window functions

Updates #1576
Updates #1585
Closes #1606
This commit is contained in:
Hajime Hoshi 2021-04-20 18:16:41 +09:00
parent 74018348c0
commit ee8bfcd837
2 changed files with 93 additions and 52 deletions

View File

@ -816,9 +816,6 @@ func (u *UserInterface) init() error {
}
u.setSizeCallbackEnabled = true
setPosition := func() {
u.iwindow.setPosition(u.getInitWindowPosition())
}
setSize := func() {
ww, wh := u.getInitWindowSize()
ww = int(u.toGLFWPixel(float64(ww)))
@ -830,11 +827,11 @@ func (u *UserInterface) init() error {
// 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.
if runtime.GOOS == "windows" {
setPosition()
u.setWindowPosition(u.getInitWindowPosition())
setSize()
} else {
setSize()
setPosition()
u.setWindowPosition(u.getInitWindowPosition())
}
u.updateWindowSizeLimits()
@ -1353,8 +1350,12 @@ func (u *UserInterface) Window() driver.Window {
return &u.iwindow
}
func (u *UserInterface) maximize() {
// Maximize invokes the SetSize callback but the callback must not be called in the game's Update (#1576).
// GLFW's functions to manipulate a window can invoke the SetSize callback (#1576, #1585, #1606).
// As the callback must not be called in the frame (between BeginFrame and EndFrame),
// disable the callback temporarily.
// maximizeWindow must be called from the main thread.
func (u *UserInterface) maximizeWindow() {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
@ -1368,8 +1369,8 @@ func (u *UserInterface) maximize() {
u.setWindowSize(w, h, u.isFullscreen())
}
func (u *UserInterface) iconify() {
// Iconify invokes the SetSize callback but the callback must not be called in the game's Update (#1576).
// iconifyWindow must be called from the main thread.
func (u *UserInterface) iconifyWindow() {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
@ -1382,8 +1383,8 @@ func (u *UserInterface) iconify() {
// Rather, the window size might be (0, 0) and it might be impossible to call setWindowSize (#1585).
}
func (u *UserInterface) restore() {
// Restore invokes the SetSize callback but the callback must not be called in the game's Update (#1576).
// restoreWindow must be called from the main thread.
func (u *UserInterface) restoreWindow() {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
@ -1397,9 +1398,8 @@ func (u *UserInterface) restore() {
u.setWindowSize(w, h, u.isFullscreen())
}
func (u *UserInterface) setDecorated(decorated bool) {
// SetAttrib with glfw.Decorated invokes the SetSize callback but the callback must not be called in the game's Update (#1586).
// SetSize callback is invoked in the limited situations like just after restoring from the fullscreen mode.
// setWindowDecorated must be called from the main thread.
func (u *UserInterface) setWindowDecorated(decorated bool) {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
@ -1412,7 +1412,75 @@ func (u *UserInterface) setDecorated(decorated bool) {
}
u.window.SetAttrib(glfw.Decorated, v)
// Just after restoring from the fullscreen mode, the window's size might be a wrong value on Windows.
// This was the cause to invoke SetSize callback unexpectedly. This sounds like a GLFW's issue, but this is not confirmed.
// As the window size should not be changed, setWindowSize doesn't have to be called anyway.
// The title can be lost when the decoration is gone. Recover this.
if decorated {
u.window.SetTitle(u.title)
}
}
// setWindowFloating must be called from the main thread.
func (u *UserInterface) setWindowFloating(floating bool) {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
u.setSizeCallbackEnabled = true
}()
}
v := glfw.False
if floating {
v = glfw.True
}
u.window.SetAttrib(glfw.Floating, v)
}
// setWindowResizable must be called from the main thread.
func (u *UserInterface) setWindowResizable(resizable bool) {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
u.setSizeCallbackEnabled = true
}()
}
v := glfw.False
if resizable {
v = glfw.True
}
u.window.SetAttrib(glfw.Resizable, v)
}
// setWindowPosition must be called from the main thread.
func (u *UserInterface) setWindowPosition(x, y int) {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
u.setSizeCallbackEnabled = true
}()
}
mx, my := currentMonitor(u.window).GetPos()
xf := u.toGLFWPixel(float64(x))
yf := u.toGLFWPixel(float64(y))
if x, y := u.adjustWindowPosition(mx+int(xf), my+int(yf)); u.isFullscreen() {
u.origPosX, u.origPosY = x, y
} else {
u.window.SetPos(x, y)
}
// Call setWindowSize explicitly in order to update the rendering since the callback is disabled now.
// This is necessary in some very limited cases (#1606).
w, h := u.window.GetSize()
u.setWindowSize(w, h, u.isFullscreen())
}
// setWindowTitle must be called from the main thread.
func (u *UserInterface) setWindowTitle(title string) {
if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false
defer func() {
u.setSizeCallbackEnabled = true
}()
}
u.window.SetTitle(title)
}

View File

@ -51,12 +51,7 @@ func (w *window) SetDecorated(decorated bool) {
return nil
}
w.ui.setDecorated(decorated)
// The title can be lost when the decoration is gone. Recover this.
if decorated {
w.ui.window.SetTitle(w.ui.title)
}
w.ui.setWindowDecorated(decorated)
return nil
})
}
@ -82,12 +77,7 @@ func (w *window) SetResizable(resizable bool) {
if w.ui.isNativeFullscreen() {
return nil
}
v := glfw.False
if resizable {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Resizable, v)
w.ui.setWindowResizable(resizable)
return nil
})
}
@ -113,12 +103,7 @@ func (w *window) SetFloating(floating bool) {
if w.ui.isNativeFullscreen() {
return nil
}
v := glfw.False
if floating {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Floating, v)
w.ui.setWindowFloating(floating)
return nil
})
}
@ -144,7 +129,7 @@ func (w *window) Maximize() {
return
}
_ = w.ui.t.Call(func() error {
w.ui.maximize()
w.ui.maximizeWindow()
return nil
})
}
@ -167,7 +152,7 @@ func (w *window) Minimize() {
return
}
_ = w.ui.t.Call(func() error {
w.ui.iconify()
w.ui.iconifyWindow()
return nil
})
}
@ -178,7 +163,7 @@ func (w *window) Restore() {
return
}
_ = w.ui.t.Call(func() error {
w.ui.restore()
w.ui.restoreWindow()
return nil
})
}
@ -212,23 +197,11 @@ func (w *window) SetPosition(x, y int) {
return
}
_ = w.ui.t.Call(func() error {
w.setPosition(x, y)
w.ui.setWindowPosition(x, y)
return nil
})
}
// setPosition must be called from the main thread
func (w *window) setPosition(x, y int) {
mx, my := currentMonitor(w.ui.window).GetPos()
xf := w.ui.toGLFWPixel(float64(x))
yf := w.ui.toGLFWPixel(float64(y))
if x, y := w.ui.adjustWindowPosition(mx+int(xf), my+int(yf)); w.ui.isFullscreen() {
w.ui.origPosX, w.ui.origPosY = x, y
} else {
w.ui.window.SetPos(x, y)
}
}
func (w *window) Size() (int, int) {
if !w.ui.isRunning() {
ww, wh := w.ui.getInitWindowSize()
@ -282,7 +255,7 @@ func (w *window) SetTitle(title string) {
}
w.ui.title = title
_ = w.ui.t.Call(func() error {
w.ui.window.SetTitle(title)
w.ui.setWindowTitle(title)
return nil
})
}