internal/ui: bug fix: SetWindowIcon crashed in the single-thread mode

Updates #1468
Closes #2513
This commit is contained in:
Hajime Hoshi 2022-12-29 23:48:53 +09:00
parent b597ed1e27
commit 7e7deeab22
5 changed files with 47 additions and 35 deletions

View File

@ -144,6 +144,10 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
ui.resetForTick()
}
// Update window icons during a frame, since an icon might be *ebiten.Image and
// getting pixels from it needs to be in a frame (#1468).
ui.updateIconIfNeeded()
// Draw the game.
if err := c.drawGame(graphicsDriver, forceDraw); err != nil {
return err

View File

@ -1046,41 +1046,6 @@ func (u *userInterfaceImpl) updateGame() error {
return err
}
// Create icon images in a different goroutine (#1478).
// In the fullscreen mode, SetIcon fails (#1578).
if imgs := u.getIconImages(); len(imgs) > 0 && !u.isFullscreen() {
u.setIconImages(nil)
// Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked
// from this goroutine. At works only in between BeginFrame and EndFrame.
go func() {
newImgs := make([]image.Image, len(imgs))
for i, img := range imgs {
// TODO: If img is not *ebiten.Image, this converting is not necessary.
// However, this package cannot refer *ebiten.Image due to the package
// dependencies.
b := img.Bounds()
rgba := image.NewRGBA(b)
for j := b.Min.Y; j < b.Max.Y; j++ {
for i := b.Min.X; i < b.Max.X; i++ {
rgba.Set(i, j, img.At(i, j))
}
}
newImgs[i] = rgba
}
u.mainThread.Call(func() {
// In the fullscreen mode, reset the icon images and try again later.
if u.isFullscreen() {
u.setIconImages(imgs)
return
}
u.window.SetIcon(newImgs)
})
}()
}
// swapBuffers also checks IsGL, so this condition is redundant.
// However, (*thread).Call is not good for performance due to channels.
// Let's avoid this whenever possible (#1367).
@ -1105,6 +1070,40 @@ func (u *userInterfaceImpl) updateGame() error {
return nil
}
func (u *userInterfaceImpl) updateIconIfNeeded() {
// In the fullscreen mode, SetIcon fails (#1578).
if u.isFullscreen() {
return
}
imgs := u.getIconImages()
if len(imgs) == 0 {
return
}
u.setIconImages(nil)
newImgs := make([]image.Image, len(imgs))
for i, img := range imgs {
// TODO: If img is not *ebiten.Image, this converting is not necessary.
// However, this package cannot refer *ebiten.Image due to the package
// dependencies.
b := img.Bounds()
rgba := image.NewRGBA(b)
for j := b.Min.Y; j < b.Max.Y; j++ {
for i := b.Min.X; i < b.Max.X; i++ {
rgba.Set(i, j, img.At(i, j))
}
}
newImgs[i] = rgba
}
u.mainThread.Call(func() {
u.window.SetIcon(newImgs)
})
}
// swapBuffers must be called from the main thread.
func (u *userInterfaceImpl) swapBuffers() {
if u.graphicsDriver.IsGL() {

View File

@ -694,6 +694,9 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func (u *userInterfaceImpl) updateIconIfNeeded() {
}
func IsScreenTransparentAvailable() bool {
return true
}

View File

@ -461,6 +461,9 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func (u *userInterfaceImpl) updateIconIfNeeded() {
}
func IsScreenTransparentAvailable() bool {
return false
}

View File

@ -144,6 +144,9 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func (u *userInterfaceImpl) updateIconIfNeeded() {
}
func IsScreenTransparentAvailable() bool {
return false
}