//
// OpenGLView.mm
// AppLauncher
//
// Created by John Ryland on 1/10/17.
// Copyright © 2017 John Ryland. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#include <OpenGL/gl.h>
#include <string>
#include <mutex>
#include "OpenGLContext.h"
#include "../Framework/Framework.h"
@interface OpenGLView : NSOpenGLView
{
CVDisplayLinkRef m_displayLink; // display link for managing rendering thread
MouseEvent m_mouseState;
//DemoContext* m_currentDemo;
OpenGLContext m_context;
std::mutex m_mutex;
}
- (void)setDemo:(const char *)demoStr;
@end
@implementation OpenGLView
- (void)prepareOpenGL
{
m_mutex.lock();
// Synchronize buffer swaps with vertical refresh rate
GLint swapInterval = 1;
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
CGLLockContext(self.openGLContext.CGLContextObj);
vec2f shape{ float(self.frame.size.width), float(self.frame.size.height) };
m_context.onResize(shape);
m_context.prepare();
CGLUnlockContext(self.openGLContext.CGLContextObj);
// Create a display link capable of being used with all active displays
CVDisplayLinkCreateWithActiveCGDisplays(&m_displayLink);
// Set the renderer output callback function
CVDisplayLinkSetOutputCallback(m_displayLink, &displayLinkCallback, (__bridge void *)self);
// Set the display link for the current renderer
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(m_displayLink, self.openGLContext.CGLContextObj, self.pixelFormat.CGLPixelFormatObj);
// Activate the display link
CVDisplayLinkStart(m_displayLink);
m_mouseState.m_x = 0.0f;
m_mouseState.m_y = 0.0f;
m_mouseState.m_buttons = 0;
m_mutex.unlock();
}
// This is the renderer output callback function
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime,
CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
OpenGLView *view = (__bridge OpenGLView*)displayLinkContext;
view->m_mutex.lock();
[view.openGLContext makeCurrentContext];
view->m_context.update();
CGLLockContext(view.openGLContext.CGLContextObj); // This is needed because this isn't running on the main thread.
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
view->m_context.draw(); // Draw the scene. This doesn't need to be in the drawRect method.
glFlush();
CGLUnlockContext(view.openGLContext.CGLContextObj);
//CGLFlushDrawable(view.openGLContext.CGLContextObj); // This does glFlush() for you.
view->m_mutex.unlock();
return kCVReturnSuccess;
}
- (void)dealloc
{
// TODO: figure out why this doesn't appear to be getting called
m_mutex.lock();
// Release the display link
CVDisplayLinkRelease(m_displayLink);
m_context.onClose();
m_mutex.unlock();
}
- (void)reshape
{
vec2f shape{ float(self.frame.size.width), float(self.frame.size.height) };
m_context.onResize(shape);
}
- (void)mouseUp:(NSEvent *)event
{
if (event.type == NSEventTypeLeftMouseUp)
m_mouseState.m_buttons &= ~LeftButton;
if (event.type == NSEventTypeOtherMouseUp)
m_mouseState.m_buttons &= ~MiddleButton;
if (event.type == NSEventTypeRightMouseUp)
m_mouseState.m_buttons &= ~RightButton;
NSPoint locationInView = [self convertPoint:[event locationInWindow] fromView:nil];
m_mouseState.m_x = (locationInView.x - self.frame.origin.x) / self.frame.size.width;
m_mouseState.m_y = (locationInView.y - self.frame.origin.y) / self.frame.size.height;
m_context.onMouseUp(m_mouseState);
}
- (void)mouseDown:(NSEvent *)event
{
if (event.type == NSEventTypeLeftMouseDown)
m_mouseState.m_buttons |= LeftButton;
if (event.type == NSEventTypeOtherMouseDown)
m_mouseState.m_buttons |= MiddleButton;
if (event.type == NSEventTypeRightMouseDown)
m_mouseState.m_buttons |= RightButton;
NSPoint locationInView = [self convertPoint:[event locationInWindow] fromView:nil];
m_mouseState.m_x = (locationInView.x - self.frame.origin.x) / self.frame.size.width;
m_mouseState.m_y = (locationInView.y - self.frame.origin.y) / self.frame.size.height;
m_context.onMouseDown(m_mouseState);
}
- (void)mouseMoved:(NSEvent *)event
{
NSPoint locationInView = [self convertPoint:[event locationInWindow] fromView:nil];
m_mouseState.m_x = (locationInView.x - self.frame.origin.x) / self.frame.size.width;
m_mouseState.m_y = (locationInView.y - self.frame.origin.y) / self.frame.size.height;
m_context.onMouseMove(m_mouseState);
}
- (void)setDemo:(const char *)demoStr
{
m_mutex.lock();
[self.openGLContext makeCurrentContext];
CGLLockContext(self.openGLContext.CGLContextObj); // This is needed because this isn't running on the main thread.
m_context.setDemo(demoStr);
vec2f shape{ float(self.frame.size.width), float(self.frame.size.height) };
m_context.onResize(shape);
CGLUnlockContext(self.openGLContext.CGLContextObj);
m_mutex.unlock();
}
@end
@interface MenuButton : NSBox
@end
@implementation MenuButton
- (void)mouseDown:(NSEvent *)event
{
[((OpenGLView*)self.superview.superview.subviews[1].subviews[0]) setDemo:((NSTextField*)self.subviews[0].subviews[0]).cell.title.UTF8String];
}
@end