diff --git a/gamecontext.go b/gamecontext.go index d98b365d8..7cbdac365 100644 --- a/gamecontext.go +++ b/gamecontext.go @@ -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 } diff --git a/graphicscontext.go b/graphicscontext.go index 027c7e7d0..fd5268a2e 100644 --- a/graphicscontext.go +++ b/graphicscontext.go @@ -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 { diff --git a/image.go b/image.go index 6022d507f..ba520fc35 100644 --- a/image.go +++ b/image.go @@ -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) } diff --git a/input.go b/internal/ui/input.go similarity index 77% rename from input.go rename to internal/ui/input.go index 68f663c38..7edc372d5 100644 --- a/input.go +++ b/internal/ui/input.go @@ -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 } diff --git a/ui.go b/internal/ui/ui.go similarity index 53% rename from ui.go rename to internal/ui/ui.go index 472287d61..3b852a114 100644 --- a/ui.go +++ b/internal/ui/ui.go @@ -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) -} diff --git a/keys.go b/keys.go index 96a738102..645fea22f 100644 --- a/keys.go +++ b/keys.go @@ -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) ) diff --git a/run.go b/run.go index c3e20d2a1..063f75371 100644 --- a/run.go +++ b/run.go @@ -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