1 /* 2 * Copyright (c) 2012, 2013, 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, NULL); 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 70 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 71 72 BOOL containsCurrentMode = NO; 73 numModes = CFArrayGetCount(validModes); 74 for (n=0; n < numModes; n++) { 75 if(CFArrayGetValueAtIndex(validModes, n) == currentMode){ 76 containsCurrentMode = YES; 77 break; 78 } 79 } 80 81 if (!containsCurrentMode) { 82 CFArrayAppendValue(validModes, currentMode); 83 } 84 85 return validModes; 86 } 87 88 /* 89 * Find the best possible match in the list of display modes that we can switch to based on 90 * the provided parameters. 91 */ 92 static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) { 93 CGDisplayModeRef bestGuess = NULL; 94 CFIndex numModes = CFArrayGetCount(allModes), n; 95 int thisBpp = 0; 96 for(n = 0; n < numModes; n++ ) { 97 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 98 if(cRef == NULL) { 99 continue; 100 } 101 CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef); 102 thisBpp = getBPPFromModeString(modeString); 103 CFRelease(modeString); 104 if (thisBpp != bpp || (int)CGDisplayModeGetHeight(cRef) != h || (int)CGDisplayModeGetWidth(cRef) != w) { 105 // One of the key parameters does not match 106 continue; 107 } 108 109 if (refrate == 0) { // REFRESH_RATE_UNKNOWN 110 return cRef; 111 } 112 113 // Refresh rate might be 0 in display mode and we ask for specific display rate 114 // but if we do not find exact match then 0 refresh rate might be just Ok 115 if (CGDisplayModeGetRefreshRate(cRef) == refrate) { 116 // Exact match 117 return cRef; 118 } 119 if (CGDisplayModeGetRefreshRate(cRef) == 0) { 120 // Not exactly what was asked for, but may fit our needs if we don't find an exact match 121 bestGuess = cRef; 122 } 123 } 124 return bestGuess; 125 } 126 127 /* 128 * Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef 129 */ 130 static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) { 131 jobject ret = NULL; 132 jint h, w, bpp, refrate; 133 JNF_COCOA_ENTER(env); 134 CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode); 135 bpp = getBPPFromModeString(currentBPP); 136 refrate = CGDisplayModeGetRefreshRate(mode); 137 h = CGDisplayModeGetHeight(mode); 138 w = CGDisplayModeGetWidth(mode); 139 CFRelease(currentBPP); 140 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 141 static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V"); 142 ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate); 143 JNF_COCOA_EXIT(env); 144 return ret; 145 } 146 147 148 /* 149 * Class: sun_awt_CGraphicsDevice 150 * Method: nativeGetXResolution 151 * Signature: (I)D 152 */ 153 JNIEXPORT jdouble JNICALL 154 Java_sun_awt_CGraphicsDevice_nativeGetXResolution 155 (JNIEnv *env, jclass class, jint displayID) 156 { 157 // TODO: this is the physically correct answer, but we probably want 158 // to use NSScreen API instead... 159 CGSize size = CGDisplayScreenSize(displayID); 160 CGRect rect = CGDisplayBounds(displayID); 161 // 1 inch == 25.4 mm 162 jfloat inches = size.width / 25.4f; 163 jfloat dpi = rect.size.width / inches; 164 return dpi; 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 // TODO: this is the physically correct answer, but we probably want 177 // to use NSScreen API instead... 178 CGSize size = CGDisplayScreenSize(displayID); 179 CGRect rect = CGDisplayBounds(displayID); 180 // 1 inch == 25.4 mm 181 jfloat inches = size.height / 25.4f; 182 jfloat dpi = rect.size.height / inches; 183 return dpi; 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 pointerValue] == 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 239 CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate); 240 __block CGError retCode = kCGErrorSuccess; 241 if (closestMatch != NULL) { 242 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 243 CGDisplayConfigRef config; 244 retCode = CGBeginDisplayConfiguration(&config); 245 if (retCode == kCGErrorSuccess) { 246 CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL); 247 retCode = CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly); 248 } 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 258 CFRelease(allModes); 259 JNF_COCOA_EXIT(env); 260 } 261 /* 262 * Class: sun_awt_CGraphicsDevice 263 * Method: nativeGetDisplayMode 264 * Signature: (I)Ljava/awt/DisplayMode 265 */ 266 JNIEXPORT jobject JNICALL 267 Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode 268 (JNIEnv *env, jclass class, jint displayID) 269 { 270 jobject ret = NULL; 271 CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID); 272 ret = createJavaDisplayMode(currentMode, env, displayID); 273 CGDisplayModeRelease(currentMode); 274 return ret; 275 } 276 277 /* 278 * Class: sun_awt_CGraphicsDevice 279 * Method: nativeGetDisplayMode 280 * Signature: (I)[Ljava/awt/DisplayModes 281 */ 282 JNIEXPORT jobjectArray JNICALL 283 Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes 284 (JNIEnv *env, jclass class, jint displayID) 285 { 286 jobjectArray jreturnArray = NULL; 287 JNF_COCOA_ENTER(env); 288 CFArrayRef allModes = getAllValidDisplayModes(displayID); 289 290 CFIndex numModes = CFArrayGetCount(allModes); 291 static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode"); 292 293 jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes); 294 if (!jreturnArray) { 295 NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects"); 296 return nil; 297 } 298 299 CFIndex n; 300 for (n=0; n < numModes; n++) { 301 CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n); 302 if (cRef != NULL) { 303 jobject oneMode = createJavaDisplayMode(cRef, env, displayID); 304 (*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode); 305 if ((*env)->ExceptionOccurred(env)) { 306 (*env)->ExceptionDescribe(env); 307 (*env)->ExceptionClear(env); 308 continue; 309 } 310 (*env)->DeleteLocalRef(env, oneMode); 311 } 312 } 313 CFRelease(allModes); 314 JNF_COCOA_EXIT(env); 315 316 return jreturnArray; 317 } 318 319 /* 320 * Class: sun_awt_CGraphicsDevice 321 * Method: nativeGetScaleFactor 322 * Signature: (I)D 323 */ 324 JNIEXPORT jdouble JNICALL 325 Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor 326 (JNIEnv *env, jclass class, jint displayID) 327 { 328 __block jdouble ret = 1.0f; 329 330 JNF_COCOA_ENTER(env); 331 332 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 333 NSArray *screens = [NSScreen screens]; 334 for (NSScreen *screen in screens) { 335 NSDictionary *screenInfo = [screen deviceDescription]; 336 NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"]; 337 if ([screenID pointerValue] == displayID){ 338 if ([screen respondsToSelector:@selector(backingScaleFactor)]) { 339 ret = [screen backingScaleFactor]; 340 } 341 break; 342 } 343 } 344 }]; 345 346 JNF_COCOA_EXIT(env); 347 return ret; 348 }