1/*
2 * Copyright (c) 2002-2008 LWJGL Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'LWJGL' nor the names of
17 *   its contributors may be used to endorse or promote products derived
18 *   from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/**
34 * $Id$
35 *
36 * @author elias_naur <elias_naur@users.sourceforge.net>
37 * @author Pelle Johnsen
38 * @author kappaOne <one.kappa@gmail.com>
39 * @version $Revision$
40 */
41
42#import <Cocoa/Cocoa.h>
43#include <jni.h>
44#include <jawt_md.h>
45#include "awt_tools.h"
46#include "org_lwjgl_opengl_MacOSXCanvasPeerInfo.h"
47#include "context.h"
48#include "common_tools.h"
49
50JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nInitHandle
51(JNIEnv *env, jclass clazz, jobject lock_buffer_handle, jobject peer_info_handle, jobject window_handle, jboolean forceCALayer, jboolean autoResizable, jint x, jint y) {
52	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
53
54	MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
55	AWTSurfaceLock *surface = (AWTSurfaceLock *)(*env)->GetDirectBufferAddress(env, lock_buffer_handle);
56	JAWT_MacOSXDrawingSurfaceInfo *macosx_dsi = (JAWT_MacOSXDrawingSurfaceInfo *)surface->dsi->platformInfo;
57
58	// force CALayer usage or check if CALayer is supported (i.e. on Java 5 and Java 6)
59	if(forceCALayer || (surface->awt.version & 0x80000000)) { //JAWT_MACOSX_USE_CALAYER) {
60
61		if (macosx_dsi != NULL) {
62
63			if (window_handle == NULL) {
64				window_handle = newJavaManagedByteBuffer(env, sizeof(MacOSXWindowInfo));
65				if (window_handle == NULL) {
66					throwException(env, "Could not create handle buffer");
67				}
68			} else if (peer_info->window_info->window != nil) {
69				return window_handle;
70			}
71
72			if (peer_info->isCALayer) {
73				[peer_info->glLayer release];
74			}
75
76			peer_info->glLayer = [GLLayer new];
77
78			peer_info->glLayer->macosx_dsi = macosx_dsi;
79			peer_info->window_info = (MacOSXWindowInfo *)(*env)->GetDirectBufferAddress(env, window_handle);
80			peer_info->glLayer->window_info = peer_info->window_info;
81			peer_info->glLayer->autoResizable = autoResizable;
82
83			/* we set bounds as requested w/ frame function */
84                        peer_info->glLayer.frame = CGRectMake(x, y, surface->dsi->bounds.width, surface->dsi->bounds.height);
85
86			[peer_info->glLayer performSelectorOnMainThread:@selector(createWindow:) withObject:peer_info->pixel_format waitUntilDone:YES];
87
88			peer_info->isCALayer = true;
89			peer_info->isWindowed = true;
90			peer_info->parent = nil;
91
92			[pool release];
93			return window_handle;
94		}
95	}
96
97	// no CALayer support, fallback to using legacy method of getting the NSView of an AWT Canvas
98	peer_info->parent = macosx_dsi->cocoaViewRef;
99	peer_info->isCALayer = false;
100	peer_info->isWindowed = true;
101
102	[pool release];
103	return NULL;
104}
105
106JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nSetLayerPosition
107(JNIEnv *env, jclass clazz, jobject peer_info_handle, jint x, jint y) {
108	MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
109
110	if (peer_info->glLayer != nil) {
111		NSPoint point = NSMakePoint(x, y);
112		NSValue *value = [NSValue valueWithPoint:point];
113		[peer_info->glLayer performSelectorOnMainThread:@selector(updatePosition:) withObject:value waitUntilDone:NO];
114	}
115}
116
117JNIEXPORT void JNICALL Java_org_lwjgl_opengl_MacOSXCanvasPeerInfo_nSetLayerBounds
118(JNIEnv *env, jclass clazz, jobject peer_info_handle, jint x, jint y, jint width, jint height) {
119	MacOSXPeerInfo *peer_info = (MacOSXPeerInfo *)(*env)->GetDirectBufferAddress(env, peer_info_handle);
120
121	if (peer_info->glLayer != nil) {
122		NSRect rect = NSMakeRect(x, y, width, height);
123		NSValue *value = [NSValue valueWithRect:rect];
124		[peer_info->glLayer performSelectorOnMainThread:@selector(updateBounds:) withObject:value waitUntilDone:NO];
125	}
126}
127
128@implementation GLLayer
129
130- (void) attachLayer {
131	self.asynchronous = YES;
132	self.needsDisplayOnBoundsChange = YES;
133	self.opaque = NO;
134	if (autoResizable) {
135		self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
136	}
137	else {
138		self.autoresizingMask = kCALayerNotSizable;
139	}
140
141	// get root layer of the AWT Canvas and add self to it
142	id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)macosx_dsi;
143
144	if (surfaceLayers.layer != self) {
145		surfaceLayers.layer = self;
146	}
147}
148
149- (void) removeLayer {
150
151    // clean up resources
152    glDeleteFramebuffersEXT(1, &fboID);
153    glDeleteRenderbuffersEXT(1, &imageRenderBufferID);
154    glDeleteRenderbuffersEXT(1, &depthRenderBufferID);
155
156	// finish any pending blits before destroying the offscreen window to prevent crashes
157	glFinish();
158
159    // destroy offscreen Display window
160    [self destroyWindow];
161
162	// remove self from root layer
163	[self removeFromSuperlayer];
164}
165
166- (void)updatePosition:(NSValue*)value {
167	NSPoint point = [value pointValue];
168    self.position = CGPointMake(point.x, point.y);
169}
170
171- (void)updateBounds:(NSValue*)value {
172	NSRect rect = [value rectValue];
173	self.frame = CGRectMake(rect.origin.x, rect.origin.y,
174                            rect.size.width, rect.size.height);
175}
176
177- (int) getWidth {
178	return canvasBounds.width;
179}
180
181- (int) getHeight {
182	return canvasBounds.height;
183}
184
185- (void) createWindow:(NSOpenGLPixelFormat*)pixel_format {
186	if (window_info->window != nil) {
187		[window_info->window close];
188	}
189
190	window_info->display_rect = [[NSScreen mainScreen] frame];
191
192	window_info->view = [[MacOSXOpenGLView alloc] initWithFrame:window_info->display_rect pixelFormat:pixel_format];
193
194	window_info->window = [[MacOSXKeyableWindow alloc] initWithContentRect:window_info->display_rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
195	[window_info->window setContentView:window_info->view];
196
197	[window_info->window orderOut:nil];
198}
199
200- (void) destroyWindow {
201	if (window_info->window != nil) {
202		[window_info->view removeFromSuperviewWithoutNeedingDisplay];
203		[window_info->window close];
204		window_info->window = nil;
205    }
206}
207
208- (void) blitFrameBuffer {
209
210	// get the size of the CALayer/AWT Canvas
211	int width = self.bounds.size.width;
212	int height = self.bounds.size.height;
213
214	if (width != fboWidth || height != fboHeight) {
215
216		// store current fbo/renderbuffers for later deletion
217		int oldFboID = fboID;
218		int oldImageRenderBufferID = imageRenderBufferID;
219		int oldDepthRenderBufferID = depthRenderBufferID;
220
221		// create new fbo
222		int tempFBO;
223		glGenFramebuffersEXT(1, &tempFBO);
224
225		// create new render buffers
226		glGenRenderbuffersEXT(1, &imageRenderBufferID);
227		glGenRenderbuffersEXT(1, &depthRenderBufferID);
228
229		// switch to new fbo to attach render buffers
230		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tempFBO);
231
232		// initialize and attach image render buffer
233		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, imageRenderBufferID);
234		glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, width, height);
235		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, imageRenderBufferID);
236
237		// initialize and attach depth render buffer
238		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderBufferID);
239		glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
240		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthRenderBufferID);
241
242		// clear garbage background on new fbo
243		glClearColor(0.0, 0.0, 0.0, 1.0);
244		glClear(GL_COLOR_BUFFER_BIT);
245
246		// blit frameBuffer to the new fbo
247		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
248		glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, tempFBO);
249		glBlitFramebufferEXT(0, 0, width, height,
250							 0, 0, width, height,
251							 GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
252							 GL_NEAREST);
253
254		glFinish(); // finish before using new fbo and resizing the window
255
256		// set new fbo and its sizes
257		fboID = tempFBO;
258		fboWidth = width;
259		fboHeight = height;
260
261		// set the size of the offscreen frame buffer window
262		window_info->display_rect = NSMakeRect(0, 0, width, height);
263
264		// clean up the old fbo and renderBuffers
265		glDeleteFramebuffersEXT(1, &oldFboID);
266		glDeleteRenderbuffersEXT(1, &oldImageRenderBufferID);
267		glDeleteRenderbuffersEXT(1, &oldDepthRenderBufferID);
268	}
269	else {
270		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
271		glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboID);
272
273		glBlitFramebufferEXT(0, 0, width, height,
274							 0, 0, width, height,
275							 GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
276							 GL_NEAREST);
277	}
278
279	// restore default framebuffer
280	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
281}
282
283-(void)drawInCGLContext:(CGLContextObj)glContext
284						pixelFormat:(CGLPixelFormatObj)pixelFormat
285						forLayerTime:(CFTimeInterval)timeInterval
286						displayTime:(const CVTimeStamp *)timeStamp {
287
288	// set the current context
289	CGLSetCurrentContext(glContext);
290
291	// get the size of the CALayer/AWT Canvas
292	int width = self.bounds.size.width;
293	int height = self.bounds.size.height;
294
295	if (width != fboWidth || height != fboHeight) {
296		// clear garbage background before lwjgl fbo blit
297		glClearColor(0.0, 0.0, 0.0, 1.0);
298		glClear(GL_COLOR_BUFFER_BIT);
299	}
300
301	// read the LWJGL FBO and blit it into this CALayers FBO
302	glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fboID);
303	glBlitFramebufferEXT(0, 0, width, height,
304						 0, 0, width, height,
305						 GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
306						 GL_NEAREST);
307
308	// call super to finalize the drawing - by default all it does is call glFlush()
309	[super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp];
310}
311
312-(BOOL)canDrawInCGLContext:(CGLContextObj)glContext
313							pixelFormat:(CGLPixelFormatObj)pixelFormat
314							forLayerTime:(CFTimeInterval)timeInterval
315							displayTime:(const CVTimeStamp *)timeStamp {
316	return YES;
317}
318
319- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
320	CGLCreateContext(pixelFormat, [window_info->context CGLContextObj], &contextObject);
321	return contextObject;
322}
323
324- (void)releaseCGLContext:(CGLContextObj)glContext {
325	CGLClearDrawable(contextObject);
326    // disable releasing context due to nvidia crash bug when releasing shared contexts
327    //CGLDestroyContext(contextObject);
328}
329
330- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
331	return CGLGetPixelFormat([window_info->context CGLContextObj]);
332}
333
334- (void)releaseCGLPixelFormat:(CGLPixelFormatObj)pixelFormat {
335
336}
337
338@end