1 /* 2 * Copyright (c) 2012, 2014, 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 #import "LWCToolkit.h" 27 #import "ThreadUtilities.h" 28 29 /* 30 * Convert the mode string to the more convinient bits per pixel value 31 */ 32 static int getBPPFromModeString(CFStringRef mode) 33 { 34 if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { 35 // This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits 36 // Java is not ready to work with this mode but we have to specify it as supported 37 return 30; 38 } 39 else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 40 return 32; 41 } 42 else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 43 return 16; 44 } 45 else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 46 return 8; 47 } 48 49 return 0; 50 } 51 52 static BOOL isValidDisplayMode(CGDisplayModeRef mode){ 53 return (1 < CGDisplayModeGetWidth(mode) && 1 < CGDisplayModeGetHeight(mode)); 54 } 55 56 static CFMutableArrayRef getAllValidDisplayModes(jint displayID){ 57 CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL); 58 59 CFIndex numModes = CFArrayGetCount(allModes); 60 CFMutableArrayRef validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, &kCFTypeArrayCallBacks); 61 62 CFIndex n; 63 for (n=0; n < numModes; n++) { 64 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 65 if (cRef != NULL && isValidDisplayMode(cRef)) { 66 CFArrayAppendValue(validModes, cRef); 67 } 68 } 69 CFRelease(allModes); 70 71 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 72 73 BOOL containsCurrentMode = NO; 74 numModes = CFArrayGetCount(validModes); 75 for (n=0; n < numModes; n++) { 76 if(CFArrayGetValueAtIndex(validModes, n) == currentMode){ 77 containsCurrentMode = YES; 78 break; 79 } 80 } 81 82 if (!containsCurrentMode) { 83 CFArrayAppendValue(validModes, currentMode); 84 } 85 CGDisplayModeRelease(currentMode); 86 87 return validModes; 88 } 89 90 /* 91 * Find the best possible match in the list of display modes that we can switch to based on 92 * the provided parameters. 93 */ 94 static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) { 95 CGDisplayModeRef bestGuess = NULL; 96 CFIndex numModes = CFArrayGetCount(allModes), n; 97 int thisBpp = 0; 98 for(n = 0; n < numModes; n++ ) { 99 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 100 if(cRef == NULL) { 101 continue; 102 } 103 CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef); 104 thisBpp = getBPPFromModeString(modeString); 105 CFRelease(modeString); 106 if (thisBpp != bpp || (int)CGDisplayModeGetHeight(cRef) != h || (int)CGDisplayModeGetWidth(cRef) != w) { 107 // One of the key parameters does not match 108 continue; 109 } 110 111 if (refrate == 0) { // REFRESH_RATE_UNKNOWN 112 return cRef; 113 } 114 115 // Refresh rate might be 0 in display mode and we ask for specific display rate 116 // but if we do not find exact match then 0 refresh rate might be just Ok 117 if (CGDisplayModeGetRefreshRate(cRef) == refrate) { 118 // Exact match 119 return cRef; 120 } 121 if (CGDisplayModeGetRefreshRate(cRef) == 0) { 122 // Not exactly what was asked for, but may fit our needs if we don't find an exact match 123 bestGuess = cRef; 124 } 125 } 126 return bestGuess; 127 } 128 129 /* 130 * Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef 131 */ 132 static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) { 133 jobject ret = NULL; 134 jint h, w, bpp, refrate; 135 JNF_COCOA_ENTER(env); 136 CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode); 137 bpp = getBPPFromModeString(currentBPP); 138 refrate = CGDisplayModeGetRefreshRate(mode); 139 h = CGDisplayModeGetHeight(mode); 140 w = CGDisplayModeGetWidth(mode); 141 CFRelease(currentBPP); 142 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 143 static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V"); 144 ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate); 145 JNF_COCOA_EXIT(env); 146 return ret; 147 } 148 149 150 /* 151 * Class: sun_awt_CGraphicsDevice 152 * Method: nativeGetXResolution 153 * Signature: (I)D 154 */ 155 JNIEXPORT jdouble JNICALL 156 Java_sun_awt_CGraphicsDevice_nativeGetXResolution 157 (JNIEnv *env, jclass class, jint displayID) 158 { 159 // CGDisplayScreenSize can return 0 if displayID is invalid 160 CGSize size = CGDisplayScreenSize(displayID); 161 CGRect rect = CGDisplayBounds(displayID); 162 // 1 inch == 25.4 mm 163 jfloat inches = size.width / 25.4f; 164 return inches > 0 ? rect.size.width / inches : 72; 165 } 166 167 /* 168 * Class: sun_awt_CGraphicsDevice 169 * Method: nativeGetYResolution 170 * Signature: (I)D 171 */ 172 JNIEXPORT jdouble JNICALL 173 Java_sun_awt_CGraphicsDevice_nativeGetYResolution 174 (JNIEnv *env, jclass class, jint displayID) 175 { 176 // CGDisplayScreenSize can return 0 if displayID is invalid 177 CGSize size = CGDisplayScreenSize(displayID); 178 CGRect rect = CGDisplayBounds(displayID); 179 // 1 inch == 25.4 mm 180 jfloat inches = size.height / 25.4f; 181 return inches > 0 ? rect.size.height / inches : 72; 182 } 183 184 /* 185 * Class: sun_awt_CGraphicsDevice 186 * Method: nativeGetScreenInsets 187 * Signature: (I)D 188 */ 189 JNIEXPORT jobject JNICALL 190 Java_sun_awt_CGraphicsDevice_nativeGetScreenInsets 191 (JNIEnv *env, jclass class, jint displayID) 192 { 193 jobject ret = NULL; 194 __block NSRect frame = NSZeroRect; 195 __block NSRect visibleFrame = NSZeroRect; 196 JNF_COCOA_ENTER(env); 197 198 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 199 NSArray *screens = [NSScreen screens]; 200 for (NSScreen *screen in screens) { 201 NSDictionary *screenInfo = [screen deviceDescription]; 202 NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"]; 203 if ([screenID pointerValue] == displayID){ 204 frame = [screen frame]; 205 visibleFrame = [screen visibleFrame]; 206 break; 207 } 208 } 209 }]; 210 // Convert between Cocoa's coordinate system and Java. 211 jint bottom = visibleFrame.origin.y - frame.origin.y; 212 jint top = frame.size.height - visibleFrame.size.height - bottom; 213 jint left = visibleFrame.origin.x - frame.origin.x; 214 jint right = frame.size.width - visibleFrame.size.width - left; 215 216 static JNF_CLASS_CACHE(jc_Insets, "java/awt/Insets"); 217 static JNF_CTOR_CACHE(jc_Insets_ctor, jc_Insets, "(IIII)V"); 218 ret = JNFNewObject(env, jc_Insets_ctor, top, left, bottom, right); 219 220 JNF_COCOA_EXIT(env); 221 222 return ret; 223 } 224 225 /* 226 * Class: sun_awt_CGraphicsDevice 227 * Method: nativeSetDisplayMode 228 * Signature: (IIIII)V 229 */ 230 JNIEXPORT void JNICALL 231 Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode 232 (JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate) 233 { 234 JNF_COCOA_ENTER(env); 235 CFArrayRef allModes = getAllValidDisplayModes(displayID); 236 CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate); 237 238 __block CGError retCode = kCGErrorSuccess; 239 if (closestMatch != NULL) { 240 CGDisplayModeRetain(closestMatch); 241 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 242 CGDisplayConfigRef config; 243 retCode = CGBeginDisplayConfiguration(&config); 244 if (retCode == kCGErrorSuccess) { 245 CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL); 246 retCode = CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly); 247 } 248 CGDisplayModeRelease(closestMatch); 249 }]; 250 } else { 251 [JNFException raise:env as:kIllegalArgumentException reason:"Invalid display mode"]; 252 } 253 254 if (retCode != kCGErrorSuccess){ 255 [JNFException raise:env as:kIllegalArgumentException reason:"Unable to set display mode!"]; 256 } 257 CFRelease(allModes); 258 JNF_COCOA_EXIT(env); 259 } 260 /* 261 * Class: sun_awt_CGraphicsDevice 262 * Method: nativeGetDisplayMode 263 * Signature: (I)Ljava/awt/DisplayMode 264 */ 265 JNIEXPORT jobject JNICALL 266 Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode 267 (JNIEnv *env, jclass class, jint displayID) 268 { 269 jobject ret = NULL; 270 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 271 ret = createJavaDisplayMode(currentMode, env, displayID); 272 CGDisplayModeRelease(currentMode); 273 return ret; 274 } 275 276 /* 277 * Class: sun_awt_CGraphicsDevice 278 * Method: nativeGetDisplayMode 279 * Signature: (I)[Ljava/awt/DisplayModes 280 */ 281 JNIEXPORT jobjectArray JNICALL 282 Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes 283 (JNIEnv *env, jclass class, jint displayID) 284 { 285 jobjectArray jreturnArray = NULL; 286 JNF_COCOA_ENTER(env); 287 CFArrayRef allModes = getAllValidDisplayModes(displayID); 288 289 CFIndex numModes = CFArrayGetCount(allModes); 290 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 291 292 jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes); 293 if (!jreturnArray) { 294 NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects"); 295 return nil; 296 } 297 298 CFIndex n; 299 for (n=0; n < numModes; n++) { 300 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 301 if (cRef != NULL) { 302 jobject oneMode = createJavaDisplayMode(cRef, env, displayID); 303 (*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode); 304 if ((*env)->ExceptionOccurred(env)) { 305 (*env)->ExceptionDescribe(env); 306 (*env)->ExceptionClear(env); 307 continue; 308 } 309 (*env)->DeleteLocalRef(env, oneMode); 310 } 311 } 312 CFRelease(allModes); 313 JNF_COCOA_EXIT(env); 314 315 return jreturnArray; 316 } 317 318 /* 319 * Class: sun_awt_CGraphicsDevice 320 * Method: nativeGetScaleFactor 321 * Signature: (I)D 322 */ 323 JNIEXPORT jdouble JNICALL 324 Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor 325 (JNIEnv *env, jclass class, jint displayID) 326 { 327 __block jdouble ret = 1.0f; 328 329 JNF_COCOA_ENTER(env); 330 331 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 332 NSArray *screens = [NSScreen screens]; 333 for (NSScreen *screen in screens) { 334 NSDictionary *screenInfo = [screen deviceDescription]; 335 NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"]; 336 if ([screenID pointerValue] == displayID){ 337 if ([screen respondsToSelector:@selector(backingScaleFactor)]) { 338 ret = [screen backingScaleFactor]; 339 } 340 break; 341 } 342 } 343 }]; 344 345 JNF_COCOA_EXIT(env); 346 return ret; 347 }