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 u.setSizeCallbackEnabled = true
setPosition := func() {
u.iwindow.setPosition(u.getInitWindowPosition())
}
setSize := func() { setSize := func() {
ww, wh := u.getInitWindowSize() ww, wh := u.getInitWindowSize()
ww = int(u.toGLFWPixel(float64(ww))) 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 // 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. // this. This doesn't matter on macOS.
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
setPosition() u.setWindowPosition(u.getInitWindowPosition())
setSize() setSize()
} else { } else {
setSize() setSize()
setPosition() u.setWindowPosition(u.getInitWindowPosition())
} }
u.updateWindowSizeLimits() u.updateWindowSizeLimits()
@ -1353,8 +1350,12 @@ func (u *UserInterface) Window() driver.Window {
return &u.iwindow return &u.iwindow
} }
func (u *UserInterface) maximize() { // GLFW's functions to manipulate a window can invoke the SetSize callback (#1576, #1585, #1606).
// Maximize invokes the SetSize callback but the callback must not be called in the game's Update (#1576). // 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 { if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false u.setSizeCallbackEnabled = false
defer func() { defer func() {
@ -1368,8 +1369,8 @@ func (u *UserInterface) maximize() {
u.setWindowSize(w, h, u.isFullscreen()) u.setWindowSize(w, h, u.isFullscreen())
} }
func (u *UserInterface) iconify() { // iconifyWindow must be called from the main thread.
// Iconify invokes the SetSize callback but the callback must not be called in the game's Update (#1576). func (u *UserInterface) iconifyWindow() {
if u.setSizeCallbackEnabled { if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false u.setSizeCallbackEnabled = false
defer func() { 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). // Rather, the window size might be (0, 0) and it might be impossible to call setWindowSize (#1585).
} }
func (u *UserInterface) restore() { // restoreWindow must be called from the main thread.
// Restore invokes the SetSize callback but the callback must not be called in the game's Update (#1576). func (u *UserInterface) restoreWindow() {
if u.setSizeCallbackEnabled { if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false u.setSizeCallbackEnabled = false
defer func() { defer func() {
@ -1397,9 +1398,8 @@ func (u *UserInterface) restore() {
u.setWindowSize(w, h, u.isFullscreen()) u.setWindowSize(w, h, u.isFullscreen())
} }
func (u *UserInterface) setDecorated(decorated bool) { // setWindowDecorated must be called from the main thread.
// SetAttrib with glfw.Decorated invokes the SetSize callback but the callback must not be called in the game's Update (#1586). func (u *UserInterface) setWindowDecorated(decorated bool) {
// SetSize callback is invoked in the limited situations like just after restoring from the fullscreen mode.
if u.setSizeCallbackEnabled { if u.setSizeCallbackEnabled {
u.setSizeCallbackEnabled = false u.setSizeCallbackEnabled = false
defer func() { defer func() {
@ -1412,7 +1412,75 @@ func (u *UserInterface) setDecorated(decorated bool) {
} }
u.window.SetAttrib(glfw.Decorated, v) u.window.SetAttrib(glfw.Decorated, v)
// Just after restoring from the fullscreen mode, the window's size might be a wrong value on Windows. // The title can be lost when the decoration is gone. Recover this.
// This was the cause to invoke SetSize callback unexpectedly. This sounds like a GLFW's issue, but this is not confirmed. if decorated {
// As the window size should not be changed, setWindowSize doesn't have to be called anyway. 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 return nil
} }
w.ui.setDecorated(decorated) w.ui.setWindowDecorated(decorated)
// The title can be lost when the decoration is gone. Recover this.
if decorated {
w.ui.window.SetTitle(w.ui.title)
}
return nil return nil
}) })
} }
@ -82,12 +77,7 @@ func (w *window) SetResizable(resizable bool) {
if w.ui.isNativeFullscreen() { if w.ui.isNativeFullscreen() {
return nil return nil
} }
w.ui.setWindowResizable(resizable)
v := glfw.False
if resizable {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Resizable, v)
return nil return nil
}) })
} }
@ -113,12 +103,7 @@ func (w *window) SetFloating(floating bool) {
if w.ui.isNativeFullscreen() { if w.ui.isNativeFullscreen() {
return nil return nil
} }
w.ui.setWindowFloating(floating)
v := glfw.False
if floating {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Floating, v)
return nil return nil
}) })
} }
@ -144,7 +129,7 @@ func (w *window) Maximize() {
return return
} }
_ = w.ui.t.Call(func() error { _ = w.ui.t.Call(func() error {
w.ui.maximize() w.ui.maximizeWindow()
return nil return nil
}) })
} }
@ -167,7 +152,7 @@ func (w *window) Minimize() {
return return
} }
_ = w.ui.t.Call(func() error { _ = w.ui.t.Call(func() error {
w.ui.iconify() w.ui.iconifyWindow()
return nil return nil
}) })
} }
@ -178,7 +163,7 @@ func (w *window) Restore() {
return return
} }
_ = w.ui.t.Call(func() error { _ = w.ui.t.Call(func() error {
w.ui.restore() w.ui.restoreWindow()
return nil return nil
}) })
} }
@ -212,23 +197,11 @@ func (w *window) SetPosition(x, y int) {
return return
} }
_ = w.ui.t.Call(func() error { _ = w.ui.t.Call(func() error {
w.setPosition(x, y) w.ui.setWindowPosition(x, y)
return nil 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) { func (w *window) Size() (int, int) {
if !w.ui.isRunning() { if !w.ui.isRunning() {
ww, wh := w.ui.getInitWindowSize() ww, wh := w.ui.getInitWindowSize()
@ -282,7 +255,7 @@ func (w *window) SetTitle(title string) {
} }
w.ui.title = title w.ui.title = title
_ = w.ui.t.Call(func() error { _ = w.ui.t.Call(func() error {
w.ui.window.SetTitle(title) w.ui.setWindowTitle(title)
return nil return nil
}) })
} }