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 72 if (refrate == 0) { // REFRESH_RATE_UNKNOWN 73 return cRef; 74 } 75 76 // Refresh rate might be 0 in display mode and we ask for specific display rate 77 // but if we do not find exact match then 0 refresh rate might be just Ok 78 if (CGDisplayModeGetRefreshRate(cRef) == refrate) { 79 // Exact match 80 return cRef; 81 } 82 if (CGDisplayModeGetRefreshRate(cRef) == 0) { 83 // Not exactly what was asked for, but may fit our needs if we don't find an exact match 84 bestGuess = cRef; 85 } 86 } 87 return bestGuess; 88 } 89 90 /* 91 * Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef 92 */ 93 static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) { 94 jobject ret = NULL; 95 jint h, w, bpp, refrate; 96 JNF_COCOA_ENTER(env); 97 CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode); 98 bpp = getBPPFromModeString(currentBPP); 99 refrate = CGDisplayModeGetRefreshRate(mode); 100 h = CGDisplayModeGetHeight(mode); 101 w = CGDisplayModeGetWidth(mode); 102 CFRelease(currentBPP); 103 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 104 static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V"); 105 ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate); 106 JNF_COCOA_EXIT(env); 107 return ret; 108 } 109 110 111 /* 112 * Class: sun_awt_CGraphicsDevice 113 * Method: nativeGetXResolution 114 * Signature: (I)D 115 */ 116 JNIEXPORT jdouble JNICALL 117 Java_sun_awt_CGraphicsDevice_nativeGetXResolution 118 (JNIEnv *env, jclass class, jint displayID) 119 { 120 // TODO: this is the physically correct answer, but we probably want 121 // to use NSScreen API instead... 122 CGSize size = CGDisplayScreenSize(displayID); 123 CGRect rect = CGDisplayBounds(displayID); 124 // 1 inch == 25.4 mm 125 jfloat inches = size.width / 25.4f; 126 jfloat dpi = rect.size.width / inches; 127 return dpi; 128 } 129 130 /* 131 * Class: sun_awt_CGraphicsDevice 132 * Method: nativeGetYResolution 133 * Signature: (I)D 134 */ 135 JNIEXPORT jdouble JNICALL 136 Java_sun_awt_CGraphicsDevice_nativeGetYResolution 137 (JNIEnv *env, jclass class, jint displayID) 138 { 139 // TODO: this is the physically correct answer, but we probably want 140 // to use NSScreen API instead... 141 CGSize size = CGDisplayScreenSize(displayID); 142 CGRect rect = CGDisplayBounds(displayID); 143 // 1 inch == 25.4 mm 144 jfloat inches = size.height / 25.4f; 145 jfloat dpi = rect.size.height / inches; 146 return dpi; 147 } 148 149 /* 150 * Class: sun_awt_CGraphicsDevice 151 * Method: nativeSetDisplayMode 152 * Signature: (IIIII)V 153 */ 154 JNIEXPORT void JNICALL 155 Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode 156 (JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate) 157 { 158 JNF_COCOA_ENTER(env); 159 CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); 160 CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate); 161 if (closestMatch != NULL) { 162 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 163 CGDisplayConfigRef config; 164 CGError retCode = CGBeginDisplayConfiguration(&config); 165 if (retCode == kCGErrorSuccess) { 166 CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL); 167 CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly); 168 if (config != NULL) { 169 CFRelease(config); 170 } 171 } 172 }]; 173 } else { 174 [JNFException raise:env as:kIllegalArgumentException reason:"Invalid display mode"]; 175 } 176 177 CFRelease(allModes); 178 JNF_COCOA_EXIT(env); 179 } 180 181 /* 182 * Class: sun_awt_CGraphicsDevice 183 * Method: nativeGetDisplayMode 184 * Signature: (I)Ljava/awt/DisplayMode 185 */ 186 JNIEXPORT jobject JNICALL 187 Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode 188 (JNIEnv *env, jclass class, jint displayID) 189 { 190 jobject ret = NULL; 191 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 192 ret = createJavaDisplayMode(currentMode, env, displayID); 193 CGDisplayModeRelease(currentMode); 194 return ret; 195 } 196 197 /* 198 * Class: sun_awt_CGraphicsDevice 199 * Method: nativeGetDisplayMode 200 * Signature: (I)[Ljava/awt/DisplayModes 201 */ 202 JNIEXPORT jobjectArray JNICALL 203 Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes 204 (JNIEnv *env, jclass class, jint displayID) 205 { 206 jobjectArray jreturnArray = NULL; 207 JNF_COCOA_ENTER(env); 208 CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); 209 CFIndex numModes = CFArrayGetCount(allModes); 210 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 211 212 jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes); 213 if (!jreturnArray) { 214 NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects"); 215 return nil; 216 } 217 218 CFIndex n; 219 for (n=0; n < numModes; n++) { 220 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 221 if (cRef != NULL) { 222 jobject oneMode = createJavaDisplayMode(cRef, env, displayID); 223 (*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode); 224 if ((*env)->ExceptionOccurred(env)) { 225 (*env)->ExceptionDescribe(env); 226 (*env)->ExceptionClear(env); 227 continue; 228 } 229 (*env)->DeleteLocalRef(env, oneMode); 230 } 231 } 232 CFRelease(allModes); 233 JNF_COCOA_EXIT(env); 234 235 return jreturnArray; 236 }