lwjgl/src/native/macosx/org_lwjgl_opengl_MacOSXCanv...

338 lines
12 KiB
Objective-C

/*
* Copyright (c) 2002-2008 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* $Id$
*
* @author elias_naur <elias_naur@users.sourceforge.net>
* @author Pelle Johnsen
* @author kappaOne <one.kappa@gmail.com>
* @version $Revision$
*/
#import <Cocoa/Cocoa.h>
#include <jni.h>
#include <jawt_md.h>
#include "awt_tools.h"
#include "org_lwjgl_opengl_MacOSXCanvasPeerInfo.h"
#include "context.h"
#include "common_tools.h"
JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nInitHandle
(JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject peer_info_handle, jobject window_handle, jboolean forceCALayer, jboolean autoResizable, jint x, jint y) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
AWTSurfaceLock *surface = (AWTSurfaceLock *)(*env)->GetDirectBufferAddress(env, lock_buffer_handle);
JAWT_MacOSXDrawingSurfaceInfo *macosx_dsi = (JAWT_MacOSXDrawingSurfaceInfo *)surface->dsi->platformInfo;
// force CALayer usage or check if CALayer is supported (i.e. on Java 5 and Java 6)
if(forceCALayer || (surface->awt.version & 0x80000000)) { //JAWT_MACOSX_USE_CALAYER) {
if (macosx_dsi != NULL) {
if (window_handle == NULL) {
window_handle = newJavaManagedByteBuffer(env, sizeof(MacOSXWindowInfo));
if (window_handle == NULL) {
throwException(env, "Could not create handle buffer");
}
} else if (peer_info->window_info->window != nil) {
return window_handle;
}
if (peer_info->isCALayer) {
[peer_info->glLayer release];
}
peer_info->glLayer = [GLLayer new];
peer_info->glLayer->macosx_dsi = macosx_dsi;
peer_info->window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle);
peer_info->glLayer->window_info = peer_info->window_info;
peer_info->glLayer->autoResizable = autoResizable;
/* we set bounds as requested w/ frame function */
peer_info->glLayer.frame = CGRectMake(x, y, surface->dsi->bounds.width, surface->dsi->bounds.height);
[peer_info->glLayer performSelectorOnMainThread:@selector(createWindow:) withObject:peer_info->pixel_format waitUntilDone:YES];
peer_info->isCALayer = true;
peer_info->isWindowed = true;
peer_info->parent = nil;
[pool release];
return window_handle;
}
}
// no CALayer support, fallback to using legacy method of getting the NSView of an AWT Canvas
peer_info->parent = macosx_dsi->cocoaViewRef;
peer_info->isCALayer = false;
peer_info->isWindowed = true;
[pool release];
return NULL;
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nSetLayerPosition
(JNIEnv *env, jclass clazz, jobject peer_info_handle, jint x, jint y) {
MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
if (peer_info->glLayer != nil) {
NSPoint point = NSMakePoint(x, y);
NSValue *value = [NSValue valueWithPoint:point];
[peer_info->glLayer performSelectorOnMainThread:@selector(updatePosition:) withObject:value waitUntilDone:NO];
}
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nSetLayerBounds
(JNIEnv *env, jclass clazz, jobject peer_info_handle, jint x, jint y, jint width, jint height) {
MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
if (peer_info->glLayer != nil) {
NSRect rect = NSMakeRect(x, y, width, height);
NSValue *value = [NSValue valueWithRect:rect];
[peer_info->glLayer performSelectorOnMainThread:@selector(updateBounds:) withObject:value waitUntilDone:NO];
}
}
@implementation GLLayer
- (void) attachLayer {
self.asynchronous = YES;
self.needsDisplayOnBoundsChange = YES;
self.opaque = NO;
if (autoResizable) {
self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
}
else {
self.autoresizingMask = kCALayerNotSizable;
}
// get root layer of the AWT Canvas and add self to it
id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)macosx_dsi;
if (surfaceLayers.layer != self) {
surfaceLayers.layer = self;
}
}
- (void) removeLayer {
// clean up resources
glDeleteFramebuffersEXT(1, &fboID);
glDeleteRenderbuffersEXT(1, &imageRenderBufferID);
glDeleteRenderbuffersEXT(1, &depthRenderBufferID);
// finish any pending blits before destroying the offscreen window to prevent crashes
glFinish();
// destroy offscreen Display window
[self destroyWindow];
// remove self from root layer
[self removeFromSuperlayer];
}
- (void)updatePosition:(NSValue*)value {
NSPoint point = [value pointValue];
self.position = CGPointMake(point.x, point.y);
}
- (void)updateBounds:(NSValue*)value {
NSRect rect = [value rectValue];
self.frame = CGRectMake(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height);
}
- (int) getWidth {
return canvasBounds.width;
}
- (int) getHeight {
return canvasBounds.height;
}
- (void) createWindow:(NSOpenGLPixelFormat*)pixel_format {
if (window_info->window != nil) {
[window_info->window close];
}
window_info->display_rect = [[NSScreen mainScreen] frame];
window_info->view = [[MacOSXOpenGLView alloc] initWithFrame:window_info->display_rect pixelFormat:pixel_format];
window_info->window = [[MacOSXKeyableWindow alloc] initWithContentRect:window_info->display_rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[window_info->window setContentView:window_info->view];
[window_info->window orderOut:nil];
}
- (void) destroyWindow {
if (window_info->window != nil) {
[window_info->view removeFromSuperviewWithoutNeedingDisplay];
[window_info->window close];
window_info->window = nil;
}
}
- (void) blitFrameBuffer {
// get the size of the CALayer/AWT Canvas
int width = self.bounds.size.width;
int height = self.bounds.size.height;
if (width != fboWidth || height != fboHeight) {
// store current fbo/renderbuffers for later deletion
int oldFboID = fboID;
int oldImageRenderBufferID = imageRenderBufferID;
int oldDepthRenderBufferID = depthRenderBufferID;
// create new fbo
int tempFBO;
glGenFramebuffersEXT(1, &tempFBO);
// create new render buffers
glGenRenderbuffersEXT(1, &imageRenderBufferID);
glGenRenderbuffersEXT(1, &depthRenderBufferID);
// switch to new fbo to attach render buffers
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tempFBO);
// initialize and attach image render buffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, imageRenderBufferID);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, imageRenderBufferID);
// initialize and attach depth render buffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderBufferID);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthRenderBufferID);
// clear garbage background on new fbo
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// blit frameBuffer to the new fbo
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, tempFBO);
glBlitFramebufferEXT(0, 0, width, height,
0, 0, width, height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
glFinish(); // finish before using new fbo and resizing the window
// set new fbo and its sizes
fboID = tempFBO;
fboWidth = width;
fboHeight = height;
// set the size of the offscreen frame buffer window
window_info->display_rect = NSMakeRect(0, 0, width, height);
// clean up the old fbo and renderBuffers
glDeleteFramebuffersEXT(1, &oldFboID);
glDeleteRenderbuffersEXT(1, &oldImageRenderBufferID);
glDeleteRenderbuffersEXT(1, &oldDepthRenderBufferID);
}
else {
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboID);
glBlitFramebufferEXT(0, 0, width, height,
0, 0, width, height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
}
// restore default framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
-(void)drawInCGLContext:(CGLContextObj)glContext
pixelFormat:(CGLPixelFormatObj)pixelFormat
forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp *)timeStamp {
// set the current context
CGLSetCurrentContext(glContext);
// get the size of the CALayer/AWT Canvas
int width = self.bounds.size.width;
int height = self.bounds.size.height;
if (width != fboWidth || height != fboHeight) {
// clear garbage background before lwjgl fbo blit
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
// read the LWJGL FBO and blit it into this CALayers FBO
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fboID);
glBlitFramebufferEXT(0, 0, width, height,
0, 0, width, height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
// call super to finalize the drawing - by default all it does is call glFlush()
[super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp];
}
-(BOOL)canDrawInCGLContext:(CGLContextObj)glContext
pixelFormat:(CGLPixelFormatObj)pixelFormat
forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp *)timeStamp {
return YES;
}
- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
CGLCreateContext(pixelFormat, [window_info->context CGLContextObj], &contextObject);
return contextObject;
}
- (void)releaseCGLContext:(CGLContextObj)glContext {
CGLClearDrawable(contextObject);
// disable releasing context due to nvidia crash bug when releasing shared contexts
//CGLDestroyContext(contextObject);
}
- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
return CGLGetPixelFormat([window_info->context CGLContextObj]);
}
- (void)releaseCGLPixelFormat:(CGLPixelFormatObj)pixelFormat {
}
@end