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

@ -94,6 +94,7 @@ type userInterfaceImpl struct {
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()
}