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 package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"image" "image"
) )
// IsKeyPressed returns a boolean indicating whether key is pressed. // IsKeyPressed returns a boolean indicating whether key is pressed.
func IsKeyPressed(key Key) bool { 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. // CursorPosition returns a position of a mouse cursor.
func CursorPosition() (x, y int) { func CursorPosition() (x, y int) {
return currentUI.input.cursorPosition() return ui.Current().Input().CursorPosition()
} }
// IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed. // IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed.
func IsMouseButtonPressed(mouseButton MouseButton) bool { func IsMouseButtonPressed(mouseButton MouseButton) bool {
return currentUI.input.isMouseButtonPressed(mouseButton) return ui.Current().Input().IsMouseButtonPressed(ui.MouseButton(mouseButton))
} }
// NewImage returns an empty image. // NewImage returns an empty image.
func NewImage(width, height int, filter Filter) (*Image, error) { 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). // NewImageFromImage creates a new image with the given image (img).
func NewImageFromImage(img image.Image, filter Filter) (*Image, error) { 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 return nil, err
} }
gc := &graphicsContext{ return &graphicsContext{
glContext: c, glContext: c,
defaultR: &innerImage{f, nil}, defaultR: &innerImage{f, nil},
screen: screen, screen: screen,
screenScale: screenScale, screenScale: screenScale,
} }, nil
return gc, nil
} }
type graphicsContext struct { type graphicsContext struct {

View File

@ -18,6 +18,7 @@ import (
"github.com/hajimehoshi/ebiten/internal" "github.com/hajimehoshi/ebiten/internal"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"image" "image"
"image/color" "image/color"
) )
@ -102,7 +103,7 @@ func (t *textureQuads) Texture(i int) (u0, v0, u1, v1 float32) {
// The pixel format is alpha-premultiplied. // The pixel format is alpha-premultiplied.
// Image implements image.Image. // Image implements image.Image.
type Image struct { type Image struct {
ui *ui ui *ui.UI
inner *innerImage inner *innerImage
pixels []uint8 pixels []uint8
} }
@ -115,8 +116,8 @@ func (i *Image) Size() (width, height int) {
// Clear resets the pixels of the image into 0. // Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) { func (i *Image) Clear() (err error) {
i.pixels = nil i.pixels = nil
i.ui.use(func() { i.ui.Use(func(c *opengl.Context) {
err = i.inner.Clear(i.ui.glContext) err = i.inner.Clear(c)
}) })
return return
} }
@ -124,8 +125,8 @@ func (i *Image) Clear() (err error) {
// Fill fills the image with a solid color. // Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) { func (i *Image) Fill(clr color.Color) (err error) {
i.pixels = nil i.pixels = nil
i.ui.use(func() { i.ui.Use(func(c *opengl.Context) {
err = i.inner.Fill(i.ui.glContext, clr) err = i.inner.Fill(c, clr)
}) })
return 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) { func (i *Image) drawImage(image *innerImage, option *DrawImageOptions) (err error) {
i.pixels = nil i.pixels = nil
i.ui.use(func() { i.ui.Use(func(c *opengl.Context) {
err = i.inner.drawImage(i.ui.glContext, image, option) err = i.inner.drawImage(c, image, option)
}) })
return return
} }
@ -169,9 +170,9 @@ func (i *Image) ColorModel() color.Model {
// This method loads pixels from GPU to VRAM if necessary. // This method loads pixels from GPU to VRAM if necessary.
func (i *Image) At(x, y int) color.Color { func (i *Image) At(x, y int) color.Color {
if i.pixels == nil { if i.pixels == nil {
i.ui.use(func() { i.ui.Use(func(c *opengl.Context) {
var err error var err error
i.pixels, err = i.inner.texture.Pixels(i.ui.glContext) i.pixels, err = i.inner.texture.Pixels(c)
if err != nil { if err != nil {
panic(err) 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ebiten package ui
import ( import (
glfw "github.com/go-gl/glfw3" glfw "github.com/go-gl/glfw3"
"math" "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 { type input struct {
keyPressed [KeyMax]bool keyPressed [KeyMax]bool
mouseButtonPressed [MouseButtonMax]bool mouseButtonPressed [MouseButtonMax]bool
@ -26,15 +46,15 @@ type input struct {
cursorY int cursorY int
} }
func (i *input) isKeyPressed(key Key) bool { func (i *input) IsKeyPressed(key Key) bool {
return i.keyPressed[key] return i.keyPressed[key]
} }
func (i *input) isMouseButtonPressed(button MouseButton) bool { func (i *input) IsMouseButtonPressed(button MouseButton) bool {
return i.mouseButtonPressed[button] return i.mouseButtonPressed[button]
} }
func (i *input) cursorPosition() (x, y int) { func (i *input) CursorPosition() (x, y int) {
return i.cursorX, i.cursorY 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ebiten package ui
import ( import (
"fmt" "fmt"
glfw "github.com/go-gl/glfw3" glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"image"
"runtime" "runtime"
) )
var currentUI *ui var currentUI *UI
func Current() *UI {
return currentUI
}
func init() { func init() {
runtime.LockOSThread() runtime.LockOSThread()
@ -42,28 +44,32 @@ func init() {
panic(err) panic(err)
} }
u := &ui{ u := &UI{
window: window, window: window,
funcs: make(chan func()), funcs: make(chan func()),
} }
u.run() go func() {
u.use(func() { runtime.LockOSThread()
u.window.MakeContextCurrent()
u.glContext = opengl.NewContext() u.glContext = opengl.NewContext()
glfw.SwapInterval(1) glfw.SwapInterval(1)
}) for f := range u.funcs {
f()
}
}()
currentUI = u currentUI = u
} }
type ui struct { type UI struct {
window *glfw.Window window *glfw.Window
scale int scale int
realScale int
glContext *opengl.Context glContext *opengl.Context
graphicsContext *graphicsContext
input input input input
funcs chan func() 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() monitor, err := glfw.GetPrimaryMonitor()
if err != nil { if err != nil {
return nil, err 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. // For retina displays, recalculate the scale with the framebuffer size.
windowWidth, _ := window.GetFramebufferSize() windowWidth, _ := window.GetFramebufferSize()
realScale := windowWidth / width ui.realScale = windowWidth / width
ui.use(func() {
ui.graphicsContext, err = newGraphicsContext(ui.glContext, width, height, realScale)
})
return ui, err return ui, err
} }
func (u *ui) doEvents() { func (u *UI) RealScale() int {
glfw.PollEvents() return u.realScale
u.update()
} }
func (u *ui) terminate() { func (u *UI) DoEvents() {
glfw.PollEvents()
u.input.update(u.window, u.scale)
}
func (u *UI) Terminate() {
glfw.Terminate() glfw.Terminate()
} }
func (u *ui) isClosed() bool { func (u *UI) IsClosed() bool {
return u.window.ShouldClose() return u.window.ShouldClose()
} }
func (u *ui) draw(f func(*Image) error) (err error) { func (u *UI) SwapBuffers() {
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() u.window.SwapBuffers()
})
return
} }
func (u *ui) newImageFromImage(img image.Image, filter Filter) (*Image, error) { func (u *UI) Input() *input {
var innerImage *innerImage return &u.input
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) newImage(width, height int, filter Filter) (*Image, error) { func (u *UI) Use(f func(*opengl.Context)) {
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()) {
ch := make(chan struct{}) ch := make(chan struct{})
u.funcs <- func() { u.funcs <- func() {
defer close(ch) defer close(ch)
f() f(u.glContext)
} }
<-ch <-ch
} }
func (u *ui) update() {
u.input.update(u.window, u.scale)
}

26
keys.go
View File

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

36
run.go
View File

@ -15,6 +15,8 @@
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/ui"
"time" "time"
) )
@ -28,20 +30,44 @@ import (
// but this is not strictly guaranteed. // but this is not strictly guaranteed.
// If you need to care about time, you need to check current time every time f is called. // 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 { 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 { if err != nil {
return err return err
} }
defer ui.terminate()
for { for {
// To avoid busy loop when the window is inactive, wait 1/120 [sec] at least. // To avoid busy loop when the window is inactive, wait 1/120 [sec] at least.
ch := time.After(1 * time.Second / 120) ch := time.After(1 * time.Second / 120)
ui.doEvents() ui.DoEvents()
if ui.isClosed() { if ui.IsClosed() {
return nil 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 return err
} }
<-ch <-ch