blob: 8d315edfe2b935e748ccb4d8c8d15a2d210e7c1c [file] [log] [blame]
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "LWCToolkit.h"
#import "ThreadUtilities.h"
/*
* Convert the mode string to the more convinient bits per pixel value
*/
static int getBPPFromModeString(CFStringRef mode)
{
if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
// This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits
// Java is not ready to work with this mode but we have to specify it as supported
return 30;
}
else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 32;
}
else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 16;
}
else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 8;
}
return 0;
}
static BOOL isValidDisplayMode(CGDisplayModeRef mode){
return (1 < CGDisplayModeGetWidth(mode) && 1 < CGDisplayModeGetHeight(mode));
}
static CFMutableArrayRef getAllValidDisplayModes(jint displayID){
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(displayID, NULL);
CFIndex numModes = CFArrayGetCount(allModes);
CFMutableArrayRef validModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes + 1, &kCFTypeArrayCallBacks);
CFIndex n;
for (n=0; n < numModes; n++) {
CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);
if (cRef != NULL && isValidDisplayMode(cRef)) {
CFArrayAppendValue(validModes, cRef);
}
}
CFRelease(allModes);
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID);
BOOL containsCurrentMode = NO;
numModes = CFArrayGetCount(validModes);
for (n=0; n < numModes; n++) {
if(CFArrayGetValueAtIndex(validModes, n) == currentMode){
containsCurrentMode = YES;
break;
}
}
if (!containsCurrentMode) {
CFArrayAppendValue(validModes, currentMode);
}
CGDisplayModeRelease(currentMode);
return validModes;
}
/*
* Find the best possible match in the list of display modes that we can switch to based on
* the provided parameters.
*/
static CGDisplayModeRef getBestModeForParameters(CFArrayRef allModes, int w, int h, int bpp, int refrate) {
CGDisplayModeRef bestGuess = NULL;
CFIndex numModes = CFArrayGetCount(allModes), n;
int thisBpp = 0;
for(n = 0; n < numModes; n++ ) {
CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);
if(cRef == NULL) {
continue;
}
CFStringRef modeString = CGDisplayModeCopyPixelEncoding(cRef);
thisBpp = getBPPFromModeString(modeString);
CFRelease(modeString);
if (thisBpp != bpp || (int)CGDisplayModeGetHeight(cRef) != h || (int)CGDisplayModeGetWidth(cRef) != w) {
// One of the key parameters does not match
continue;
}
if (refrate == 0) { // REFRESH_RATE_UNKNOWN
return cRef;
}
// Refresh rate might be 0 in display mode and we ask for specific display rate
// but if we do not find exact match then 0 refresh rate might be just Ok
if (CGDisplayModeGetRefreshRate(cRef) == refrate) {
// Exact match
return cRef;
}
if (CGDisplayModeGetRefreshRate(cRef) == 0) {
// Not exactly what was asked for, but may fit our needs if we don't find an exact match
bestGuess = cRef;
}
}
return bestGuess;
}
/*
* Create a new java.awt.DisplayMode instance based on provided CGDisplayModeRef
*/
static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env, jint displayID) {
jobject ret = NULL;
jint h, w, bpp, refrate;
JNF_COCOA_ENTER(env);
CFStringRef currentBPP = CGDisplayModeCopyPixelEncoding(mode);
bpp = getBPPFromModeString(currentBPP);
refrate = CGDisplayModeGetRefreshRate(mode);
h = CGDisplayModeGetHeight(mode);
w = CGDisplayModeGetWidth(mode);
CFRelease(currentBPP);
static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode");
static JNF_CTOR_CACHE(jc_DisplayMode_ctor, jc_DisplayMode, "(IIII)V");
ret = JNFNewObject(env, jc_DisplayMode_ctor, w, h, bpp, refrate);
JNF_COCOA_EXIT(env);
return ret;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetXResolution
* Signature: (I)D
*/
JNIEXPORT jdouble JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetXResolution
(JNIEnv *env, jclass class, jint displayID)
{
// TODO: this is the physically correct answer, but we probably want
// to use NSScreen API instead...
CGSize size = CGDisplayScreenSize(displayID);
CGRect rect = CGDisplayBounds(displayID);
// 1 inch == 25.4 mm
jfloat inches = size.width / 25.4f;
jfloat dpi = rect.size.width / inches;
return dpi;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetYResolution
* Signature: (I)D
*/
JNIEXPORT jdouble JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetYResolution
(JNIEnv *env, jclass class, jint displayID)
{
// TODO: this is the physically correct answer, but we probably want
// to use NSScreen API instead...
CGSize size = CGDisplayScreenSize(displayID);
CGRect rect = CGDisplayBounds(displayID);
// 1 inch == 25.4 mm
jfloat inches = size.height / 25.4f;
jfloat dpi = rect.size.height / inches;
return dpi;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetScreenInsets
* Signature: (I)D
*/
JNIEXPORT jobject JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetScreenInsets
(JNIEnv *env, jclass class, jint displayID)
{
jobject ret = NULL;
__block NSRect frame = NSZeroRect;
__block NSRect visibleFrame = NSZeroRect;
JNF_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
NSArray *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
NSDictionary *screenInfo = [screen deviceDescription];
NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"];
if ([screenID pointerValue] == displayID){
frame = [screen frame];
visibleFrame = [screen visibleFrame];
break;
}
}
}];
// Convert between Cocoa's coordinate system and Java.
jint bottom = visibleFrame.origin.y - frame.origin.y;
jint top = frame.size.height - visibleFrame.size.height - bottom;
jint left = visibleFrame.origin.x - frame.origin.x;
jint right = frame.size.width - visibleFrame.size.width - left;
static JNF_CLASS_CACHE(jc_Insets, "java/awt/Insets");
static JNF_CTOR_CACHE(jc_Insets_ctor, jc_Insets, "(IIII)V");
ret = JNFNewObject(env, jc_Insets_ctor, top, left, bottom, right);
JNF_COCOA_EXIT(env);
return ret;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeSetDisplayMode
* Signature: (IIIII)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_CGraphicsDevice_nativeSetDisplayMode
(JNIEnv *env, jclass class, jint displayID, jint w, jint h, jint bpp, jint refrate)
{
JNF_COCOA_ENTER(env);
CFArrayRef allModes = getAllValidDisplayModes(displayID);
CGDisplayModeRef closestMatch = getBestModeForParameters(allModes, (int)w, (int)h, (int)bpp, (int)refrate);
__block CGError retCode = kCGErrorSuccess;
if (closestMatch != NULL) {
CGDisplayModeRetain(closestMatch);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGDisplayConfigRef config;
retCode = CGBeginDisplayConfiguration(&config);
if (retCode == kCGErrorSuccess) {
CGConfigureDisplayWithDisplayMode(config, displayID, closestMatch, NULL);
retCode = CGCompleteDisplayConfiguration(config, kCGConfigureForAppOnly);
}
CGDisplayModeRelease(closestMatch);
}];
} else {
[JNFException raise:env as:kIllegalArgumentException reason:"Invalid display mode"];
}
if (retCode != kCGErrorSuccess){
[JNFException raise:env as:kIllegalArgumentException reason:"Unable to set display mode!"];
}
CFRelease(allModes);
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetDisplayMode
* Signature: (I)Ljava/awt/DisplayMode
*/
JNIEXPORT jobject JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetDisplayMode
(JNIEnv *env, jclass class, jint displayID)
{
jobject ret = NULL;
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(displayID);
ret = createJavaDisplayMode(currentMode, env, displayID);
CGDisplayModeRelease(currentMode);
return ret;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetDisplayMode
* Signature: (I)[Ljava/awt/DisplayModes
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetDisplayModes
(JNIEnv *env, jclass class, jint displayID)
{
jobjectArray jreturnArray = NULL;
JNF_COCOA_ENTER(env);
CFArrayRef allModes = getAllValidDisplayModes(displayID);
CFIndex numModes = CFArrayGetCount(allModes);
static JNF_CLASS_CACHE(jc_DisplayMode, "java/awt/DisplayMode");
jreturnArray = JNFNewObjectArray(env, &jc_DisplayMode, (jsize) numModes);
if (!jreturnArray) {
NSLog(@"CGraphicsDevice can't create java array of DisplayMode objects");
return nil;
}
CFIndex n;
for (n=0; n < numModes; n++) {
CGDisplayModeRef cRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, n);
if (cRef != NULL) {
jobject oneMode = createJavaDisplayMode(cRef, env, displayID);
(*env)->SetObjectArrayElement(env, jreturnArray, n, oneMode);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
continue;
}
(*env)->DeleteLocalRef(env, oneMode);
}
}
CFRelease(allModes);
JNF_COCOA_EXIT(env);
return jreturnArray;
}
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetScaleFactor
* Signature: (I)D
*/
JNIEXPORT jdouble JNICALL
Java_sun_awt_CGraphicsDevice_nativeGetScaleFactor
(JNIEnv *env, jclass class, jint displayID)
{
__block jdouble ret = 2.0f;
JNF_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
NSArray *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
NSDictionary *screenInfo = [screen deviceDescription];
NSNumber *screenID = [screenInfo objectForKey:@"NSScreenNumber"];
if ([screenID pointerValue] == displayID){
if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
ret = [screen backingScaleFactor];
}
break;
}
}
}];
JNF_COCOA_EXIT(env);
return ret;
}