driver: Add interface Window

This commit is contained in:
Hajime Hoshi 2019-12-25 00:05:56 +09:00
parent f8d47e5c6a
commit 9ed8279fc8
9 changed files with 270 additions and 277 deletions

View File

@ -37,7 +37,13 @@ var (
gophersImage *ebiten.Image
)
func update(screen *ebiten.Image) error {
type Game struct{}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return outsideWidth, outsideHeight
}
func (g *Game) Update(screen *ebiten.Image) error {
count++
if ebiten.IsDrawingSkipped() {
return nil
@ -55,7 +61,8 @@ func update(screen *ebiten.Image) error {
op.GeoM.Rotate(float64(count%360) * 2 * math.Pi / 360)
// Move the image to the screen's center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
sw, sh := screen.Size()
op.GeoM.Translate(float64(sw)/2, float64(sh)/2)
screen.DrawImage(gophersImage, op)
return nil
@ -77,7 +84,11 @@ func main() {
}
gophersImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Rotate (Ebiten Demo)"); err != nil {
g := &Game{}
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Rotate (Resizable Window Demo)")
ebiten.SetWindowResizable(true)
if err := ebiten.RunGame(g); err != nil {
log.Fatal(err)
}
}

View File

@ -39,26 +39,33 @@ type UI interface {
IsFullscreen() bool
IsRunnableInBackground() bool
IsVsyncEnabled() bool
IsWindowDecorated() bool
IsWindowResizable() bool
ScreenSizeInFullscreen() (int, int)
WindowPosition() (int, int)
WindowSize() (int, int)
IsScreenTransparent() bool
MonitorPosition() (int, int)
CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
SetCursorMode(mode CursorMode)
SetFullscreen(fullscreen bool)
SetRunnableInBackground(runnableInBackground bool)
SetVsyncEnabled(enabled bool)
SetWindowDecorated(decorated bool)
SetWindowIcon(iconImages []image.Image)
SetWindowResizable(resizable bool)
SetWindowTitle(title string)
SetWindowPosition(x, y int)
SetWindowSize(width, height int)
SetScreenTransparent(transparent bool)
Input() Input
Window() Window
}
type Window interface {
IsDecorated() bool
SetDecorated(decorated bool)
IsResizable() bool
SetResizable(resizable bool)
Position() (int, int)
SetPosition(x, y int)
Size() (int, int)
SetSize(width, height int)
SetIcon(iconImages []image.Image)
SetTitle(title string)
}

View File

@ -74,6 +74,7 @@ type UserInterface struct {
graphics driver.Graphics
input Input
iwindow window
t *thread.Thread
m sync.RWMutex
@ -101,6 +102,7 @@ var (
func init() {
theUI.input.ui = theUI
theUI.iwindow.ui = theUI
}
func Get() *UserInterface {
@ -435,29 +437,6 @@ func (u *UserInterface) IsVsyncEnabled() bool {
return r
}
func (u *UserInterface) SetWindowTitle(title string) {
if !u.isRunning() {
u.setInitTitle(title)
return
}
u.title = title
_ = u.t.Call(func() error {
u.window.SetTitle(title)
return nil
})
}
func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
if !u.isRunning() {
u.setInitIconImages(iconImages)
return
}
_ = u.t.Call(func() error {
u.window.SetIcon(iconImages)
return nil
})
}
func (u *UserInterface) CursorMode() driver.CursorMode {
if !u.isRunning() {
return u.getInitCursorMode()
@ -502,66 +481,6 @@ func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
})
}
func (u *UserInterface) IsWindowDecorated() bool {
if !u.isRunning() {
return u.isInitWindowDecorated()
}
v := false
_ = u.t.Call(func() error {
v = u.window.GetAttrib(glfw.Decorated) == glfw.True
return nil
})
return v
}
func (u *UserInterface) SetWindowDecorated(decorated bool) {
if !u.isRunning() {
u.setInitWindowDecorated(decorated)
return
}
_ = u.t.Call(func() error {
v := glfw.False
if decorated {
v = glfw.True
}
u.window.SetAttrib(glfw.Decorated, v)
// The title can be lost when the decoration is gone. Recover this.
if v == glfw.True {
u.window.SetTitle(u.title)
}
return nil
})
}
func (u *UserInterface) IsWindowResizable() bool {
if !u.isRunning() {
return u.isInitWindowResizable()
}
v := false
_ = u.t.Call(func() error {
v = u.window.GetAttrib(glfw.Resizable) == glfw.True
return nil
})
return v
}
func (u *UserInterface) SetWindowResizable(resizable bool) {
if !u.isRunning() {
u.setInitWindowResizable(resizable)
return
}
_ = u.t.Call(func() error {
v := glfw.False
if resizable {
v = glfw.True
}
u.window.SetAttrib(glfw.Resizable, v)
return nil
})
}
func (u *UserInterface) DeviceScaleFactor() float64 {
if !u.isRunning() {
return devicescale.GetAt(u.initMonitor.GetPos())
@ -715,7 +634,7 @@ func (u *UserInterface) run(context driver.UIContext) error {
return err
}
u.SetWindowPosition(u.getInitWindowPosition())
u.iwindow.SetPosition(u.getInitWindowPosition())
ww, wh := u.getInitWindowSize()
ww = int(u.toDeviceDependentPixel(float64(ww)))
wh = int(u.toDeviceDependentPixel(float64(wh)))
@ -1049,44 +968,6 @@ func (u *UserInterface) currentMonitor() *glfw.Monitor {
return u.currentMonitorFromPosition()
}
func (u *UserInterface) SetWindowPosition(x, y int) {
if !u.isRunning() {
u.setInitWindowPosition(x, y)
return
}
_ = u.t.Call(func() error {
xf := u.toDeviceDependentPixel(float64(x))
yf := u.toDeviceDependentPixel(float64(y))
x, y := adjustWindowPosition(int(xf), int(yf))
if u.isFullscreen() {
u.origPosX, u.origPosY = x, y
} else {
u.window.SetPos(x, y)
}
return nil
})
}
func (u *UserInterface) WindowPosition() (int, int) {
if !u.isRunning() {
panic("glfw: WindowPosition can't be called before the main loop starts")
}
x, y := 0, 0
_ = u.t.Call(func() error {
var wx, wy int
if u.isFullscreen() {
wx, wy = u.origPosX, u.origPosY
} else {
wx, wy = u.window.GetPos()
}
xf := u.toDeviceIndependentPixel(float64(wx))
yf := u.toDeviceIndependentPixel(float64(wy))
x, y = int(xf), int(yf)
return nil
})
return x, y
}
func (u *UserInterface) SetScreenTransparent(transparent bool) {
if !u.isRunning() {
u.setInitScreenTransparent(transparent)
@ -1107,25 +988,6 @@ func (u *UserInterface) IsScreenTransparent() bool {
return val
}
func (u *UserInterface) WindowSize() (int, int) {
if !u.isRunning() {
return u.getInitWindowSize()
}
w := int(u.toDeviceIndependentPixel(float64(u.windowWidth)))
h := int(u.toDeviceIndependentPixel(float64(u.windowHeight)))
return w, h
}
func (u *UserInterface) SetWindowSize(width, height int) {
if !u.isRunning() {
u.setInitWindowSize(width, height)
return
}
w := int(u.toDeviceDependentPixel(float64(width)))
h := int(u.toDeviceDependentPixel(float64(height)))
u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
}
func (u *UserInterface) MonitorPosition() (int, int) {
if !u.isRunning() {
return u.monitorPosition()
@ -1143,10 +1005,10 @@ func (u *UserInterface) monitorPosition() (int, int) {
return u.currentMonitor().GetPos()
}
func (u *UserInterface) CanHaveWindow() bool {
return true
}
func (u *UserInterface) Input() driver.Input {
return &u.input
}
func (u *UserInterface) Window() driver.Window {
return &u.iwindow
}

View File

@ -0,0 +1,170 @@
// Copyright 2019 The Ebiten 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.
// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios
package glfw
import (
"image"
"github.com/hajimehoshi/ebiten/internal/glfw"
)
type window struct {
ui *UserInterface
}
func (w *window) IsDecorated() bool {
if !w.ui.isRunning() {
return w.ui.isInitWindowDecorated()
}
v := false
_ = w.ui.t.Call(func() error {
v = w.ui.window.GetAttrib(glfw.Decorated) == glfw.True
return nil
})
return v
}
func (w *window) SetDecorated(decorated bool) {
if !w.ui.isRunning() {
w.ui.setInitWindowDecorated(decorated)
return
}
_ = w.ui.t.Call(func() error {
v := glfw.False
if decorated {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Decorated, v)
// The title can be lost when the decoration is gone. Recover this.
if v == glfw.True {
w.ui.window.SetTitle(w.ui.title)
}
return nil
})
}
func (w *window) IsResizable() bool {
if !w.ui.isRunning() {
return w.ui.isInitWindowResizable()
}
v := false
_ = w.ui.t.Call(func() error {
v = w.ui.window.GetAttrib(glfw.Resizable) == glfw.True
return nil
})
return v
}
func (w *window) SetResizable(resizable bool) {
if !w.ui.isRunning() {
w.ui.setInitWindowResizable(resizable)
return
}
_ = w.ui.t.Call(func() error {
v := glfw.False
if resizable {
v = glfw.True
}
w.ui.window.SetAttrib(glfw.Resizable, v)
return nil
})
}
func (w *window) Position() (int, int) {
if !w.ui.isRunning() {
panic("glfw: WindowPosition can't be called before the main loop starts")
}
x, y := 0, 0
_ = w.ui.t.Call(func() error {
var wx, wy int
if w.ui.isFullscreen() {
wx, wy = w.ui.origPosX, w.ui.origPosY
} else {
wx, wy = w.ui.window.GetPos()
}
xf := w.ui.toDeviceIndependentPixel(float64(wx))
yf := w.ui.toDeviceIndependentPixel(float64(wy))
x, y = int(xf), int(yf)
return nil
})
return x, y
}
func (w *window) SetPosition(x, y int) {
if !w.ui.isRunning() {
w.ui.setInitWindowPosition(x, y)
return
}
_ = w.ui.t.Call(func() error {
xf := w.ui.toDeviceDependentPixel(float64(x))
yf := w.ui.toDeviceDependentPixel(float64(y))
x, y := adjustWindowPosition(int(xf), int(yf))
if w.ui.isFullscreen() {
w.ui.origPosX, w.ui.origPosY = x, y
} else {
w.ui.window.SetPos(x, y)
}
return nil
})
}
func (w *window) Size() (int, int) {
if !w.ui.isRunning() {
return w.ui.getInitWindowSize()
}
ww := int(w.ui.toDeviceIndependentPixel(float64(w.ui.windowWidth)))
wh := int(w.ui.toDeviceIndependentPixel(float64(w.ui.windowHeight)))
return ww, wh
}
func (w *window) SetSize(width, height int) {
if !w.ui.isRunning() {
w.ui.setInitWindowSize(width, height)
return
}
ww := int(w.ui.toDeviceDependentPixel(float64(width)))
wh := int(w.ui.toDeviceDependentPixel(float64(height)))
w.ui.setWindowSize(ww, wh, w.ui.isFullscreen(), w.ui.vsync)
}
func (w *window) SetIcon(iconImages []image.Image) {
if !w.ui.isRunning() {
w.ui.setInitIconImages(iconImages)
return
}
_ = w.ui.t.Call(func() error {
w.ui.window.SetIcon(iconImages)
return nil
})
}
func (w *window) SetTitle(title string) {
if !w.ui.isRunning() {
w.ui.setInitTitle(title)
return
}
w.ui.title = title
_ = w.ui.t.Call(func() error {
w.ui.window.SetTitle(title)
return nil
})
}

View File

@ -17,7 +17,6 @@
package js
import (
"image"
"log"
"runtime"
"syscall/js"
@ -117,41 +116,6 @@ func (u *UserInterface) SetCursorMode(mode driver.CursorMode) {
}
}
func (u *UserInterface) SetWindowTitle(title string) {
// TODO: As the page should be in an iframe, this might be useless.
document.Set("title", title)
}
func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
// Do nothing
}
func (u *UserInterface) IsWindowDecorated() bool {
return false
}
func (u *UserInterface) SetWindowDecorated(decorated bool) {
// Do nothing
}
func (u *UserInterface) IsWindowResizable() bool {
return false
}
func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing
}
func (u *UserInterface) WindowSize() (int, int) {
return 0, 0
}
func (u *UserInterface) SetWindowSize(width, height int) {
// TODO: This is too tricky: Even though browsers don't have windows, SetWindowSize is called whenever the
// screen size is changed. Fix this hack.
u.sizeChanged = true
}
func (u *UserInterface) DeviceScaleFactor() float64 {
return devicescale.GetAt(0, 0)
}
@ -441,17 +405,6 @@ func (u *UserInterface) updateScreenSize() {
u.sizeChanged = true
}
func (u *UserInterface) SetWindowPosition(x, y int) {
// Do nothing
}
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")
@ -474,10 +427,10 @@ func (u *UserInterface) MonitorPosition() (int, int) {
return 0, 0
}
func (u *UserInterface) CanHaveWindow() bool {
return false
}
func (u *UserInterface) Input() driver.Input {
return &u.input
}
func (u *UserInterface) Window() driver.Window {
return nil
}

View File

@ -19,7 +19,6 @@ package mobile
import (
"context"
"fmt"
"image"
"runtime/debug"
"sync"
"time"
@ -377,30 +376,6 @@ func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
// Do nothing
}
func (u *UserInterface) SetWindowTitle(title string) {
// Do nothing
}
func (u *UserInterface) SetWindowIcon(iconImages []image.Image) {
// Do nothing
}
func (u *UserInterface) IsWindowDecorated() bool {
return false
}
func (u *UserInterface) SetWindowDecorated(decorated bool) {
// Do nothing
}
func (u *UserInterface) IsWindowResizable() bool {
return false
}
func (u *UserInterface) SetWindowResizable(decorated bool) {
// Do nothing
}
func (u *UserInterface) IsVsyncEnabled() bool {
return true
}
@ -413,14 +388,6 @@ func (u *UserInterface) DeviceScaleFactor() float64 {
return deviceScale()
}
func (u *UserInterface) SetWindowPosition(x, y int) {
// Do nothing
}
func (u *UserInterface) WindowPosition() (int, int) {
return 0, 0
}
func (u *UserInterface) SetScreenTransparent(transparent bool) {
// Do nothing
}
@ -429,18 +396,6 @@ func (u *UserInterface) IsScreenTransparent() bool {
return false
}
func (u *UserInterface) SetWindowSize(width, height int) {
// Do nothing
}
func (u *UserInterface) WindowSize() (int, int) {
return 0, 0
}
func (u *UserInterface) CanHaveWindow() bool {
return false
}
func (u *UserInterface) MonitorPosition() (int, int) {
return 0, 0
}
@ -449,6 +404,10 @@ func (u *UserInterface) Input() driver.Input {
return &u.input
}
func (u *UserInterface) Window() driver.Window {
return nil
}
type Touch struct {
ID int
X int

4
run.go
View File

@ -209,9 +209,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
//
// Don't call RunGame twice or more in one process.
func RunGame(game Game) error {
if uiDriver().CanHaveWindow() {
fixWindowPosition(WindowSize())
}
fixWindowPosition(WindowSize())
return runGame(game, 0)
}

View File

@ -96,9 +96,11 @@ func (c *uiContext) setScaleForWindow(scale float64) {
panic("ebiten: setScaleForWindow can be called only when Run is used")
}
w, h := g.width, g.height
c.scaleForWindow = scale
uiDriver().SetWindowSize(int(float64(w)*scale), int(float64(h)*scale))
if w := uiDriver().Window(); w != nil {
ww, wh := g.width, g.height
c.scaleForWindow = scale
w.SetSize(int(float64(ww)*scale), int(float64(wh)*scale))
}
}
func (c *uiContext) getScaleForWindow() float64 {
@ -135,8 +137,10 @@ func (c *uiContext) SetScreenSize(width, height int) {
g.width = width
g.height = height
s := c.scaleForWindow
uiDriver().SetWindowSize(int(float64(width)*s), int(float64(height)*s))
if w := uiDriver().Window(); w != nil {
s := c.scaleForWindow
w.SetSize(int(float64(width)*s), int(float64(height)*s))
}
}
func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
@ -196,7 +200,9 @@ func (c *uiContext) setWindowResizable(resizable bool) {
panic("ebiten: a resizable window works with RunGame, not Run")
}
}
uiDriver().SetWindowResizable(resizable)
if w := uiDriver().Window(); w != nil {
w.SetResizable(resizable)
}
}
func (c *uiContext) screenScale() float64 {

View File

@ -34,14 +34,19 @@ const (
//
// SetWindowDecorated is concurrent-safe.
func SetWindowDecorated(decorated bool) {
uiDriver().SetWindowDecorated(decorated)
if w := uiDriver().Window(); w != nil {
w.SetDecorated(decorated)
}
}
// IsWindowDecorated reports whether the window is decorated.
//
// IsWindowDecorated is concurrent-safe.
func IsWindowDecorated() bool {
return uiDriver().IsWindowDecorated()
if w := uiDriver().Window(); w != nil {
return w.IsDecorated()
}
return false
}
// setWindowResizable is unexported until specification is determined (#320)
@ -59,7 +64,9 @@ func IsWindowDecorated() bool {
//
// setWindowResizable is concurrent-safe.
func setWindowResizable(resizable bool) {
uiDriver().SetWindowResizable(resizable)
if w := uiDriver().Window(); w != nil {
w.SetResizable(resizable)
}
}
// IsWindowResizable reports whether the window is resizable by the user's dragging on desktops.
@ -67,7 +74,10 @@ func setWindowResizable(resizable bool) {
//
// IsWindowResizable is concurrent-safe.
func IsWindowResizable() bool {
return uiDriver().IsWindowResizable()
if w := uiDriver().Window(); w != nil {
return w.IsResizable()
}
return false
}
// SetWindowResizable sets whether the window is resizable by the user's dragging on desktops.
@ -82,11 +92,15 @@ func SetWindowResizable(resizable bool) {
// SetWindowTitle sets the title of the window.
//
// SetWindowTitle updated the title on browsers, but now does nothing on browsers as of 1.11.0-alpha.
//
// SetWindowTitle does nothing on mobiles.
//
// SetWindowTitle is concurrent-safe.
func SetWindowTitle(title string) {
uiDriver().SetWindowTitle(title)
if w := uiDriver().Window(); w != nil {
w.SetTitle(title)
}
}
// SetWindowIcon sets the icon of the game window.
@ -110,7 +124,9 @@ func SetWindowTitle(title string) {
//
// SetWindowIcon is concurrent-safe.
func SetWindowIcon(iconImages []image.Image) {
uiDriver().SetWindowIcon(iconImages)
if w := uiDriver().Window(); w != nil {
w.SetIcon(iconImages)
}
}
// WindowPosition returns the window position.
@ -126,7 +142,10 @@ func WindowPosition() (x, y int) {
if x, y, ok := initWindowPosition(); ok {
return x, y
}
return uiDriver().WindowPosition()
if w := uiDriver().Window(); w != nil {
return w.Position()
}
return 0, 0
}
// SetWindowPosition sets the window position.
@ -140,7 +159,9 @@ func SetWindowPosition(x, y int) {
if setInitWindowPosition(x, y) {
return
}
uiDriver().SetWindowPosition(x, y)
if w := uiDriver().Window(); w != nil {
w.SetPosition(x, y)
}
}
var (
@ -180,7 +201,8 @@ func fixWindowPosition(width, height int) {
mainLoopStarted = true
}()
if !uiDriver().CanHaveWindow() {
w := uiDriver().Window()
if w == nil {
return
}
@ -189,9 +211,9 @@ func fixWindowPosition(width, height int) {
sw, sh := uiDriver().ScreenSizeInFullscreen()
x := mx + (sw-width)/2
y := my + (sh-height)/3
uiDriver().SetWindowPosition(x, y)
w.SetPosition(x, y)
} else {
uiDriver().SetWindowPosition(initWindowPositionX, initWindowPositionY)
w.SetPosition(initWindowPositionX, initWindowPositionY)
}
}
@ -202,7 +224,10 @@ func fixWindowPosition(width, height int) {
//
// WindowSize is concurrent-safe.
func WindowSize() (int, int) {
return uiDriver().WindowSize()
if w := uiDriver().Window(); w != nil {
return w.Size()
}
return 0, 0
}
// SetWindowSize sets the window size on desktops.
@ -217,5 +242,7 @@ func SetWindowSize(width, height int) {
if width <= 0 || height <= 0 {
panic("ebiten: width and height must be positive")
}
uiDriver().SetWindowSize(width, height)
if w := uiDriver().Window(); w != nil {
w.SetSize(width, height)
}
}