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 (
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() {
@ -86,6 +87,7 @@ func update(screen *ebiten.Image) error {
tps := ebiten.MaxTPS()
decorated := ebiten.IsWindowDecorated()
positionX, positionY := ebiten.WindowPosition()
transparent := ebiten.IsScreenTransparent()
if ebiten.IsKeyPressed(ebiten.KeyShift) {
if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
@ -182,7 +184,9 @@ func update(screen *ebiten.Image) error {
return nil
}
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff})
if !transparent {
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff})
}
w, h := gophersImage.Size()
w2, h2 := screen.Size()
op := &ebiten.DrawImageOptions{}
@ -261,6 +265,7 @@ func main() {
if x, y, ok := parseWindowPosition(); ok {
ebiten.SetWindowPosition(x, y)
}
ebiten.SetScreenTransparent(*flagScreenTransparent)
if err := ebiten.Run(update, initScreenWidth, initScreenHeight, initScreenScale, "Window Size (Ebiten Demo)"); err != nil {
log.Fatal(err)

View File

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

View File

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

View File

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

View File

@ -78,6 +78,15 @@ func (ml MetalLayer) SetDevice(device mtl.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.
//
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,

View File

@ -16,12 +16,14 @@
#include <stdint.h>
typedef signed char BOOL;
typedef unsigned long uint_t;
void *MakeMetalLayer();
uint16_t MetalLayer_PixelFormat(void *metalLayer);
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_SetMaximumDrawableCount(void *metalLayer,
uint_t maximumDrawableCount);

View File

@ -38,6 +38,10 @@ void MetalLayer_SetDevice(void *metalLayer, void *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) {
@try {
((CAMetalLayer *)metalLayer).pixelFormat = (MTLPixelFormat)pixelFormat;

View File

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

View File

@ -54,6 +54,10 @@ func (d *Driver) SetWindow(window unsafe.Pointer) {
// Do nothing.
}
func (d *Driver) SetTransparent(transparent bool) {
// Do nothings.
}
func (d *Driver) checkSize(width, height int) {
if 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
initMonitor *glfw.Monitor
initFullscreenWidth int
initFullscreenHeight int
initFullscreen bool
initCursorVisible bool
initWindowDecorated bool
initWindowResizable bool
initWindowPositionX int
initWindowPositionY int
initIconImages []image.Image
initMonitor *glfw.Monitor
initFullscreenWidth int
initFullscreenHeight int
initFullscreen bool
initCursorVisible bool
initWindowDecorated bool
initWindowResizable bool
initWindowPositionX int
initWindowPositionY int
initScreenTransparent bool
initIconImages []image.Image
reqWidth int
reqHeight int
@ -260,6 +261,19 @@ func (u *UserInterface) setInitWindowResizable(resizable bool) {
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 {
u.m.RLock()
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)
}
// 'decorated' must be solved before creating a window (#556).
decorated := glfw.False
if u.isInitWindowDecorated() {
decorated = glfw.True
}
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
if u.isInitWindowResizable() {
resizable = glfw.True
@ -1050,6 +1070,26 @@ func (u *UserInterface) WindowPosition() (int, int) {
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 {
return &u.input
}

View File

@ -34,6 +34,7 @@ type UserInterface struct {
scale float64
runnableInBackground bool
vsync bool
running bool
sizeChanged bool
contextLost bool
@ -156,6 +157,9 @@ func (u *UserInterface) IsWindowResizable() bool {
func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing
if u.running {
panic("js: SetWindowResizable can't be called after the main loop starts")
}
}
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.pseudoScale = scale
canvas.Call("focus")
u.running = true
ch := u.loop(context)
if runtime.GOARCH == "wasm" {
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).
// Return immediately.
go func() {
defer func() {
u.running = false
}()
if err := <-ch; err != nil {
log.Fatal(err)
}
@ -484,9 +492,30 @@ func (u *UserInterface) SetWindowPosition(x, y 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
}
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 {
return &u.input
}

View File

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

14
run.go
View File

@ -411,3 +411,17 @@ func SetMaxTPS(tps int) {
}
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)
}