ebiten: add SetWindowMousePassthrough and IsWindowMousePassthrough

Closes #2511
This commit is contained in:
Hajime Hoshi 2023-09-18 17:48:07 +09:00
parent c8d38f7f25
commit 14f2ee198e
6 changed files with 128 additions and 24 deletions

View File

@ -142,6 +142,7 @@ func main() {
ebiten.SetWindowDecorated(false) ebiten.SetWindowDecorated(false)
ebiten.SetWindowFloating(true) ebiten.SetWindowFloating(true)
ebiten.SetWindowSize(width, height) ebiten.SetWindowSize(width, height)
ebiten.SetWindowMousePassthrough(true)
op := &ebiten.RunGameOptions{} op := &ebiten.RunGameOptions{}
op.ScreenTransparent = true op.ScreenTransparent = true

View File

@ -147,6 +147,7 @@ func (g *game) Update() error {
floating := ebiten.IsWindowFloating() floating := ebiten.IsWindowFloating()
resizingMode := ebiten.WindowResizingMode() resizingMode := ebiten.WindowResizingMode()
screenCleared := ebiten.IsScreenClearedEveryFrame() screenCleared := ebiten.IsScreenClearedEveryFrame()
mousePassthrough := ebiten.IsWindowMousePassthrough()
const d = 16 const d = 16
toUpdateWindowSize := false toUpdateWindowSize := false
@ -267,6 +268,9 @@ func (g *game) Update() error {
restore = inpututil.IsKeyJustPressed(ebiten.KeyE) restore = inpututil.IsKeyJustPressed(ebiten.KeyE)
} }
} }
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
mousePassthrough = !mousePassthrough
}
if toUpdateWindowSize { if toUpdateWindowSize {
g.width = screenWidth g.width = screenWidth
@ -304,6 +308,8 @@ func (g *game) Update() error {
ebiten.SetWindowIcon([]image.Image{createRandomIconImage()}) ebiten.SetWindowIcon([]image.Image{createRandomIconImage()})
} }
ebiten.SetWindowMousePassthrough(mousePassthrough)
g.count++ g.count++
return nil return nil
} }
@ -357,6 +363,7 @@ func (g *game) Draw(screen *ebiten.Image) {
[D] Switch the window decoration (only for desktops) [D] Switch the window decoration (only for desktops)
[L] Switch the window floating state (only for desktops) [L] Switch the window floating state (only for desktops)
[W] Switch whether to skip clearing the screen [W] Switch whether to skip clearing the screen
[P] Switch whether a mouse cursor passthroughs the window (only for desktops)
%s %s
IsFocused?: %s IsFocused?: %s
Window Position: (%d, %d) Window Position: (%d, %d)

View File

@ -84,16 +84,17 @@ type userInterfaceImpl struct {
initFullscreenWidthInDIP int initFullscreenWidthInDIP int
initFullscreenHeightInDIP int initFullscreenHeightInDIP int
initFullscreen bool initFullscreen bool
initCursorMode CursorMode initCursorMode CursorMode
initWindowDecorated bool initWindowDecorated bool
initWindowMonitor int initWindowMonitor int
initWindowPositionXInDIP int initWindowPositionXInDIP int
initWindowPositionYInDIP int initWindowPositionYInDIP int
initWindowWidthInDIP int initWindowWidthInDIP int
initWindowHeightInDIP int initWindowHeightInDIP int
initWindowFloating bool initWindowFloating bool
initWindowMaximized bool initWindowMaximized bool
initWindowMousePassthrough bool
// bufferOnceSwapped must be accessed from the main thread. // bufferOnceSwapped must be accessed from the main thread.
bufferOnceSwapped bool bufferOnceSwapped bool
@ -525,6 +526,18 @@ func (u *userInterfaceImpl) setInitWindowMaximized(maximized bool) {
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterfaceImpl) isInitWindowMousePassthrough() bool {
u.m.RLock()
defer u.m.RUnlock()
return u.initWindowMousePassthrough
}
func (u *userInterfaceImpl) setInitWindowMousePassthrough(enabled bool) {
u.m.Lock()
defer u.m.Unlock()
u.initWindowMousePassthrough = enabled
}
func (u *userInterfaceImpl) isWindowClosingHandled() bool { func (u *userInterfaceImpl) isWindowClosingHandled() bool {
u.m.RLock() u.m.RLock()
v := u.windowClosingHandled v := u.windowClosingHandled
@ -984,6 +997,12 @@ func (u *userInterfaceImpl) initOnMainThread(options *RunOptions) error {
} }
glfw.WindowHint(glfw.FocusOnShow, focused) glfw.WindowHint(glfw.FocusOnShow, focused)
mousePassthrough := glfw.False
if u.isInitWindowMousePassthrough() {
mousePassthrough = glfw.True
}
glfw.WindowHint(glfw.MousePassthrough, mousePassthrough)
// Set the window visible explicitly or the application freezes on Wayland (#974). // Set the window visible explicitly or the application freezes on Wayland (#974).
if os.Getenv("WAYLAND_DISPLAY") != "" { if os.Getenv("WAYLAND_DISPLAY") != "" {
glfw.WindowHint(glfw.Visible, glfw.True) glfw.WindowHint(glfw.Visible, glfw.True)
@ -1717,6 +1736,19 @@ func (u *userInterfaceImpl) setOrigWindowPos(x, y int) {
u.origWindowPosY = y u.origWindowPosY = y
} }
// setWindowMousePassthrough must be called from the main thread.
func (u *userInterfaceImpl) setWindowMousePassthrough(enabled bool) {
if microsoftgdk.IsXbox() {
return
}
v := glfw.False
if enabled {
v = glfw.True
}
u.window.SetAttrib(glfw.MousePassthrough, v)
}
func IsScreenTransparentAvailable() bool { func IsScreenTransparentAvailable() bool {
return true return true
} }

View File

@ -41,6 +41,8 @@ type Window interface {
Restore() Restore()
SetClosingHandled(handled bool) SetClosingHandled(handled bool)
IsClosingHandled() bool IsClosingHandled() bool
SetMousePassthrough(enabled bool)
IsMousePassthrough() bool
} }
type nullWindow struct{} type nullWindow struct{}
@ -119,3 +121,10 @@ func (*nullWindow) SetClosingHandled(handled bool) {
func (*nullWindow) IsClosingHandled() bool { func (*nullWindow) IsClosingHandled() bool {
return false return false
} }
func (*nullWindow) SetMousePassthrough(enabled bool) {
}
func (*nullWindow) IsMousePassthrough() bool {
return false
}

View File

@ -387,3 +387,36 @@ func (w *glfwWindow) SetClosingHandled(handled bool) {
func (w *glfwWindow) IsClosingHandled() bool { func (w *glfwWindow) IsClosingHandled() bool {
return w.ui.isWindowClosingHandled() return w.ui.isWindowClosingHandled()
} }
func (w *glfwWindow) SetMousePassthrough(enabled bool) {
if w.ui.isTerminated() {
return
}
if !w.ui.isRunning() {
w.ui.setInitWindowMousePassthrough(enabled)
return
}
w.ui.mainThread.Call(func() {
if w.ui.isTerminated() {
return
}
w.ui.setWindowMousePassthrough(enabled)
})
}
func (w *glfwWindow) IsMousePassthrough() bool {
if w.ui.isTerminated() {
return false
}
if !w.ui.isRunning() {
return w.ui.isInitWindowMousePassthrough()
}
var v bool
w.ui.mainThread.Call(func() {
if w.ui.isTerminated() {
return
}
v = w.ui.window.GetAttrib(glfw.MousePassthrough) == glfw.True
})
return v
}

View File

@ -54,7 +54,7 @@ func IsWindowDecorated() bool {
// The window is decorated by default. // The window is decorated by default.
// //
// SetWindowDecorated works only on desktops. // SetWindowDecorated works only on desktops.
// SetWindowDecorated does nothing on other platforms. // SetWindowDecorated does nothing if the platform is not a desktop.
// //
// SetWindowDecorated is concurrent-safe. // SetWindowDecorated is concurrent-safe.
func SetWindowDecorated(decorated bool) { func SetWindowDecorated(decorated bool) {
@ -99,7 +99,7 @@ func SetWindowResizable(resizable bool) {
// SetWindowTitle sets the title of the window. // SetWindowTitle sets the title of the window.
// //
// SetWindowTitle does nothing on browsers or mobiles. // SetWindowTitle does nothing if the platform is not a desktop.
// //
// SetWindowTitle is concurrent-safe. // SetWindowTitle is concurrent-safe.
func SetWindowTitle(title string) { func SetWindowTitle(title string) {
@ -123,7 +123,7 @@ func SetWindowTitle(title string) {
// //
// As macOS windows don't have icons, SetWindowIcon doesn't work on macOS. // As macOS windows don't have icons, SetWindowIcon doesn't work on macOS.
// //
// SetWindowIcon doesn't work on browsers or mobiles. // SetWindowIcon doesn't work if the platform is not a desktop.
// //
// SetWindowIcon is concurrent-safe. // SetWindowIcon is concurrent-safe.
func SetWindowIcon(iconImages []image.Image) { func SetWindowIcon(iconImages []image.Image) {
@ -138,7 +138,7 @@ func SetWindowIcon(iconImages []image.Image) {
// //
// WindowPosition returns the original window position in fullscreen mode. // WindowPosition returns the original window position in fullscreen mode.
// //
// WindowPosition returns (0, 0) on browsers and mobiles. // WindowPosition returns (0, 0) if the platform is not a desktop.
// //
// WindowPosition is concurrent-safe. // WindowPosition is concurrent-safe.
func WindowPosition() (x, y int) { func WindowPosition() (x, y int) {
@ -151,7 +151,7 @@ func WindowPosition() (x, y int) {
// //
// SetWindowPosition sets the original window position in fullscreen mode. // SetWindowPosition sets the original window position in fullscreen mode.
// //
// SetWindowPosition does nothing on browsers and mobiles. // SetWindowPosition does nothing if the platform is not a desktop.
// //
// SetWindowPosition is concurrent-safe. // SetWindowPosition is concurrent-safe.
func SetWindowPosition(x, y int) { func SetWindowPosition(x, y int) {
@ -215,7 +215,7 @@ func SetWindowSizeLimits(minw, minh, maxw, maxh int) {
// IsWindowFloating reports whether the window is always shown above all the other windows. // IsWindowFloating reports whether the window is always shown above all the other windows.
// //
// IsWindowFloating returns false on browsers and mobiles. // IsWindowFloating returns false if the platform is not a desktop.
// //
// IsWindowFloating is concurrent-safe. // IsWindowFloating is concurrent-safe.
func IsWindowFloating() bool { func IsWindowFloating() bool {
@ -224,7 +224,7 @@ func IsWindowFloating() bool {
// SetWindowFloating sets the state whether the window is always shown above all the other windows. // SetWindowFloating sets the state whether the window is always shown above all the other windows.
// //
// SetWindowFloating does nothing on browsers or mobiles. // SetWindowFloating does nothing if the platform is not a desktop.
// //
// SetWindowFloating is concurrent-safe. // SetWindowFloating is concurrent-safe.
func SetWindowFloating(float bool) { func SetWindowFloating(float bool) {
@ -235,7 +235,7 @@ func SetWindowFloating(float bool) {
// //
// MaximizeWindow does nothing when the window is not resizable (WindowResizingModeEnabled). // MaximizeWindow does nothing when the window is not resizable (WindowResizingModeEnabled).
// //
// MaximizeWindow does nothing on browsers or mobiles. // MaximizeWindow does nothing if the platform is not a desktop.
// //
// MaximizeWindow is concurrent-safe. // MaximizeWindow is concurrent-safe.
func MaximizeWindow() { func MaximizeWindow() {
@ -246,7 +246,7 @@ func MaximizeWindow() {
// //
// IsWindowMaximized returns false when the window is not resizable (WindowResizingModeEnabled). // IsWindowMaximized returns false when the window is not resizable (WindowResizingModeEnabled).
// //
// IsWindowMaximized always returns false on browsers and mobiles. // IsWindowMaximized always returns false if the platform is not a desktop.
// //
// IsWindowMaximized is concurrent-safe. // IsWindowMaximized is concurrent-safe.
func IsWindowMaximized() bool { func IsWindowMaximized() bool {
@ -257,7 +257,7 @@ func IsWindowMaximized() bool {
// //
// If the main loop does not start yet, MinimizeWindow does nothing. // If the main loop does not start yet, MinimizeWindow does nothing.
// //
// MinimizeWindow does nothing on browsers or mobiles. // MinimizeWindow does nothing if the platform is not a desktop.
// //
// MinimizeWindow is concurrent-safe. // MinimizeWindow is concurrent-safe.
func MinimizeWindow() { func MinimizeWindow() {
@ -266,7 +266,7 @@ func MinimizeWindow() {
// IsWindowMinimized reports whether the window is minimized or not. // IsWindowMinimized reports whether the window is minimized or not.
// //
// IsWindowMinimized always returns false on browsers and mobiles. // IsWindowMinimized always returns false if the platform is not a desktop.
// //
// IsWindowMinimized is concurrent-safe. // IsWindowMinimized is concurrent-safe.
func IsWindowMinimized() bool { func IsWindowMinimized() bool {
@ -289,7 +289,7 @@ func RestoreWindow() {
// As the window is closed immediately by default, // As the window is closed immediately by default,
// you might want to call SetWindowClosingHandled(true) to prevent the window is automatically closed. // you might want to call SetWindowClosingHandled(true) to prevent the window is automatically closed.
// //
// IsWindowBeingClosed always returns false on other platforms. // IsWindowBeingClosed always returns false if the platform is not a desktop.
// //
// IsWindowBeingClosed is concurrent-safe. // IsWindowBeingClosed is concurrent-safe.
func IsWindowBeingClosed() bool { func IsWindowBeingClosed() bool {
@ -304,7 +304,7 @@ func IsWindowBeingClosed() bool {
// To end the game, you have to return an error value at the Game's Update function. // To end the game, you have to return an error value at the Game's Update function.
// //
// SetWindowClosingHandled works only on desktops. // SetWindowClosingHandled works only on desktops.
// SetWindowClosingHandled does nothing on other platforms. // SetWindowClosingHandled does nothing if the platform is not a desktop.
// //
// SetWindowClosingHandled is concurrent-safe. // SetWindowClosingHandled is concurrent-safe.
func SetWindowClosingHandled(handled bool) { func SetWindowClosingHandled(handled bool) {
@ -313,9 +313,31 @@ func SetWindowClosingHandled(handled bool) {
// IsWindowClosingHandled reports whether the window closing is handled or not on desktops by SetWindowClosingHandled. // IsWindowClosingHandled reports whether the window closing is handled or not on desktops by SetWindowClosingHandled.
// //
// IsWindowClosingHandled always returns false on other platforms. // IsWindowClosingHandled always returns false if the platform is not a desktop.
// //
// IsWindowClosingHandled is concurrent-safe. // IsWindowClosingHandled is concurrent-safe.
func IsWindowClosingHandled() bool { func IsWindowClosingHandled() bool {
return ui.Get().Window().IsClosingHandled() return ui.Get().Window().IsClosingHandled()
} }
// SetWindowMousePassthrough sets whether a mouse cursor passthroughs the window or not on desktops. The default state is false.
//
// Even if this is set true, some platforms might requrie a window to be undecorated
// in order to make the mouse cursor passthrough the window.
//
// SetWindowMousePassthrough works only on desktops.
// SetWindowMousePassthrough does nothing if the platform is not a desktop.
//
// SetWindowMousePassthrough is concurrent-safe.
func SetWindowMousePassthrough(enabled bool) {
ui.Get().Window().SetMousePassthrough(enabled)
}
// IsWindowMousePassthrough reports whether a mouse cursor passthroughs the window or not on desktops.
//
// IsWindowMousePassthrough alaywas returns false if the platform is not a desktop.
//
// IsWindowMousePassthrough is concurrent-safe.
func IsWindowMousePassthrough() bool {
return ui.Get().Window().IsMousePassthrough()
}