diff --git a/examples/windowsize/main.go b/examples/windowsize/main.go index 7daef3469..d0cf7305f 100644 --- a/examples/windowsize/main.go +++ b/examples/windowsize/main.go @@ -61,6 +61,7 @@ var ( flagFloating = flag.Bool("floating", false, "make the window floating") flagMaximize = flag.Bool("maximize", false, "maximize the window") flagVsync = flag.Bool("vsync", true, "enable vsync") + flagInitFocused = flag.Bool("initfocused", true, "whether the window is focused on start") ) func init() { @@ -420,6 +421,11 @@ func main() { ebiten.SetWindowResizable(true) } + ebiten.SetInitFocused(*flagInitFocused) + if !*flagInitFocused { + ebiten.SetRunnableOnUnfocused(true) + } + const title = "Window Size (Ebiten Demo)" if *flagLegacy { update := func(screen *ebiten.Image) error { diff --git a/internal/driver/ui.go b/internal/driver/ui.go index feb45d87c..1660be246 100644 --- a/internal/driver/ui.go +++ b/internal/driver/ui.go @@ -54,6 +54,7 @@ type UI interface { IsScreenTransparent() bool SetScreenTransparent(transparent bool) + SetInitFocused(focused bool) Input() Input Window() Window diff --git a/internal/glfw/const.go b/internal/glfw/const.go index 0069dba35..e7e26e09c 100644 --- a/internal/glfw/const.go +++ b/internal/glfw/const.go @@ -79,6 +79,7 @@ const ( Decorated = Hint(0x00020005) Floating = Hint(0x00020007) Focused = Hint(0x00020001) + FocusOnShow = Hint(0x0002000C) Iconified = Hint(0x00020002) Maximized = Hint(0x00020008) Resizable = Hint(0x00020003) diff --git a/internal/uidriver/glfw/ui.go b/internal/uidriver/glfw/ui.go index 5c305e826..734807076 100644 --- a/internal/uidriver/glfw/ui.go +++ b/internal/uidriver/glfw/ui.go @@ -72,6 +72,7 @@ type UserInterface struct { initWindowMaximized bool initScreenTransparent bool initIconImages []image.Image + initFocused bool vsyncInited bool @@ -102,6 +103,8 @@ var ( initWindowPositionYInDP: invalidPos, initWindowWidthInDP: 640, initWindowHeightInDP: 480, + initFocused: true, + vsync: true, } ) @@ -371,6 +374,18 @@ func (u *UserInterface) isInitWindowMaximized() bool { func (u *UserInterface) setInitWindowMaximized(floating bool) { u.m.Lock() u.initWindowMaximized = floating +} + +func (u *UserInterface) isInitFocused() bool { + u.m.Lock() + v := u.initFocused + u.m.Unlock() + return v +} + +func (u *UserInterface) setInitFocused(focused bool) { + u.m.Lock() + u.initFocused = focused u.m.Unlock() } @@ -693,6 +708,12 @@ func (u *UserInterface) run() error { } glfw.WindowHint(glfw.Floating, floating) + focused := glfw.False + if u.isInitFocused() { + focused = glfw.True + } + glfw.WindowHint(glfw.FocusOnShow, focused) + // Set the window visible explicitly or the application freezes on Wayland (#974). if os.Getenv("WAYLAND_DISPLAY") != "" { glfw.WindowHint(glfw.Visible, glfw.True) @@ -1109,6 +1130,30 @@ func (u *UserInterface) ResetForFrame() { u.input.resetForFrame() } +func (u *UserInterface) MonitorPosition() (int, int) { + if !u.isRunning() { + return u.monitorPosition() + } + var mx, my int + _ = u.t.Call(func() error { + mx, my = u.monitorPosition() + return nil + }) + return mx, my +} + +func (u *UserInterface) SetInitFocused(focused bool) { + if u.isRunning() { + panic("ui: SetInitFocused must be called before the main loop") + } + u.setInitFocused(focused) +} + +func (u *UserInterface) monitorPosition() (int, int) { + // TODO: toDeviceIndependentPixel might be required. + return u.currentMonitor().GetPos() +} + func (u *UserInterface) Input() driver.Input { return &u.input } diff --git a/internal/uidriver/js/ui.go b/internal/uidriver/js/ui.go index 693fa0740..fddf36daf 100644 --- a/internal/uidriver/js/ui.go +++ b/internal/uidriver/js/ui.go @@ -34,6 +34,7 @@ type UserInterface struct { runnableOnUnfocused bool vsync bool running bool + initFocused bool sizeChanged bool contextLost bool @@ -47,6 +48,7 @@ type UserInterface struct { var theUI = &UserInterface{ sizeChanged: true, vsync: true, + initFocused: true, } func init() { @@ -404,7 +406,9 @@ func init() { } func (u *UserInterface) Run(context driver.UIContext) error { - canvas.Call("focus") + if u.initFocused { + canvas.Call("focus") + } u.running = true ch := u.loop(context) if runtime.GOARCH == "wasm" { @@ -460,6 +464,13 @@ func (u *UserInterface) ResetForFrame() { u.input.resetForFrame() } +func (u *UserInterface) SetInitFocused(focused bool) { + if u.running { + panic("ui: SetInitFocused must be called before the main loop") + } + u.initFocused = focused +} + func (u *UserInterface) Input() driver.Input { return &u.input } diff --git a/internal/uidriver/mobile/ui.go b/internal/uidriver/mobile/ui.go index fa9ba77d4..69f05e4a7 100644 --- a/internal/uidriver/mobile/ui.go +++ b/internal/uidriver/mobile/ui.go @@ -441,6 +441,10 @@ func (u *UserInterface) ResetForFrame() { u.input.resetForFrame() } +func (u *UserInterface) SetInitFocused(focused bool) { + // Do nothing +} + func (u *UserInterface) Input() driver.Input { return &u.input } diff --git a/run.go b/run.go index 508329b5d..7dfc012f5 100644 --- a/run.go +++ b/run.go @@ -618,3 +618,17 @@ func IsScreenTransparent() bool { func SetScreenTransparent(transparent bool) { uiDriver().SetScreenTransparent(transparent) } + +// SetInitFocused sets whether the application is focused on show. +// The default value is true, i.e., the application is focused. +// Note that the application does not proceed if this is not focused by default. +// This behavior can be changed by SetRunnableInBackground. +// +// SetInitFocused does nothing on mobile. +// +// SetInitFocused panics if this is called after the main loop. +// +// SetInitFocused is cuncurrent-safe. +func SetInitFocused(focused bool) { + uiDriver().SetInitFocused(focused) +}