| /* |
| * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #import "LWCToolkit.h" |
| #import "ThreadUtilities.h" |
| |
| #import <JavaNativeFoundation/JavaNativeFoundation.h> |
| |
| /* |
| * Convert the mode string to the more convinient bits per pixel value |
| */ |
| static int getBPPFromModeString(CFStringRef mode) |
| { |
| if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { |
| // This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits |
| // Java is not ready to work with this mode but we have to specify it as supported |
| return 30; |
| } |
| else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { |
| return 32; |
| } |
| else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { |
| return 16; |
| } |
| else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { |
| return 8; |
| } |
| |
| return 0; |
| } |
| |
| static BOOL isValidDisplayMode(CGDisplayModeRef mode){ |
| return (1 < CGDisplayModeGetWidth(mode) && 1 < CGDisplayModeGetHeight(mode)); |
| } |
| |
| static CFMutableArrayRef getAllValidDisplayModes(jint displayID){ |
| CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); |
| |
| CFIndex numModes = CFArrayGetCount(allModes); |
| CFMutableArrayRef validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, &kCFTypeArrayCallBacks); |
| |
| CFIndex n; |
| for (n=0; n < numModes; n++) { |
| CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); |
| if (cRef != NULL && isValidDisplayMode(cRef)) { |
| CFArrayAppendValue(validModes, cRef); |
| } |
| } |
| CFRelease(allModes); |
| |
| CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); |
| |
| BOOL containsCurrentMode = NO; |
| numModes = CFArrayGetCount(validModes); |
| for (n=0; n < numModes; n++) { |
| if(CFArrayGetValueAtIndex(validModes, n) == currentMode){ |
| containsCurrentMode = YES; |
| break; |
| } |
| } |
| |
| if (!containsCurrentMode) { |
| CFArrayAppendValue(validModes, currentMode); |
| } |
| CGDisplayModeRelease(currentMode); |
| |
| return validModes; |
| } |
| |
| /* |
| * Find the best possible match in the list of display modes that we can switch to based on |
| * the provided parameters. |
| */ |
| static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) { |
| CGDisplayModeRef bestGuess = NULL; |
| CFIndex numModes = CFArrayGetCount(allModes), n; |
| int thisBpp = 0; |
| for(n = 0; n < numModes; n++ ) { |
| CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); |
| if(cRef == NULL) { |
| continue; |
| } |
| CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef); |
| thisBpp = getBPPFromModeString(modeString); |
| CFRelease(modeString); |
| if (thisBpp != bpp || (int)CGDisplayModeGetHeight(cRef) != h || (int)CGDisplayModeGetWidth(cRef) != w) { |
| // One of the key parameters does not match |
| continue; |
| } |
| |
| if (refrate == 0) { // REFRESH_RATE_UNKNOWN |
| return cRef; |
| } |
| |
| // Refresh rate might be 0 in display mode and we ask for specific display rate |
| // but if we do not find exact match then 0 refresh rate might be just Ok |
| if (CGDisplayModeGetRefreshRate(cRef) == refrate) { |
| // Exact match |
| return cRef; |
| } |
| if (CGDisplayModeGetRefreshRate(cRef) == 0) { |
| // Not exactly what was asked for, but may fit our needs if we don't find an exact match |
| bestGuess = cRef; |
| } |
| } |
| return bestGuess; |
| } |
| |
| /* |
| * Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef |
| */ |
| static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) { |
| jobject ret = NULL; |
| jint h, w, bpp, refrate; |
| JNF_COCOA_ENTER(env); |
| CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode); |
| bpp = getBPPFromModeString(currentBPP); |
| refrate = CGDisplayModeGetRefreshRate(mode); |
| h = CGDisplayModeGetHeight(mode); |
| w = CGDisplayModeGetWidth(mode); |
| CFRelease(currentBPP); |
| static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); |
| static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V"); |
| ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate); |
| JNF_COCOA_EXIT(env); |
| return ret; |
| } |
| |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetXResolution |
| * Signature: (I)D |
| */ |
| JNIEXPORT jdouble JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetXResolution |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| // CGDisplayScreenSize can return 0 if displayID is invalid |
| CGSize size = CGDisplayScreenSize(displayID); |
| CGRect rect = CGDisplayBounds(displayID); |
| // 1 inch == 25.4 mm |
| jfloat inches = size.width / 25.4f; |
| return inches > 0 ? rect.size.width / inches : 72; |
| } |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetYResolution |
| * Signature: (I)D |
| */ |
| JNIEXPORT jdouble JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetYResolution |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| // CGDisplayScreenSize can return 0 if displayID is invalid |
| CGSize size = CGDisplayScreenSize(displayID); |
| CGRect rect = CGDisplayBounds(displayID); |
| // 1 inch == 25.4 mm |
| jfloat inches = size.height / 25.4f; |
| return inches > 0 ? rect.size.height / inches : 72; |
| } |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetScreenInsets |
| * Signature: (I)D |
| */ |
| JNIEXPORT jobject JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetScreenInsets |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| jobject ret = NULL; |
| __block NSRect frame = NSZeroRect; |
| __block NSRect visibleFrame = NSZeroRect; |
| JNF_COCOA_ENTER(env); |
| |
| [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
| NSArray *screens = [NSScreen screens]; |
| for (NSScreen *screen in screens) { |
| NSDictionary *screenInfo = [screen deviceDescription]; |
| NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"]; |
| if ([screenID unsignedIntValue] == displayID){ |
| frame = [screen frame]; |
| visibleFrame = [screen visibleFrame]; |
| break; |
| } |
| } |
| }]; |
| // Convert between Cocoa's coordinate system and Java. |
| jint bottom = visibleFrame.origin.y - frame.origin.y; |
| jint top = frame.size.height - visibleFrame.size.height - bottom; |
| jint left = visibleFrame.origin.x - frame.origin.x; |
| jint right = frame.size.width - visibleFrame.size.width - left; |
| |
| static JNF_CLASS_CACHE(jc_Insets, "java/awt/Insets"); |
| static JNF_CTOR_CACHE(jc_Insets_ctor, jc_Insets, "(IIII)V"); |
| ret = JNFNewObject(env, jc_Insets_ctor, top, left, bottom, right); |
| |
| JNF_COCOA_EXIT(env); |
| |
| return ret; |
| } |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeSetDisplayMode |
| * Signature: (IIIII)V |
| */ |
| JNIEXPORT void JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode |
| (JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate) |
| { |
| JNF_COCOA_ENTER(env); |
| CFArrayRef allModes = getAllValidDisplayModes(displayID); |
| CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate); |
| |
| __block CGError retCode = kCGErrorSuccess; |
| if (closestMatch != NULL) { |
| CGDisplayModeRetain(closestMatch); |
| [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
| CGDisplayConfigRef config; |
| retCode = CGBeginDisplayConfiguration(&config); |
| if (retCode == kCGErrorSuccess) { |
| CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL); |
| retCode = CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly); |
| } |
| CGDisplayModeRelease(closestMatch); |
| }]; |
| } else { |
| [JNFException raise:env as:kIllegalArgumentException reason:"Invalid display mode"]; |
| } |
| |
| if (retCode != kCGErrorSuccess){ |
| [JNFException raise:env as:kIllegalArgumentException reason:"Unable to set display mode!"]; |
| } |
| CFRelease(allModes); |
| JNF_COCOA_EXIT(env); |
| } |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetDisplayMode |
| * Signature: (I)Ljava/awt/DisplayMode |
| */ |
| JNIEXPORT jobject JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| jobject ret = NULL; |
| CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); |
| ret = createJavaDisplayMode(currentMode, env, displayID); |
| CGDisplayModeRelease(currentMode); |
| return ret; |
| } |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetDisplayMode |
| * Signature: (I)[Ljava/awt/DisplayModes |
| */ |
| JNIEXPORT jobjectArray JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| jobjectArray jreturnArray = NULL; |
| JNF_COCOA_ENTER(env); |
| CFArrayRef allModes = getAllValidDisplayModes(displayID); |
| |
| CFIndex numModes = CFArrayGetCount(allModes); |
| static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); |
| |
| jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes); |
| if (!jreturnArray) { |
| NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects"); |
| return nil; |
| } |
| |
| CFIndex n; |
| for (n=0; n < numModes; n++) { |
| CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); |
| if (cRef != NULL) { |
| jobject oneMode = createJavaDisplayMode(cRef, env, displayID); |
| (*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode); |
| if ((*env)->ExceptionOccurred(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| continue; |
| } |
| (*env)->DeleteLocalRef(env, oneMode); |
| } |
| } |
| CFRelease(allModes); |
| JNF_COCOA_EXIT(env); |
| |
| return jreturnArray; |
| } |
| |
| /* |
| * Class: sun_awt_CGraphicsDevice |
| * Method: nativeGetScaleFactor |
| * Signature: (I)D |
| */ |
| JNIEXPORT jdouble JNICALL |
| Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor |
| (JNIEnv *env, jclass class, jint displayID) |
| { |
| __block jdouble ret = 1.0f; |
| |
| JNF_COCOA_ENTER(env); |
| |
| [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
| NSArray *screens = [NSScreen screens]; |
| for (NSScreen *screen in screens) { |
| NSDictionary *screenInfo = [screen deviceDescription]; |
| NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"]; |
| if ([screenID unsignedIntValue] == displayID){ |
| if ([screen respondsToSelector:@selector(backingScaleFactor)]) { |
| ret = [screen backingScaleFactor]; |
| } |
| break; |
| } |
| } |
| }]; |
| |
| JNF_COCOA_EXIT(env); |
| return ret; |
| } |