Add internal/ui module

This commit is contained in:
Hajime Hoshi 2015-01-02 01:20:20 +09:00
parent 5f4aa33edf
commit 96f5315c49
7 changed files with 159 additions and 131 deletions

View File

@ -15,30 +15,60 @@
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"image"
)
// IsKeyPressed returns a boolean indicating whether key is pressed.
func IsKeyPressed(key Key) bool {
return currentUI.input.isKeyPressed(key)
return ui.Current().Input().IsKeyPressed(ui.Key(key))
}
// CursorPosition returns a position of a mouse cursor.
func CursorPosition() (x, y int) {
return currentUI.input.cursorPosition()
return ui.Current().Input().CursorPosition()
}
// IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed.
func IsMouseButtonPressed(mouseButton MouseButton) bool {
return currentUI.input.isMouseButtonPressed(mouseButton)
return ui.Current().Input().IsMouseButtonPressed(ui.MouseButton(mouseButton))
}
// NewImage returns an empty image.
func NewImage(width, height int, filter Filter) (*Image, error) {
return currentUI.newImage(width, height, filter)
var innerImage *innerImage
var err error
ui.Current().Use(func(c *opengl.Context) {
var texture *graphics.Texture
texture, err = graphics.NewTexture(c, width, height, glFilter(c, filter))
if err != nil {
return
}
innerImage, err = newInnerImage(c, texture)
innerImage.Clear(c)
})
if err != nil {
return nil, err
}
return &Image{ui: ui.Current(), inner: innerImage}, nil
}
// NewImageFromImage creates a new image with the given image (img).
func NewImageFromImage(img image.Image, filter Filter) (*Image, error) {
return currentUI.newImageFromImage(img, filter)
var innerImage *innerImage
var err error
ui.Current().Use(func(c *opengl.Context) {
var texture *graphics.Texture
texture, err = graphics.NewTextureFromImage(c, img, glFilter(c, filter))
if err != nil {
return
}
innerImage, err = newInnerImage(c, texture)
})
if err != nil {
return nil, err
}
return &Image{ui: ui.Current(), inner: innerImage}, nil
}

View File

@ -34,13 +34,12 @@ func newGraphicsContext(c *opengl.Context, screenWidth, screenHeight, screenScal
return nil, err
}
gc := &graphicsContext{
return &graphicsContext{
glContext: c,
defaultR: &innerImage{f, nil},
screen: screen,
screenScale: screenScale,
}
return gc, nil
}, nil
}
type graphicsContext struct {

View File

@ -18,6 +18,7 @@ import (
"github.com/hajimehoshi/ebiten/internal"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"image"
"image/color"
)
@ -102,7 +103,7 @@ func (t *textureQuads) Texture(i int) (u0, v0, u1, v1 float32) {
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
ui *ui
ui *ui.UI
inner *innerImage
pixels []uint8
}
@ -115,8 +116,8 @@ func (i *Image) Size() (width, height int) {
// Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) {
i.pixels = nil
i.ui.use(func() {
err = i.inner.Clear(i.ui.glContext)
i.ui.Use(func(c *opengl.Context) {
err = i.inner.Clear(c)
})
return
}
@ -124,8 +125,8 @@ func (i *Image) Clear() (err error) {
// Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) {
i.pixels = nil
i.ui.use(func() {
err = i.inner.Fill(i.ui.glContext, clr)
i.ui.Use(func(c *opengl.Context) {
err = i.inner.Fill(c, clr)
})
return
}
@ -147,8 +148,8 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
func (i *Image) drawImage(image *innerImage, option *DrawImageOptions) (err error) {
i.pixels = nil
i.ui.use(func() {
err = i.inner.drawImage(i.ui.glContext, image, option)
i.ui.Use(func(c *opengl.Context) {
err = i.inner.drawImage(c, image, option)
})
return
}
@ -169,9 +170,9 @@ func (i *Image) ColorModel() color.Model {
// This method loads pixels from GPU to VRAM if necessary.
func (i *Image) At(x, y int) color.Color {
if i.pixels == nil {
i.ui.use(func() {
i.ui.Use(func(c *opengl.Context) {
var err error
i.pixels, err = i.inner.texture.Pixels(i.ui.glContext)
i.pixels, err = i.inner.texture.Pixels(c)
if err != nil {
panic(err)
}

View File

@ -1,4 +1,4 @@
// Copyright 2014 Hajime Hoshi
// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,13 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package ebiten
package ui
import (
glfw "github.com/go-gl/glfw3"
"math"
)
type Key int
const (
KeyUp Key = iota
KeyDown
KeyLeft
KeyRight
KeySpace
KeyMax
)
type MouseButton int
const (
MouseButtonLeft MouseButton = iota
MouseButtonRight
MouseButtonMiddle
MouseButtonMax
)
type input struct {
keyPressed [KeyMax]bool
mouseButtonPressed [MouseButtonMax]bool
@ -26,15 +46,15 @@ type input struct {
cursorY int
}
func (i *input) isKeyPressed(key Key) bool {
func (i *input) IsKeyPressed(key Key) bool {
return i.keyPressed[key]
}
func (i *input) isMouseButtonPressed(button MouseButton) bool {
func (i *input) IsMouseButtonPressed(button MouseButton) bool {
return i.mouseButtonPressed[button]
}
func (i *input) cursorPosition() (x, y int) {
func (i *input) CursorPosition() (x, y int) {
return i.cursorX, i.cursorY
}

View File

@ -1,4 +1,4 @@
// Copyright 2014 Hajime Hoshi
// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package ebiten
package ui
import (
"fmt"
glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"image"
"runtime"
)
var currentUI *ui
var currentUI *UI
func Current() *UI {
return currentUI
}
func init() {
runtime.LockOSThread()
@ -42,28 +44,32 @@ func init() {
panic(err)
}
u := &ui{
u := &UI{
window: window,
funcs: make(chan func()),
}
u.run()
u.use(func() {
go func() {
runtime.LockOSThread()
u.window.MakeContextCurrent()
u.glContext = opengl.NewContext()
glfw.SwapInterval(1)
})
for f := range u.funcs {
f()
}
}()
currentUI = u
}
type ui struct {
window *glfw.Window
scale int
glContext *opengl.Context
graphicsContext *graphicsContext
input input
funcs chan func()
type UI struct {
window *glfw.Window
scale int
realScale int
glContext *opengl.Context
input input
funcs chan func()
}
func startUI(width, height, scale int, title string) (*ui, error) {
func New(width, height, scale int, title string) (*UI, error) {
monitor, err := glfw.GetPrimaryMonitor()
if err != nil {
return nil, err
@ -103,101 +109,41 @@ func startUI(width, height, scale int, title string) (*ui, error) {
// For retina displays, recalculate the scale with the framebuffer size.
windowWidth, _ := window.GetFramebufferSize()
realScale := windowWidth / width
ui.use(func() {
ui.graphicsContext, err = newGraphicsContext(ui.glContext, width, height, realScale)
})
ui.realScale = windowWidth / width
return ui, err
}
func (u *ui) doEvents() {
glfw.PollEvents()
u.update()
func (u *UI) RealScale() int {
return u.realScale
}
func (u *ui) terminate() {
func (u *UI) DoEvents() {
glfw.PollEvents()
u.input.update(u.window, u.scale)
}
func (u *UI) Terminate() {
glfw.Terminate()
}
func (u *ui) isClosed() bool {
func (u *UI) IsClosed() bool {
return u.window.ShouldClose()
}
func (u *ui) draw(f func(*Image) error) (err error) {
u.use(func() {
err = u.graphicsContext.preUpdate()
})
if err != nil {
return
}
err = f(&Image{ui: u, inner: u.graphicsContext.screen})
if err != nil {
return
}
u.use(func() {
err = u.graphicsContext.postUpdate()
if err != nil {
return
}
u.window.SwapBuffers()
})
return
func (u *UI) SwapBuffers() {
u.window.SwapBuffers()
}
func (u *ui) newImageFromImage(img image.Image, filter Filter) (*Image, error) {
var innerImage *innerImage
var err error
u.use(func() {
var texture *graphics.Texture
texture, err = graphics.NewTextureFromImage(u.glContext, img, glFilter(u.glContext, filter))
if err != nil {
return
}
innerImage, err = newInnerImage(u.glContext, texture)
})
if err != nil {
return nil, err
}
return &Image{ui: u, inner: innerImage}, nil
func (u *UI) Input() *input {
return &u.input
}
func (u *ui) newImage(width, height int, filter Filter) (*Image, error) {
var innerImage *innerImage
var err error
u.use(func() {
var texture *graphics.Texture
texture, err = graphics.NewTexture(u.glContext, width, height, glFilter(u.glContext, filter))
if err != nil {
return
}
innerImage, err = newInnerImage(u.glContext, texture)
innerImage.Clear(u.glContext)
})
if err != nil {
return nil, err
}
return &Image{ui: u, inner: innerImage}, nil
}
func (u *ui) run() {
go func() {
runtime.LockOSThread()
u.window.MakeContextCurrent()
for f := range u.funcs {
f()
}
}()
}
func (u *ui) use(f func()) {
func (u *UI) Use(f func(*opengl.Context)) {
ch := make(chan struct{})
u.funcs <- func() {
defer close(ch)
f()
f(u.glContext)
}
<-ch
}
func (u *ui) update() {
u.input.update(u.window, u.scale)
}

26
keys.go
View File

@ -14,19 +14,25 @@
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/ui"
)
// A Key represents a keyboard key.
type Key int
// TODO: Add more keys.
// TODO: Generate this automatically.
// Keys
const (
KeyUp Key = iota
KeyDown
KeyLeft
KeyRight
KeySpace
KeyMax
KeyUp = Key(ui.KeyUp)
KeyDown = Key(ui.KeyDown)
KeyLeft = Key(ui.KeyLeft)
KeyRight = Key(ui.KeyRight)
KeySpace = Key(ui.KeySpace)
KeyMax = Key(ui.KeyMax)
)
// A MouseButton represents a mouse button.
@ -34,8 +40,8 @@ type MouseButton int
// MouseButtons
const (
MouseButtonLeft MouseButton = iota
MouseButtonRight
MouseButtonMiddle
MouseButtonMax
MouseButtonLeft = MouseButton(ui.MouseButtonLeft)
MouseButtonRight = MouseButton(ui.MouseButtonRight)
MouseButtonMiddle = MouseButton(ui.MouseButtonMiddle)
MouseButtonMax = MouseButton(ui.MouseButtonMax)
)

36
run.go
View File

@ -15,6 +15,8 @@
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"time"
)
@ -28,20 +30,44 @@ import (
// but this is not strictly guaranteed.
// If you need to care about time, you need to check current time every time f is called.
func Run(f func(*Image) error, width, height, scale int, title string) error {
ui, err := startUI(width, height, scale, title)
ui, err := ui.New(width, height, scale, title)
if err != nil {
return err
}
defer ui.Terminate()
var graphicsContext *graphicsContext
ui.Use(func(c *opengl.Context) {
graphicsContext, err = newGraphicsContext(c, width, height, ui.RealScale())
})
if err != nil {
return err
}
defer ui.terminate()
for {
// To avoid busy loop when the window is inactive, wait 1/120 [sec] at least.
ch := time.After(1 * time.Second / 120)
ui.doEvents()
if ui.isClosed() {
ui.DoEvents()
if ui.IsClosed() {
return nil
}
if err := ui.draw(f); err != nil {
ui.Use(func(*opengl.Context) {
err = graphicsContext.preUpdate()
})
if err != nil {
return err
}
if err := f(&Image{ui: ui, inner: graphicsContext.screen}); err != nil {
return err
}
ui.Use(func(*opengl.Context) {
err = graphicsContext.postUpdate()
if err != nil {
return
}
ui.SwapBuffers()
})
if err != nil {
return err
}
<-ch