mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 19:28:57 +01:00
internal/uidriver/glfw: Make a 'native' fullscreen on macOS
This is a breaking change. On macOS, `SetFullscreen` creates a new independent space and maximize the size of the window there. Updates #1506 Closes #1579
This commit is contained in:
parent
2a390c18c1
commit
428c079e52
@ -351,6 +351,10 @@ func WaitEvents() {
|
||||
glfw.WaitEvents()
|
||||
}
|
||||
|
||||
func WaitEventsTimeout(timeout float64) {
|
||||
glfw.WaitEventsTimeout(timeout)
|
||||
}
|
||||
|
||||
func WindowHint(target Hint, hint int) {
|
||||
glfw.WindowHint(glfw.Hint(target), hint)
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ func (u *UserInterface) isFullscreen() bool {
|
||||
if !u.isRunning() {
|
||||
panic("glfw: isFullscreen can't be called before the main loop starts")
|
||||
}
|
||||
return u.window.GetMonitor() != nil
|
||||
return u.window.GetMonitor() != nil || u.isNativeFullscreen()
|
||||
}
|
||||
|
||||
func (u *UserInterface) IsFullscreen() bool {
|
||||
@ -576,10 +576,6 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) {
|
||||
}
|
||||
|
||||
_ = u.t.Call(func() error {
|
||||
if u.isNativeFullscreen() {
|
||||
return nil
|
||||
}
|
||||
|
||||
w, h := u.windowWidth, u.windowHeight
|
||||
u.setWindowSize(w, h, fullscreen)
|
||||
return nil
|
||||
@ -783,7 +779,7 @@ func (u *UserInterface) registerWindowSetSizeCallback() {
|
||||
if u.window.GetAttrib(glfw.Resizable) == glfw.False {
|
||||
return
|
||||
}
|
||||
if u.isFullscreen() {
|
||||
if u.isFullscreen() && !u.isNativeFullscreen() {
|
||||
return
|
||||
}
|
||||
|
||||
@ -849,8 +845,8 @@ func (u *UserInterface) init() error {
|
||||
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
|
||||
}
|
||||
|
||||
// Enable auto-iconifying on Windows and macOS until some fullscreen issues are solved (#1506).
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
// Enable auto-iconifying on Windows until some fullscreen issues are solved (#1506).
|
||||
if runtime.GOOS == "windows" {
|
||||
glfw.WindowHint(glfw.AutoIconify, glfw.True)
|
||||
} else {
|
||||
glfw.WindowHint(glfw.AutoIconify, glfw.False)
|
||||
@ -1205,7 +1201,7 @@ func (u *UserInterface) adjustWindowSizeBasedOnSizeLimitsInDP(width, height int)
|
||||
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
width, height = u.adjustWindowSizeBasedOnSizeLimits(width, height)
|
||||
|
||||
u.Graphics().SetFullscreen(fullscreen || u.isNativeFullscreen())
|
||||
u.Graphics().SetFullscreen(fullscreen)
|
||||
|
||||
if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
|
||||
return
|
||||
@ -1235,21 +1231,42 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
}()
|
||||
}
|
||||
|
||||
windowRecreated := u.setWindowSizeImpl(width, height, fullscreen)
|
||||
|
||||
// As width might be updated, update windowWidth/Height here.
|
||||
u.windowWidth = width
|
||||
u.windowHeight = height
|
||||
|
||||
u.toChangeSize = true
|
||||
|
||||
if windowRecreated {
|
||||
if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
|
||||
g.SetWindow(u.nativeWindow())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserInterface) setWindowSizeImpl(width, height int, fullscreen bool) bool {
|
||||
var windowRecreated bool
|
||||
|
||||
if fullscreen {
|
||||
if u.origPosX == invalidPos || u.origPosY == invalidPos {
|
||||
u.origPosX, u.origPosY = u.window.GetPos()
|
||||
if x, y := u.origPos(); x == invalidPos || y == invalidPos {
|
||||
u.setOrigPos(u.window.GetPos())
|
||||
}
|
||||
m := currentMonitor(u.window)
|
||||
v := m.GetVideoMode()
|
||||
u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
|
||||
|
||||
// Swapping buffer is necesary to prevent the image lag (#1004).
|
||||
// TODO: This might not work when vsync is disabled.
|
||||
if u.Graphics().IsGL() {
|
||||
glfw.PollEvents()
|
||||
u.swapBuffers()
|
||||
if u.isNativeFullscreenAvailable() {
|
||||
u.setNativeFullscreen(fullscreen)
|
||||
} else {
|
||||
m := currentMonitor(u.window)
|
||||
v := m.GetVideoMode()
|
||||
u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
|
||||
|
||||
// Swapping buffer is necesary to prevent the image lag (#1004).
|
||||
// TODO: This might not work when vsync is disabled.
|
||||
if u.Graphics().IsGL() {
|
||||
glfw.PollEvents()
|
||||
u.swapBuffers()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// On Windows, giving a too small width doesn't call a callback (#165).
|
||||
@ -1263,7 +1280,9 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
width = minWindowWidth
|
||||
}
|
||||
|
||||
if u.window.GetMonitor() != nil {
|
||||
if u.isNativeFullscreenAvailable() && u.isNativeFullscreen() {
|
||||
u.setNativeFullscreen(false)
|
||||
} else if !u.isNativeFullscreenAvailable() && u.window.GetMonitor() != nil {
|
||||
if u.Graphics().IsGL() {
|
||||
// When OpenGL is used, swapping buffer is enough to solve the image-lag
|
||||
// issue (#1004). Rather, recreating window destroys GPU resources.
|
||||
@ -1289,9 +1308,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if u.origPosX != invalidPos && u.origPosY != invalidPos {
|
||||
x := u.origPosX
|
||||
y := u.origPosY
|
||||
if x, y := u.origPos(); x != invalidPos && y != invalidPos {
|
||||
u.window.SetPos(x, y)
|
||||
// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
|
||||
// work with two or more SetPos.
|
||||
@ -1299,8 +1316,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
u.window.SetPos(x+1, y)
|
||||
u.window.SetPos(x, y)
|
||||
}
|
||||
u.origPosX = invalidPos
|
||||
u.origPosY = invalidPos
|
||||
u.setOrigPos(invalidPos, invalidPos)
|
||||
}
|
||||
|
||||
// Set the window size after the position. The order matters.
|
||||
@ -1350,17 +1366,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
|
||||
u.window.SetTitle(u.title)
|
||||
}
|
||||
|
||||
// As width might be updated, update windowWidth/Height here.
|
||||
u.windowWidth = width
|
||||
u.windowHeight = height
|
||||
|
||||
u.toChangeSize = true
|
||||
|
||||
if windowRecreated {
|
||||
if g, ok := u.Graphics().(interface{ SetWindow(uintptr) }); ok {
|
||||
g.SetWindow(u.nativeWindow())
|
||||
}
|
||||
}
|
||||
return windowRecreated
|
||||
}
|
||||
|
||||
// updateVsync must be called on the main thread.
|
||||
@ -1625,7 +1631,7 @@ func (u *UserInterface) setWindowPosition(x, y int) {
|
||||
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
|
||||
u.setOrigPos(x, y)
|
||||
} else {
|
||||
u.window.SetPos(x, y)
|
||||
}
|
||||
@ -1654,3 +1660,26 @@ func (u *UserInterface) setWindowTitle(title string) {
|
||||
|
||||
u.window.SetTitle(title)
|
||||
}
|
||||
|
||||
func (u *UserInterface) origPos() (int, int) {
|
||||
// On macOS, the window can be fullscreened without calling an Ebiten function.
|
||||
// Then, an original position might not be available by u.window.GetPos().
|
||||
// Do not rely on the window position.
|
||||
if u.isNativeFullscreenAvailable() {
|
||||
return invalidPos, invalidPos
|
||||
}
|
||||
return u.origPosX, u.origPosY
|
||||
}
|
||||
|
||||
func (u *UserInterface) setOrigPos(x, y int) {
|
||||
// TODO: The original position should be updated at a 'PosCallback'.
|
||||
|
||||
// On macOS, the window can be fullscreened without calling an Ebiten function.
|
||||
// Then, an original position might not be available by u.window.GetPos().
|
||||
// Do not rely on the window position.
|
||||
if u.isNativeFullscreenAvailable() {
|
||||
return
|
||||
}
|
||||
u.origPosX = x
|
||||
u.origPosY = y
|
||||
}
|
||||
|
@ -50,6 +50,21 @@ package glfw
|
||||
// return (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
|
||||
// }
|
||||
//
|
||||
// static void setNativeFullscreen(uintptr_t windowPtr, bool fullscreen) {
|
||||
// NSWindow* window = (NSWindow*)windowPtr;
|
||||
// if (((window.styleMask & NSWindowStyleMaskFullScreen) != 0) == fullscreen) {
|
||||
// return;
|
||||
// }
|
||||
// bool origResizable = window.styleMask & NSWindowStyleMaskResizable;
|
||||
// if (!origResizable) {
|
||||
// window.styleMask |= NSWindowStyleMaskResizable;
|
||||
// }
|
||||
// [window toggleFullScreen:nil];
|
||||
// if (!origResizable) {
|
||||
// window.styleMask &= ~NSWindowStyleMaskResizable;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void setNativeCursor(int cursorID) {
|
||||
// id cursor = [[NSCursor class] performSelector:@selector(arrowCursor)];
|
||||
// switch (cursorID) {
|
||||
@ -139,3 +154,13 @@ func (u *UserInterface) isNativeFullscreen() bool {
|
||||
func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
|
||||
C.setNativeCursor(C.int(shape))
|
||||
}
|
||||
|
||||
func (u *UserInterface) isNativeFullscreenAvailable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
|
||||
// Toggling fullscreen might ignore events like keyUp. Ensure that events are fired.
|
||||
glfw.WaitEventsTimeout(1)
|
||||
C.setNativeFullscreen(C.uintptr_t(u.window.GetCocoaWindow()), C.bool(fullscreen))
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package glfw
|
||||
|
||||
import (
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
||||
@ -146,3 +147,11 @@ func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
|
||||
// TODO: Use native API in the future (#1571)
|
||||
u.window.SetCursor(glfwSystemCursors[shape])
|
||||
}
|
||||
|
||||
func (u *UserInterface) isNativeFullscreenAvailable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
|
||||
panic(fmt.Sprintf("glfw: setNativeFullscreen is not implemented in this environment: %s", runtime.GOOS))
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package glfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
@ -194,3 +195,11 @@ func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
|
||||
// TODO: Use native API in the future (#1571)
|
||||
u.window.SetCursor(glfwSystemCursors[shape])
|
||||
}
|
||||
|
||||
func (u *UserInterface) isNativeFullscreenAvailable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
|
||||
panic(fmt.Sprintf("glfw: setNativeFullscreen is not implemented in this environment: %s", runtime.GOOS))
|
||||
}
|
||||
|
@ -176,8 +176,8 @@ func (w *window) Position() (int, int) {
|
||||
x, y := 0, 0
|
||||
_ = w.ui.t.Call(func() error {
|
||||
var wx, wy int
|
||||
if w.ui.isFullscreen() {
|
||||
wx, wy = w.ui.origPosX, w.ui.origPosY
|
||||
if w.ui.isFullscreen() && !w.ui.isNativeFullscreenAvailable() {
|
||||
wx, wy = w.ui.origPos()
|
||||
} else {
|
||||
wx, wy = w.ui.window.GetPos()
|
||||
}
|
||||
@ -223,6 +223,12 @@ func (w *window) SetSize(width, height int) {
|
||||
return
|
||||
}
|
||||
_ = w.ui.t.Call(func() error {
|
||||
// When a window is a native fullscreen, forcing to resize the window might leave unexpected image lags.
|
||||
// Forbid this.
|
||||
if w.ui.isNativeFullscreen() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ww := int(w.ui.toGLFWPixel(float64(width)))
|
||||
wh := int(w.ui.toGLFWPixel(float64(height)))
|
||||
w.ui.setWindowSize(ww, wh, w.ui.isFullscreen())
|
||||
|
Loading…
Reference in New Issue
Block a user