From eb365c51d58d90329cb54763a1789ae903d2fbdb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 21 Sep 2024 23:24:59 +0900 Subject: [PATCH] ebiten: add RequestAttention Closes #2998 --- examples/requestattention/main.go | 70 +++++++++++++++++++++++++++++++ internal/glfw/window_unix.go | 3 +- internal/ui/window.go | 4 ++ internal/ui/window_glfw.go | 19 +++++++++ window.go | 10 +++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 examples/requestattention/main.go diff --git a/examples/requestattention/main.go b/examples/requestattention/main.go new file mode 100644 index 000000000..627aa1582 --- /dev/null +++ b/examples/requestattention/main.go @@ -0,0 +1,70 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "math" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" +) + +const ( + screenWidth = 640 + screenHeight = 480 +) + +type Game struct { + count int +} + +func (g *Game) Update() error { + if g.count > 0 { + g.count-- + if g.count == 0 { + ebiten.RequestAttention() + } + } + if ebiten.IsKeyPressed(ebiten.KeyR) { + g.count = ebiten.TPS() * 3 + } + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + if g.count > 0 { + c := int(math.Ceil(float64(g.count) / float64(ebiten.TPS()))) + msg := fmt.Sprintf("Requesting attention in %d seconds...", c) + if ebiten.IsFocused() { + msg += "\nPlease unfocus this window to see the effect." + } + ebitenutil.DebugPrint(screen, msg) + return + } + ebitenutil.DebugPrint(screen, "Press R to request attention after 3 seconds.") +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + ebiten.SetWindowSize(screenWidth, screenHeight) + ebiten.SetWindowTitle("Request Attention (Ebitengine Demo)") + if err := ebiten.RunGame(&Game{}); err != nil { + panic(err) + } +} diff --git a/internal/glfw/window_unix.go b/internal/glfw/window_unix.go index a0d22de11..46964e199 100644 --- a/internal/glfw/window_unix.go +++ b/internal/glfw/window_unix.go @@ -546,8 +546,9 @@ func (w *Window) SetOpacity(opacity float32) { // application, the system will end the request automatically. // // This function must only be called from the main thread. -func (w *Window) RequestAttention() { +func (w *Window) RequestAttention() error { C.glfwRequestWindowAttention(w.data) + return nil } // Focus brings the specified window to front and sets input focus. diff --git a/internal/ui/window.go b/internal/ui/window.go index aae945c37..830264b66 100644 --- a/internal/ui/window.go +++ b/internal/ui/window.go @@ -43,6 +43,7 @@ type Window interface { IsClosingHandled() bool SetMousePassthrough(enabled bool) IsMousePassthrough() bool + RequestAttention() } type nullWindow struct{} @@ -128,3 +129,6 @@ func (*nullWindow) SetMousePassthrough(enabled bool) { func (*nullWindow) IsMousePassthrough() bool { return false } + +func (*nullWindow) RequestAttention() { +} diff --git a/internal/ui/window_glfw.go b/internal/ui/window_glfw.go index 4cdc7fec1..0f23511da 100644 --- a/internal/ui/window_glfw.go +++ b/internal/ui/window_glfw.go @@ -506,3 +506,22 @@ func (w *glfwWindow) IsMousePassthrough() bool { }) return v } + +func (w *glfwWindow) RequestAttention() { + if w.ui.isTerminated() { + return + } + if !w.ui.isRunning() { + // Do nothing + return + } + w.ui.mainThread.Call(func() { + if w.ui.isTerminated() { + return + } + if err := w.ui.window.RequestAttention(); err != nil { + w.ui.setError(err) + return + } + }) +} diff --git a/window.go b/window.go index 70f7efd2e..fa0509f79 100644 --- a/window.go +++ b/window.go @@ -341,3 +341,13 @@ func SetWindowMousePassthrough(enabled bool) { func IsWindowMousePassthrough() bool { return ui.Get().Window().IsMousePassthrough() } + +// RequestAttention requests user attention to the current window and/or the current application. +// +// RequestAttention works only on desktops. +// RequestAttention does nothing if the platform is not a desktop. +// +// RequestAttention is concurrent-safe. +func RequestAttention() { + ui.Get().Window().RequestAttention() +}