/*
 * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
 * Copyright (c) 2011 JogAmp Community. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * - Redistribution 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 Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 */

#import <inttypes.h>

#import "jogamp_newt_driver_macosx_WindowDriver.h"
#import "MacNewtNSWindow.h"

#import "MouseEvent.h"
#import "KeyEvent.h"
#import "ScreenMode.h"

#import <QuartzCore/QuartzCore.h>
#import <ApplicationServices/ApplicationServices.h>

#import <stdio.h>

#ifdef DBG_PERF
    #include "timespec.h"
#endif

static const char * const ClazzNamePoint = "com/jogamp/nativewindow/util/Point";
static const char * const ClazzAnyCstrName = "<init>";
static const char * const ClazzNamePointCstrSignature = "(II)V";
static jclass pointClz = NULL;
static jmethodID pointCstr = NULL;

static NSString* jstringToNSString(JNIEnv* env, jstring jstr)
{
    const jchar* jstrChars = (*env)->GetStringChars(env, jstr, NULL);
    NSString* str = [[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstr)];
    (*env)->ReleaseStringChars(env, jstr, jstrChars);
    return str;
}

static void setWindowClientTopLeftPoint(NewtNSWindow* mWin, jint x, jint y, BOOL doDisplay) {
    DBG_PRINT( "setWindowClientTopLeftPoint.0 - window: %p %d/%d, display %d\n", mWin, (int)x, (int)y, (int)doDisplay);
    NSPoint pS = [mWin newtTLScreenPos2BLScreenPos: NSMakePoint(x, y)];
    DBG_PRINT( "setWindowClientTopLeftPoint.1: window: (parent %p) %p visible %d: %d/%d\n", 
        [mWin parentWindow], mWin, [mWin isVisible], (int)pS.x, (int)pS.y);

    [mWin setFrameOrigin: pS];

    DBG_PRINT( "setWindowClientTopLeftPoint.X: window: (parent %p) %p visible %d: %d/%d\n", 
        [mWin parentWindow], mWin, [mWin isVisible], (int)pS.x, (int)pS.y);

    if( doDisplay ) {
        NSView* mView = [mWin contentView];
        [mWin invalidateCursorRectsForView: mView];
    }
}

static void setWindowClientTopLeftPointAndSize(NewtNSWindow* mWin, jint x, jint y, jint width, jint height, BOOL doDisplay) {
    DBG_PRINT( "setWindowClientTopLeftPointAndSize.0 - window: %p %d/%d %dx%d, display %d\n", mWin, (int)x, (int)y, (int)width, (int)height, (int)doDisplay);
    NSSize clientSZ = NSMakeSize(width, height);
    NSPoint pS = [mWin newtTLScreenPos2BLScreenPos: NSMakePoint(x, y) size: clientSZ];
    NSSize topSZ = [mWin newtClientSize2TLSize: clientSZ];
    NSRect rect = { pS, topSZ };

    DBG_PRINT( "setWindowClientTopLeftPointAndSize.1: window: (parent %p) %p visible %d: %d/%d %dx%d\n", 
        [mWin parentWindow], mWin, [mWin isVisible], 
        (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height);

    [mWin setFrame: rect display:doDisplay];

    DBG_PRINT( "setWindowClientTopLeftPointAndSize.X: window: (parent %p) %p visible %d: %d/%d %dx%d\n", 
        [mWin parentWindow], mWin, [mWin isVisible], 
        (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height);

    // -> display:YES
    // if( doDisplay ) {
    //   NSView* mView = [mWin contentView];
    //   [mWin invalidateCursorRectsForView: mView];
    // }
}

#ifdef VERBOSE_ON
static int getRetainCount(NSObject * obj) {
    return ( NULL == obj ) ? -1 : (int)([obj retainCount]) ;
}
#endif

static void setJavaWindowObject(JNIEnv *env, jobject newJavaWindowObject, NewtNSView *view) {
    DBG_PRINT( "setJavaWindowObject.0: View %p\n", view);
    if( NULL == newJavaWindowObject ) {
        // disable
        jobject globJavaWindowObject = [view getJavaWindowObject];
        if( NULL != globJavaWindowObject ) {
            DBG_PRINT( "setJavaWindowObject.1: View %p - Clear old javaWindowObject %p\n", view, globJavaWindowObject);
            (*env)->DeleteGlobalRef(env, globJavaWindowObject);
            [view setJavaWindowObject: NULL];
        }
    } else {
        // enable
        DBG_PRINT( "setJavaWindowObject.2: View %p - Set new javaWindowObject %p\n", view, newJavaWindowObject);
        jobject globJavaWindowObject = (*env)->NewGlobalRef(env, newJavaWindowObject);
        [view setJavaWindowObject: globJavaWindowObject];
    }
    DBG_PRINT( "setJavaWindowObject.X: View %p\n", view);
}

static void changeContentView(JNIEnv *env, jobject javaWindowObject, NSView *pview, NewtNSWindow *win, NewtNSView *newView, BOOL setJavaWindow) {
    NSView* oldNSView = [win contentView];
    NewtNSView* oldNewtNSView = NULL;
#ifdef VERBOSE_ON
    int dbgIdx = 1;
#endif
    if( [oldNSView isKindOfClass:[NewtNSView class]] ) {
        oldNewtNSView = (NewtNSView *) oldNSView;
    }
    DBG_PRINT( "changeContentView.%d win %p, view (%p,%d (%d) -> %p,%d), parent view %p\n", 
        dbgIdx++, win, oldNSView, getRetainCount(oldNSView), NULL!=oldNewtNSView, newView, getRetainCount(newView), pview);

    if( NULL!=oldNSView ) {
NS_DURING
        // Available >= 10.5 - Makes the menubar disapear
        BOOL iifs;
        if ( [oldNSView respondsToSelector:@selector(isInFullScreenMode)] ) {
            iifs = [oldNSView isInFullScreenMode];
        } else {
            iifs = NO;
        }
        if(iifs && [oldNSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)] ) {
            [oldNSView exitFullScreenModeWithOptions: NULL];
        }
NS_HANDLER
NS_ENDHANDLER
        DBG_PRINT( "changeContentView.%d win %p, view (%p,%d (%d) -> %p,%d)\n", 
            dbgIdx++, win, oldNSView, getRetainCount(oldNSView), NULL!=oldNewtNSView, newView, getRetainCount(newView));

        if( NULL != oldNewtNSView ) {
            [oldNewtNSView setDestroyNotifySent: false];
            setJavaWindowObject(env, NULL, oldNewtNSView);
        }
        [oldNSView removeFromSuperviewWithoutNeedingDisplay];
    }
    DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", 
        dbgIdx++, win, oldNSView, getRetainCount(oldNSView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]);

    if( NULL!=newView ) {
        [newView setDestroyNotifySent: false];
        if( setJavaWindow ) {
            setJavaWindowObject(env, javaWindowObject, newView);
        }
        DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d)\n", 
            dbgIdx++, win, oldNSView, getRetainCount(oldNSView), newView, getRetainCount(newView));

        if(NULL!=pview) {
            [pview addSubview: newView positioned: NSWindowAbove relativeTo: nil];
        }
    }
    DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", 
        dbgIdx++, win, oldNSView, getRetainCount(oldNSView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]);

    [win setContentView: newView];

    DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", 
        dbgIdx++, win, oldNSView, getRetainCount(oldNSView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]);

    // make sure the insets are updated in the java object
    [win updateInsets: env jwin:javaWindowObject];

    DBG_PRINT( "changeContentView.X win %p, view (%p,%d -> %p,%d)\n", 
        win, oldNSView, getRetainCount(oldNSView), newView, getRetainCount(newView));
}

/*
 * Class:     jogamp_newt_driver_macosx_DisplayDriver
 * Method:    initIDs
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_initNSApplication0
  (JNIEnv *env, jclass clazz)
{
    static int initialized = 0;

    if(initialized) return JNI_TRUE;
    initialized = 1;

    NewtCommon_init(env);

    // This little bit of magic is needed in order to receive mouse
    // motion events and allow key focus to be properly transferred.
    // FIXME: are these Carbon APIs? They come from the
    // ApplicationServices.framework.
    ProcessSerialNumber psn;
    if (GetCurrentProcess(&psn) == noErr) {
        TransformProcessType(&psn, kProcessTransformToForegroundApplication);
        SetFrontProcess(&psn);
    }

    // Initialize the shared NSApplication instance
    [NSApplication sharedApplication];

    // Need this when debugging, as it is necessary to attach gdb to
    // the running java process -- "gdb java" doesn't work
    //    printf("Going to sleep for 10 seconds\n");
    //    sleep(10);

    return (jboolean) JNI_TRUE;
}

/*
 * Class:     jogamp_newt_driver_macosx_DisplayDriver
 * Method:    runNSApplication0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_runNSApplication0
  (JNIEnv *env, jclass clazz)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    DBG_PRINT( "\nrunNSApplication0.0\n");

    [NSApp run];

    DBG_PRINT( "\nrunNSApplication0.X\n");
    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_DisplayDriver
 * Method:    stopNSApplication0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_stopNSApplication0
  (JNIEnv *env, jclass clazz)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    DBG_PRINT( "\nstopNSApplication0.0 nsApp.running %d\n", (NSApp && [NSApp isRunning]));

    if(NSApp && [NSApp isRunning]) {
        [NSApp performSelectorOnMainThread:@selector(stop:) withObject:nil waitUntilDone:YES];
        // [NSApp stop: nil];
        NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined
                                            location: NSMakePoint(0,0)
                                       modifierFlags: 0
                                           timestamp: 0.0
                                        windowNumber: 0
                                             context: nil
                                             subtype: 0
                                               data1: 0
                                               data2: 0];
        DBG_PRINT( "\nstopNSApplication0.1\n");
        [NSApp postEvent: event atStart: true];
    }
    /**
    DBG_PRINT( "\nstopNSApplication0.2\n");
    if(NSApp && [NSApp isRunning]) {
        DBG_PRINT( "\nstopNSApplication0.3\n");
        [NSApp terminate:nil];
    } */

    DBG_PRINT( "\nstopNSApplication0.X\n");
    [pool release];
}

static NSImage * createNSImageFromData(JNIEnv *env, unsigned char * iconData, jint jiconWidth, jint jiconHeight) {
    if( NULL != iconData ) {
        NSInteger iconWidth = (NSInteger) jiconWidth;
        NSInteger iconHeight = (NSInteger) jiconHeight;
        const NSInteger bpc = 8 /* bits per component */, spp=4 /* RGBA */, bpp = bpc * spp;
        const NSBitmapFormat bfmt = NSAlphaNonpremultipliedBitmapFormat;
        const BOOL hasAlpha = YES;

        NSBitmapImageRep* bir = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &iconData
                                    pixelsWide: iconWidth
                                    pixelsHigh: iconHeight
                                    bitsPerSample: bpc
                                    samplesPerPixel: spp
                                    hasAlpha: hasAlpha
                                    isPlanar: NO
                                    colorSpaceName: NSCalibratedRGBColorSpace
                                    bitmapFormat: bfmt
                                    bytesPerRow: iconWidth*4
                                    bitsPerPixel: bpp];
        [bir autorelease];
        NSImage* nsImage = [[NSImage alloc] initWithCGImage: [bir CGImage] size:NSZeroSize];
        return nsImage;
    }
    return NULL;
}

/*
 * Class:     jogamp_newt_driver_macosx_DisplayDriver
 * Method:    setAppIcon0
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_setAppIcon0
  (JNIEnv *env, jobject unused, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height)
{
    if( 0 == pixels ) {
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    // NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly!
    unsigned char * pixelPtr = (unsigned char *) ( JNI_TRUE == pixels_is_direct ? 
                                            (*env)->GetDirectBufferAddress(env, pixels) : 
                                            (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) );
    NSImage * nsImage = createNSImageFromData(env, pixelPtr + pixels_byte_offset, width, height);
    if( NULL != nsImage ) {
        [nsImage autorelease];
        [NSApp setApplicationIconImage: nsImage];
    }
    if ( JNI_FALSE == pixels_is_direct ) {
        (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT);  
    }
    [pool release];
}

JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_createPointerIcon0
  (JNIEnv *env, jobject unused, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height, jint hotX, jint hotY)
{
    if( 0 == pixels ) {
        return 0;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    unsigned char * pixelPtr = (unsigned char *) ( JNI_TRUE == pixels_is_direct ? 
                                            (*env)->GetDirectBufferAddress(env, pixels) : 
                                            (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) );
    NSImage * nsImage = createNSImageFromData(env, pixelPtr + pixels_byte_offset, width, height);
    NSCursor * res = NULL;
    if( NULL != nsImage ) {
        [nsImage autorelease];
        NSPoint hotP = { hotX, hotY };
        res = [[NSCursor alloc] initWithImage: nsImage hotSpot: hotP];
    }
    if ( JNI_FALSE == pixels_is_direct ) {
        (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT);  
    }
    [pool release];
    DBG_PRINT( "createPointerIcon0 %p\n", res);
    return (jlong) (intptr_t) res;
}

JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_destroyPointerIcon0
  (JNIEnv *env, jobject unused, jlong handle)
{
    NSCursor * c = (NSCursor*) (intptr_t) handle ;
    if( NULL != c && NO == [c isKindOfClass:[NSCursor class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NSCursor %p", c);
        return;
    }
    DBG_PRINT( "destroyPointerIcon0 %p\n", c);
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [c release];
    [pool release];
}

#if 0
static NSScreen * NewtScreen_getNSScreenByIndex(int screen_idx) {
    NSArray *screens = [NSScreen screens];
    if( screen_idx<0 || screen_idx>=[screens count] ) {
        return NULL;
    }
    return (NSScreen *) [screens objectAtIndex: screen_idx];
}
static NSScreen * NewtScreen_getNSScreenByCoord(int x, int y) {
    NSArray *screens = [NSScreen screens];
    int i;
    for(i=[screens count]-1; i>=0; i--) {
        NSScreen * screen = (NSScreen *) [screens objectAtIndex: i];
        NSRect frame = [screen frame];
        if( x >= frame.origin.x && 
            y >= frame.origin.y &&
            x <  frame.origin.x + frame.size.width &&
            y <  frame.origin.y + frame.size.height ) {
            return screen;
        }
    }
    return NULL;
}
#endif
static void NewtScreen_dump() {
#ifdef VERBOSE_ON
    NSArray *screens = [NSScreen screens];
    int i;
    for(i=0; i<[screens count]; i++) {
        NSScreen * screen = (NSScreen *) [screens objectAtIndex: i];
        NSRect screenFrame = [screen frame];
        NSRect screenVisibleFrame = [screen visibleFrame];
        CGFloat pixelScale = 1.0; // default
NS_DURING
        // Available >= 10.7
        pixelScale = [screen backingScaleFactor]; // HiDPI scaling
NS_HANDLER
NS_ENDHANDLER
        NSWindowDepth depth = [screen depth]; // an (int) value!
        DBG_PRINT( "NSScreen #%d (%p): Frame %lf/%lf %lfx%lf (vis %lf/%lf %lfx%lf), scale %lf, depth %d\n",
            i, screen,
            screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height,
            screenVisibleFrame.origin.x, screenVisibleFrame.origin.y, screenVisibleFrame.size.width, screenVisibleFrame.size.height,
            pixelScale, depth);
    }
#endif
}


CGDirectDisplayID NewtScreen_getCGDirectDisplayIDByNSScreen(NSScreen *screen) {
    if( NULL == screen ) {
        return (CGDirectDisplayID)0;
    }
    // Mind: typedef uint32_t CGDirectDisplayID;
    NSDictionary * dict = [screen deviceDescription];
    NSNumber * val = (NSNumber *) [dict objectForKey: @"NSScreenNumber"];
    // [NSNumber integerValue] returns NSInteger which is 32 or 64 bit native size
    return (CGDirectDisplayID) [val integerValue];
}
static NSScreen * NewtScreen_getNSScreenByCGDirectDisplayID(CGDirectDisplayID displayID) {
    NSArray *screens = [NSScreen screens];
    int i;
    for(i=[screens count]-1; i>=0; i--) {
        NSScreen * screen = (NSScreen *) [screens objectAtIndex: i];
        CGDirectDisplayID dID = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
        if( dID == displayID ) {
            return screen;
        }
    }
    return NULL;
}

/**
 * Only in >= 10.6:
 *   CGDisplayModeGetWidth(mode)
 *   CGDisplayModeGetRefreshRate(mode)
 *   CGDisplayModeGetHeight(mode)
 */
static long GetDictionaryLong(CFDictionaryRef theDict, const void* key) 
{
    long value = 0;
    CFNumberRef numRef;
    numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key); 
    if (numRef != NULL)
        CFNumberGetValue(numRef, kCFNumberLongType, &value);    
    return value;
}
#define CGDDGetModeWidth(mode) GetDictionaryLong((mode), kCGDisplayWidth)
#define CGDDGetModeHeight(mode) GetDictionaryLong((mode), kCGDisplayHeight)
#define CGDDGetModeRefreshRate(mode) GetDictionaryLong((mode), kCGDisplayRefreshRate)
#define CGDDGetModeBitsPerPixel(mode) GetDictionaryLong((mode), kCGDisplayBitsPerPixel)

// Duplicate each Mode by all possible rotations (4):
// For each real-mode: [mode, 0], [mode, 90], [mode, 180], [mode, 270]
#define ROTMODES_PER_REALMODE 4

/*
 * Class:     jogamp_newt_driver_macosx_ScreenDriver
 * Method:    getMonitorDeviceIds0
 * Signature: ()I
 */
JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_ScreenDriver_getMonitorDeviceIds0
  (JNIEnv *env, jobject obj)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSArray *screens = [NSScreen screens];
    int count = [screens count];
    int32_t displayIDs[count];
    int i;
    for(i=0; i<count; i++) {
        NSScreen * screen = (NSScreen *) [screens objectAtIndex: i];
        displayIDs[i] = (int32_t)NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
    }
    jintArray properties = (*env)->NewIntArray(env, count);
    if (properties == NULL) {
        NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", count);
    }
    (*env)->SetIntArrayRegion(env, properties, 0, count, displayIDs);
    [pool release];
    return properties;
}

/*
 * Class:     jogamp_newt_driver_macosx_ScreenDriver
 * Method:    getMonitorProps0
 * Signature: (I)[I
 */
JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_ScreenDriver_getMonitorProps0
  (JNIEnv *env, jobject obj, jint crt_id)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

#ifdef DBG_PERF
    struct timespec t0, t1, td;
    long td_ms;
    timespec_now(&t0);
#endif

#ifdef DBG_PERF
    timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td);
    fprintf(stderr, "MacScreen_getMonitorProps0.1: %ld ms\n", td_ms); fflush(NULL);
#endif
    CGDirectDisplayID displayID = (CGDirectDisplayID)crt_id;
    NSScreen *screen =  NewtScreen_getNSScreenByCGDirectDisplayID(displayID);
    if( NULL == screen ) {
        [pool release];
        return NULL;
    }
    CGDirectDisplayID mainDisplayID = CGMainDisplayID();
    BOOL isPrimary = mainDisplayID == displayID;
#ifdef DBG_PERF
    timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td);
    fprintf(stderr, "MacScreen_getMonitorProps0.2: %ld ms\n", td_ms); fflush(NULL);
#endif

    CGSize sizeMM = CGDisplayScreenSize(displayID);
#ifdef DBG_PERF
    timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td);
    fprintf(stderr, "MacScreen_getMonitorProps0.3: %ld ms\n", td_ms); fflush(NULL);
#endif

    CGRect dBounds = CGDisplayBounds (displayID); // origin top-left
#ifdef VERBOSE_ON
    BOOL usesGL = CGDisplayUsesOpenGLAcceleration(displayID);
    NSRect sFrame = [screen frame]; // origin bottom-left
    DBG_PRINT( "getMonitorProps0: crt_id 0x%X (prim %d), top-left displayBounds[%d/%d %dx%d], bottom-left screenFrame[%d/%d %dx%d], usesGL %d\n", 
                 (int)crt_id, isPrimary,
                 (int)dBounds.origin.x, (int)dBounds.origin.y, (int)dBounds.size.width, (int)dBounds.size.height,
                 (int)sFrame.origin.x, (int)sFrame.origin.y, (int)sFrame.size.width, (int)sFrame.size.height,
                 (int)usesGL);
#endif

    jsize propCount = MIN_MONITOR_DEVICE_PROPERTIES - 1 - NUM_MONITOR_MODE_PROPERTIES;
    jint prop[ propCount ];
    int offset = 0;
    prop[offset++] = propCount;
    prop[offset++] = crt_id;
    prop[offset++] = 0; // isClone
    prop[offset++] = isPrimary ? 1 : 0; // isPrimary
    prop[offset++] = (jint) sizeMM.width;
    prop[offset++] = (jint) sizeMM.height;
    prop[offset++] = (jint) dBounds.origin.x;    // rotated viewport x      (pixel units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.origin.y;    // rotated viewport y      (pixel units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.size.width;  // rotated viewport width  (pixel units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.size.height; // rotated viewport height (pixel units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.origin.x;    // rotated viewport x      (window units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.origin.y;    // rotated viewport y      (window units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.size.width;  // rotated viewport width  (window units, will be fixed in java code)
    prop[offset++] = (jint) dBounds.size.height; // rotated viewport height (window units, will be fixed in java code)

    jintArray properties = (*env)->NewIntArray(env, propCount);
    if (properties == NULL) {
        NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", propCount);
    }
    (*env)->SetIntArrayRegion(env, properties, 0, propCount, prop);
    
    [pool release];

    return properties;
}

/*
 * Class:     jogamp_newt_driver_macosx_ScreenDriver
 * Method:    getMonitorMode0
 * Signature: (II)[I
 */
JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_macosx_ScreenDriver_getMonitorMode0
  (JNIEnv *env, jobject obj, jint crt_id, jint mode_idx)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    CGDirectDisplayID displayID = (CGDirectDisplayID)crt_id;
    NSScreen *screen =  NewtScreen_getNSScreenByCGDirectDisplayID(displayID);
    if( NULL == screen ) {
        [pool release];
        return NULL;
    }
    CGFloat pixelScale = 1.0; // default
NS_DURING
    // Available >= 10.7
    pixelScale = [screen backingScaleFactor]; // HiDPI scaling
NS_HANDLER
NS_ENDHANDLER

    CFArrayRef availableModes = CGDisplayAvailableModes(displayID);
    CFIndex numberOfAvailableModes = CFArrayGetCount(availableModes);
    CFIndex numberOfAvailableModesRots = ROTMODES_PER_REALMODE * numberOfAvailableModes; 
    CFDictionaryRef mode = NULL;
    int currentCCWRot = (int)CGDisplayRotation(displayID);
    jint ccwRot = 0;
    int nativeId = 0;

#ifdef VERBOSE_ON
    if(0 >= mode_idx) {
        // only for current mode (-1) and first mode (scanning)
        DBG_PRINT( "getScreenMode0: crtID 0x%X (s %p, pscale %lf), mode %d, avail: %d/%d, current rot %d ccw\n",  
            (uint32_t)displayID, screen, pixelScale, (int)mode_idx, (int)numberOfAvailableModes, (int)numberOfAvailableModesRots, currentCCWRot);
    }
#endif

    if(numberOfAvailableModesRots<=mode_idx) {
        // n/a - end of modes
        DBG_PRINT( "getScreenMode0: end of modes: mode %d, avail: %d/%d\n",
            (int)mode_idx, (int)numberOfAvailableModes, (int)numberOfAvailableModesRots);
        [pool release];
        return NULL;
    } else if(-1 < mode_idx) {
        // only at initialization time, where index >= 0
        nativeId = mode_idx / ROTMODES_PER_REALMODE;
        ccwRot = mode_idx % ROTMODES_PER_REALMODE * 90;
        mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, nativeId);
    } else {
        // current mode
        mode = CGDisplayCurrentMode(displayID);
        ccwRot = currentCCWRot;
        CFRange range = CFRangeMake (0, numberOfAvailableModes);
        nativeId = CFArrayGetFirstIndexOfValue(availableModes, range, (CFDictionaryRef)mode);
    }
    // mode = CGDisplayModeRetain(mode); // 10.6 on CGDisplayModeRef

    int mWidth = CGDDGetModeWidth(mode);
    int mHeight = CGDDGetModeHeight(mode);
    if( -1 == mode_idx ) {
        mWidth *= (int)pixelScale;   // accomodate HiDPI
        mHeight *= (int)pixelScale; // accomodate HiDPI
    }

    // swap width and height, since OSX reflects rotated dimension, we don't
    if ( 90 == currentCCWRot || 270 == currentCCWRot ) {
        int tempWidth = mWidth;
        mWidth = mHeight;
        mHeight = tempWidth;
    }

    jint prop[ NUM_MONITOR_MODE_PROPERTIES_ALL ];
    int propIndex = 0;

    int refreshRate = CGDDGetModeRefreshRate(mode);
    int fRefreshRate = ( 0 < refreshRate ) ? refreshRate : 60; // default .. (experienced on OSX 10.6.8)
    prop[propIndex++] = NUM_MONITOR_MODE_PROPERTIES_ALL;
    prop[propIndex++] = mWidth;
    prop[propIndex++] = mHeight;
    prop[propIndex++] = CGDDGetModeBitsPerPixel(mode);
    prop[propIndex++] = fRefreshRate * 100; // Hz*100
    prop[propIndex++] = 0; // flags
    prop[propIndex++] = nativeId;
    prop[propIndex++] = ccwRot;
 
    DBG_PRINT( "getScreenMode0: Mode %d/%d (%d): %dx%d, %d bpp, %d / %d Hz, nativeId %d, rot %d ccw\n",
        (int)mode_idx, (int)numberOfAvailableModesRots, (int)numberOfAvailableModes, 
        (int)prop[1], (int)prop[2], (int)prop[3],
        (int)prop[4], refreshRate, (int)prop[6], (int)prop[7]);

    jintArray properties = (*env)->NewIntArray(env, NUM_MONITOR_MODE_PROPERTIES_ALL);
    if (properties == NULL) {
        NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", NUM_MONITOR_MODE_PROPERTIES_ALL);
    }
    (*env)->SetIntArrayRegion(env, properties, 0, NUM_MONITOR_MODE_PROPERTIES_ALL, prop);
    
    // CGDisplayModeRelease(mode); // 10.6 on CGDisplayModeRef
    [pool release];

    return properties;
}

/*
 * Class:     jogamp_newt_driver_macosx_ScreenDriver
 * Method:    setMonitorMode0
 * Signature: (III)Z
 */
JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_ScreenDriver_setMonitorMode0
  (JNIEnv *env, jobject object, jint crt_id, jint nativeId, jint ccwRot)
{
    jboolean res = JNI_TRUE;
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    CGDirectDisplayID displayID = (CGDirectDisplayID)crt_id;
    NSScreen *screen =  NewtScreen_getNSScreenByCGDirectDisplayID(displayID);
    if( NULL == screen ) {
        [pool release];
        return JNI_FALSE;
    }
    CFArrayRef availableModes = CGDisplayAvailableModes(displayID);
    CFIndex numberOfAvailableModes = CFArrayGetCount(availableModes);
#ifdef VERBOSE_ON
    CFIndex numberOfAvailableModesRots = ROTMODES_PER_REALMODE * numberOfAvailableModes;
#endif

    DBG_PRINT( "setScreenMode0: crtID 0x%X (%p), nativeID %d, rot %d ccw, avail: %d/%d\n",  
        (uint32_t)displayID, screen, (int)nativeId, ccwRot, (int)numberOfAvailableModes, (int)numberOfAvailableModesRots);

    CFDictionaryRef mode = NULL;

    if( 0 != ccwRot ) {
        // FIXME: How to rotate the display/screen on OSX programmatically ?
        DBG_PRINT( "setScreenMode0: Don't know how to rotate screen on OS X: rot %d ccw\n", ccwRot);
        res = JNI_FALSE;
    } else {
        if( numberOfAvailableModes <= nativeId ) {
            res = JNI_FALSE;
        } else {
            mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, nativeId);
            // mode = CGDisplayModeRetain(mode); // 10.6 on CGDisplayModeRef
        }
    }

    if( NULL != mode ) {
        CGError err = CGDisplaySwitchToMode(displayID, mode);
        if(kCGErrorSuccess != err) {
            DBG_PRINT( "setScreenMode0: SetMode failed: %d\n", (int)err);
            res = JNI_FALSE;
        }
    }

    // CGDisplayModeRelease(mode); // 10.6 on CGDisplayModeRef
    [pool release];

    return res;
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    initIDs
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0
  (JNIEnv *env, jclass clazz)
{
    static int initialized = 0;

    if(initialized) return JNI_TRUE;
    initialized = 1;

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NewtScreen_dump();

    jclass c;
    c = (*env)->FindClass(env, ClazzNamePoint);
    if(NULL==c) {
        NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0: can't find %s", ClazzNamePoint);
    }
    pointClz = (jclass)(*env)->NewGlobalRef(env, c);
    (*env)->DeleteLocalRef(env, c);
    if(NULL==pointClz) {
        NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0: can't use %s", ClazzNamePoint);
    }
    pointCstr = (*env)->GetMethodID(env, pointClz, ClazzAnyCstrName, ClazzNamePointCstrSignature);
    if(NULL==pointCstr) {
        NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0: can't fetch %s.%s %s",
            ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature);
    }

    // Need this when debugging, as it is necessary to attach gdb to
    // the running java process -- "gdb java" doesn't work
    //    printf("Going to sleep for 10 seconds\n");
    //    sleep(10);

    BOOL res =  [NewtNSWindow initNatives: env forClass: clazz];
    [pool release];

    return (jboolean) res;
}

/**
 * Class:     jogamp_newt_driver_ios_WindowDriver
 * Method:    createWindow1
 * Signature: (JJIIIIFZZZZJ)J
 */
JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow1
  (JNIEnv *env, jobject jthis, jlong joldwin, jlong jparent, jint x, jint y, jint w, jint h, jfloat reqPixelScale, 
   jboolean fullscreen, jint styleMask, jint bufferingType, 
   jboolean opaque, jboolean atop, jboolean abottom, jboolean visible, jlong jview)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    // [CATransaction begin];

    NewtNSWindow* oldWindow = (NewtNSWindow*) (intptr_t) joldwin ;
    NewtNSView* myView = (NewtNSView*) (intptr_t) jview ;

    NSObject* nsParentObj = (NSObject*) ((intptr_t) jparent);
    NSWindow* parentWindow = NULL;
    NSView* parentView = NULL;
    if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSWindow class]] ) {
        parentWindow = (NSWindow*) nsParentObj;
        parentView = [parentWindow contentView];
        DBG_PRINT( "createWindow1 - Parent is NSWindow : %p (win) -> %p (view) \n", parentWindow, parentView);
    } else if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSView class]] ) {
        parentView = (NSView*) nsParentObj;
        parentWindow = [parentView window];
        DBG_PRINT( "createWindow1 - Parent is NSView : %p -(view) > %p (win) \n", parentView, parentWindow);
    } else {
        DBG_PRINT( "createWindow1 - Parent is neither NSWindow nor NSView : %p\n", nsParentObj);
    }
    DBG_PRINT( "createWindow1.0 - %p (this), oldWin %p, parent(win %p, view %p), myView %p, %d/%d %dx%d, reqPixelScale %f, fullscreen %d, opaque %d, atop %d, abottom %d, visible %d (START)\n",
        (void*)(intptr_t)jthis, oldWindow, parentWindow, parentView, myView, (int)x, (int)y, (int)w, (int)h, (float)reqPixelScale,
        fullscreen, opaque, atop, abottom, visible);

    if( NULL != oldWindow ) {
        if( NULL == myView ) {
            NewtCommon_throwNewRuntimeException(env, "oldWindow %p given but no view %p (this %p)", oldWindow, myView, (void*)(intptr_t)jthis);
        }
        changeContentView(env, jthis, parentView, oldWindow, NULL, NO);
        Java_jogamp_newt_driver_macosx_WindowDriver_close0(env, NULL, joldwin);
    } else if( NULL != myView ) {
        NewtCommon_throwNewRuntimeException(env, "view %p given but no oldWindow %p (this %p)", oldWindow, myView, (void*)(intptr_t)jthis);
    }

    if (fullscreen) {
        styleMask = NSBorderlessWindowMask;
    }
    NSRect rectWin = NSMakeRect(x, y, w, h);
    NewtNSWindow* myWindow = [[NewtNSWindow alloc] initWithContentRect: rectWin
                                               styleMask: (NSUInteger) styleMask
                                               backing: (NSBackingStoreType) bufferingType
                                               defer: YES
                                               isFullscreenWindow: fullscreen];
#ifdef VERBOSE_ON
    int dbgIdx = 1;
#endif
    DBG_PRINT( "createWindow1.%d - %p, isVisible %d\n", dbgIdx++, myWindow, [myWindow isVisible]);

    if( NULL == myView ) {
        NSRect rectView = NSMakeRect(0, 0, w, h);
        myView = [[NewtNSView alloc] initWithFrame: rectView] ;
    }
NS_DURING
    // HiDPI scaling: Setup - Available >= 10.7
    if( 1.0 == reqPixelScale ) {
        [myView setWantsBestResolutionOpenGLSurface: NO];
    } else {
        [myView setWantsBestResolutionOpenGLSurface: YES];
    }
NS_HANDLER
NS_ENDHANDLER

    [myWindow setReleasedWhenClosed: NO]; // We control NSWindow destruction!
    [myWindow setPreservesContentDuringLiveResize: NO];
NS_DURING
        if ( [myWindow respondsToSelector:@selector(setRestorable:)] ) {
            // Available >= 10.7 - Removes restauration 'feature', really close
            [myWindow setRestorable: NO];
        }
NS_HANDLER
NS_ENDHANDLER

    // Remove animations for child windows
    if(NULL != parentWindow) {
NS_DURING
        if ( [myWindow respondsToSelector:@selector(setAnimationBehavior:)] ) {
            // Available >= 10.7 - Removes default animations
            [myWindow setAnimationBehavior: NSWindowAnimationBehaviorNone];
        }
NS_HANDLER
NS_ENDHANDLER
    }

    DBG_PRINT( "createWindow1.%d\n", dbgIdx++);
    if(opaque) {
        [myWindow setOpaque: YES];
        if (!fullscreen) {
            [myWindow setShowsResizeIndicator: YES];
        }
    } else {
        [myWindow setOpaque: NO];
        [myWindow setBackgroundColor: [NSColor clearColor]];
    }
    DBG_PRINT( "createWindow1.%d\n", dbgIdx++);

    [myWindow setAlwaysOn: atop bottom:abottom];

    // specify we want mouse-moved events
    [myWindow setAcceptsMouseMovedEvents:YES];

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

    // Set the content view
    changeContentView(env, jthis, parentView, myWindow, myView, NO);
    [myWindow setInitialFirstResponder: myView];

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

    if(NULL!=parentWindow) {
        [myWindow attachToParent: parentWindow];
    }

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d, visible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible], visible);

    // Immediately re-position this window based on an upper-left coordinate system
    setWindowClientTopLeftPointAndSize(myWindow, x, y, w, h, NO);

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

NS_DURING
    // concurrent view rendering
    // Available >= 10.6 - Makes the menubar disapear
    if ( [myWindow respondsToSelector:@selector(setAllowsConcurrentViewDrawing:)] ) {
        [myWindow setAllowsConcurrentViewDrawing: YES];
    }

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

    if ( [myView respondsToSelector:@selector(setCanDrawConcurrently:)] ) {
        [myView setCanDrawConcurrently: YES];
    }
NS_HANDLER
NS_ENDHANDLER

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

    if( visible ) {
      #if 1
        if( NULL == parentWindow ) {
            [myWindow orderFront: myWindow];
        } else {
            [myWindow orderWindow: NSWindowAbove relativeTo: [parentWindow windowNumber]];
        }
        // [myWindow performSelector:@selector(orderFront) withObject:myWindow afterDelay:0];
      #elif 0
        [myWindow makeKeyAndOrderFront: myWindow];
        if( NULL != parentWindow ) {
            [myWindow orderWindow: NSWindowAbove relativeTo: [parentWindow windowNumber]];
        }
      #else
        [myView setNextResponder: myWindow];
        if( NULL == parentWindow ) {
            [myWindow orderFrontRegardless];
        } else {
            [myWindow orderWindow: NSWindowAbove relativeTo: [parentWindow windowNumber]];
        }
      #endif
    }

    DBG_PRINT( "createWindow1.%d - %p view %p, isVisible %d\n", 
        dbgIdx++, myWindow, myView, [myWindow isVisible]);

    // force surface creation
    // [myView lockFocus];
    // [myView unlockFocus];

    // Set the next responder to be the window so that we can forward
    // right mouse button down events
    [myView setNextResponder: myWindow];

    DBG_PRINT( "createWindow1.%d - %p (this), %p (parent): new window: %p, view %p\n",
        dbgIdx++, (void*)(intptr_t)jthis, (void*)(intptr_t)jparent, myWindow, myView);

    [myView setDestroyNotifySent: false];
    setJavaWindowObject(env, jthis, myView);

    DBG_PRINT( "createWindow1.%d - %p view %p, fullscreen %d, setPresentationOptions has %d, options 0x%X\n", 
        dbgIdx++, myWindow, myView, (int)fullscreen, (int)myWindow->hasPresentationSwitch, (int)myWindow->fullscreenPresentationOptions);
NS_DURING
    if( fullscreen ) {
        /** 
         * See Bug 914: We don't use exclusive fullscreen anymore (capturing display)
         * allowing ALT-TAB to allow process/app switching!
         * Shall have no penalty on modern GPU and is also recommended, see bottom box @
         * <https://developer.apple.com/library/mac/documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Articles/DisplayCapture.html>
         *
        NSScreen *myScreen =  NewtScreen_getNSScreenByCoord(x, y);
        if( NULL != myScreen ) {
            if ( [myView respondsToSelector:@selector(enterFullScreenMode:withOptions:)] ) {
                // Available >= 10.5 - Makes the menubar disapear
                [myView enterFullScreenMode: myScreen withOptions:NULL];
            } 
        }
        */
        if( myWindow->hasPresentationSwitch ) {
            [NSApp setPresentationOptions: myWindow->fullscreenPresentationOptions];
        }
    }
NS_HANDLER
NS_ENDHANDLER

    DBG_PRINT( "createWindow1.%d - %p (this), %p (parent): new window: %p, view %p\n",
        dbgIdx++, (void*)(intptr_t)jthis, (void*)(intptr_t)jparent, myWindow, myView);

    // [CATransaction commit];
    [pool release];
    DBG_PRINT( "createWindow1.X - %p (this), %p (parent): new window: %p, view %p\n",
        (void*)(intptr_t)jthis, (void*)(intptr_t)jparent, myWindow, myView);

    return (jlong) (intptr_t) myWindow;
}

JNIEXPORT jint JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_getDisplayID0(JNIEnv *env, jobject jthis, jlong window) {
    NewtNSWindow* myWindow = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == myWindow ) {
        DBG_PRINT( "getDisplayID0 - NULL NEWT win - abort\n");
        return 0;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSScreen *screen =  [myWindow screen];
    int32_t displayID = (int32_t)NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
    [pool release];
    return (jint) displayID;
}

/**
 * Method is called on Main-Thread, hence no special invocation required inside method.
 *
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    setPixelScale0
 * Signature: (JJF)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPixelScale0
  (JNIEnv *env, jobject jthis, jlong window, jlong view, jfloat reqPixelScale)
{
    NewtNSWindow* myWindow = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == myWindow ) {
        DBG_PRINT( "setPixelScale0 - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NewtNSView* myView = (NewtNSView*) (intptr_t) view ;

    DBG_PRINT( "setPixelScale0 - %p (this), %p (window), view %p, reqPixScale %f (START)\n",
        (void*)(intptr_t)jthis, myWindow, myView, (float)reqPixelScale);
    (void)myWindow;

NS_DURING
    // HiDPI scaling: Setup - Available >= 10.7
    if( 1.0 == reqPixelScale ) {
        [myView setWantsBestResolutionOpenGLSurface: NO];
    } else {
        [myView setWantsBestResolutionOpenGLSurface: YES];
    }
NS_HANDLER
NS_ENDHANDLER

    [pool release];
    DBG_PRINT( "setPixelScale0.X - %p (this), window: %p, view %p\n",
        (void*)(intptr_t)jthis, myWindow, myView);
}

/**
 * Method is called on Main-Thread, hence no special invocation required inside method.
 *
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    close0
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_close0
  (JNIEnv *env, jobject unused, jlong window)
{
    NewtNSWindow* mWin = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == mWin ) {
        DBG_PRINT( "windowClose.0 - NULL NEWT win - abort\n");
        return;
    }
    BOOL isNSWin = [mWin isKindOfClass:[NSWindow class]];
    BOOL isNewtWin = [mWin isKindOfClass:[NewtNSWindow class]];
    NSWindow *pWin = [mWin parentWindow];
    DBG_PRINT( "windowClose.0 - %p [isNSWindow %d, isNewtWin %d], parent %p\n", mWin, isNSWin, isNewtWin, pWin);
    (void)isNSWin; // silence
    if( !isNewtWin ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NewtNSView* mView = (NewtNSView *)[mWin contentView];
    BOOL fullscreen = mWin->isFullscreenWindow;
    BOOL destroyNotifySent, isNSView, isNewtNSView;
    if( NULL != mView ) {
        isNSView = [mView isKindOfClass:[NSView class]];
        isNewtNSView = [mView isKindOfClass:[NewtNSView class]];
        destroyNotifySent = isNewtNSView ? [mView getDestroyNotifySent] : false;
    } else {
        isNSView = false;
        isNewtNSView = false;
        destroyNotifySent = false;
    }

    DBG_PRINT( "windowClose.0 - %p, destroyNotifySent %d, view %p [isNSView %d, isNewtNSView %d], fullscreen %d, parent %p\n", 
        mWin, destroyNotifySent, mView, isNSView, isNewtNSView, (int)fullscreen, pWin);

    [mWin setRealized: NO];

    if( isNewtNSView ) {
        // cleanup view
        [mView setDestroyNotifySent: true];
        setJavaWindowObject(env, NULL, mView);
    }

NS_DURING
    /** 
     * See Bug 914: We don't use exclusive fullscreen anymore (capturing display)
     * See createWindow1(..) above ..
    if(NULL!=mView) {
        BOOL iifs;
        if ( [mView respondsToSelector:@selector(isInFullScreenMode)] ) {
            iifs = [mView isInFullScreenMode];
        } else {
            iifs = NO;
        }
        if(iifs && [mView respondsToSelector:@selector(exitFullScreenModeWithOptions:)] ) {
            [mView exitFullScreenModeWithOptions: NULL];
        }
    } */
    // Note: mWin's release will also release it's mView!
    DBG_PRINT( "windowClose.1a - %p view %p, fullscreen %d, hasPresSwitch %d, defaultPresentationOptions 0x%X\n", 
        mWin, mView, (int)fullscreen, (int)mWin->hasPresentationSwitch, (int)mWin->defaultPresentationOptions);

    if( fullscreen && mWin->hasPresentationSwitch ) {
        DBG_PRINT( "windowClose.1b - %p view %p, setPresentationOptions 0x%X\n", 
            mWin, mView, (int)mWin->defaultPresentationOptions);
        [NSApp setPresentationOptions: mWin->defaultPresentationOptions];
    }
NS_HANDLER
NS_ENDHANDLER

    if(NULL!=pWin) {
        [mWin detachFromParent: pWin];
    }
    [mWin orderOut: mWin];

    DBG_PRINT( "windowClose.2 - %p view %p, parent %p\n", mWin, mView, pWin);

    [mWin release];

    DBG_PRINT( "windowClose.Xp\n");

    [pool release];
}

/*
 * Class:     Java_jogamp_newt_driver_macosx_WindowDriver
 * Method:    lockSurface0
 * Signature: (JJ)Z
 */
JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_lockSurface0
  (JNIEnv *env, jclass clazz, jlong window, jlong view)
{
    NewtNSWindow *mWin = (NewtNSWindow*) ((intptr_t) window);
    if(NO == [mWin isRealized]) {
        return JNI_FALSE;
    }
    NewtNSView * mView = (NewtNSView *) ((intptr_t) view);
    return [mView softLock] == YES ? JNI_TRUE : JNI_FALSE;
    /** deadlocks, since we render independent of focus
    return [mView lockFocusIfCanDraw] == YES ? JNI_TRUE : JNI_FALSE; */
}

/*
 * Class:     Java_jogamp_newt_driver_macosx_WindowDriver
 * Method:    unlockSurface0
 * Signature: (JJ)Z
 */
JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_unlockSurface0
  (JNIEnv *env, jclass clazz, jlong window, jlong view)
{
    // NewtNSWindow *mWin = (NewtNSWindow*) ((intptr_t) window);
    (void) window;
    NewtNSView * mView = (NewtNSView *) ((intptr_t) view);
    return [mView softUnlock] == YES ? JNI_TRUE : JNI_FALSE;
    /** deadlocks, since we render independent of focus
    [mView unlockFocus]; */
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    requestFocus0
 * Signature: (JZ)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_requestFocus0
  (JNIEnv *env, jobject window, jlong w, jboolean force)
{
    NSWindow* mWin = (NSWindow*) ((intptr_t) w);
    if( NULL == mWin ) {
        DBG_PRINT( "requestFocus - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
#ifdef VERBOSE_ON
    BOOL hasFocus = [mWin isKeyWindow];
#endif
    DBG_PRINT( "requestFocus - window: %p, parent %p, force %d, hasFocus %d (START)\n", mWin, [mWin parentWindow], force, hasFocus);

    [mWin setAcceptsMouseMovedEvents: YES];
    [mWin makeFirstResponder: nil];
    [mWin orderFrontRegardless];
    [mWin makeKeyWindow];

    DBG_PRINT( "requestFocus - window: %p, parent %p, force %d (END)\n", mWin, [mWin parentWindow], force);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    resignFocus0
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_resignFocus0
  (JNIEnv *env, jobject window, jlong w)
{
    NSWindow* mWin = (NSWindow*) ((intptr_t) w);
    if( NULL == mWin ) {
        DBG_PRINT( "resignFocus0 - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSWindow* pWin = [mWin parentWindow];
    BOOL hasFocus = [mWin isKeyWindow];

    DBG_PRINT( "resignFocus0 - window: %p, parent %p, hasFocus %d (START)\n", mWin, pWin, hasFocus );
    if( hasFocus ) {
        if(NULL != pWin) {
            // [mWin makeFirstResponder: pWin];
            [pWin makeKeyWindow];
        } else {
            [pWin resignKeyWindow];
        }
    }
    DBG_PRINT( "resignFocus0 - window: %p, parent %p (END)\n", mWin, pWin);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    orderFront0
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_orderFront0
  (JNIEnv *env, jobject unused, jlong window)
{
    NewtNSWindow* mWin = (NewtNSWindow*) (intptr_t) window;
    if( NULL == mWin ) {
        DBG_PRINT( "orderFront0 - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSWindow* pWin = [mWin parentWindow];

    DBG_PRINT( "orderFront0 - window: (parent %p) %p visible %d (START)\n", pWin, mWin, [mWin isVisible]);

    if( NULL == pWin ) {
        [mWin orderFront: mWin];
    } else {
        [mWin orderWindow: NSWindowAbove relativeTo: [pWin windowNumber]];
    }

    DBG_PRINT( "orderFront0 - window: (parent %p) %p visible %d (END)\n", pWin, mWin, [mWin isVisible]);
    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    orderOut
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_orderOut0
  (JNIEnv *env, jobject unused, jlong window)
{
    NewtNSWindow* mWin = (NewtNSWindow*) (intptr_t) window;
    if( NULL == mWin ) {
        DBG_PRINT( "orderOut0 - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSWindow* pWin = [mWin parentWindow];

    DBG_PRINT( "orderOut0 - window: (parent %p) %p visible %d (START)\n", pWin, mWin, [mWin isVisible]);

    // NSWindow - Child Windows Ordering Out
    //   For applications linked on 10.7 and later, ordering out a child window will now first remove itself from its parent window. 
    //   Previously, ordering out a child window would implicitly order out the parent window too.
    // https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/index.html#X10_7Notes
    //
    // '[mWin detachFromParent: pWin];' would cause serious troubles with other toolkits like SWT
    // hence we only can move the window back!
    if( NULL == pWin ) {
        [mWin orderOut: mWin];
    } else {
        // NSWindowOut: The window is removed from the screen list and otherWin is ignored.
        // This would remove this window from its parent -> AVOID!
        // [mWin orderWindow: NSWindowOut relativeTo: [pWin windowNumber]];
        [mWin orderWindow: NSWindowBelow relativeTo: [pWin windowNumber]];
    }

    DBG_PRINT( "orderOut0 - window: (parent %p) %p visible %d (END)\n", pWin, mWin, [mWin isVisible]);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    setTitle0
 * Signature: (JLjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setTitle0
  (JNIEnv *env, jobject unused, jlong window, jstring title)
{
    NSWindow* win = (NSWindow*) ((intptr_t) window);
    if( NULL == win ) {
        DBG_PRINT( "setTitle0 - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    DBG_PRINT( "setTitle0 - window: %p (START)\n", win);

    NSString* str = jstringToNSString(env, title);
    [str autorelease];
    [win setTitle: str];

    DBG_PRINT( "setTitle0 - window: %p (END)\n", win);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    contentView0
 * Signature: (J)J
 */
JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_contentView0
  (JNIEnv *env, jobject unused, jlong window)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSWindow* win = (NSWindow*) ((intptr_t) window);
    NSView* nsView = [win contentView];
    NewtNSView* newtView = NULL;

    if( [nsView isKindOfClass:[NewtNSView class]] ) {
        newtView = (NewtNSView *) nsView;
    }

    DBG_PRINT( "contentView0 - window: %p, parent: %p view: %p, newtView %p\n", win, [win parentWindow], nsView, newtView);

    jlong res = (jlong) ((intptr_t) nsView);

    [pool release];
    return res;
}

/**
 * Method is called on Main-Thread, hence no special invocation required inside method.
 *
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    changeContentView
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_changeContentView0
  (JNIEnv *env, jobject jthis, jlong parentWindowOrView, jlong window, jlong jview)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NewtNSView* newView = (NewtNSView *) ((intptr_t) jview);
    NewtNSWindow* win = (NewtNSWindow*) ((intptr_t) window);

    DBG_PRINT( "changeContentView0.0 -  win %p, parent %p, view (%p,%d)\n", 
        win, [win parentWindow], newView, getRetainCount(newView));

    NSObject *nsParentObj = (NSObject*) ((intptr_t) parentWindowOrView);
    NSView* pView = NULL;
    if( NULL != nsParentObj ) {
        if( [nsParentObj isKindOfClass:[NSWindow class]] ) {
            NSWindow * pWin = (NSWindow*) nsParentObj;
            pView = [pWin contentView];
        } else if( [nsParentObj isKindOfClass:[NSView class]] ) {
            pView = (NSView*) nsParentObj;
        }
    }

    changeContentView(env, jthis, pView, win, newView, YES);

    DBG_PRINT( "changeContentView0.X\n");

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    updateSizePosInsets0
 * Signature: (JZ)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_updateSizePosInsets0
  (JNIEnv *env, jobject jthis, jlong window, jboolean defer)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NewtNSWindow* mWin = (NewtNSWindow*) ((intptr_t) window);

    DBG_PRINT( "updateSizePosInsets - window: %p, parent %p, defer %d (START)\n", mWin, [mWin parentWindow], (int)defer);

    [mWin updateSizePosInsets: env jwin:jthis defer:defer];

    DBG_PRINT( "updateSizePosInsets - window: %p, parent %p, defer %d (END)\n", mWin, [mWin parentWindow], (int)defer);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    setWindowClientTopLeftPointAndSize0
 * Signature: (JIIIIZ)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setWindowClientTopLeftPointAndSize0
  (JNIEnv *env, jobject unused, jlong window, jint x, jint y, jint w, jint h, jboolean display)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NewtNSWindow* mWin = (NewtNSWindow*) ((intptr_t) window);

    DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p, parent %p (START)\n", mWin, [mWin parentWindow]);

    setWindowClientTopLeftPointAndSize(mWin, x, y, w, h, display);

    DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p, parent %p (END)\n", mWin, [mWin parentWindow]);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    setWindowClientTopLeftPoint0
 * Signature: (JIIZ)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setWindowClientTopLeftPoint0
  (JNIEnv *env, jobject unused, jlong window, jint x, jint y, jboolean display)
{
    NewtNSWindow* mWin = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == mWin ) {
        DBG_PRINT( "setWindowClientTopLeftPoint - NULL NEWT win - abort\n");
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    DBG_PRINT( "setWindowClientTopLeftPoint - window: %p, parent %p (START)\n", mWin, [mWin parentWindow]);

    setWindowClientTopLeftPoint(mWin, x, y, display);

    DBG_PRINT( "setWindowClientTopLeftPoint - window: %p, parent %p (END)\n", mWin, [mWin parentWindow]);

    [pool release];
}

/*
 * Class:     jogamp_newt_driver_macosx_WindowDriver
 * Method:    getLocationOnScreen0
 * Signature: (JII)Lcom/jogamp/nativewindow/util/Point;
 */
JNIEXPORT jobject JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_getLocationOnScreen0
  (JNIEnv *env, jclass unused, jlong win, jint src_x, jint src_y)
{
    NewtNSWindow *mWin = (NewtNSWindow*) (intptr_t) win;
    if( NULL == mWin ) {
        DBG_PRINT( "getLocationOnScreen0 - NULL NEWT win - abort\n");
        return NULL;
    }
    if( ![mWin isKindOfClass:[NewtNSWindow class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return NULL;
    }
    NSPoint p0 = [mWin getLocationOnScreen: NSMakePoint(src_x, src_y)];
    return (*env)->NewObject(env, pointClz, pointCstr, (jint)p0.x, (jint)p0.y);
}

JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPointerIcon0
  (JNIEnv *env, jobject unused, jlong window, jlong handle)
{
    NewtNSWindow *mWin = (NewtNSWindow*) (intptr_t) window;
    if( NULL == mWin ) {
        DBG_PRINT( "setPointerIcon0 - NULL NEWT win - abort\n");
        return;
    }
    NSCursor *c = (NSCursor*) (intptr_t) handle ;
    if ( NULL != c && NO == [c isKindOfClass:[NSCursor class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NSCursor %p", c);
        return;
    }
    if( ! [mWin isKindOfClass:[NewtNSWindow class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return;
    }
    NewtNSView* nView = (NewtNSView *) [mWin contentView];
    if( ! [nView isKindOfClass:[NewtNSView class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSView %p", nView);
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [nView setPointerIcon: c];
    [pool release];
}

/*
 * Class:     Java_jogamp_newt_driver_macosx_WindowDriver
 * Method:    setPointerVisible0
 * Signature: (JZ)Z
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPointerVisible0
  (JNIEnv *env, jclass clazz, jlong window, jboolean hasFocus, jboolean mouseVisible)
{
    NewtNSWindow *mWin = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == mWin ) {
        DBG_PRINT( "setPointerVisible0 - NULL NEWT win - abort\n");
        return;
    }
    if( ! [mWin isKindOfClass:[NewtNSWindow class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return;
    }
    NewtNSView* nView = (NewtNSView *) [mWin contentView];
    if( ! [nView isKindOfClass:[NewtNSView class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSView %p", nView);
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [nView setMouseVisible: ( JNI_TRUE == mouseVisible ) ? YES : NO 
                  hasFocus: ( JNI_TRUE == hasFocus ) ? YES : NO];
    [pool release];
}

/*
 * Class:     Java_jogamp_newt_driver_macosx_WindowDriver
 * Method:    confinePointer0
 * Signature: (JZ)Z
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_confinePointer0
  (JNIEnv *env, jclass clazz, jlong window, jboolean confine)
{
    NewtNSWindow *mWin = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == mWin ) {
        DBG_PRINT( "confinePointer0 - NULL NEWT win - abort\n");
        return;
    }
    if( ! [mWin isKindOfClass:[NewtNSWindow class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return;
    }
    NewtNSView* nView = (NewtNSView *) [mWin contentView];
    if( ! [nView isKindOfClass:[NewtNSView class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSView %p", nView);
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [nView setMouseConfined: ( JNI_TRUE == confine ) ? YES : NO];
    [pool release];
}

/*
 * Class:     Java_jogamp_newt_driver_macosx_WindowDriver
 * Method:    warpPointer0
 * Signature: (JJII)V
 */
JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_warpPointer0
  (JNIEnv *env, jclass clazz, jlong window, jint x, jint y)
{
    NewtNSWindow *mWin = (NewtNSWindow*) ((intptr_t) window);
    if( NULL == mWin ) {
        DBG_PRINT( "warpPointer0 - NULL NEWT win - abort\n");
        return;
    }
    if( ! [mWin isKindOfClass:[NewtNSWindow class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSWindow %p", mWin);
        return;
    }
    NewtNSView* nView = (NewtNSView *) [mWin contentView];
    if( ! [nView isKindOfClass:[NewtNSView class]] ) {
        NewtCommon_throwNewRuntimeException(env, "Not a NewtNSView %p", nView);
        return;
    }
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [nView setMousePosition: [mWin newtTLViewPos2BLScreenPos: NSMakePoint(x, y)]];
    [pool release];
}

