ui: Add SetScreenTransparent / IsScreenTransparent

Fixes #1001
This commit is contained in:
Hajime Hoshi 2019-12-01 00:07:41 +09:00
parent b8a099e354
commit 0ec447e0d0
13 changed files with 150 additions and 25 deletions

View File

@ -37,7 +37,8 @@ import (
) )
var ( var (
flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)") flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)")
flagScreenTransparent = flag.Bool("screentransparent", false, "screen transparent")
) )
func init() { func init() {
@ -86,6 +87,7 @@ func update(screen *ebiten.Image) error {
tps := ebiten.MaxTPS() tps := ebiten.MaxTPS()
decorated := ebiten.IsWindowDecorated() decorated := ebiten.IsWindowDecorated()
positionX, positionY := ebiten.WindowPosition() positionX, positionY := ebiten.WindowPosition()
transparent := ebiten.IsScreenTransparent()
if ebiten.IsKeyPressed(ebiten.KeyShift) { if ebiten.IsKeyPressed(ebiten.KeyShift) {
if inpututil.IsKeyJustPressed(ebiten.KeyUp) { if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
@ -182,7 +184,9 @@ func update(screen *ebiten.Image) error {
return nil return nil
} }
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff}) if !transparent {
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff})
}
w, h := gophersImage.Size() w, h := gophersImage.Size()
w2, h2 := screen.Size() w2, h2 := screen.Size()
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
@ -261,6 +265,7 @@ func main() {
if x, y, ok := parseWindowPosition(); ok { if x, y, ok := parseWindowPosition(); ok {
ebiten.SetWindowPosition(x, y) ebiten.SetWindowPosition(x, y)
} }
ebiten.SetScreenTransparent(*flagScreenTransparent)
if err := ebiten.Run(update, initScreenWidth, initScreenHeight, initScreenScale, "Window Size (Ebiten Demo)"); err != nil { if err := ebiten.Run(update, initScreenWidth, initScreenHeight, initScreenScale, "Window Size (Ebiten Demo)"); err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -26,6 +26,7 @@ type Graphics interface {
Begin() Begin()
End() End()
SetWindow(window unsafe.Pointer) SetWindow(window unsafe.Pointer)
SetTransparent(transparent bool)
SetVertices(vertices []float32, indices []uint16) SetVertices(vertices []float32, indices []uint16)
Flush() Flush()
NewImage(width, height int) (Image, error) NewImage(width, height int) (Image, error)

View File

@ -46,6 +46,7 @@ type UI interface {
ScreenScale() float64 ScreenScale() float64
ScreenSizeInFullscreen() (int, int) ScreenSizeInFullscreen() (int, int)
WindowPosition() (int, int) WindowPosition() (int, int)
IsScreenTransparent() bool
SetCursorVisible(visible bool) SetCursorVisible(visible bool)
SetFullscreen(fullscreen bool) SetFullscreen(fullscreen bool)
@ -58,6 +59,7 @@ type UI interface {
SetWindowResizable(resizable bool) SetWindowResizable(resizable bool)
SetWindowTitle(title string) SetWindowTitle(title string)
SetWindowPosition(x, y int) SetWindowPosition(x, y int)
SetScreenTransparent(transparent bool)
Input() Input Input() Input
} }

View File

@ -73,13 +73,14 @@ const (
) )
const ( const (
ClientAPI = Hint(0x00022001) ClientAPI = Hint(0x00022001)
ContextVersionMajor = Hint(0x00022002) ContextVersionMajor = Hint(0x00022002)
ContextVersionMinor = Hint(0x00022003) ContextVersionMinor = Hint(0x00022003)
Decorated = Hint(0x00020005) Decorated = Hint(0x00020005)
Focused = Hint(0x00020001) Focused = Hint(0x00020001)
Resizable = Hint(0x00020003) Resizable = Hint(0x00020003)
Visible = Hint(0x00020004) TransparentFramebuffer = Hint(0x0002000A)
Visible = Hint(0x00020004)
) )
const ( const (

View File

@ -78,6 +78,15 @@ func (ml MetalLayer) SetDevice(device mtl.Device) {
C.MetalLayer_SetDevice(ml.metalLayer, device.Device()) C.MetalLayer_SetDevice(ml.metalLayer, device.Device())
} }
// SetOpaque a Boolean value indicating whether the layer contains completely opaque content.
func (ml MetalLayer) SetOpaque(opaque bool) {
if opaque {
C.MetalLayer_SetOpaque(ml.metalLayer, 1)
} else {
C.MetalLayer_SetOpaque(ml.metalLayer, 0)
}
}
// SetPixelFormat controls the pixel format of textures for rendering layer content. // SetPixelFormat controls the pixel format of textures for rendering layer content.
// //
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB, // The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,

View File

@ -16,12 +16,14 @@
#include <stdint.h> #include <stdint.h>
typedef signed char BOOL;
typedef unsigned long uint_t; typedef unsigned long uint_t;
void *MakeMetalLayer(); void *MakeMetalLayer();
uint16_t MetalLayer_PixelFormat(void *metalLayer); uint16_t MetalLayer_PixelFormat(void *metalLayer);
void MetalLayer_SetDevice(void *metalLayer, void *device); void MetalLayer_SetDevice(void *metalLayer, void *device);
void MetalLayer_SetOpaque(void *metalLayer, BOOL opaque);
const char *MetalLayer_SetPixelFormat(void *metalLayer, uint16_t pixelFormat); const char *MetalLayer_SetPixelFormat(void *metalLayer, uint16_t pixelFormat);
const char *MetalLayer_SetMaximumDrawableCount(void *metalLayer, const char *MetalLayer_SetMaximumDrawableCount(void *metalLayer,
uint_t maximumDrawableCount); uint_t maximumDrawableCount);

View File

@ -38,6 +38,10 @@ void MetalLayer_SetDevice(void *metalLayer, void *device) {
((CAMetalLayer *)metalLayer).device = (id<MTLDevice>)device; ((CAMetalLayer *)metalLayer).device = (id<MTLDevice>)device;
} }
void MetalLayer_SetOpaque(void *metalLayer, BOOL opaque) {
((CAMetalLayer *)metalLayer).opaque = opaque;
}
const char *MetalLayer_SetPixelFormat(void *metalLayer, uint16_t pixelFormat) { const char *MetalLayer_SetPixelFormat(void *metalLayer, uint16_t pixelFormat) {
@try { @try {
((CAMetalLayer *)metalLayer).pixelFormat = (MTLPixelFormat)pixelFormat; ((CAMetalLayer *)metalLayer).pixelFormat = (MTLPixelFormat)pixelFormat;

View File

@ -293,15 +293,14 @@ type Driver struct {
screenDrawable ca.MetalDrawable screenDrawable ca.MetalDrawable
vb mtl.Buffer vb mtl.Buffer
ib mtl.Buffer ib mtl.Buffer
src *Image src *Image
dst *Image dst *Image
drawCalled bool transparent bool
maxImageSize int maxImageSize int
drawCalled bool
t *thread.Thread t *thread.Thread
@ -440,6 +439,10 @@ func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, err
}, nil }, nil
} }
func (d *Driver) SetTransparent(transparent bool) {
d.transparent = transparent
}
func (d *Driver) Reset() error { func (d *Driver) Reset() error {
if err := d.t.Call(func() error { if err := d.t.Call(func() error {
if d.cq != (mtl.CommandQueue{}) { if d.cq != (mtl.CommandQueue{}) {
@ -455,6 +458,9 @@ func (d *Driver) Reset() error {
if err := d.view.reset(); err != nil { if err := d.view.reset(); err != nil {
return err return err
} }
if d.transparent {
d.view.ml.SetOpaque(false)
}
replaces := map[string]string{ replaces := map[string]string{
"{{.FilterNearest}}": fmt.Sprintf("%d", driver.FilterNearest), "{{.FilterNearest}}": fmt.Sprintf("%d", driver.FilterNearest),

View File

@ -54,6 +54,10 @@ func (d *Driver) SetWindow(window unsafe.Pointer) {
// Do nothing. // Do nothing.
} }
func (d *Driver) SetTransparent(transparent bool) {
// Do nothings.
}
func (d *Driver) checkSize(width, height int) { func (d *Driver) checkSize(width, height int) {
if width < 1 { if width < 1 {
panic(fmt.Sprintf("opengl: width (%d) must be equal or more than %d", width, 1)) panic(fmt.Sprintf("opengl: width (%d) must be equal or more than %d", width, 1))

View File

@ -53,16 +53,17 @@ type UserInterface struct {
lastActualScale float64 lastActualScale float64
initMonitor *glfw.Monitor initMonitor *glfw.Monitor
initFullscreenWidth int initFullscreenWidth int
initFullscreenHeight int initFullscreenHeight int
initFullscreen bool initFullscreen bool
initCursorVisible bool initCursorVisible bool
initWindowDecorated bool initWindowDecorated bool
initWindowResizable bool initWindowResizable bool
initWindowPositionX int initWindowPositionX int
initWindowPositionY int initWindowPositionY int
initIconImages []image.Image initScreenTransparent bool
initIconImages []image.Image
reqWidth int reqWidth int
reqHeight int reqHeight int
@ -260,6 +261,19 @@ func (u *UserInterface) setInitWindowResizable(resizable bool) {
u.m.Unlock() u.m.Unlock()
} }
func (u *UserInterface) isInitScreenTransparent() bool {
u.m.RLock()
v := u.initScreenTransparent
u.m.RUnlock()
return v
}
func (u *UserInterface) setInitScreenTransparent(transparent bool) {
u.m.RLock()
u.initScreenTransparent = transparent
u.m.RUnlock()
}
func (u *UserInterface) getInitIconImages() []image.Image { func (u *UserInterface) getInitIconImages() []image.Image {
u.m.RLock() u.m.RLock()
i := u.initIconImages i := u.initIconImages
@ -617,13 +631,19 @@ func (u *UserInterface) run(width, height int, scale float64, title string, cont
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI) glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
} }
// 'decorated' must be solved before creating a window (#556).
decorated := glfw.False decorated := glfw.False
if u.isInitWindowDecorated() { if u.isInitWindowDecorated() {
decorated = glfw.True decorated = glfw.True
} }
glfw.WindowHint(glfw.Decorated, decorated) glfw.WindowHint(glfw.Decorated, decorated)
transparent := glfw.False
if u.isInitScreenTransparent() {
transparent = glfw.True
}
glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
u.graphics.SetTransparent(u.isInitScreenTransparent())
resizable := glfw.False resizable := glfw.False
if u.isInitWindowResizable() { if u.isInitWindowResizable() {
resizable = glfw.True resizable = glfw.True
@ -1050,6 +1070,26 @@ func (u *UserInterface) WindowPosition() (int, int) {
return x, y return x, y
} }
func (u *UserInterface) SetScreenTransparent(transparent bool) {
if !u.isRunning() {
u.setInitScreenTransparent(transparent)
return
}
panic("ui: SetScreenTransparent can't be called after Run.")
}
func (u *UserInterface) IsScreenTransparent() bool {
if !u.isRunning() {
return u.isInitScreenTransparent()
}
val := false
_ = u.t.Call(func() error {
val = u.window.GetAttrib(glfw.TransparentFramebuffer) == glfw.True
return nil
})
return val
}
func (u *UserInterface) Input() driver.Input { func (u *UserInterface) Input() driver.Input {
return &u.input return &u.input
} }

View File

@ -34,6 +34,7 @@ type UserInterface struct {
scale float64 scale float64
runnableInBackground bool runnableInBackground bool
vsync bool vsync bool
running bool
sizeChanged bool sizeChanged bool
contextLost bool contextLost bool
@ -156,6 +157,9 @@ func (u *UserInterface) IsWindowResizable() bool {
func (u *UserInterface) SetWindowResizable(decorated bool) { func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing // Do nothing
if u.running {
panic("js: SetWindowResizable can't be called after the main loop starts")
}
} }
func (u *UserInterface) DeviceScaleFactor() float64 { func (u *UserInterface) DeviceScaleFactor() float64 {
@ -426,6 +430,7 @@ func (u *UserInterface) Run(width, height int, scale float64, title string, cont
u.setScreenSize(width, height) u.setScreenSize(width, height)
u.pseudoScale = scale u.pseudoScale = scale
canvas.Call("focus") canvas.Call("focus")
u.running = true
ch := u.loop(context) ch := u.loop(context)
if runtime.GOARCH == "wasm" { if runtime.GOARCH == "wasm" {
return <-ch return <-ch
@ -434,6 +439,9 @@ func (u *UserInterface) Run(width, height int, scale float64, title string, cont
// On GopherJS, the main goroutine cannot be blocked due to the bug (gopherjs/gopherjs#826). // On GopherJS, the main goroutine cannot be blocked due to the bug (gopherjs/gopherjs#826).
// Return immediately. // Return immediately.
go func() { go func() {
defer func() {
u.running = false
}()
if err := <-ch; err != nil { if err := <-ch; err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -484,9 +492,30 @@ func (u *UserInterface) SetWindowPosition(x, y int) {
} }
func (u *UserInterface) WindowPosition() (int, int) { func (u *UserInterface) WindowPosition() (int, int) {
if !u.running {
panic("js: WindowPosition can't be called before the main loop starts")
}
return 0, 0 return 0, 0
} }
func (u *UserInterface) SetScreenTransparent(transparent bool) {
if u.running {
panic("js: 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 *UserInterface) IsScreenTransparent() bool {
bodyStyle := document.Get("body").Get("style")
return bodyStyle.Get("backgroundColor").String() == "transparent"
}
func (u *UserInterface) Input() driver.Input { func (u *UserInterface) Input() driver.Input {
return &u.input return &u.input
} }

View File

@ -451,6 +451,14 @@ func (u *UserInterface) WindowPosition() (int, int) {
return 0, 0 return 0, 0
} }
func (u *UserInterface) SetScreenTransparent(transparent bool) {
// Do nothing
}
func (u *UserInterface) IsScreenTransparent() bool {
return false
}
func (u *UserInterface) Input() driver.Input { func (u *UserInterface) Input() driver.Input {
return &u.input return &u.input
} }

14
run.go
View File

@ -411,3 +411,17 @@ func SetMaxTPS(tps int) {
} }
atomic.StoreInt32(&currentMaxTPS, int32(tps)) atomic.StoreInt32(&currentMaxTPS, int32(tps))
} }
// IsScreenTransparent reports whether the window is transparent.
func IsScreenTransparent() bool {
return uiDriver().IsScreenTransparent()
}
// SetScreenTransparent sets the state if the window is transparent.
//
// SetScreenTransparent panics if SetScreenTransparent is called after Run.
//
// SetScreenTransparent does nothing on mobiles.
func SetScreenTransparent(transparent bool) {
uiDriver().SetScreenTransparent(transparent)
}