Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
41152 views
/*1* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "LWCToolkit.h"26#import "ThreadUtilities.h"27#include "GeomUtilities.h"28#include "JNIUtilities.h"2930/**31* Some default values for invalid CoreGraphics display ID.32*/33#define DEFAULT_DEVICE_WIDTH 102434#define DEFAULT_DEVICE_HEIGHT 76835#define DEFAULT_DEVICE_DPI 723637/*38* Convert the mode string to the more convinient bits per pixel value39*/40static int getBPPFromModeString(CFStringRef mode)41{42if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {43// This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits44// Java is not ready to work with this mode but we have to specify it as supported45return 30;46}47else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {48return 32;49}50else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {51return 16;52}53else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {54return 8;55}5657return 0;58}5960static BOOL isValidDisplayMode(CGDisplayModeRef mode){61return (1 < CGDisplayModeGetWidth(mode) && 1 < CGDisplayModeGetHeight(mode));62}6364static CFMutableArrayRef getAllValidDisplayModes(jint displayID){65// CGDisplayCopyAllDisplayModes can return NULL if displayID is invalid66CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL);67CFMutableArrayRef validModes = nil;68if (allModes) {69CFIndex numModes = CFArrayGetCount(allModes);70validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, &kCFTypeArrayCallBacks);7172CFIndex n;73for (n=0; n < numModes; n++) {74CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);75if (cRef != NULL && isValidDisplayMode(cRef)) {76CFArrayAppendValue(validModes, cRef);77}78}79CFRelease(allModes);8081// CGDisplayCopyDisplayMode can return NULL if displayID is invalid82CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID);83if (currentMode) {84BOOL containsCurrentMode = NO;85numModes = CFArrayGetCount(validModes);86for (n=0; n < numModes; n++) {87if(CFArrayGetValueAtIndex(validModes, n) == currentMode){88containsCurrentMode = YES;89break;90}91}92if (!containsCurrentMode) {93CFArrayAppendValue(validModes, currentMode);94}95CGDisplayModeRelease(currentMode);96}97}9899return validModes;100}101102/*103* Find the best possible match in the list of display modes that we can switch to based on104* the provided parameters.105*/106static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) {107CGDisplayModeRef bestGuess = NULL;108CFIndex numModes = allModes ? CFArrayGetCount(allModes) : 0, n;109110for(n = 0; n < numModes; n++ ) {111CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);112if(cRef == NULL) {113continue;114}115CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef);116int thisBpp = getBPPFromModeString(modeString);117CFRelease(modeString);118int thisH = (int)CGDisplayModeGetHeight(cRef);119int thisW = (int)CGDisplayModeGetWidth(cRef);120if (thisBpp != bpp || thisH != h || thisW != w) {121// One of the key parameters does not match122continue;123}124125if (refrate == 0) { // REFRESH_RATE_UNKNOWN126return cRef;127}128129// Refresh rate might be 0 in display mode and we ask for specific display rate130// but if we do not find exact match then 0 refresh rate might be just Ok131int thisRefrate = (int)CGDisplayModeGetRefreshRate(cRef);132if (thisRefrate == refrate) {133// Exact match134return cRef;135}136if (thisRefrate == 0) {137// Not exactly what was asked for, but may fit our needs if we don't find an exact match138bestGuess = cRef;139}140}141return bestGuess;142}143144/*145* Create a new java.awt.DisplayMode instance based on provided146* CGDisplayModeRef, if CGDisplayModeRef is NULL, then some stub is returned.147*/148static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env) {149jobject ret = NULL;150jint h = DEFAULT_DEVICE_HEIGHT, w = DEFAULT_DEVICE_WIDTH, bpp = 0, refrate = 0;151JNI_COCOA_ENTER(env);152if (mode) {153CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode);154bpp = getBPPFromModeString(currentBPP);155refrate = CGDisplayModeGetRefreshRate(mode);156h = CGDisplayModeGetHeight(mode);157w = CGDisplayModeGetWidth(mode);158CFRelease(currentBPP);159}160DECLARE_CLASS_RETURN(jc_DisplayMode, "java/awt/DisplayMode", ret);161DECLARE_METHOD_RETURN(jc_DisplayMode_ctor, jc_DisplayMode, "<init>", "(IIII)V", ret);162ret = (*env)->NewObject(env, jc_DisplayMode, jc_DisplayMode_ctor, w, h, bpp, refrate);163CHECK_EXCEPTION();164JNI_COCOA_EXIT(env);165return ret;166}167168169/*170* Class: sun_awt_CGraphicsDevice171* Method: nativeGetXResolution172* Signature: (I)D173*/174JNIEXPORT jdouble JNICALL175Java_sun_awt_CGraphicsDevice_nativeGetXResolution176(JNIEnv *env, jclass class, jint displayID)177{178// CGDisplayScreenSize can return 0 if displayID is invalid179CGSize size = CGDisplayScreenSize(displayID);180CGRect rect = CGDisplayBounds(displayID);181// 1 inch == 25.4 mm182jfloat inches = size.width / 25.4f;183return inches > 0 ? rect.size.width / inches : DEFAULT_DEVICE_DPI;184}185186/*187* Class: sun_awt_CGraphicsDevice188* Method: nativeGetYResolution189* Signature: (I)D190*/191JNIEXPORT jdouble JNICALL192Java_sun_awt_CGraphicsDevice_nativeGetYResolution193(JNIEnv *env, jclass class, jint displayID)194{195// CGDisplayScreenSize can return 0 if displayID is invalid196CGSize size = CGDisplayScreenSize(displayID);197CGRect rect = CGDisplayBounds(displayID);198// 1 inch == 25.4 mm199jfloat inches = size.height / 25.4f;200return inches > 0 ? rect.size.height / inches : DEFAULT_DEVICE_DPI;201}202203/*204* Class: sun_awt_CGraphicsDevice205* Method: nativeGetBounds206* Signature: (I)Ljava/awt/Rectangle;207*/208JNIEXPORT jobject JNICALL209Java_sun_awt_CGraphicsDevice_nativeGetBounds210(JNIEnv *env, jclass class, jint displayID)211{212CGRect rect = CGDisplayBounds(displayID);213if (rect.size.width == 0) {214rect.size.width = DEFAULT_DEVICE_WIDTH;215}216if (rect.size.height == 0) {217rect.size.height = DEFAULT_DEVICE_HEIGHT;218}219return CGToJavaRect(env, rect);220}221222/*223* Class: sun_awt_CGraphicsDevice224* Method: nativeGetScreenInsets225* Signature: (I)D226*/227JNIEXPORT jobject JNICALL228Java_sun_awt_CGraphicsDevice_nativeGetScreenInsets229(JNIEnv *env, jclass class, jint displayID)230{231jobject ret = NULL;232__block NSRect frame = NSZeroRect;233__block NSRect visibleFrame = NSZeroRect;234JNI_COCOA_ENTER(env);235236[ThreadUtilities performOnMainThreadWaiting:YES block:^(){237NSArray *screens = [NSScreen screens];238for (NSScreen *screen in screens) {239NSDictionary *screenInfo = [screen deviceDescription];240NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"];241if ([screenID unsignedIntValue] == displayID){242frame = [screen frame];243visibleFrame = [screen visibleFrame];244break;245}246}247}];248// Convert between Cocoa's coordinate system and Java.249jint bottom = visibleFrame.origin.y - frame.origin.y;250jint top = frame.size.height - visibleFrame.size.height - bottom;251jint left = visibleFrame.origin.x - frame.origin.x;252jint right = frame.size.width - visibleFrame.size.width - left;253254DECLARE_CLASS_RETURN(jc_Insets, "java/awt/Insets", ret);255DECLARE_METHOD_RETURN(jc_Insets_ctor, jc_Insets, "<init>", "(IIII)V", ret);256ret = (*env)->NewObject(env, jc_Insets, jc_Insets_ctor, top, left, bottom, right);257258JNI_COCOA_EXIT(env);259260return ret;261}262263/*264* Class: sun_awt_CGraphicsDevice265* Method: nativeSetDisplayMode266* Signature: (IIIII)V267*/268JNIEXPORT void JNICALL269Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode270(JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate)271{272JNI_COCOA_ENTER(env);273CFArrayRef allModes = getAllValidDisplayModes(displayID);274CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate);275276__block CGError retCode = kCGErrorSuccess;277if (closestMatch != NULL) {278CGDisplayModeRetain(closestMatch);279[ThreadUtilities performOnMainThreadWaiting:YES block:^(){280CGDisplayConfigRef config;281retCode = CGBeginDisplayConfiguration(&config);282if (retCode == kCGErrorSuccess) {283CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL);284retCode = CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly);285}286CGDisplayModeRelease(closestMatch);287}];288} else {289JNU_ThrowIllegalArgumentException(env, "Invalid display mode");290}291292if (retCode != kCGErrorSuccess){293JNU_ThrowIllegalArgumentException(env, "Unable to set display mode!");294}295CFRelease(allModes);296JNI_COCOA_EXIT(env);297}298/*299* Class: sun_awt_CGraphicsDevice300* Method: nativeGetDisplayMode301* Signature: (I)Ljava/awt/DisplayMode302*/303JNIEXPORT jobject JNICALL304Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode305(JNIEnv *env, jclass class, jint displayID)306{307jobject ret = NULL;308// CGDisplayCopyDisplayMode can return NULL if displayID is invalid309CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID);310ret = createJavaDisplayMode(currentMode, env);311CGDisplayModeRelease(currentMode);312return ret;313}314315/*316* Class: sun_awt_CGraphicsDevice317* Method: nativeGetDisplayMode318* Signature: (I)[Ljava/awt/DisplayModes319*/320JNIEXPORT jobjectArray JNICALL321Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes322(JNIEnv *env, jclass class, jint displayID)323{324jobjectArray jreturnArray = NULL;325JNI_COCOA_ENTER(env);326CFArrayRef allModes = getAllValidDisplayModes(displayID);327328CFIndex numModes = allModes ? CFArrayGetCount(allModes): 0;329DECLARE_CLASS_RETURN(jc_DisplayMode, "java/awt/DisplayMode", NULL);330331jreturnArray = (*env)->NewObjectArray(env, (jsize)numModes, jc_DisplayMode, NULL);332if (!jreturnArray) {333NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects");334return nil;335}336337CFIndex n;338for (n=0; n < numModes; n++) {339CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);340if (cRef != NULL) {341jobject oneMode = createJavaDisplayMode(cRef, env);342(*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode);343if ((*env)->ExceptionOccurred(env)) {344(*env)->ExceptionDescribe(env);345(*env)->ExceptionClear(env);346continue;347}348(*env)->DeleteLocalRef(env, oneMode);349}350}351if (allModes) {352CFRelease(allModes);353}354JNI_COCOA_EXIT(env);355356return jreturnArray;357}358359/*360* Class: sun_awt_CGraphicsDevice361* Method: nativeGetScaleFactor362* Signature: (I)D363*/364JNIEXPORT jdouble JNICALL365Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor366(JNIEnv *env, jclass class, jint displayID)367{368__block jdouble ret = 1.0f;369370JNI_COCOA_ENTER(env);371372[ThreadUtilities performOnMainThreadWaiting:YES block:^(){373NSArray *screens = [NSScreen screens];374for (NSScreen *screen in screens) {375NSDictionary *screenInfo = [screen deviceDescription];376NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"];377if ([screenID unsignedIntValue] == displayID){378if ([screen respondsToSelector:@selector(backingScaleFactor)]) {379ret = [screen backingScaleFactor];380}381break;382}383}384}];385386JNI_COCOA_EXIT(env);387return ret;388}389390391