You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
6.4 KiB
217 lines
6.4 KiB
8 years ago
|
//
|
||
|
// NSWrapper.mm
|
||
|
// XcodeOpenCL
|
||
|
//
|
||
|
// Created by Joshua Moerman on 13/04/14.
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#include "NSWrapper.hpp"
|
||
|
#include "NSGLWrapper.hpp"
|
||
|
|
||
|
#include <vector>
|
||
|
#import <Cocoa/Cocoa.h>
|
||
|
#import <GLKit/GLKit.h>
|
||
|
|
||
|
|
||
|
GLViewFunctionality simple_draw(std::function<void()> f){
|
||
|
return {nullptr, [f](ContextParameters){ f(); }, nullptr};
|
||
|
}
|
||
|
|
||
|
|
||
|
@interface GLView : NSOpenGLView
|
||
|
// All three will only be called with the current gl context already set
|
||
|
// May be called from different threads (but the properties are atomic by default)
|
||
|
@property GLViewFunctionality functionality;
|
||
|
@end
|
||
|
|
||
|
|
||
|
struct NSAppWrapperImpl {
|
||
|
NSAppWrapperImpl() {
|
||
|
app = [NSApplication sharedApplication];
|
||
|
app.activationPolicy = NSApplicationActivationPolicyRegular;
|
||
|
appName = [[NSProcessInfo processInfo] processName];
|
||
|
}
|
||
|
|
||
|
void set_up_menu() {
|
||
|
NSMenu* appMenu = [NSMenu new];
|
||
|
[appMenu addItemWithTitle:[@"Quit " stringByAppendingString:appName]
|
||
|
action:@selector(terminate:)
|
||
|
keyEquivalent:@"q"];
|
||
|
|
||
|
NSMenuItem * menuItem = [NSMenuItem new];
|
||
|
menuItem.submenu = appMenu;
|
||
|
|
||
|
app.mainMenu = [NSMenu new];
|
||
|
[app.mainMenu addItem:menuItem];
|
||
|
}
|
||
|
|
||
|
void create_window(GLViewFunctionality const & f) {
|
||
|
NSWindow * window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 500, 500)
|
||
|
styleMask:NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
|
||
|
backing:NSBackingStoreBuffered
|
||
|
defer:YES];
|
||
|
window.title = appName;
|
||
|
window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
|
||
|
[window center];
|
||
|
[window makeKeyAndOrderFront:nil];
|
||
|
|
||
|
NSRect frame = [window contentRectForFrameRect:window.frame];
|
||
|
frame.origin = {0, 0};
|
||
|
GLView * view = [[GLView alloc] initWithFrame:frame];
|
||
|
|
||
|
view.functionality = f;
|
||
|
|
||
|
window.contentView = view;
|
||
|
|
||
|
windows.push_back(window);
|
||
|
}
|
||
|
|
||
|
void run() {
|
||
|
[app run];
|
||
|
}
|
||
|
|
||
|
NSApplication * app;
|
||
|
std::vector<NSWindow *> windows;
|
||
|
NSString * appName;
|
||
|
};
|
||
|
|
||
|
NSAppWrapper::NSAppWrapper()
|
||
|
: impl(new NSAppWrapperImpl){
|
||
|
impl->set_up_menu();
|
||
|
}
|
||
|
|
||
|
NSAppWrapper::~NSAppWrapper() = default;
|
||
|
|
||
|
void NSAppWrapper::run() {
|
||
|
impl->run();
|
||
|
}
|
||
|
|
||
|
void NSAppWrapper::create_window(GLViewFunctionality const & f){
|
||
|
impl->create_window(f);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
Some general notes about GLView:
|
||
|
- It is in a multithreaded context. The CVDisplayLink will run on a diferent thread than the main thread. However the main thread *will also draw* (eg. when reshaping, to avoid flickering). So there are some locks here and there.
|
||
|
- Hopefully the C++ objects are destructed nicely
|
||
|
- There is syntax for the constructor, so I used unique_ptr...
|
||
|
*/
|
||
|
|
||
|
@implementation GLView {
|
||
|
std::unique_ptr<GLContext> context;
|
||
|
std::unique_ptr<CVDisplayLinky> linky;
|
||
|
}
|
||
|
|
||
|
@synthesize functionality;
|
||
|
|
||
|
- (id) initWithFrame:(NSRect)frameRect {
|
||
|
if(self = [super initWithFrame:frameRect]){
|
||
|
context = std::unique_ptr<GLContext>(new GLContext);
|
||
|
|
||
|
self.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithCGLPixelFormatObj: context->pix];
|
||
|
self.openGLContext = [[NSOpenGLContext alloc] initWithCGLContextObj: context->ctx];
|
||
|
self.wantsBestResolutionOpenGLSurface = YES; // Opt-in for retina
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (void) prepareOpenGL{
|
||
|
[super prepareOpenGL];
|
||
|
|
||
|
context->set_as_current_context();
|
||
|
|
||
|
// Synchronize buffer swaps with vertical refresh rate
|
||
|
GLint swapInt = 1;
|
||
|
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||
|
|
||
|
if(functionality.initialize_callback){
|
||
|
functionality.initialize_callback({*context});
|
||
|
}
|
||
|
|
||
|
// Create a display link capable of being used with all active displays
|
||
|
linky = std::unique_ptr<CVDisplayLinky>(new CVDisplayLinky(*context));
|
||
|
linky->callback = [=]{
|
||
|
// will be called in a different thread => need new autoreleasepool
|
||
|
@autoreleasepool {
|
||
|
[self drawView];
|
||
|
}
|
||
|
};
|
||
|
linky->start();
|
||
|
|
||
|
// Register to be notified when the window closes so we can stop the displaylink
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||
|
selector:@selector(windowWillClose:)
|
||
|
name:NSWindowWillCloseNotification
|
||
|
object:[self window]];
|
||
|
}
|
||
|
|
||
|
- (void) windowWillClose:(NSNotification*)notification{
|
||
|
// The default OpenGL render buffers will be destroyed. If display link
|
||
|
// continues to fire without renderbuffers, OpenGL draw calls will set errors.
|
||
|
|
||
|
linky->stop();
|
||
|
}
|
||
|
|
||
|
- (void) reshape{
|
||
|
[super reshape];
|
||
|
|
||
|
context->lock();
|
||
|
|
||
|
// [NSView convertRectToBacking] converts point sizes to pixel sizes.
|
||
|
// viewRectPixels will be larger (2x) than viewRectPoints for retina displays.
|
||
|
// viewRectPixels will be the same as viewRectPoints for non-retina displays
|
||
|
NSRect viewRectPixels = [self convertRectToBacking:self.bounds];
|
||
|
|
||
|
if(functionality.resize_callback){
|
||
|
functionality.resize_callback({*context}, viewRectPixels.size.width, viewRectPixels.size.height);
|
||
|
}
|
||
|
|
||
|
context->unlock();
|
||
|
}
|
||
|
|
||
|
- (void)renewGState{
|
||
|
// Called whenever graphics state updated (such as window resize)
|
||
|
|
||
|
// OpenGL rendering is not synchronous with other rendering on the OSX.
|
||
|
// Therefore, call disableScreenUpdatesUntilFlush so the window server
|
||
|
// doesn't render non-OpenGL content in the window asynchronously from
|
||
|
// OpenGL content, which could cause flickering. (non-OpenGL content
|
||
|
// includes the title bar and drawing done by the app with other APIs)
|
||
|
[[self window] disableScreenUpdatesUntilFlush];
|
||
|
|
||
|
[super renewGState];
|
||
|
}
|
||
|
|
||
|
- (void) drawRect: (NSRect) theRect{
|
||
|
[self drawView];
|
||
|
}
|
||
|
|
||
|
- (void) drawView{
|
||
|
context->set_as_current_context();
|
||
|
|
||
|
// We draw on a secondary thread through the display link
|
||
|
// When resizing the view, -reshape is called automatically on the main
|
||
|
// thread. Add a mutex around to avoid the threads accessing the context
|
||
|
// simultaneously when resizing
|
||
|
context->lock();
|
||
|
|
||
|
NSRect viewRectPixels = [self convertRectToBacking:self.bounds];
|
||
|
|
||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||
|
glViewport(0, 0, (int)viewRectPixels.size.width, (int)viewRectPixels.size.height);
|
||
|
|
||
|
if(functionality.draw_callback){
|
||
|
functionality.draw_callback({*context});
|
||
|
}
|
||
|
|
||
|
CGLFlushDrawable(context->ctx);
|
||
|
context->unlock();
|
||
|
}
|
||
|
|
||
|
@end
|
||
|
|