Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / nativewindow / native / macosx / OSXmisc.m
1 /**
2  * Copyright 2011 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  * 
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  * 
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  * 
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  * 
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28  
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <unistd.h>
34 #include <AppKit/AppKit.h>
35 #import <QuartzCore/QuartzCore.h>
36 #import "NativeWindowProtocols.h"
37
38 #include "NativewindowCommon.h"
39 #include "jogamp_nativewindow_macosx_OSXUtil.h"
40 #include "jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow.h"
41
42 #include <jawt_md.h>
43
44 // #define VERBOSE 1
45 //
46 #ifdef VERBOSE
47     // #define DBG_PRINT(...) NSLog(@ ## __VA_ARGS__)
48     #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
49 #else
50     #define DBG_PRINT(...)
51 #endif
52
53 // #define VERBOSE2 1
54 //
55 #ifdef VERBOSE2
56     #define DBG_PRINT2(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
57 #else
58     #define DBG_PRINT2(...)
59 #endif
60
61 // #define DBG_LIFECYCLE 1
62
63 static const char * const ClazzNameRunnable = "java/lang/Runnable";
64 static jmethodID runnableRunID = NULL;
65
66 static const char * const ClazzAnyCstrName = "<init>";
67
68 static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point";
69 static const char * const ClazzNamePointCstrSignature = "(II)V";
70 static jclass pointClz = NULL;
71 static jmethodID pointCstr = NULL;
72
73 static const char * const ClazzNameInsets = "javax/media/nativewindow/util/Insets";
74 static const char * const ClazzNameInsetsCstrSignature = "(IIII)V";
75 static jclass insetsClz = NULL;
76 static jmethodID insetsCstr = NULL;
77
78 JNIEXPORT jboolean JNICALL 
79 Java_jogamp_nativewindow_macosx_OSXUtil_initIDs0(JNIEnv *env, jclass _unused) {
80     if( NativewindowCommon_init(env) ) {
81         jclass c;
82         c = (*env)->FindClass(env, ClazzNamePoint);
83         if(NULL==c) {
84             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't find %s", ClazzNamePoint);
85         }
86         pointClz = (jclass)(*env)->NewGlobalRef(env, c);
87         (*env)->DeleteLocalRef(env, c);
88         if(NULL==pointClz) {
89             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't use %s", ClazzNamePoint);
90         }
91         pointCstr = (*env)->GetMethodID(env, pointClz, ClazzAnyCstrName, ClazzNamePointCstrSignature);
92         if(NULL==pointCstr) {
93             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't fetch %s.%s %s",
94                 ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature);
95         }
96
97         c = (*env)->FindClass(env, ClazzNameInsets);
98         if(NULL==c) {
99             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't find %s", ClazzNameInsets);
100         }
101         insetsClz = (jclass)(*env)->NewGlobalRef(env, c);
102         (*env)->DeleteLocalRef(env, c);
103         if(NULL==insetsClz) {
104             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't use %s", ClazzNameInsets);
105         }
106         insetsCstr = (*env)->GetMethodID(env, insetsClz, ClazzAnyCstrName, ClazzNameInsetsCstrSignature);
107         if(NULL==insetsCstr) {
108             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't fetch %s.%s %s",
109                 ClazzNameInsets, ClazzAnyCstrName, ClazzNameInsetsCstrSignature);
110         }
111
112         c = (*env)->FindClass(env, ClazzNameRunnable);
113         if(NULL==c) {
114             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't find %s", ClazzNameRunnable);
115         }
116         runnableRunID = (*env)->GetMethodID(env, c, "run", "()V");
117         if(NULL==runnableRunID) {
118             NativewindowCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_MacWindow_initIDs0: can't fetch %s.run()V", ClazzNameRunnable);
119         }
120     }
121     return JNI_TRUE;
122 }
123
124 JNIEXPORT jboolean JNICALL 
125 Java_jogamp_nativewindow_macosx_OSXUtil_isNSView0(JNIEnv *env, jclass _unused, jlong object) {
126     NSObject *nsObj = (NSObject*) (intptr_t) object;
127     jboolean u = [nsObj isKindOfClass:[NSView class]];
128     DBG_PRINT( "isNSView(obj: %p): %s -> %d\n", nsObj, [[nsObj description] UTF8String], u);
129     return u;
130 }
131
132 JNIEXPORT jboolean JNICALL 
133 Java_jogamp_nativewindow_macosx_OSXUtil_isNSWindow0(JNIEnv *env, jclass _unused, jlong object) {
134     NSObject *nsObj = (NSObject*) (intptr_t) object;
135     jboolean u = [nsObj isKindOfClass:[NSWindow class]];
136     DBG_PRINT( "isNSWindow(obj: %p): %s -> %d\n", nsObj, [[nsObj description] UTF8String], u);
137     return u;
138 }
139
140 static CGDirectDisplayID OSXUtil_getCGDirectDisplayIDByNSScreen(NSScreen *screen) {
141     // Mind: typedef uint32_t CGDirectDisplayID; - however, we assume it's 64bit on 64bit ?!
142     NSDictionary * dict = [screen deviceDescription];
143     NSNumber * val = (NSNumber *) [dict objectForKey: @"NSScreenNumber"];
144     // [NSNumber integerValue] returns NSInteger which is 32 or 64 bit native size
145     return (CGDirectDisplayID) [val integerValue];
146 }
147
148 /*
149  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
150  * Method:    getLocationOnScreen0
151  * Signature: (JII)Ljavax/media/nativewindow/util/Point;
152  */
153 JNIEXPORT jobject JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetLocationOnScreen0
154   (JNIEnv *env, jclass unused, jlong winOrView, jint src_x, jint src_y)
155 {
156     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
157
158     /**
159      * return location in 0/0 top-left space,
160      * OSX is 0/0 bottom-left space naturally
161      */
162     NSObject *nsObj = (NSObject*) (intptr_t) winOrView;
163     NSWindow* win = NULL;
164     NSView* view = NULL;
165
166     if( [nsObj isKindOfClass:[NSWindow class]] ) {
167         win = (NSWindow*) nsObj;
168         view = [win contentView];
169     } else if( nsObj != NULL && [nsObj isKindOfClass:[NSView class]] ) {
170         view = (NSView*) nsObj;
171         win = [view window];
172     } else {
173         NativewindowCommon_throwNewRuntimeException(env, "neither win nor view %p\n", nsObj);
174     }
175     NSRect viewFrame = [view frame];
176
177     NSRect r;
178     r.origin.x = src_x;
179     r.origin.y = viewFrame.size.height - src_y; // y-flip for 0/0 top-left
180     r.size.width = 0;
181     r.size.height = 0;
182     // NSRect rS = [win convertRectToScreen: r]; // 10.7
183     NSPoint oS = [win convertBaseToScreen: r.origin]; // BL-screen
184
185     NSScreen* screen = [win screen];
186     CGDirectDisplayID display = OSXUtil_getCGDirectDisplayIDByNSScreen(screen);
187     CGRect frameTL = CGDisplayBounds (display); // origin top-left
188     NSRect frameBL = [screen frame]; // origin bottom-left
189     oS.y = frameTL.origin.y + frameTL.size.height - ( oS.y - frameBL.origin.y ); // y-flip from BL-screen -> TL-screen
190
191 #ifdef VERBOSE
192     NSRect winFrame = [win frame];
193     DBG_PRINT( "GetLocationOnScreen0(window: %p):: point-in[%d/%d], winFrame[%d/%d %dx%d], viewFrame[%d/%d %dx%d], screen tl[%d/%d %dx%d] bl[%d/%d %dx%d] -> %d/%d\n",
194         win, (int)src_x, (int)src_y,
195         (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height,
196         (int)viewFrame.origin.x, (int)viewFrame.origin.y, (int)viewFrame.size.width, (int)viewFrame.size.height,
197         (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
198         (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
199         (int)oS.x, (int)oS.y);
200 #endif
201
202     jobject res = (*env)->NewObject(env, pointClz, pointCstr, (jint)oS.x, (jint)oS.y);
203
204     [pool release];
205
206     return res;
207 }
208
209 /*
210  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
211  * Method:    getInsets0
212  * Signature: (J)Ljavax/media/nativewindow/util/Insets;
213  */
214 JNIEXPORT jobject JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetInsets0
215   (JNIEnv *env, jclass unused, jlong winOrView)
216 {
217     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
218
219     NSObject *nsObj = (NSObject*) (intptr_t) winOrView;
220     NSWindow* win = NULL;
221     NSView* view = NULL;
222     jint il,ir,it,ib;
223
224     if( [nsObj isKindOfClass:[NSWindow class]] ) {
225         win = (NSWindow*) nsObj;
226         view = [win contentView];
227     } else if( nsObj != NULL && [nsObj isKindOfClass:[NSView class]] ) {
228         view = (NSView*) nsObj;
229         win = [view window];
230     } else {
231         NativewindowCommon_throwNewRuntimeException(env, "neither win nor view %p\n", nsObj);
232     }
233
234     NSRect frameRect = [win frame];
235     NSRect contentRect = [win contentRectForFrameRect: frameRect];
236
237     // note: this is a simplistic implementation which doesn't take
238     // into account DPI and scaling factor
239     CGFloat l = contentRect.origin.x - frameRect.origin.x;
240     il = (jint)l;                                                     // l
241     ir = (jint)(frameRect.size.width - (contentRect.size.width + l)); // r
242     it = (jint)(frameRect.size.height - contentRect.size.height);     // t
243     ib = (jint)(contentRect.origin.y - frameRect.origin.y);           // b
244
245     jobject res = (*env)->NewObject(env, insetsClz, insetsCstr, il, ir, it, ib);
246
247     [pool release];
248
249     return res;
250 }
251
252 /*
253  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
254  * Method:    GetPixelScale0
255  * Signature: (I)D
256  */
257 JNIEXPORT jdouble JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetPixelScale0
258   (JNIEnv *env, jclass unused, jint screen_idx)
259 {
260     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
261
262     CGFloat pixelScale;
263     NSScreen *screen;
264     NSArray *screens = [NSScreen screens];
265     if( screen_idx<0 || screen_idx>=[screens count] ) {
266         screen = NULL;
267         pixelScale = 0.0;
268     } else {
269         screen = (NSScreen *) [screens objectAtIndex: screen_idx];
270         pixelScale = 1.0; // default
271 NS_DURING
272         // Available >= 10.7
273         pixelScale = [screen backingScaleFactor]; // HiDPI scaling
274 NS_HANDLER
275 NS_ENDHANDLER
276     }
277     [pool release];
278
279     return (jdouble)pixelScale;
280 }
281
282 /*
283  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
284  * Method:    GetPixelScale1
285  * Signature: (J)D
286  */
287 JNIEXPORT jdouble JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetPixelScale1
288   (JNIEnv *env, jclass unused, jlong winOrView)
289 {
290     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
291
292     NSObject *nsObj = (NSObject*) (intptr_t) winOrView;
293     NSWindow* win = NULL;
294     NSView* view = NULL;
295     NSScreen *screen = NULL;
296
297     if( [nsObj isKindOfClass:[NSWindow class]] ) {
298         win = (NSWindow*) nsObj;
299         view = [win contentView];
300         screen = [win screen];
301     } else if( nsObj != NULL && [nsObj isKindOfClass:[NSView class]] ) {
302         view = (NSView*) nsObj;
303         win = [view window];
304         screen = [win screen];
305     } else {
306         NativewindowCommon_throwNewRuntimeException(env, "neither win nor view %p\n", nsObj);
307     }
308
309     CGFloat pixelScale = 1.0; // default
310 NS_DURING
311     // Available >= 10.7
312     pixelScale = [screen backingScaleFactor]; // HiDPI scaling
313 NS_HANDLER
314 NS_ENDHANDLER
315
316     [pool release];
317
318     return (jdouble)pixelScale;
319 }
320
321 /*
322  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
323  * Method:    CreateNSWindow0
324  * Signature: (IIIIZ)J
325  */
326 JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateNSWindow0
327   (JNIEnv *env, jclass unused, jint x, jint y, jint width, jint height)
328 {
329     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
330     NSRect rect = NSMakeRect(x, y, width, height);
331
332     // Allocate the window
333     NSWindow* myWindow = [[NSWindow alloc] initWithContentRect: rect
334                                            styleMask: NSBorderlessWindowMask
335                                            backing: NSBackingStoreBuffered
336                                            defer: YES];
337     [myWindow setReleasedWhenClosed: YES]; // default
338     [myWindow setPreservesContentDuringLiveResize: YES];
339     // Remove animations
340 NS_DURING
341     if ( [myWindow respondsToSelector:@selector(setAnimationBehavior:)] ) {
342         // Available >= 10.7 - Removes default animations
343         [myWindow setAnimationBehavior: NSWindowAnimationBehaviorNone];
344     }
345 NS_HANDLER
346 NS_ENDHANDLER
347
348     // invisible ..
349     [myWindow setOpaque: NO];
350     [myWindow setBackgroundColor: [NSColor clearColor]];
351
352     [pool release];
353
354     return (jlong) ((intptr_t) myWindow);
355 }
356
357 /*
358  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
359  * Method:    DestroyNSWindow0
360  * Signature: (J)V
361  */
362 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_DestroyNSWindow0
363   (JNIEnv *env, jclass unused, jlong nsWindow)
364 {
365     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
366     NSWindow* mWin = (NSWindow*) ((intptr_t) nsWindow);
367
368     [mWin close]; // performs release!
369     [pool release];
370 }
371
372 /*
373  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
374  * Method:    GetNSView0
375  * Signature: (J)J
376  */
377 JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetNSView0
378   (JNIEnv *env, jclass unused, jlong window)
379 {
380     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
381     NSWindow* win = (NSWindow*) ((intptr_t) window);
382
383     jlong res = (jlong) ((intptr_t) [win contentView]);
384
385     DBG_PRINT( "GetNSView(window: %p): %p\n", win, (void*) (intptr_t) res);
386
387     [pool release];
388     return res;
389 }
390
391 /*
392  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
393  * Method:    GetNSWindow0
394  * Signature: (J)J
395  */
396 JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetNSWindow0
397   (JNIEnv *env, jclass unused, jlong view)
398 {
399     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
400     NSView* v = (NSView*) ((intptr_t) view);
401
402     jlong res = (jlong) ((intptr_t) [v window]);
403
404     DBG_PRINT( "GetNSWindow(view: %p): %p\n", v, (void*) (intptr_t) res);
405
406     [pool release];
407     return res;
408 }
409
410 /**
411  * Track lifecycle via DBG_PRINT messages, if VERBOSE is enabled!
412  */
413 @interface MyCALayer: CALayer
414 {
415 @private
416   BOOL fixedFrameSet;
417   CGRect fixedFrame;
418   float visibleOpacity;
419   BOOL visibleOpacityZeroed;
420 }
421 - (id)init;
422 #ifdef DBG_LIFECYCLE
423 - (id)retain;
424 - (oneway void)release;
425 - (void)dealloc;
426 #endif
427 - (id<CAAction>)actionForKey:(NSString *)key ;
428 - (void)layoutSublayers;
429 - (void)setFrame:(CGRect) frame;
430 - (void)fixCALayerLayout: (CALayer*) subLayer visible:(BOOL)visible x:(jint)x y:(jint)y width:(jint)width height:(jint)height caLayerQuirks:(jint)caLayerQuirks force:(jboolean) force;
431
432 @end
433
434 @implementation MyCALayer
435
436 - (id)init
437 {
438     DBG_PRINT("MyCALayer::init.0\n");
439     MyCALayer * o = [super init];
440     o->fixedFrameSet = 0;
441     o->fixedFrame = CGRectMake(0, 0, 0, 0);
442     o->visibleOpacity = 1.0;
443     o->visibleOpacityZeroed = 0;
444     DBG_PRINT("MyCALayer::init.X: new %p\n", o);
445     return o;
446 }
447
448 #ifdef DBG_LIFECYCLE
449
450 - (id)retain
451 {
452     DBG_PRINT("MyCALayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
453     // NSLog(@"MyCALayer::retain: %@",[NSThread callStackSymbols]);
454     id o = [super retain];
455     DBG_PRINT("MyCALayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
456     return o;
457 }
458
459 - (oneway void)release
460 {
461     DBG_PRINT("MyCALayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
462     // NSLog(@"MyCALayer::release: %@",[NSThread callStackSymbols]);
463     [super release];
464     // DBG_PRINT("MyCALayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
465 }
466
467 - (void)dealloc
468 {
469     DBG_PRINT("MyCALayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
470     // NSLog(@"MyCALayer::dealloc: %@",[NSThread callStackSymbols]);
471     [super dealloc];
472     // DBG_PRINT("MyCALayer.dealloc.X: %p\n", self);
473 }
474
475 #endif
476
477 - (id<CAAction>)actionForKey:(NSString *)key 
478 {
479     DBG_PRINT("MyCALayer::actionForKey.0 %p key %s -> NIL\n", self, [key UTF8String]);
480     return nil;
481     // return [super actionForKey: key];
482 }
483
484 - (void)layoutSublayers
485 {
486     if( fixedFrameSet ) {
487         NSArray* subs = [self sublayers];
488         if( NULL != subs ) {
489             CGRect rFrame = [self frame];
490             if( !CGRectEqualToRect(fixedFrame, rFrame) ) {
491                 #ifdef VERBOSE
492                 DBG_PRINT("CALayer::layoutSublayers.0: Root %p frame %lf/%lf %lfx%lf -> %lf/%lf %lfx%lf\n",
493                     self,
494                     rFrame.origin.x, rFrame.origin.y, rFrame.size.width, rFrame.size.height,
495                     fixedFrame.origin.x, fixedFrame.origin.y, fixedFrame.size.width, fixedFrame.size.height);
496                 #endif
497                 [super setFrame: fixedFrame];
498             }
499             NSUInteger i = 0;
500             for(i=0; i<[subs count]; i++) {
501                 CALayer* sub = [subs objectAtIndex: i];
502                 CGRect sFrame = [sub frame];
503                 CGRect sFrame2 = CGRectMake(0, 0, fixedFrame.size.width, fixedFrame.size.height);
504                 if( !CGRectEqualToRect(sFrame2, sFrame) ) {
505                     #ifdef VERBOSE
506                     DBG_PRINT("CALayer::layoutSublayers.1: Sub[%d] %p frame %lf/%lf %lfx%lf -> %lf/%lf %lfx%lf\n",
507                         (int)i, sub,
508                         sFrame.origin.x, sFrame.origin.y, sFrame.size.width, sFrame.size.height,
509                         sFrame2.origin.x, sFrame2.origin.y, sFrame2.size.width, sFrame2.size.height);
510                     #endif
511                     [sub setFrame: sFrame2];
512                 }
513                 #ifdef VERBOSE
514                 DBG_PRINT("CALayer::layoutSublayers.X: Root %p . Sub[%d] %p : frame r: %lf/%lf %lfx%lf . s: %lf/%lf %lfx%lf\n",
515                     self, (int)i, sub,
516                     rFrame.origin.x, rFrame.origin.y, rFrame.size.width, rFrame.size.height,
517                     sFrame.origin.x, sFrame.origin.y, sFrame.size.width, sFrame.size.height);
518                 #endif
519             }
520         }
521     } else {
522         [super layoutSublayers];
523     }
524 }
525
526 - (void) setFrame:(CGRect) frame
527 {
528     if( fixedFrameSet ) {
529         [super setFrame: fixedFrame];
530     } else {
531         [super setFrame: frame];
532     }
533 }
534
535 - (void)fixCALayerLayout: (CALayer*) subLayer visible:(BOOL)visible x:(jint)x y:(jint)y width:(jint)width height:(jint)height caLayerQuirks:(jint)caLayerQuirks force:(jboolean) force
536 {
537     int loutQuirk = 0 != ( NW_DEDICATEDFRAME_QUIRK_LAYOUT & caLayerQuirks );
538     {
539         CALayer* superLayer = [self superlayer];
540         CGRect superFrame = [superLayer frame];
541         CGRect lFrame = [self frame];
542         if( visible ) {
543             // Opacity must be 0 to see through the disabled CALayer
544             [subLayer setOpacity: visibleOpacity];
545             [self setOpacity: visibleOpacity];
546             [self setHidden: NO];
547             [subLayer setHidden: NO];
548             visibleOpacityZeroed = 0;
549         } else {
550             [subLayer setHidden: YES];
551             [self setHidden: YES];
552             if( !visibleOpacityZeroed ) {
553                 visibleOpacity = [self opacity];
554             }
555             [subLayer setOpacity: 0.0];
556             [self setOpacity: 0.0];
557             visibleOpacityZeroed = 1;
558         }
559         int posQuirk  = 0 != ( NW_DEDICATEDFRAME_QUIRK_POSITION & caLayerQuirks ) && ( lFrame.origin.x!=0 || lFrame.origin.y!=0 );
560         int sizeQuirk = 0 != ( NW_DEDICATEDFRAME_QUIRK_SIZE & caLayerQuirks ) && ( lFrame.size.width!=width || lFrame.size.height!=height );
561         if( !posQuirk || loutQuirk ) {
562             // Use root layer position, sub-layer will be on 0/0,
563             // Given AWT position is location on screen w/o insets and top/left origin!
564             fixedFrame.origin.x = x;
565             fixedFrame.origin.y = superFrame.size.height - height - y; // AWT's position top/left -> bottom/left
566             posQuirk |= 8;
567         } else {
568             // Buggy super layer position, always use 0/0
569             fixedFrame.origin.x = 0;
570             fixedFrame.origin.y = 0;
571         }
572         if( !sizeQuirk ) {
573             fixedFrame.size.width = lFrame.size.width;
574             fixedFrame.size.height = lFrame.size.height;
575         } else {
576             fixedFrame.size.width = width;
577             fixedFrame.size.height = height;
578         }
579         DBG_PRINT("CALayer::FixCALayerLayout0.0: Visible %d, Quirks [%d, pos %d, size %d, lout %d, force %d], Super %p frame %lf/%lf %lfx%lf, Root %p frame %lf/%lf %lfx%lf, usr %d/%d %dx%d -> %lf/%lf %lfx%lf\n",
580             (int)visible, caLayerQuirks, posQuirk, sizeQuirk, loutQuirk, (int)force,
581             superLayer, superFrame.origin.x, superFrame.origin.y, superFrame.size.width, superFrame.size.height, 
582             self, lFrame.origin.x, lFrame.origin.y, lFrame.size.width, lFrame.size.height, 
583             x, y, width, height, fixedFrame.origin.x, fixedFrame.origin.y, fixedFrame.size.width, fixedFrame.size.height);
584         if( posQuirk || sizeQuirk || loutQuirk ) {
585             fixedFrameSet = 1;
586             [super setFrame: fixedFrame];
587         }
588     }
589     if( NULL != subLayer ) {
590         CGRect lFrame = [subLayer frame];
591         int sizeQuirk = 0 != ( NW_DEDICATEDFRAME_QUIRK_SIZE & caLayerQuirks ) && ( lFrame.size.width!=width || lFrame.size.height!=height );
592         CGFloat _x, _y, _w, _h;
593         int posQuirk  = 0 != ( NW_DEDICATEDFRAME_QUIRK_POSITION & caLayerQuirks ) && ( lFrame.origin.x!=0 || lFrame.origin.y!=0 );
594         // Sub rel. to used root layer
595         _x = 0;
596         _y = 0;
597         posQuirk |= 8;
598         if( !sizeQuirk ) {
599             _w = lFrame.size.width;
600             _h = lFrame.size.height;
601         } else {
602             _w = width;
603             _h = height;
604         }
605         DBG_PRINT("CALayer::FixCALayerLayout1.0: Visible %d, Quirks [%d, pos %d, size %d, lout %d, force %d], SubL %p frame %lf/%lf %lfx%lf, usr %dx%d -> %lf/%lf %lfx%lf\n",
606             (int)visible, caLayerQuirks, posQuirk, sizeQuirk, loutQuirk, (int)force,
607             subLayer, lFrame.origin.x, lFrame.origin.y, lFrame.size.width, lFrame.size.height,
608             width, height, _x, _y, _w, _h);
609         if( force || posQuirk || sizeQuirk || loutQuirk ) {
610             lFrame.origin.x = _x;
611             lFrame.origin.y = _y;
612             lFrame.size.width = _w;
613             lFrame.size.height = _h;
614             if( [subLayer conformsToProtocol:@protocol(NWDedicatedFrame)] ) {
615                 CALayer <NWDedicatedFrame> * subLayerDS = (CALayer <NWDedicatedFrame> *) subLayer;
616                 [subLayerDS setDedicatedFrame: lFrame quirks: caLayerQuirks];
617             } else {
618                 [subLayer setFrame: lFrame];
619             }
620         }
621     }
622 }
623
624 @end
625
626 /*
627  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
628  * Method:    CreateCALayer0
629  * Signature: (IIF)J
630  */
631 JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0
632   (JNIEnv *env, jclass unused, jint width, jint height, jfloat contentsScale)
633 {
634     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
635
636     MyCALayer* layer = [[MyCALayer alloc] init];
637     DBG_PRINT("CALayer::CreateCALayer.0: root %p 0/0 %dx%d @ scale %lf (refcnt %d)\n", layer, (int)width, (int)height, (double)contentsScale, (int)[layer retainCount]);
638     // avoid zero size
639     if(0 == width) { width = 32; }
640     if(0 == height) { height = 32; }
641
642 NS_DURING
643     // Available >= 10.7
644     [layer setContentsScale: (CGFloat)contentsScale];
645 NS_HANDLER
646 NS_ENDHANDLER
647
648     // initial dummy size !
649     CGRect lFrame = [layer frame];
650     lFrame.origin.x = 0;
651     lFrame.origin.y = 0;
652     lFrame.size.width = width;
653     lFrame.size.height = height;
654     [layer setFrame: lFrame];
655     // no animations for add/remove/swap sublayers etc 
656     // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition]
657     [layer removeAllAnimations];
658     // [layer addAnimation:nil forKey:kCATransition];
659     [layer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
660     [layer setNeedsDisplayOnBoundsChange: YES];
661     DBG_PRINT("CALayer::CreateCALayer.1: root %p %lf/%lf %lfx%lf\n", layer, lFrame.origin.x, lFrame.origin.y, lFrame.size.width, lFrame.size.height);
662     [pool release];
663     DBG_PRINT("CALayer::CreateCALayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]);
664
665     return (jlong) ((intptr_t) layer);
666 }
667
668 /*
669  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
670  * Method:    AddCASublayer0
671  * Signature: (JJIIIIIF)V
672  */
673 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0
674   (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jint x, jint y, jint width, jint height, jfloat contentsScale, jint caLayerQuirks)
675 {
676     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
677     MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer);
678     CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer);
679
680     [CATransaction begin];
681     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
682
683     [rootLayer retain]; // Pairs w/ RemoveCASublayer
684     [subLayer retain]; // Pairs w/ RemoveCASublayer
685
686     CGRect lRectRoot = [rootLayer frame];
687
688     // Available >= 10.7
689     DBG_PRINT("CALayer::AddCASublayer0.0: Quirks %d, Root %p (refcnt %d), Sub %p (refcnt %d), frame0: %lf/%lf %lfx%lf scale %lf\n",
690         caLayerQuirks, rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount],
691         lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (float)contentsScale);
692
693 NS_DURING
694     [subLayer setContentsScale: (CGFloat)contentsScale];
695 NS_HANDLER
696 NS_ENDHANDLER
697
698     [subLayer setFrame:lRectRoot];
699     [rootLayer addSublayer:subLayer];
700
701     // no animations for add/remove/swap sublayers etc 
702     // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition]
703     [rootLayer removeAllAnimations];
704     // [rootLayer addAnimation:nil forKey:kCATransition]; // JAU
705     [rootLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
706     [rootLayer setNeedsDisplayOnBoundsChange: YES];
707     [subLayer removeAllAnimations];
708     // [subLayer addAnimation:nil forKey:kCATransition]; // JAU
709     [subLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
710     [subLayer setNeedsDisplayOnBoundsChange: YES];
711
712     if( 0 != caLayerQuirks ) {
713         [rootLayer fixCALayerLayout: subLayer visible:1 x:x y:y width:width height:height caLayerQuirks:caLayerQuirks force:JNI_TRUE];
714     }
715
716     [CATransaction commit];
717
718     [pool release];
719     DBG_PRINT("CALayer::AddCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", 
720         rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]);
721 }
722
723 /*
724  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
725  * Method:    FixCALayerLayout0
726  * Signature: (JJIII)V
727  */
728 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_FixCALayerLayout0
729   (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jboolean visible, jint x, jint y, jint width, jint height, jint caLayerQuirks)
730 {
731     if( 0 != caLayerQuirks ) {
732         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
733         MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer);
734         if( NULL == rootLayer ) {
735             NativewindowCommon_throwNewRuntimeException(env, "Argument \"rootLayer\" is null");
736         }
737         CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer);
738
739         [CATransaction begin];
740         [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
741
742         [rootLayer fixCALayerLayout: subLayer visible:(BOOL)visible x:x y:y width:width height:height caLayerQuirks:caLayerQuirks force:JNI_FALSE];
743
744         [CATransaction commit];
745
746         [pool release];
747     }
748 }
749
750 /*
751  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
752  * Method:    SetCALayerPixelScale0
753  * Signature: (JJF)V
754  */
755 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_SetCALayerPixelScale0
756   (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jfloat contentsScale)
757 {
758     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
759     MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer);
760     if( NULL == rootLayer ) {
761         NativewindowCommon_throwNewRuntimeException(env, "Argument \"rootLayer\" is null");
762     }
763     CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer);
764
765     [CATransaction begin];
766     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
767
768 NS_DURING
769     [rootLayer setContentsScale: (CGFloat)contentsScale];
770     if( NULL != subLayer ) {
771         [subLayer setContentsScale: (CGFloat)contentsScale];
772     }
773 NS_HANDLER
774 NS_ENDHANDLER
775
776     [CATransaction commit];
777
778     [pool release];
779 }
780
781 /*
782  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
783  * Method:    RemoveCASublayer0
784  * Signature: (JJ)V
785  */
786 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RemoveCASublayer0
787   (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer)
788 {
789     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
790     MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer);
791     CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer);
792
793     (void)rootLayer; // no warnings
794
795     DBG_PRINT("CALayer::RemoveCASublayer0.0: root %p (refcnt %d) .sub %p (refcnt %d)\n", 
796         rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]);
797
798     [CATransaction begin];
799     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
800
801     [subLayer removeFromSuperlayer];
802     [subLayer release]; // Pairs w/ AddCASublayer
803     [rootLayer release]; // Pairs w/ AddCASublayer
804
805     [CATransaction commit];
806
807     [pool release];
808     DBG_PRINT("CALayer::RemoveCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", 
809         rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]);
810 }
811
812 /*
813  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
814  * Method:    DestroyCALayer0
815  * Signature: (J)V
816  */
817 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_DestroyCALayer0
818   (JNIEnv *env, jclass unused, jlong caLayer)
819 {
820     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
821     MyCALayer* layer = (MyCALayer*) ((intptr_t) caLayer);
822
823     [CATransaction begin];
824     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
825
826     DBG_PRINT("CALayer::DestroyCALayer0.0: root %p (refcnt %d)\n", layer, (int)[layer retainCount]);
827     [layer release]; // Trigger release and dealloc of root CALayer, it's child etc ..
828
829     [CATransaction commit];
830
831     [pool release];
832     DBG_PRINT("CALayer::DestroyCALayer0.X: root %p\n", layer);
833 }
834
835 /*
836  * Class:     Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow
837  * Method:    GetJAWTSurfaceLayersHandle0
838  * Signature: (J)J
839  */
840 JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_GetJAWTSurfaceLayersHandle0
841   (JNIEnv *env, jclass unused, jobject jawtDrawingSurfaceInfoBuffer)
842 {
843     JAWT_DrawingSurfaceInfo* dsi = (JAWT_DrawingSurfaceInfo*) (*env)->GetDirectBufferAddress(env, jawtDrawingSurfaceInfoBuffer);
844     if (NULL == dsi) {
845         NativewindowCommon_throwNewRuntimeException(env, "Argument \"jawtDrawingSurfaceInfoBuffer\" was not a direct buffer");
846         return 0;
847     }
848     id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)dsi->platformInfo;
849     return (jlong) ((intptr_t) surfaceLayers);
850 }
851
852 /*
853  * Class:     Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow
854  * Method:    SetJAWTRootSurfaceLayer0
855  * Signature: (JJ)V
856  */
857 JNIEXPORT void JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_SetJAWTRootSurfaceLayer0
858   (JNIEnv *env, jclass unused, jlong jawtSurfaceLayersHandle, jlong caLayer)
859 {
860     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
861
862     [CATransaction begin];
863     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
864
865     id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)(intptr_t)jawtSurfaceLayersHandle;
866     MyCALayer* layer = (MyCALayer*) (intptr_t) caLayer;
867     DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.0: pre %p -> root %p (refcnt %d)\n", [surfaceLayers layer], layer, (int)[layer retainCount]);
868     [surfaceLayers setLayer: [layer retain]]; // Pairs w/ Unset
869
870     [CATransaction commit];
871
872     [pool release];
873     DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]);
874 }
875
876 /*
877  * Class:     Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow
878  * Method:    UnsetJAWTRootSurfaceLayer0
879  * Signature: (JJ)V
880  */
881 JNIEXPORT void JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_UnsetJAWTRootSurfaceLayer0
882   (JNIEnv *env, jclass unused, jlong jawtSurfaceLayersHandle, jlong caLayer)
883 {
884     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
885
886     [CATransaction begin];
887     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
888
889     id <JAWT_SurfaceLayers> surfaceLayers = (id <JAWT_SurfaceLayers>)(intptr_t)jawtSurfaceLayersHandle;
890     MyCALayer* layer = (MyCALayer*) (intptr_t) caLayer;
891     if(layer != [surfaceLayers layer]) {
892         NativewindowCommon_throwNewRuntimeException(env, "Attached layer %p doesn't match given layer %p\n", surfaceLayers.layer, layer);
893         return;
894     }
895     DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.0: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]);
896     [layer release]; // Pairs w/ Set
897     [surfaceLayers setLayer: NULL];
898
899     [CATransaction commit];
900
901     [pool release];
902     DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.X: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]);
903 }
904
905 @interface MainRunnable : NSObject
906
907 {
908     jobject runnableObj;
909 }
910
911 - (id) initWithRunnable: (jobject)runnable;
912 - (void) jRun;
913
914 #ifdef DBG_LIFECYCLE
915 - (id)retain;
916 - (oneway void)release;
917 - (void)dealloc;
918 #endif
919
920
921 @end
922
923 @implementation MainRunnable
924
925 - (id) initWithRunnable: (jobject)runnable
926 {
927     runnableObj = runnable;
928     return [super init];
929 }
930
931 - (void) jRun
932 {
933     int shallBeDetached = 0;
934     JNIEnv* env = NativewindowCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
935     DBG_PRINT2("MainRunnable.1 env: %d\n", (int)(NULL!=env));
936     if(NULL!=env) {
937         DBG_PRINT2("MainRunnable.1.0\n");
938         (*env)->CallVoidMethod(env, runnableObj, runnableRunID);
939         DBG_PRINT2("MainRunnable.1.1\n");
940         (*env)->DeleteGlobalRef(env, runnableObj);
941
942         DBG_PRINT2("MainRunnable.1.3\n");
943         // detaching thread not required - daemon
944         // NativewindowCommon_ReleaseJNIEnv(shallBeDetached);
945     }
946     DBG_PRINT2("MainRunnable.X\n");
947 }
948
949 #ifdef DBG_LIFECYCLE
950
951 - (id)retain
952 {
953     DBG_PRINT2("MainRunnable::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
954     id o = [super retain];
955     DBG_PRINT2("MainRunnable::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
956     return o;
957 }
958
959 - (oneway void)release
960 {
961     DBG_PRINT2("MainRunnable::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
962     [super release];
963     // DBG_PRINT2("MainRunnable::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
964 }
965
966 - (void)dealloc
967 {
968     DBG_PRINT2("MainRunnable::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
969     [super dealloc];
970     // DBG_PRINT2("MainRunnable.dealloc.X: %p\n", self);
971 }
972
973 #endif
974
975 @end
976
977 static void RunOnThread (JNIEnv *env, jobject runnable, BOOL onMain, jint delayInMS)
978 {
979     BOOL isMainThread = [NSThread isMainThread];
980     BOOL forkOnMain = onMain && ( NO == isMainThread || 0 < delayInMS );
981
982     DBG_PRINT2( "RunOnThread0: forkOnMain %d [onMain %d, delay %dms, isMainThread %d], NSApp %d, NSApp-isRunning %d\n", 
983         (int)forkOnMain, (int)onMain, (int)delayInMS, (int)isMainThread, (int)(NULL!=NSApp), (int)([NSApp isRunning]));
984
985     if ( forkOnMain ) {
986         jobject runnableObj = (*env)->NewGlobalRef(env, runnable);
987
988         DBG_PRINT2( "RunOnThread.1.0\n");
989         MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableObj];
990
991         if( onMain ) {
992             [mr performSelectorOnMainThread:@selector(jRun) withObject:nil waitUntilDone:NO];
993         } else {
994             NSTimeInterval delay = (double)delayInMS/1000.0;
995             [mr performSelector:@selector(jRun) withObject:nil afterDelay:delay];
996         }
997         DBG_PRINT2( "RunOnThread.1.1\n");
998
999         [mr release];
1000         DBG_PRINT2( "RunOnThread.1.2\n");
1001
1002     } else {
1003         DBG_PRINT2( "RunOnThread.2\n");
1004         (*env)->CallVoidMethod(env, runnable, runnableRunID);
1005     }
1006     DBG_PRINT2( "RunOnThread.X\n");
1007 }
1008
1009 /*
1010  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
1011  * Method:    RunOnMainThread0
1012  * Signature: (ZLjava/lang/Runnable;)V
1013  */
1014 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0
1015   (JNIEnv *env, jclass unused, jobject runnable)
1016 {
1017     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1018     RunOnThread (env, runnable, YES, 0);
1019     [pool release];
1020 }
1021
1022 /*
1023  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
1024  * Method:    RunLater0
1025  * Signature: (ZLjava/lang/Runnable;I)V
1026  */
1027 JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunLater0
1028   (JNIEnv *env, jclass unused, jboolean onMain, jobject runnable, jint delay)
1029 {
1030     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1031     RunOnThread (env, runnable, onMain ? YES : NO, delay);
1032     [pool release];
1033 }
1034
1035 /*
1036  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
1037  * Method:    IsMainThread0
1038  * Signature: (V)Z
1039  */
1040 JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_IsMainThread0
1041   (JNIEnv *env, jclass unused)
1042 {
1043     return ( [NSThread isMainThread] == YES ) ? JNI_TRUE : JNI_FALSE ;
1044 }
1045
1046 /***
1047  * The following static functions are copied out of NEWT's OSX impl. <src/newt/native/MacWindow.m>
1048  * May need to push code to NativeWindow, to remove duplication.
1049  */
1050 static NSScreen * NewtScreen_getNSScreenByIndex(int screen_idx) {
1051     NSArray *screens = [NSScreen screens];
1052     if(screen_idx<0) screen_idx=0;
1053     if(screen_idx>=[screens count]) screen_idx=0;
1054     return (NSScreen *) [screens objectAtIndex: screen_idx];
1055 }
1056 static CGDirectDisplayID NewtScreen_getCGDirectDisplayIDByNSScreen(NSScreen *screen) {
1057     // Mind: typedef uint32_t CGDirectDisplayID; - however, we assume it's 64bit on 64bit ?!
1058     NSDictionary * dict = [screen deviceDescription];
1059     NSNumber * val = (NSNumber *) [dict objectForKey: @"NSScreenNumber"];
1060     // [NSNumber integerValue] returns NSInteger which is 32 or 64 bit native size
1061     return (CGDirectDisplayID) [val integerValue];
1062 }
1063 static long GetDictionaryLong(CFDictionaryRef theDict, const void* key) 
1064 {
1065     long value = 0;
1066     CFNumberRef numRef;
1067     numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key); 
1068     if (numRef != NULL)
1069         CFNumberGetValue(numRef, kCFNumberLongType, &value);    
1070     return value;
1071 }
1072 #define CGDDGetModeRefreshRate(mode) GetDictionaryLong((mode), kCGDisplayRefreshRate)
1073
1074 /*
1075  * Class:     Java_jogamp_nativewindow_macosx_OSXUtil
1076  * Method:    GetScreenRefreshRate
1077  * Signature: (I)I
1078  */
1079 JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshRate0
1080   (JNIEnv *env, jclass unused, jint scrn_idx)
1081 {
1082     int res = 0;
1083     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1084     NSScreen *screen = NewtScreen_getNSScreenByIndex((int)scrn_idx);
1085     DBG_PRINT("GetScreenRefreshRate.0: screen %p\n", (void *)screen);
1086     if(NULL != screen) {
1087         CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
1088         DBG_PRINT("GetScreenRefreshRate.1: display %p\n", (void *)(intptr_t)display);
1089         if(0 != display) {
1090             CFDictionaryRef mode = CGDisplayCurrentMode(display);
1091             DBG_PRINT("GetScreenRefreshRate.2: mode %p\n", (void *)mode);
1092             if(NULL != mode) {
1093                 res = CGDDGetModeRefreshRate(mode);
1094                 DBG_PRINT("GetScreenRefreshRate.3: res %d\n", res);
1095             }
1096         }
1097     }
1098     if(0 == res) {
1099         res = 60; // default .. (experienced on OSX 10.6.8)
1100     }
1101     DBG_PRINT("GetScreenRefreshRate.X: %d\n", (int)res);
1102     [pool release];
1103     return res;
1104 }
1105
http://JogAmp.org git info: FAQ, tutorial and man pages.