ebiten: add RunGameOptions.ScreenTransparent

Closes #2378
This commit is contained in:
Hajime Hoshi 2022-12-09 21:23:41 +09:00
parent d31b0189a2
commit 3024e07ecc
10 changed files with 78 additions and 82 deletions

View File

@ -404,7 +404,6 @@ func main() {
if x, y, ok := parseWindowPosition(); ok {
ebiten.SetWindowPosition(x, y)
}
ebiten.SetScreenTransparent(*flagTransparent)
g := &game{
width: initScreenWidth,
@ -464,6 +463,7 @@ func main() {
log.Fatalf("unexpected graphics library: %s", *flagGraphicsLibrary)
}
op.InitUnfocused = !*flagInitFocused
op.ScreenTransparent = *flagTransparent
const title = "Window Size (Ebitengine Demo)"
ww := int(float64(g.width) * initScreenScale)

View File

@ -80,11 +80,13 @@ type gameForUI struct {
screen *Image
screenShader *Shader
imageDumper imageDumper
transparent bool
}
func newGameForUI(game Game) *gameForUI {
func newGameForUI(game Game, transparent bool) *gameForUI {
g := &gameForUI{
game: game,
game: game,
transparent: transparent,
}
s, err := NewShader([]byte(screenShaderSrc))
@ -157,7 +159,7 @@ func (g *gameForUI) Update() error {
func (g *gameForUI) DrawOffscreen() error {
g.game.Draw(g.offscreen)
if err := g.imageDumper.dump(g.offscreen); err != nil {
if err := g.imageDumper.dump(g.offscreen, g.transparent); err != nil {
return err
}
return nil

View File

@ -31,7 +31,7 @@ func datetimeForFilename() string {
return now.Format(datetimeFormat)
}
func takeScreenshot(screen *Image) error {
func takeScreenshot(screen *Image, transparent bool) error {
name := "screenshot_" + datetimeForFilename() + ".png"
// Use the home directory for mobiles as a provisional implementation.
if runtime.GOOS == "android" || runtime.GOOS == "ios" {
@ -41,8 +41,7 @@ func takeScreenshot(screen *Image) error {
}
name = filepath.Join(home, name)
}
blackbg := !IsScreenTransparent()
dumpedName, err := screen.image.DumpScreenshot(name, blackbg)
dumpedName, err := screen.image.DumpScreenshot(name, !transparent)
if err != nil {
return err
}
@ -155,10 +154,10 @@ func (i *imageDumper) update() error {
return nil
}
func (i *imageDumper) dump(screen *Image) error {
func (i *imageDumper) dump(screen *Image, transparent bool) error {
if i.toTakeScreenshot {
i.toTakeScreenshot = false
if err := takeScreenshot(screen); err != nil {
if err := takeScreenshot(screen, transparent); err != nil {
return err
}
}

View File

@ -97,6 +97,7 @@ func (u *UserInterface) dumpImages(dir string) (string, error) {
}
type RunOptions struct {
GraphicsLibrary GraphicsLibrary
InitUnfocused bool
GraphicsLibrary GraphicsLibrary
InitUnfocused bool
ScreenTransparent bool
}

View File

@ -92,7 +92,6 @@ type userInterfaceImpl struct {
initWindowHeightInDIP int
initWindowFloating bool
initWindowMaximized bool
initScreenTransparent bool
origWindowPosX int
origWindowPosY int
@ -360,19 +359,6 @@ func (u *userInterfaceImpl) setRunnableOnUnfocused(runnableOnUnfocused bool) {
u.m.Unlock()
}
func (u *userInterfaceImpl) isInitScreenTransparent() bool {
u.m.RLock()
v := u.initScreenTransparent
u.m.RUnlock()
return v
}
func (u *userInterfaceImpl) setInitScreenTransparent(transparent bool) {
u.m.Lock()
u.initScreenTransparent = transparent
u.m.Unlock()
}
func (u *userInterfaceImpl) getIconImages() []image.Image {
u.m.RLock()
i := u.iconImages
@ -874,21 +860,20 @@ func (u *userInterfaceImpl) init(options *RunOptions) error {
}
glfw.WindowHint(glfw.Decorated, decorated)
transparent := u.isInitScreenTransparent()
glfwTransparent := glfw.False
if transparent {
if options.ScreenTransparent {
glfwTransparent = glfw.True
}
glfw.WindowHint(glfw.TransparentFramebuffer, glfwTransparent)
g, err := newGraphicsDriver(&graphicsDriverCreatorImpl{
transparent: transparent,
transparent: options.ScreenTransparent,
}, options.GraphicsLibrary)
if err != nil {
return err
}
u.graphicsDriver = g
u.graphicsDriver.SetTransparent(u.isInitScreenTransparent())
u.graphicsDriver.SetTransparent(options.ScreenTransparent)
if u.graphicsDriver.IsGL() {
u.graphicsDriver.(interface{ SetGLFWClientAPI() }).SetGLFWClientAPI()
@ -1417,25 +1402,6 @@ func monitorFromWindow(window *glfw.Window) *glfw.Monitor {
return nil
}
func (u *userInterfaceImpl) SetScreenTransparent(transparent bool) {
if !u.isRunning() {
u.setInitScreenTransparent(transparent)
return
}
panic("ui: SetScreenTransparent can't be called after the main loop starts")
}
func (u *userInterfaceImpl) IsScreenTransparent() bool {
if !u.isRunning() {
return u.isInitScreenTransparent()
}
val := false
u.t.Call(func() {
val = u.window.GetAttrib(glfw.TransparentFramebuffer) == glfw.True
})
return val
}
func (u *userInterfaceImpl) resetForTick() {
u.input.resetForTick()
@ -1617,3 +1583,7 @@ func (u *userInterfaceImpl) setOrigWindowPos(x, y int) {
u.origWindowPosX = x
u.origWindowPosY = y
}
func IsScreenTransparentAvailable() bool {
return true
}

View File

@ -652,6 +652,13 @@ func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error {
return err
}
u.graphicsDriver = g
if bodyStyle := document.Get("body").Get("style"); options.ScreenTransparent {
bodyStyle.Set("backgroundColor", "transparent")
} else {
bodyStyle.Set("backgroundColor", "#000")
}
return <-u.loop(game)
}
@ -670,17 +677,6 @@ func (u *userInterfaceImpl) SetScreenTransparent(transparent bool) {
panic("ui: SetScreenTransparent can't be called after the main loop starts")
}
bodyStyle := document.Get("body").Get("style")
if transparent {
bodyStyle.Set("backgroundColor", "transparent")
} else {
bodyStyle.Set("backgroundColor", "#000")
}
}
func (u *userInterfaceImpl) IsScreenTransparent() bool {
bodyStyle := document.Get("body").Get("style")
return bodyStyle.Get("backgroundColor").Equal(stringTransparent)
}
func (u *userInterfaceImpl) resetForTick() {
@ -700,3 +696,7 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func IsScreenTransparentAvailable() bool {
return true
}

View File

@ -421,14 +421,6 @@ func (u *userInterfaceImpl) DeviceScaleFactor() float64 {
return deviceScale()
}
func (u *userInterfaceImpl) SetScreenTransparent(transparent bool) {
// Do nothing
}
func (u *userInterfaceImpl) IsScreenTransparent() bool {
return false
}
func (u *userInterfaceImpl) resetForTick() {
u.input.resetForTick()
}
@ -475,3 +467,7 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func IsScreenTransparentAvailable() bool {
return false
}

View File

@ -126,13 +126,6 @@ func (*userInterfaceImpl) SetFPSMode(mode FPSModeType) {
func (*userInterfaceImpl) ScheduleFrame() {
}
func (*userInterfaceImpl) IsScreenTransparent() bool {
return false
}
func (*userInterfaceImpl) SetScreenTransparent(transparent bool) {
}
func (*userInterfaceImpl) Input() *Input {
return &theUI.input
}
@ -146,3 +139,7 @@ func (u *userInterfaceImpl) beginFrame() {
func (u *userInterfaceImpl) endFrame() {
}
func IsScreenTransparentAvailable() bool {
return false
}

44
run.go
View File

@ -241,6 +241,12 @@ type RunGameOptions struct {
//
// The default (zero) value is false, which means that the window is focused.
InitUnfocused bool
// ScreenTransparent represents whether the window is transparent or not.
// ScreenTransparent is valid on desktops and browsers.
//
// The default (zero) value is false, which means that the window is not transparent.
ScreenTransparent bool
}
// RunGameWithOptions starts the main loop and runs the game with the specified options.
@ -280,8 +286,17 @@ func RunGameWithOptions(game Game, options *RunGameOptions) error {
defer atomic.StoreInt32(&isRunGameEnded_, 1)
initializeWindowPositionIfNeeded(WindowSize())
g := newGameForUI(game)
if err := ui.Get().Run(g, toUIRunOptions(options)); err != nil {
op := toUIRunOptions(options)
// This is necessary to change the result of IsScreenTransparent.
if op.ScreenTransparent {
atomic.StoreInt32(&screenTransparent, 1)
} else {
atomic.StoreInt32(&screenTransparent, 0)
}
g := newGameForUI(game, op.ScreenTransparent)
if err := ui.Get().Run(g, op); err != nil {
if errors.Is(err, Termination) {
return nil
}
@ -582,8 +597,13 @@ func SetMaxTPS(tps int) {
// IsScreenTransparent reports whether the window is transparent.
//
// IsScreenTransparent is concurrent-safe.
//
// Deprecated: as of v2.5.
func IsScreenTransparent() bool {
return ui.Get().IsScreenTransparent()
if !ui.IsScreenTransparentAvailable() {
return false
}
return atomic.LoadInt32(&screenTransparent) != 0
}
// SetScreenTransparent sets the state if the window is transparent.
@ -593,10 +613,18 @@ func IsScreenTransparent() bool {
// SetScreenTransparent does nothing on mobiles.
//
// SetScreenTransparent is concurrent-safe.
//
// Deprecated: as of v2.5. Use RunGameWithOptions instead.
func SetScreenTransparent(transparent bool) {
ui.Get().SetScreenTransparent(transparent)
if transparent {
atomic.StoreInt32(&screenTransparent, 1)
} else {
atomic.StoreInt32(&screenTransparent, 0)
}
}
var screenTransparent int32 = 0
// SetInitFocused sets whether the application is focused on show.
// The default value is true, i.e., the application is focused.
//
@ -620,11 +648,13 @@ var initUnfocused int32 = 0
func toUIRunOptions(options *RunGameOptions) *ui.RunOptions {
if options == nil {
return &ui.RunOptions{
InitUnfocused: atomic.LoadInt32(&initUnfocused) != 0,
InitUnfocused: atomic.LoadInt32(&initUnfocused) != 0,
ScreenTransparent: atomic.LoadInt32(&screenTransparent) != 0,
}
}
return &ui.RunOptions{
GraphicsLibrary: ui.GraphicsLibrary(options.GraphicsLibrary),
InitUnfocused: options.InitUnfocused,
GraphicsLibrary: ui.GraphicsLibrary(options.GraphicsLibrary),
InitUnfocused: options.InitUnfocused,
ScreenTransparent: options.ScreenTransparent,
}
}

View File

@ -28,5 +28,6 @@ import (
//
// TODO: Remove this. In order to remove this, the gameForUI should be in another package.
func RunGameWithoutMainLoop(game Game, options *RunGameOptions) {
ui.RunWithoutMainLoop(newGameForUI(game), toUIRunOptions(options))
op := toUIRunOptions(options)
ui.RunWithoutMainLoop(newGameForUI(game, op.ScreenTransparent), op)
}