1 /* 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "LWCToolkit.h" 27 28 /* 29 * Convert the mode string to the more convinient bits per pixel value 30 */ 31 static int getBPPFromModeString(CFStringRef mode) 32 { 33 if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { 34 // This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits 35 // Java is not ready to work with this mode but we have to specify it as supported 36 return 30; 37 } 38 else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 39 return 32; 40 } 41 else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 42 return 16; 43 } 44 else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 45 return 8; 46 } 47 48 return 0; 49 } 50 51 /* 52 * Find the best possible match in the list of display modes that we can switch to based on 53 * the provided parameters. 54 */ 55 static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) { 56 CGDisplayModeRef bestGuess = NULL; 57 CFIndex numModes = CFArrayGetCount(allModes), n; 58 int thisBpp = 0; 59 for(n = 0; n < numModes; n++ ) { 60 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 61 if(cRef == NULL) { 62 continue; 63 } 64 CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef); 65 thisBpp = getBPPFromModeString(modeString); 66 CFRelease(modeString); 67 if (thisBpp != bpp || (int)CGDisplayModeGetHeight(cRef) != h || (int)CGDisplayModeGetWidth(cRef) != w) { 68 // One of the key parameters does not match 69 continue; 70 } 71 // Refresh rate might be 0 in display mode and we ask for specific display rate 72 // but if we do not find exact match then 0 refresh rate might be just Ok 73 if (CGDisplayModeGetRefreshRate(cRef) == refrate) { 74 // Exact match 75 return cRef; 76 } 77 if (CGDisplayModeGetRefreshRate(cRef) == 0) { 78 // Not exactly what was asked for, but may fit our needs if we don't find an exact match 79 bestGuess = cRef; 80 } 81 } 82 return bestGuess; 83 } 84 85 /* 86 * Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef 87 */ 88 static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) { 89 jobject ret = NULL; 90 jint h, w, bpp, refrate; 91 JNF_COCOA_ENTER(env); 92 CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode); 93 bpp = getBPPFromModeString(currentBPP); 94 refrate = CGDisplayModeGetRefreshRate(mode); 95 h = CGDisplayModeGetHeight(mode); 96 w = CGDisplayModeGetWidth(mode); 97 CFRelease(currentBPP); 98 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 99 static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V"); 100 ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate); 101 JNF_COCOA_EXIT(env); 102 return ret; 103 } 104 105 106 /* 107 * Class: sun_awt_CGraphicsDevice 108 * Method: nativeGetXResolution 109 * Signature: (I)D 110 */ 111 JNIEXPORT jdouble JNICALL 112 Java_sun_awt_CGraphicsDevice_nativeGetXResolution 113 (JNIEnv *env, jclass class, jint displayID) 114 { 115 // TODO: this is the physically correct answer, but we probably want 116 // to use NSScreen API instead... 117 CGSize size = CGDisplayScreenSize(displayID); 118 CGRect rect = CGDisplayBounds(displayID); 119 // 1 inch == 25.4 mm 120 jfloat inches = size.width / 25.4f; 121 jfloat dpi = rect.size.width / inches; 122 return dpi; 123 } 124 125 /* 126 * Class: sun_awt_CGraphicsDevice 127 * Method: nativeGetYResolution 128 * Signature: (I)D 129 */ 130 JNIEXPORT jdouble JNICALL 131 Java_sun_awt_CGraphicsDevice_nativeGetYResolution 132 (JNIEnv *env, jclass class, jint displayID) 133 { 134 // TODO: this is the physically correct answer, but we probably want 135 // to use NSScreen API instead... 136 CGSize size = CGDisplayScreenSize(displayID); 137 CGRect rect = CGDisplayBounds(displayID); 138 // 1 inch == 25.4 mm 139 jfloat inches = size.height / 25.4f; 140 jfloat dpi = rect.size.height / inches; 141 return dpi; 142 } 143 144 /* 145 * Class: sun_awt_CGraphicsDevice 146 * Method: nativeSetDisplayMode 147 * Signature: (IIIII)V 148 */ 149 JNIEXPORT void JNICALL 150 Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode 151 (JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate) 152 { 153 JNF_COCOA_ENTER(env); 154 CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); 155 CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate); 156 if (closestMatch != NULL) { 157 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 158 CGDisplayConfigRef config; 159 CGError retCode = CGBeginDisplayConfiguration(&config); 160 if (retCode == kCGErrorSuccess) { 161 CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL); 162 CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly); 163 if (config != NULL) { 164 CFRelease(config); 165 } 166 } 167 }]; 168 } 169 CFRelease(allModes); 170 JNF_COCOA_EXIT(env); 171 } 172 173 /* 174 * Class: sun_awt_CGraphicsDevice 175 * Method: nativeGetDisplayMode 176 * Signature: (I)Ljava/awt/DisplayMode 177 */ 178 JNIEXPORT jobject JNICALL 179 Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode 180 (JNIEnv *env, jclass class, jint displayID) 181 { 182 jobject ret = NULL; 183 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 184 ret = createJavaDisplayMode(currentMode, env, displayID); 185 CGDisplayModeRelease(currentMode); 186 return ret; 187 } 188 189 /* 190 * Class: sun_awt_CGraphicsDevice 191 * Method: nativeGetDisplayMode 192 * Signature: (I)[Ljava/awt/DisplayModes 193 */ 194 JNIEXPORT jobjectArray JNICALL 195 Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes 196 (JNIEnv *env, jclass class, jint displayID) 197 { 198 jobjectArray jreturnArray = NULL; 199 JNF_COCOA_ENTER(env); 200 CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); 201 CFIndex numModes = CFArrayGetCount(allModes); 202 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 203 204 jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes); 205 if (!jreturnArray) { 206 NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects"); 207 return nil; 208 } 209 210 CFIndex n; 211 for (n=0; n < numModes; n++) { 212 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 213 if (cRef != NULL) { 214 jobject oneMode = createJavaDisplayMode(cRef, env, displayID); 215 (*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode); 216 if ((*env)->ExceptionOccurred(env)) { 217 (*env)->ExceptionDescribe(env); 218 (*env)->ExceptionClear(env); 219 continue; 220 } 221 (*env)->DeleteLocalRef(env, oneMode); 222 } 223 } 224 CFRelease(allModes); 225 JNF_COCOA_EXIT(env); 226 227 return jreturnArray; 228 }