mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 19:58:54 +01:00
Implement input in ui/glfw (#7)
This commit is contained in:
parent
ca97ee6961
commit
7eb6b2f51f
@ -1,12 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#ifndef GO_EBITEN_UI_COCOA_EBITEN_CONTROLLER_H_
|
|
||||||
#define GO_EBITEN_UI_COCOA_EBITEN_CONTROLLER_H_
|
|
||||||
|
|
||||||
#include <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface EbitenController : NSObject<NSApplicationDelegate>
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,54 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#include "ebiten_game_content_view.h"
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
void ebiten_KeyDown(NSWindow* nativeWindow, int keyCode);
|
|
||||||
void ebiten_KeyUp(NSWindow* nativeWindow, int keyCode);
|
|
||||||
void ebiten_MouseStateUpdated(NSWindow* nativeWindow, InputType inputType, int x, int y);
|
|
||||||
|
|
||||||
@implementation EbitenGameContentView {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isFlipped {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent*)theEvent {
|
|
||||||
ebiten_KeyDown([self window], [theEvent keyCode]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)keyUp:(NSEvent*)theEvent {
|
|
||||||
ebiten_KeyUp([self window], [theEvent keyCode]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent*)theEvent {
|
|
||||||
NSPoint location = [self convertPoint:[theEvent locationInWindow]
|
|
||||||
fromView:nil];
|
|
||||||
int x = location.x;
|
|
||||||
int y = location.y;
|
|
||||||
ebiten_MouseStateUpdated([self window], InputTypeMouseDown, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent*)theEvent {
|
|
||||||
(void)theEvent;
|
|
||||||
NSPoint location = [self convertPoint:[theEvent locationInWindow]
|
|
||||||
fromView:nil];
|
|
||||||
int x = location.x;
|
|
||||||
int y = location.y;
|
|
||||||
ebiten_MouseStateUpdated([self window], InputTypeMouseUp, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseDragged:(NSEvent*)theEvent {
|
|
||||||
NSPoint location = [self convertPoint:[theEvent locationInWindow]
|
|
||||||
fromView:nil];
|
|
||||||
int x = location.x;
|
|
||||||
int y = location.y;
|
|
||||||
ebiten_MouseStateUpdated([self window], InputTypeMouseDragged, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,13 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#ifndef GO_EBITEN_UI_COCOA_EBITEN_GAME_CONTENT_VIEW_H_
|
|
||||||
#define GO_EBITEN_UI_COCOA_EBITEN_GAME_CONTENT_VIEW_H_
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import <QuartzCore/QuartzCore.h>
|
|
||||||
|
|
||||||
@interface EbitenGameContentView : NSView
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,88 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#import "ebiten_game_window.h"
|
|
||||||
|
|
||||||
#import "ebiten_game_content_view.h"
|
|
||||||
|
|
||||||
@class NSOpenGLContext;
|
|
||||||
|
|
||||||
void ebiten_WindowClosed(void* nativeWindow);
|
|
||||||
|
|
||||||
@implementation EbitenGameWindow {
|
|
||||||
@private
|
|
||||||
NSOpenGLContext* glContext_;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithSize:(NSSize)size
|
|
||||||
glContext:(NSOpenGLContext*)glContext {
|
|
||||||
self->glContext_ = glContext;
|
|
||||||
[self->glContext_ retain];
|
|
||||||
|
|
||||||
NSUInteger style = (NSTitledWindowMask | NSClosableWindowMask |
|
|
||||||
NSMiniaturizableWindowMask);
|
|
||||||
NSRect windowRect =
|
|
||||||
[NSWindow frameRectForContentRect:NSMakeRect(0, 0, size.width, size.height)
|
|
||||||
styleMask:style];
|
|
||||||
NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
|
|
||||||
NSSize screenSize = [screen visibleFrame].size;
|
|
||||||
NSRect contentRect = NSMakeRect(0, 0, size.width, size.height);
|
|
||||||
self = [super initWithContentRect:contentRect
|
|
||||||
styleMask:style
|
|
||||||
backing:NSBackingStoreBuffered
|
|
||||||
defer:YES];
|
|
||||||
if (self != nil) {
|
|
||||||
[self center];
|
|
||||||
[self setReleasedWhenClosed:YES];
|
|
||||||
[self setDelegate:self];
|
|
||||||
[self setDocumentEdited:YES];
|
|
||||||
|
|
||||||
NSRect rect = NSMakeRect(0, 0, size.width, size.height);
|
|
||||||
NSView* contentView = [[EbitenGameContentView alloc] initWithFrame:rect];
|
|
||||||
[self setContentView:contentView];
|
|
||||||
[contentView release];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
[self->glContext_ release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSOpenGLContext*)glContext {
|
|
||||||
return self->glContext_;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)windowShouldClose:(id)sender {
|
|
||||||
if ([sender isDocumentEdited]) {
|
|
||||||
// TODO: add the application's name
|
|
||||||
NSAlert* alert = [NSAlert new];
|
|
||||||
[alert setMessageText:@"Quit the game?"];
|
|
||||||
[alert addButtonWithTitle:@"Quit"];
|
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
|
||||||
[alert setAlertStyle:NSWarningAlertStyle];
|
|
||||||
SEL selector = @selector(alertDidEnd:returnCode:contextInfo:);
|
|
||||||
[alert beginSheetModalForWindow:sender
|
|
||||||
modalDelegate:self
|
|
||||||
didEndSelector:selector
|
|
||||||
contextInfo:nil];
|
|
||||||
[alert release];
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)alertDidEnd:(NSAlert*)alert
|
|
||||||
returnCode:(NSInteger)returnCode
|
|
||||||
contextInfo:(void*)contextInfo {
|
|
||||||
if (returnCode == NSAlertFirstButtonReturn) {
|
|
||||||
[self close];
|
|
||||||
ebiten_WindowClosed(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canBecomeMainWindow {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,16 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#ifndef GO_EBITEN_UI_COCOA_EBITEN_GAME_WINDOW_H_
|
|
||||||
#define GO_EBITEN_UI_COCOA_EBITEN_GAME_WINDOW_H_
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface EbitenGameWindow : NSWindow<NSWindowDelegate>
|
|
||||||
|
|
||||||
- (id)initWithSize:(NSSize)size
|
|
||||||
glContext:(NSOpenGLContext*)glContext;
|
|
||||||
- (NSOpenGLContext*)glContext;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,268 +0,0 @@
|
|||||||
package cocoa
|
|
||||||
|
|
||||||
// #include <stdlib.h>
|
|
||||||
//
|
|
||||||
// #include "input.h"
|
|
||||||
//
|
|
||||||
// @class EbitenGameWindow;
|
|
||||||
// @class NSOpenGLContext;
|
|
||||||
//
|
|
||||||
// typedef EbitenGameWindow* EbitenGameWindowPtr;
|
|
||||||
//
|
|
||||||
// EbitenGameWindow* CreateGameWindow(size_t width, size_t height, const char* title, NSOpenGLContext* glContext);
|
|
||||||
// NSOpenGLContext* CreateGLContext(NSOpenGLContext* sharedGLContext);
|
|
||||||
//
|
|
||||||
// void UseGLContext(NSOpenGLContext* glContext);
|
|
||||||
// void UnuseGLContext(void);
|
|
||||||
//
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics"
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics/opengl"
|
|
||||||
"github.com/hajimehoshi/ebiten/ui"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Keys map[ui.Key]struct{}
|
|
||||||
|
|
||||||
func newKeys() Keys {
|
|
||||||
return Keys(map[ui.Key]struct{}{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) clone() Keys {
|
|
||||||
n := newKeys()
|
|
||||||
for key, value := range k {
|
|
||||||
n[key] = value
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) add(key ui.Key) {
|
|
||||||
k[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) remove(key ui.Key) {
|
|
||||||
delete(k, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) Includes(key ui.Key) bool {
|
|
||||||
_, ok := k[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputState struct {
|
|
||||||
pressedKeys Keys
|
|
||||||
mouseX int
|
|
||||||
mouseY int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) PressedKeys() ui.Keys {
|
|
||||||
return i.pressedKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) MouseX() int {
|
|
||||||
return i.mouseX
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) MouseY() int {
|
|
||||||
return i.mouseY
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) setMouseXY(x, y int) {
|
|
||||||
i.mouseX = x
|
|
||||||
i.mouseY = y
|
|
||||||
}
|
|
||||||
|
|
||||||
type GameWindow struct {
|
|
||||||
width int
|
|
||||||
height int
|
|
||||||
scale int
|
|
||||||
isClosed bool
|
|
||||||
inputState *InputState
|
|
||||||
title string
|
|
||||||
native *C.EbitenGameWindow
|
|
||||||
funcs chan func(*opengl.Context)
|
|
||||||
funcsDone chan struct{}
|
|
||||||
closed chan struct{}
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
var windows = map[*C.EbitenGameWindow]*GameWindow{}
|
|
||||||
|
|
||||||
func newGameWindow(width, height, scale int, title string) *GameWindow {
|
|
||||||
inputState := &InputState{
|
|
||||||
pressedKeys: newKeys(),
|
|
||||||
mouseX: -1,
|
|
||||||
mouseY: -1,
|
|
||||||
}
|
|
||||||
return &GameWindow{
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
scale: scale,
|
|
||||||
inputState: inputState,
|
|
||||||
title: title,
|
|
||||||
funcs: make(chan func(*opengl.Context)),
|
|
||||||
funcsDone: make(chan struct{}),
|
|
||||||
closed: make(chan struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) IsClosed() bool {
|
|
||||||
w.RLock()
|
|
||||||
defer w.RUnlock()
|
|
||||||
return w.isClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) run(sharedGLContext *C.NSOpenGLContext) {
|
|
||||||
cTitle := C.CString(w.title)
|
|
||||||
defer C.free(unsafe.Pointer(cTitle))
|
|
||||||
|
|
||||||
ch := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
runtime.LockOSThread()
|
|
||||||
glContext := C.CreateGLContext(sharedGLContext)
|
|
||||||
w.native = C.CreateGameWindow(
|
|
||||||
C.size_t(w.width*w.scale),
|
|
||||||
C.size_t(w.height*w.scale),
|
|
||||||
cTitle,
|
|
||||||
glContext)
|
|
||||||
windows[w.native] = w
|
|
||||||
close(ch)
|
|
||||||
|
|
||||||
C.UseGLContext(glContext)
|
|
||||||
context := opengl.NewContext(
|
|
||||||
w.width, w.height, w.scale)
|
|
||||||
C.UnuseGLContext()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
C.UseGLContext(glContext)
|
|
||||||
context.Dispose()
|
|
||||||
C.UnuseGLContext()
|
|
||||||
}()
|
|
||||||
|
|
||||||
w.loop(context, glContext)
|
|
||||||
}()
|
|
||||||
<-ch
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) loop(context *opengl.Context, glContext *C.NSOpenGLContext) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-w.closed:
|
|
||||||
return
|
|
||||||
case f := <-w.funcs:
|
|
||||||
// Wait 10 millisecond at least to avoid busy loop.
|
|
||||||
after := time.After(time.Duration(int64(time.Millisecond) * 10))
|
|
||||||
C.UseGLContext(glContext)
|
|
||||||
f(context)
|
|
||||||
C.UnuseGLContext()
|
|
||||||
<-after
|
|
||||||
w.funcsDone <- struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) Draw(f func(graphics.Context)) {
|
|
||||||
select {
|
|
||||||
case <-w.closed:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
w.useGLContext(func(context *opengl.Context) {
|
|
||||||
context.Update(f)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) useGLContext(f func(*opengl.Context)) {
|
|
||||||
w.funcs <- f
|
|
||||||
<-w.funcsDone
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *GameWindow) InputState() ui.InputState {
|
|
||||||
w.RLock()
|
|
||||||
defer w.RUnlock()
|
|
||||||
return &InputState{
|
|
||||||
pressedKeys: w.inputState.pressedKeys.clone(),
|
|
||||||
mouseX: w.inputState.mouseX,
|
|
||||||
mouseY: w.inputState.mouseY,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cocoaKeyCodeToKey = map[int]ui.Key{
|
|
||||||
49: ui.KeySpace,
|
|
||||||
123: ui.KeyLeft,
|
|
||||||
124: ui.KeyRight,
|
|
||||||
125: ui.KeyDown,
|
|
||||||
126: ui.KeyUp,
|
|
||||||
}
|
|
||||||
|
|
||||||
//export ebiten_KeyDown
|
|
||||||
func ebiten_KeyDown(nativeWindow C.EbitenGameWindowPtr, keyCode int) {
|
|
||||||
key, ok := cocoaKeyCodeToKey[keyCode]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w := windows[nativeWindow]
|
|
||||||
|
|
||||||
w.Lock()
|
|
||||||
defer w.Unlock()
|
|
||||||
w.inputState.pressedKeys.add(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export ebiten_KeyUp
|
|
||||||
func ebiten_KeyUp(nativeWindow C.EbitenGameWindowPtr, keyCode int) {
|
|
||||||
key, ok := cocoaKeyCodeToKey[keyCode]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w := windows[nativeWindow]
|
|
||||||
|
|
||||||
w.Lock()
|
|
||||||
defer w.Unlock()
|
|
||||||
w.inputState.pressedKeys.remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export ebiten_MouseStateUpdated
|
|
||||||
func ebiten_MouseStateUpdated(nativeWindow C.EbitenGameWindowPtr, inputType C.InputType, cx, cy C.int) {
|
|
||||||
w := windows[nativeWindow]
|
|
||||||
|
|
||||||
if inputType == C.InputTypeMouseUp {
|
|
||||||
w.Lock()
|
|
||||||
defer w.Unlock()
|
|
||||||
w.inputState.setMouseXY(-1, -1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
x, y := int(cx), int(cy)
|
|
||||||
x /= w.scale
|
|
||||||
y /= w.scale
|
|
||||||
if x < 0 {
|
|
||||||
x = 0
|
|
||||||
} else if w.width <= x {
|
|
||||||
x = w.width - 1
|
|
||||||
}
|
|
||||||
if y < 0 {
|
|
||||||
y = 0
|
|
||||||
} else if w.height <= y {
|
|
||||||
y = w.height - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Lock()
|
|
||||||
defer w.Unlock()
|
|
||||||
w.inputState.setMouseXY(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export ebiten_WindowClosed
|
|
||||||
func ebiten_WindowClosed(nativeWindow C.EbitenGameWindowPtr) {
|
|
||||||
w := windows[nativeWindow]
|
|
||||||
close(w.closed)
|
|
||||||
|
|
||||||
w.Lock()
|
|
||||||
defer w.Unlock()
|
|
||||||
w.isClosed = true
|
|
||||||
|
|
||||||
delete(windows, nativeWindow)
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
#ifndef GO_EBITEN_UI_COCOA_INPUT_H_
|
|
||||||
#define GO_EBITEN_UI_COCOA_INPUT_H_
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
InputTypeMouseUp,
|
|
||||||
InputTypeMouseDragged,
|
|
||||||
InputTypeMouseDown,
|
|
||||||
} InputType;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||||||
// -*- objc -*-
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <OpenGL/gl.h>
|
|
||||||
|
|
||||||
#import "ebiten_game_window.h"
|
|
||||||
|
|
||||||
static NSAutoreleasePool* pool = NULL;
|
|
||||||
|
|
||||||
void initMenu(void) {
|
|
||||||
NSString* processName = [[NSProcessInfo processInfo] processName];
|
|
||||||
|
|
||||||
NSMenu* menuBar = [NSMenu new];
|
|
||||||
[NSApp setMainMenu: menuBar];
|
|
||||||
[menuBar release];
|
|
||||||
|
|
||||||
NSMenuItem* rootMenuItem = [NSMenuItem new];
|
|
||||||
[menuBar addItem:rootMenuItem];
|
|
||||||
[rootMenuItem release];
|
|
||||||
|
|
||||||
NSMenu* appMenu = [NSMenu new];
|
|
||||||
[rootMenuItem setSubmenu:appMenu];
|
|
||||||
[appMenu release];
|
|
||||||
|
|
||||||
[appMenu addItemWithTitle:[@"Quit " stringByAppendingString:processName]
|
|
||||||
action:@selector(performClose:)
|
|
||||||
keyEquivalent:@"q"];
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartApplication(void) {
|
|
||||||
pool = [NSAutoreleasePool new];
|
|
||||||
|
|
||||||
NSApplication* app = [NSApplication sharedApplication];
|
|
||||||
[app setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
||||||
initMenu();
|
|
||||||
[app finishLaunching];
|
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoEvents(void) {
|
|
||||||
for (;;) {
|
|
||||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
|
||||||
untilDate:[NSDate distantPast]
|
|
||||||
inMode:NSDefaultRunLoopMode
|
|
||||||
dequeue:YES];
|
|
||||||
if (event == nil) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[NSApp sendEvent:event];
|
|
||||||
}
|
|
||||||
|
|
||||||
[pool drain];
|
|
||||||
pool = [NSAutoreleasePool new];
|
|
||||||
}
|
|
||||||
|
|
||||||
void TerminateApplication(void) {
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSOpenGLContext* CreateGLContext(NSOpenGLContext* sharedGLContext) {
|
|
||||||
NSOpenGLPixelFormatAttribute attributes[] = {
|
|
||||||
NSOpenGLPFAWindow,
|
|
||||||
NSOpenGLPFADoubleBuffer,
|
|
||||||
NSOpenGLPFAAccelerated,
|
|
||||||
NSOpenGLPFADepthSize, 32,
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]
|
|
||||||
initWithAttributes:attributes];
|
|
||||||
NSOpenGLContext* glContext =
|
|
||||||
[[NSOpenGLContext alloc] initWithFormat:format
|
|
||||||
shareContext:sharedGLContext];
|
|
||||||
[format release];
|
|
||||||
return glContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
EbitenGameWindow* CreateGameWindow(size_t width, size_t height, const char* title, NSOpenGLContext* glContext) {
|
|
||||||
NSSize size = NSMakeSize(width, height);
|
|
||||||
EbitenGameWindow* window = [[EbitenGameWindow alloc]
|
|
||||||
initWithSize:size
|
|
||||||
glContext:glContext];
|
|
||||||
[glContext release];
|
|
||||||
|
|
||||||
NSString* nsTitle = [[NSString alloc]
|
|
||||||
initWithUTF8String:title];
|
|
||||||
[window setTitle: nsTitle];
|
|
||||||
[nsTitle release];
|
|
||||||
|
|
||||||
[window makeKeyAndOrderFront:nil];
|
|
||||||
[glContext setView:[window contentView]];
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UseGLContext(NSOpenGLContext* glContext) {
|
|
||||||
CGLContextObj cglContext = [glContext CGLContextObj];
|
|
||||||
CGLLockContext(cglContext);
|
|
||||||
[glContext makeCurrentContext];
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnuseGLContext(void) {
|
|
||||||
NSOpenGLContext* glContext = [NSOpenGLContext currentContext];
|
|
||||||
[glContext flushBuffer];
|
|
||||||
[NSOpenGLContext clearCurrentContext];
|
|
||||||
CGLContextObj cglContext = [glContext CGLContextObj];
|
|
||||||
CGLUnlockContext(cglContext);
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package cocoa
|
|
||||||
|
|
||||||
// @class NSOpenGLContext;
|
|
||||||
//
|
|
||||||
// NSOpenGLContext* CreateGLContext(NSOpenGLContext* sharedGLContext);
|
|
||||||
// void UseGLContext(NSOpenGLContext* glContext);
|
|
||||||
// void UnuseGLContext(void);
|
|
||||||
//
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics"
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics/opengl"
|
|
||||||
"image"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type sharedContext struct {
|
|
||||||
inited chan struct{}
|
|
||||||
funcs chan func()
|
|
||||||
funcsDone chan struct{}
|
|
||||||
gameWindows chan *GameWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSharedContext() *sharedContext {
|
|
||||||
return &sharedContext{
|
|
||||||
inited: make(chan struct{}),
|
|
||||||
funcs: make(chan func()),
|
|
||||||
funcsDone: make(chan struct{}),
|
|
||||||
gameWindows: make(chan *GameWindow),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) run() {
|
|
||||||
var sharedGLContext *C.NSOpenGLContext
|
|
||||||
go func() {
|
|
||||||
runtime.LockOSThread()
|
|
||||||
sharedGLContext = C.CreateGLContext(nil)
|
|
||||||
close(t.inited)
|
|
||||||
t.loop(sharedGLContext)
|
|
||||||
}()
|
|
||||||
<-t.inited
|
|
||||||
go func() {
|
|
||||||
for w := range t.gameWindows {
|
|
||||||
w.run(sharedGLContext)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) loop(sharedGLContext *C.NSOpenGLContext) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case f := <-t.funcs:
|
|
||||||
C.UseGLContext(sharedGLContext)
|
|
||||||
f()
|
|
||||||
C.UnuseGLContext()
|
|
||||||
t.funcsDone <- struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) useGLContext(f func()) {
|
|
||||||
t.funcs <- f
|
|
||||||
<-t.funcsDone
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) createGameWindow(width, height, scale int, title string) *GameWindow {
|
|
||||||
w := newGameWindow(width, height, scale, title)
|
|
||||||
go func() {
|
|
||||||
t.gameWindows <- w
|
|
||||||
}()
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) CreateTexture(
|
|
||||||
img image.Image,
|
|
||||||
filter graphics.Filter) (graphics.TextureId, error) {
|
|
||||||
<-t.inited
|
|
||||||
var id graphics.TextureId
|
|
||||||
var err error
|
|
||||||
t.useGLContext(func() {
|
|
||||||
id, err = opengl.CreateTexture(img, filter)
|
|
||||||
})
|
|
||||||
return id, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *sharedContext) CreateRenderTarget(
|
|
||||||
width, height int,
|
|
||||||
filter graphics.Filter) (graphics.RenderTargetId, error) {
|
|
||||||
<-t.inited
|
|
||||||
var id graphics.RenderTargetId
|
|
||||||
var err error
|
|
||||||
t.useGLContext(func() {
|
|
||||||
id, err = opengl.CreateRenderTarget(width, height, filter)
|
|
||||||
})
|
|
||||||
return id, err
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package cocoa
|
|
||||||
|
|
||||||
// #cgo CFLAGS: -x objective-c
|
|
||||||
// #cgo LDFLAGS: -framework Cocoa -framework OpenGL
|
|
||||||
//
|
|
||||||
// void StartApplication(void);
|
|
||||||
// void DoEvents(void);
|
|
||||||
// void TerminateApplication(void);
|
|
||||||
//
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics"
|
|
||||||
"github.com/hajimehoshi/ebiten/ui"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cocoaUI struct {
|
|
||||||
sharedContext *sharedContext
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentUI *cocoaUI
|
|
||||||
|
|
||||||
func getCurrentUI() *cocoaUI {
|
|
||||||
if currentUI != nil {
|
|
||||||
return currentUI
|
|
||||||
}
|
|
||||||
|
|
||||||
currentUI = &cocoaUI{}
|
|
||||||
currentUI.sharedContext = newSharedContext()
|
|
||||||
|
|
||||||
return currentUI
|
|
||||||
}
|
|
||||||
|
|
||||||
func UI() ui.UI {
|
|
||||||
return getCurrentUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TextureFactory() graphics.TextureFactory {
|
|
||||||
return getCurrentUI().sharedContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *cocoaUI) CreateCanvas(width, height, scale int, title string) ui.Canvas {
|
|
||||||
return u.sharedContext.createGameWindow(width, height, scale, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *cocoaUI) DoEvents() {
|
|
||||||
C.DoEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *cocoaUI) Start() {
|
|
||||||
C.StartApplication()
|
|
||||||
currentUI.sharedContext.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *cocoaUI) Terminate() {
|
|
||||||
// TODO: Close existing windows
|
|
||||||
C.TerminateApplication()
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package dummy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hajimehoshi/ebiten/graphics"
|
|
||||||
"github.com/hajimehoshi/ebiten/ui"
|
|
||||||
"image"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TextureFactory struct{}
|
|
||||||
|
|
||||||
func (t *TextureFactory) CreateRenderTarget(width, height int, filter graphics.Filter) (graphics.RenderTargetId, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TextureFactory) CreateTexture(img image.Image, filter graphics.Filter) (graphics.TextureId, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UI struct{}
|
|
||||||
|
|
||||||
func (u *UI) CreateCanvas(widht, height, scale int, title string) ui.Canvas {
|
|
||||||
return &Canvas{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UI) Start() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UI) DoEvents() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UI)Terminate() {
|
|
||||||
}
|
|
||||||
|
|
||||||
type Keys struct{}
|
|
||||||
|
|
||||||
func (k *Keys) Includes(key ui.Key) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputState struct{}
|
|
||||||
|
|
||||||
func (i *InputState) PressedKeys() ui.Keys {
|
|
||||||
return &Keys{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) MouseX() int {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InputState) MouseY() int {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
type Canvas struct{}
|
|
||||||
|
|
||||||
func (c *Canvas) Draw(func(graphics.Context)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Canvas) IsClosed() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Canvas) InputState() ui.InputState {
|
|
||||||
return &InputState{}
|
|
||||||
}
|
|
@ -10,10 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Canvas struct {
|
type Canvas struct {
|
||||||
window *glfw.Window
|
window *glfw.Window
|
||||||
context *opengl.Context
|
inputState *InputState
|
||||||
funcs chan func()
|
context *opengl.Context
|
||||||
funcsDone chan struct{}
|
funcs chan func()
|
||||||
|
funcsDone chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCanvas(width, height, scale int, title string) *Canvas {
|
func NewCanvas(width, height, scale int, title string) *Canvas {
|
||||||
@ -22,15 +23,15 @@ func NewCanvas(width, height, scale int, title string) *Canvas {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
canvas := &Canvas{
|
canvas := &Canvas{
|
||||||
window: window,
|
window: window,
|
||||||
funcs: make(chan func()),
|
inputState: newInputState(),
|
||||||
funcsDone: make(chan struct{}),
|
funcs: make(chan func()),
|
||||||
|
funcsDone: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// For retina displays, recalculate the scale with the framebuffer size.
|
// For retina displays, recalculate the scale with the framebuffer size.
|
||||||
windowWidth, windowHeight := window.GetFramebufferSize()
|
windowWidth, _ := window.GetFramebufferSize()
|
||||||
realScale := windowWidth / width
|
realScale := windowWidth / width
|
||||||
_ = windowHeight
|
|
||||||
|
|
||||||
canvas.run()
|
canvas.run()
|
||||||
canvas.use(func() {
|
canvas.use(func() {
|
||||||
@ -51,7 +52,7 @@ func (c *Canvas) IsClosed() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Canvas) InputState() ui.InputState {
|
func (c *Canvas) InputState() ui.InputState {
|
||||||
return &InputState{newKeys(), -1, -1}
|
return c.inputState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Canvas) CreateTexture(img image.Image, filter graphics.Filter) (graphics.TextureId, error) {
|
func (c *Canvas) CreateTexture(img image.Image, filter graphics.Filter) (graphics.TextureId, error) {
|
||||||
@ -89,3 +90,7 @@ func (c *Canvas) use(f func()) {
|
|||||||
c.funcs <- f
|
c.funcs <- f
|
||||||
<-c.funcsDone
|
<-c.funcsDone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Canvas) update() {
|
||||||
|
c.inputState.update(c.window)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package glfw
|
package glfw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
glfw "github.com/go-gl/glfw3"
|
||||||
"github.com/hajimehoshi/ebiten/ui"
|
"github.com/hajimehoshi/ebiten/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,14 +11,6 @@ func newKeys() Keys {
|
|||||||
return Keys(map[ui.Key]struct{}{})
|
return Keys(map[ui.Key]struct{}{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keys) clone() Keys {
|
|
||||||
n := newKeys()
|
|
||||||
for key, value := range k {
|
|
||||||
n[key] = value
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) add(key ui.Key) {
|
func (k Keys) add(key ui.Key) {
|
||||||
k[key] = struct{}{}
|
k[key] = struct{}{}
|
||||||
}
|
}
|
||||||
@ -37,6 +30,14 @@ type InputState struct {
|
|||||||
mouseY int
|
mouseY int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newInputState() *InputState {
|
||||||
|
return &InputState{
|
||||||
|
pressedKeys: newKeys(),
|
||||||
|
mouseX: -1,
|
||||||
|
mouseY: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (i *InputState) PressedKeys() ui.Keys {
|
func (i *InputState) PressedKeys() ui.Keys {
|
||||||
return i.pressedKeys
|
return i.pressedKeys
|
||||||
}
|
}
|
||||||
@ -48,3 +49,21 @@ func (i *InputState) MouseX() int {
|
|||||||
func (i *InputState) MouseY() int {
|
func (i *InputState) MouseY() int {
|
||||||
return i.mouseY
|
return i.mouseY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var glfwKeyCodeToKey = map[glfw.Key]ui.Key{
|
||||||
|
glfw.KeySpace: ui.KeySpace,
|
||||||
|
glfw.KeyLeft: ui.KeyLeft,
|
||||||
|
glfw.KeyRight: ui.KeyRight,
|
||||||
|
glfw.KeyUp: ui.KeyUp,
|
||||||
|
glfw.KeyDown: ui.KeyDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InputState) update(window *glfw.Window) {
|
||||||
|
for g, u := range glfwKeyCodeToKey {
|
||||||
|
if window.GetKey(g) == glfw.Press {
|
||||||
|
i.pressedKeys.add(u)
|
||||||
|
} else {
|
||||||
|
i.pressedKeys.remove(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@ func (u *UI) CreateCanvas(width, height, scale int, title string) ui.Canvas {
|
|||||||
panic("glfw.Init() fails")
|
panic("glfw.Init() fails")
|
||||||
}
|
}
|
||||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||||
//glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLESAPI)
|
|
||||||
u.canvas = NewCanvas(width, height, scale, title)
|
u.canvas = NewCanvas(width, height, scale, title)
|
||||||
return u.canvas
|
return u.canvas
|
||||||
}
|
}
|
||||||
@ -32,6 +31,7 @@ func (u *UI) Start() {
|
|||||||
|
|
||||||
func (u *UI) DoEvents() {
|
func (u *UI) DoEvents() {
|
||||||
glfw.PollEvents()
|
glfw.PollEvents()
|
||||||
|
u.canvas.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UI) Terminate() {
|
func (u *UI) Terminate() {
|
||||||
|
Loading…
Reference in New Issue
Block a user