diff --git a/example/main.go b/example/main.go index 0d85a49ec..3197f7c63 100644 --- a/example/main.go +++ b/example/main.go @@ -9,9 +9,7 @@ import ( "github.com/hajimehoshi/go-ebiten/example/game/rotating" "github.com/hajimehoshi/go-ebiten/example/game/sprites" "github.com/hajimehoshi/go-ebiten/example/game/testpattern" - "github.com/hajimehoshi/go-ebiten/ui" "github.com/hajimehoshi/go-ebiten/ui/cocoa" - // "github.com/hajimehoshi/go-ebiten/ui/glut" "os" "runtime" ) @@ -23,10 +21,6 @@ func main() { if 2 <= len(os.Args) { gameName = os.Args[1] } - uiName := "cocoa" - if 3 <= len(os.Args) { - uiName = os.Args[2] - } var game ebiten.Game switch gameName { @@ -52,12 +46,11 @@ func main() { const screenHeight = 240 const screenScale = 2 const title = "Ebiten Demo" - var u ui.UI - switch uiName { - default: - fallthrough - case "cocoa": - u = cocoa.New(screenWidth, screenHeight, screenScale, title) + ui := cocoa.New(screenWidth, screenHeight, screenScale, title) + ui.Start(game) + ui.InitTextures(game) + for { + ui.WaitEvents() + ui.Draw(game.Draw) } - u.Run(game) } diff --git a/graphics/opengl/device.go b/graphics/opengl/device.go index 0380001ac..841c581e1 100644 --- a/graphics/opengl/device.go +++ b/graphics/opengl/device.go @@ -6,13 +6,13 @@ import ( ) type Device struct { - context *Context + context *Context } func NewDevice(screenWidth, screenHeight, screenScale int) *Device { - graphicsContext := newContext(screenWidth, screenHeight, screenScale) + context := newContext(screenWidth, screenHeight, screenScale) return &Device{ - context: graphicsContext, + context: context, } } diff --git a/ui/cocoa/cocoa.go b/ui/cocoa/cocoa.go index db94c92aa..42cb244cc 100644 --- a/ui/cocoa/cocoa.go +++ b/ui/cocoa/cocoa.go @@ -1,20 +1,24 @@ package cocoa -// #cgo CFLAGS: -x objective-c -fobjc-arc +// #cgo CFLAGS: -x objective-c // #cgo LDFLAGS: -framework Cocoa -framework OpenGL -framework QuartzCore -// +// // #include // #include "input.h" // -// void Run(size_t width, size_t height, size_t scale, const char* title); +// void Start(size_t width, size_t height, size_t scale, const char* title); +// void WaitEvents(void); +// void BeginDrawing(void); +// void EndDrawing(void); // import "C" import ( "github.com/hajimehoshi/go-ebiten" + "github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics/opengl" + "runtime" "sync" "time" - "runtime" "unsafe" ) @@ -41,16 +45,16 @@ func (context *GameContext) InputState() ebiten.InputState { } type UI struct { - screenWidth int - screenHeight int - screenScale int - title string - updating chan ebiten.Game - updated chan ebiten.Game - input chan ebiten.InputState - graphicsDevice *opengl.Device - funcsExecutedOnMainThread []func() // TODO: map? - lock sync.Mutex + screenWidth int + screenHeight int + screenScale int + title string + updating chan struct{} + updated chan struct{} + input chan ebiten.InputState + graphicsDevice *opengl.Device + lock sync.Mutex + gameContext *GameContext } var currentUI *UI @@ -60,14 +64,18 @@ func New(screenWidth, screenHeight, screenScale int, title string) *UI { panic("UI can't be duplicated.") } ui := &UI{ - screenWidth: screenWidth, - screenHeight: screenHeight, - screenScale: screenScale, - title: title, - updating: make(chan ebiten.Game), - updated: make(chan ebiten.Game), - input: make(chan ebiten.InputState), - funcsExecutedOnMainThread: []func(){}, + screenWidth: screenWidth, + screenHeight: screenHeight, + screenScale: screenScale, + title: title, + updating: make(chan struct{}), + updated: make(chan struct{}), + input: make(chan ebiten.InputState), + gameContext: &GameContext{ + screenWidth: screenWidth, + screenHeight: screenHeight, + inputState: ebiten.InputState{-1, -1}, + }, } currentUI = ui return ui @@ -76,50 +84,45 @@ func New(screenWidth, screenHeight, screenScale int, title string) *UI { func (ui *UI) gameMainLoop(game ebiten.Game) { frameTime := time.Duration(int64(time.Second) / int64(ebiten.FPS)) tick := time.Tick(frameTime) - gameContext := &GameContext{ - screenWidth: ui.screenWidth, - screenHeight: ui.screenHeight, - inputState: ebiten.InputState{-1, -1}, - } - ui.InitializeGame(game) for { select { - case gameContext.inputState = <-ui.input: + case ui.gameContext.inputState = <-ui.input: case <-tick: - game.Update(gameContext) - case ui.updating <- game: - game = <-ui.updated + game.Update(ui.gameContext) + case ui.updating <- struct{}{}: + //ui.DrawGame(game) + <-ui.updated } } } -func (ui *UI) Run(game ebiten.Game) { +func (ui *UI) Start(game ebiten.Game) { go ui.gameMainLoop(game) - runtime.LockOSThread() cTitle := C.CString(ui.title) defer C.free(unsafe.Pointer(cTitle)) - C.Run(C.size_t(ui.screenWidth), + C.Start(C.size_t(ui.screenWidth), C.size_t(ui.screenHeight), C.size_t(ui.screenScale), cTitle) + C.WaitEvents() } -func (ui *UI) InitializeGame(game ebiten.Game) { - ui.lock.Lock() - defer ui.lock.Unlock() - ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() { - game.InitTextures(ui.graphicsDevice.TextureFactory()) - }) +func (ui *UI) WaitEvents() { + C.WaitEvents() } -func (ui *UI) DrawGame(game ebiten.Game) { - ui.lock.Lock() - defer ui.lock.Unlock() - ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() { - ui.graphicsDevice.Update(game.Draw) - }) +func (ui *UI) InitTextures(game ebiten.Game) { + C.BeginDrawing() + game.InitTextures(ui.graphicsDevice.TextureFactory()) + C.EndDrawing() +} + +func (ui *UI) Draw(f func(graphics.Context)) { + C.BeginDrawing() + ui.graphicsDevice.Update(f) + C.EndDrawing() } //export ebiten_EbitenOpenGLView_Initialized @@ -127,7 +130,6 @@ func ebiten_EbitenOpenGLView_Initialized() { if currentUI.graphicsDevice != nil { panic("The graphics device is already initialized") } - currentUI.graphicsDevice = opengl.NewDevice( currentUI.screenWidth, currentUI.screenHeight, @@ -136,16 +138,6 @@ func ebiten_EbitenOpenGLView_Initialized() { //export ebiten_EbitenOpenGLView_Updating func ebiten_EbitenOpenGLView_Updating() { - currentUI.lock.Lock() - defer currentUI.lock.Unlock() - for _, f := range currentUI.funcsExecutedOnMainThread { - f() - } - currentUI.funcsExecutedOnMainThread = currentUI.funcsExecutedOnMainThread[0:0] - - game := <-currentUI.updating - currentUI.graphicsDevice.Update(game.Draw) - currentUI.updated <- game } //export ebiten_EbitenOpenGLView_InputUpdated diff --git a/ui/cocoa/ebiten_opengl_view.c b/ui/cocoa/ebiten_opengl_view.c index 0a280a38b..801313639 100644 --- a/ui/cocoa/ebiten_opengl_view.c +++ b/ui/cocoa/ebiten_opengl_view.c @@ -13,7 +13,7 @@ void ebiten_EbitenOpenGLView_InputUpdated(InputType inputType, int x, int y); // TODO: Use NSViewController? -static CVReturn +/*static CVReturn EbitenDisplayLinkCallback(CVDisplayLinkRef displayLink, CVTimeStamp const* now, CVTimeStamp const* outputTime, @@ -28,19 +28,26 @@ EbitenDisplayLinkCallback(CVDisplayLinkRef displayLink, EbitenOpenGLView* view = (__bridge EbitenOpenGLView*)displayLinkContext; return [view getFrameForTime:outputTime]; } -} + }*/ @implementation EbitenOpenGLView { -@private - CVDisplayLinkRef displayLink_; + /*@private + CVDisplayLinkRef displayLink_;*/ } -- (void)dealloc { +/*- (void)dealloc { CVDisplayLinkRelease(self->displayLink_); - // Do not call [super dealloc] because of ARC. -} +#if !__has_feature(objc_arc) + [super dealloc]; +#endif +}*/ -- (void)prepareOpenGL { +/*- (void)prepareOpenGL { + [super prepareOpenGL]; + ebiten_EbitenOpenGLView_Initialized(); + }*/ + +/*- (void)prepareOpenGL { [super prepareOpenGL]; NSOpenGLContext* openGLContext = [self openGLContext]; assert(openGLContext != nil); @@ -60,9 +67,9 @@ EbitenDisplayLinkCallback(CVDisplayLinkRef displayLink, CVDisplayLinkStart(self->displayLink_); ebiten_EbitenOpenGLView_Initialized(); -} + }*/ -- (CVReturn)getFrameForTime:(CVTimeStamp const*)outputTime { +/*- (CVReturn)getFrameForTime:(CVTimeStamp const*)outputTime { (void)outputTime; NSOpenGLContext* context = [self openGLContext]; assert(context != nil); @@ -74,7 +81,7 @@ EbitenDisplayLinkCallback(CVDisplayLinkRef displayLink, CGLUnlockContext((CGLContextObj)[context CGLContextObj]); } return kCVReturnSuccess; -} + }*/ - (BOOL)isFlipped { return YES; diff --git a/ui/cocoa/ebiten_opengl_view.h b/ui/cocoa/ebiten_opengl_view.h index 802065c0a..d395bdcfd 100644 --- a/ui/cocoa/ebiten_opengl_view.h +++ b/ui/cocoa/ebiten_opengl_view.h @@ -8,7 +8,7 @@ @interface EbitenOpenGLView : NSOpenGLView -- (CVReturn)getFrameForTime:(CVTimeStamp const*)outputTime; +//- (CVReturn)getFrameForTime:(CVTimeStamp const*)outputTime; @end diff --git a/ui/cocoa/ebiten_window.c b/ui/cocoa/ebiten_window.c index 58f845270..2fbac108a 100644 --- a/ui/cocoa/ebiten_window.c +++ b/ui/cocoa/ebiten_window.c @@ -2,9 +2,16 @@ #import "ebiten_window.h" +#include + #import "ebiten_opengl_view.h" +void ebiten_EbitenOpenGLView_Initialized(void); + @implementation EbitenWindow +{ + NSOpenGLContext* glContext_; +} - (id)initWithSize:(NSSize)size { NSUInteger style = (NSTitledWindowMask | NSClosableWindowMask | @@ -31,6 +38,20 @@ [self setDocumentEdited:YES]; NSRect rect = NSMakeRect(0, 0, size.width, size.height); + NSView* contentView = [[NSView alloc] initWithFrame:rect]; + [self setContentView:contentView]; + + return self; + + /*EbitenOpenGLView* contentView = + [[EbitenOpenGLView alloc] initWithFrame:rect + pixelFormat:format];*/ +} + +- (NSOpenGLContext*)glContext { + if (self->glContext_ != nil) + return self->glContext_; + NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, @@ -40,11 +61,15 @@ }; NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - EbitenOpenGLView* glView = - [[EbitenOpenGLView alloc] initWithFrame:rect - pixelFormat:format]; - [self setContentView:glView]; - return self; + self->glContext_ = [[NSOpenGLContext alloc] initWithFormat:format + shareContext:nil]; + [self->glContext_ setView:[self contentView]]; + [self->glContext_ makeCurrentContext]; + ebiten_EbitenOpenGLView_Initialized(); + + [format release]; + + return self->glContext_; } - (BOOL)windowShouldClose:(id)sender { @@ -60,6 +85,7 @@ modalDelegate:self didEndSelector:selector contextInfo:nil]; + [alert release]; } return NO; } @@ -74,5 +100,13 @@ } } -@end +- (void)beginDrawing { + [[self glContext] makeCurrentContext]; + glClear(GL_COLOR_BUFFER_BIT); +} +- (void)endDrawing { + [[self glContext] flushBuffer]; +} + +@end diff --git a/ui/cocoa/ebiten_window.h b/ui/cocoa/ebiten_window.h index 1c69bf49e..b5bc4f961 100644 --- a/ui/cocoa/ebiten_window.h +++ b/ui/cocoa/ebiten_window.h @@ -12,6 +12,8 @@ returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo; - (BOOL)windowShouldClose:(id)sender; +- (void)beginDrawing; +- (void)endDrawing; @end diff --git a/ui/cocoa/mainloop.c b/ui/cocoa/mainloop.c new file mode 100644 index 000000000..6b55efd0b --- /dev/null +++ b/ui/cocoa/mainloop.c @@ -0,0 +1,45 @@ +// -*- objc -*- + +#include + +#import "ebiten_controller.h" +#import "ebiten_window.h" + +static EbitenWindow* currentWindow = 0; + +void Start(size_t width, size_t height, size_t scale, const char* title) { + NSSize size = NSMakeSize(width * scale, height * scale); + EbitenWindow* window = [[EbitenWindow alloc] + initWithSize:size]; + [window setTitle: [[NSString alloc] initWithUTF8String:title]]; + EbitenController* controller = [[EbitenController alloc] + initWithWindow:window]; + NSApplication* app = [NSApplication sharedApplication]; + [app setActivationPolicy:NSApplicationActivationPolicyRegular]; + [app setDelegate:controller]; + [app finishLaunching]; + [app activateIgnoringOtherApps:YES]; + + currentWindow = window; +} + +void WaitEvents(void) { + for (;;) { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) { + break; + } + [NSApp sendEvent:event]; + } +} + +void BeginDrawing(void) { + [currentWindow beginDrawing]; +} + +void EndDrawing(void) { + [currentWindow endDrawing]; +} diff --git a/ui/cocoa/run.c b/ui/cocoa/run.c deleted file mode 100644 index fc687888a..000000000 --- a/ui/cocoa/run.c +++ /dev/null @@ -1,23 +0,0 @@ -// -*- objc -*- - -#include - -#import "ebiten_controller.h" -#import "ebiten_window.h" - -void Run(size_t width, size_t height, size_t scale, const char* title) { - @autoreleasepool { - NSSize size = NSMakeSize(width * scale, height * scale); - EbitenWindow* window = [[EbitenWindow alloc] - initWithSize:size]; - [window setTitle: [[NSString alloc] initWithUTF8String:title]]; - EbitenController* controller = [[EbitenController alloc] - initWithWindow:window]; - NSApplication* app = [NSApplication sharedApplication]; - [app setActivationPolicy:NSApplicationActivationPolicyRegular]; - [app setDelegate:controller]; - [app finishLaunching]; - [app activateIgnoringOtherApps:YES]; - [app run]; - } -} diff --git a/ui/ui.go b/ui/ui.go index 161537aed..5b1faa293 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -1,9 +1 @@ package ui - -import ( - "github.com/hajimehoshi/go-ebiten" -) - -type UI interface { - Run(game ebiten.Game) -}