mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2026-04-22 15:57:15 +08:00
internal/ui: defer GLFW initialization to RunGame
Move initializePlatform(), initializeGLFW(), and the monitor callback registration from UserInterface.init() (called at package init time) to initOnMainThread() (called only when RunGame() is invoked). This prevents go test from hanging for packages that transitively import Ebitengine, since GLFW initialization requires a display and blocks in headless environments. Also move initializeWindowPositionIfNeeded logic into initOnMainThread since it depends on the monitor being available, which requires GLFW. The window size and position-set flag are now passed through RunOptions. Closes #3430 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,6 +181,9 @@ type RunOptions struct {
|
||||
ApplePressAndHoldEnabled bool
|
||||
X11ClassName string
|
||||
X11InstanceName string
|
||||
InitWindowWidthInDIP int
|
||||
InitWindowHeightInDIP int
|
||||
WindowPositionSet bool
|
||||
}
|
||||
|
||||
// InitialWindowPosition returns the position for centering the given second width/height pair within the first width/height pair.
|
||||
|
||||
+47
-14
@@ -118,6 +118,7 @@ type userInterfaceImpl struct {
|
||||
cachedCurrentMonitor *Monitor
|
||||
cachedCurrentMonitorTime int64
|
||||
|
||||
initOnce sync.Once
|
||||
darwinInitOnce sync.Once
|
||||
showWindowOnce sync.Once
|
||||
bufferOnceSwappedOnce sync.Once
|
||||
@@ -161,20 +162,6 @@ func (u *UserInterface) init() error {
|
||||
|
||||
u.iwindow.ui = u
|
||||
|
||||
if err := u.initializePlatform(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.initializeGLFW(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := glfw.SetMonitorCallback(func(monitor *glfw.Monitor, event glfw.PeripheralEvent) {
|
||||
if err := theMonitors.update(); err != nil {
|
||||
u.setError(err)
|
||||
}
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -265,6 +252,30 @@ func (u *UserInterface) initializeGLFW() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureGLFWInit lazily initializes GLFW and related state on the first call.
|
||||
// This is safe to call multiple times; initialization happens only once.
|
||||
func (u *UserInterface) ensureGLFWInit() error {
|
||||
u.initOnce.Do(func() {
|
||||
if err := u.initializePlatform(); err != nil {
|
||||
u.setError(err)
|
||||
return
|
||||
}
|
||||
if err := u.initializeGLFW(); err != nil {
|
||||
u.setError(err)
|
||||
return
|
||||
}
|
||||
if _, err := glfw.SetMonitorCallback(func(monitor *glfw.Monitor, event glfw.PeripheralEvent) {
|
||||
if err := theMonitors.update(); err != nil {
|
||||
u.setError(err)
|
||||
}
|
||||
}); err != nil {
|
||||
u.setError(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
return u.error()
|
||||
}
|
||||
|
||||
func (u *UserInterface) setInitMonitor(m *Monitor) {
|
||||
u.initMonitor.Store(m)
|
||||
}
|
||||
@@ -275,12 +286,20 @@ func (u *UserInterface) getInitMonitor() *Monitor {
|
||||
|
||||
// AppendMonitors appends the current monitors to the passed in mons slice and returns it.
|
||||
func (u *UserInterface) AppendMonitors(monitors []*Monitor) []*Monitor {
|
||||
// Ensure GLFW is initialized so that the monitor list is available.
|
||||
if err := u.ensureGLFWInit(); err != nil {
|
||||
return monitors
|
||||
}
|
||||
return theMonitors.append(monitors)
|
||||
}
|
||||
|
||||
// Monitor returns the window's current monitor. Returns nil if there is no current monitor yet.
|
||||
func (u *UserInterface) Monitor() *Monitor {
|
||||
if !u.isRunning() {
|
||||
// Ensure GLFW is initialized so that the init monitor is available.
|
||||
if err := u.ensureGLFWInit(); err != nil {
|
||||
return nil
|
||||
}
|
||||
return u.getInitMonitor()
|
||||
}
|
||||
var monitor *Monitor
|
||||
@@ -989,6 +1008,20 @@ event:
|
||||
}
|
||||
|
||||
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
|
||||
if err := u.ensureGLFWInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Center the window on the monitor if the position was not explicitly set.
|
||||
if !options.WindowPositionSet {
|
||||
m := u.getInitMonitor()
|
||||
if m != nil {
|
||||
sw, sh := m.sizeInDIP()
|
||||
x, y := InitialWindowPosition(int(sw), int(sh), options.InitWindowWidthInDIP, options.InitWindowHeightInDIP)
|
||||
u.Window().SetPosition(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
u.setApplePressAndHoldEnabled(options.ApplePressAndHoldEnabled)
|
||||
|
||||
if err := glfw.WindowHint(glfw.AutoIconify, glfw.False); err != nil {
|
||||
|
||||
@@ -350,9 +350,11 @@ type RunGameOptions struct {
|
||||
func RunGameWithOptions(game Game, options *RunGameOptions) error {
|
||||
defer isRunGameEnded_.Store(true)
|
||||
|
||||
initializeWindowPositionIfNeeded(WindowSize())
|
||||
|
||||
op := toUIRunOptions(options)
|
||||
ww, wh := WindowSize()
|
||||
op.InitWindowWidthInDIP = ww
|
||||
op.InitWindowHeightInDIP = wh
|
||||
op.WindowPositionSet = windowPositionSetExplicitly.Load()
|
||||
|
||||
// This is necessary to change the result of IsScreenTransparent.
|
||||
screenTransparent.Store(op.ScreenTransparent)
|
||||
|
||||
@@ -184,14 +184,6 @@ var (
|
||||
windowPositionSetExplicitly atomic.Bool
|
||||
)
|
||||
|
||||
func initializeWindowPositionIfNeeded(width, height int) {
|
||||
if !windowPositionSetExplicitly.Load() {
|
||||
sw, sh := ui.Get().Monitor().Size()
|
||||
x, y := ui.InitialWindowPosition(sw, sh, width, height)
|
||||
ui.Get().Window().SetPosition(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// WindowSize returns the window size on desktops.
|
||||
// WindowSize returns (0, 0) on other environments.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user