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