1 /*
   2  * Copyright (c) 2011, 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 "common.h"
  27 #import "com_sun_glass_ui_Screen.h"
  28 
  29 #import "GlassMacros.h"
  30 #import "GlassScreen.h"
  31 #import "GlassTimer.h"
  32 
  33 //#define VERBOSE
  34 #ifndef VERBOSE
  35     #define LOG(MSG, ...)
  36 #else
  37     #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  38 #endif
  39 
  40 #define MAX_DISPLAY_COUNT 1024
  41 
  42 CGFloat GetScreenScaleFactor(NSScreen *screen)
  43 {
  44     if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
  45         return [screen backingScaleFactor];
  46     } else {
  47         return [screen userSpaceScaleFactor];
  48     }
  49 }
  50 
  51 jobject createJavaScreen(JNIEnv *env, NSScreen* screen)
  52 {
  53     jobject jscreen = NULL;
  54 
  55     if (screen != nil)
  56     {
  57         jmethodID screenInit = (*env)->GetMethodID(env, jScreenClass,
  58                                                    "<init>",
  59                                                    "(JIIIIIIIIIIIF)V");
  60         GLASS_CHECK_EXCEPTION(env);
  61 
  62         // Note that NSDeviceResolution always reports 72 DPI, so we use Core Graphics API instead
  63         const CGDirectDisplayID displayID = [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
  64         CGSize size = CGDisplayScreenSize(displayID);
  65         CGRect rect = CGDisplayBounds(displayID);
  66         const CGFloat MM_PER_INCH = 25.4f; // 1 inch == 25.4 mm
  67         // Avoid division by zero, default to 72 DPI
  68         if (size.width == 0) size.width = rect.size.width * MM_PER_INCH / 72.f;
  69         if (size.height == 0) size.height = rect.size.height * MM_PER_INCH / 72.f;
  70         NSSize resolution = {rect.size.width / (size.width / MM_PER_INCH), rect.size.height / (size.height / MM_PER_INCH)};
  71 
  72         NSRect primaryFrame = [[[NSScreen screens] objectAtIndex:0] frame];
  73 
  74         if (floor(NSAppKitVersionNumber) > 1265) // NSAppKitVersionNumber10_9
  75         {
  76             // On MacOS X 10.10 beta the objects returned by [NSScreen screens] are released
  77             // without any notification. This means JavaFX must retain its own references to
  78             // avoid crashes when using them later on.
  79             // Note, this fix is deliberately allowing the objects to leak. This is the
  80             // safest and least intrusive fix appropriate for a maintenance release.
  81             // Screens are usually create and released when an external monitor is added
  82             // or removed, therefore the memory leaked should never grow too much.
  83             [screen retain];
  84         }
  85 
  86         jscreen = (jobject)(*env)->NewObject(env, jScreenClass, screenInit,
  87                                              ptr_to_jlong(screen),
  88 
  89                                              (jint)NSBitsPerPixelFromDepth([screen depth]),
  90 
  91                                              (jint)[screen frame].origin.x,
  92                                              (jint)(primaryFrame.size.height - [screen frame].size.height - [screen frame].origin.y),
  93                                              (jint)[screen frame].size.width,
  94                                              (jint)[screen frame].size.height,
  95 
  96                                              (jint)[screen visibleFrame].origin.x,
  97                                              (jint)(primaryFrame.size.height - [screen visibleFrame].size.height - [screen visibleFrame].origin.y),
  98                                              (jint)[screen visibleFrame].size.width,
  99                                              (jint)[screen visibleFrame].size.height,
 100 
 101 
 102                                              (jint)resolution.width,
 103                                              (jint)resolution.height,
 104                                              (jfloat)GetScreenScaleFactor(screen));
 105 
 106         GLASS_CHECK_EXCEPTION(env);
 107     }
 108 
 109     return jscreen;
 110 }
 111 
 112 jobjectArray createJavaScreens(JNIEnv* env) {
 113     //Update the Java notion of screens[]
 114     NSArray* screens = [NSScreen screens];
 115 
 116     if (jScreenClass == NULL)
 117     {
 118         jclass jcls = (*env)->FindClass(env, "com/sun/glass/ui/Screen");
 119         GLASS_CHECK_EXCEPTION(env);
 120         jScreenClass = (*env)->NewGlobalRef(env, jcls);
 121     }
 122 
 123     jobjectArray screenArray = (*env)->NewObjectArray(env,
 124                                                       [screens count],
 125                                                       jScreenClass,
 126                                                       NULL);
 127     GLASS_CHECK_EXCEPTION(env);
 128     for (NSUInteger index = 0; index < [screens count]; index++) {
 129         jobject javaScreen = createJavaScreen(env, [screens objectAtIndex:index]);
 130         (*env)->SetObjectArrayElement(env, screenArray, index, javaScreen);
 131         GLASS_CHECK_EXCEPTION(env);
 132     }
 133 
 134     return screenArray;
 135 }
 136 
 137 void GlassScreenDidChangeScreenParameters(JNIEnv *env)
 138 {
 139     if (jScreenNotifySettingsChanged == NULL)
 140     {
 141         jScreenNotifySettingsChanged = (*env)->GetStaticMethodID(env, jScreenClass, "notifySettingsChanged", "()V");
 142         GLASS_CHECK_EXCEPTION(env);
 143     }
 144 
 145     (*env)->CallStaticVoidMethod(env, jScreenClass, jScreenNotifySettingsChanged);
 146     GLASS_CHECK_EXCEPTION(env);
 147 }
 148 
 149 @implementation NSScreen (FullscreenAdditions)
 150 
 151 - (CGDirectDisplayID)enterFullscreenAndHideCursor:(BOOL)hide
 152 {
 153     CGDirectDisplayID displayID = 0;
 154 
 155     CGDisplayCount displayCount = 0;
 156     CGDirectDisplayID activeDisplays[MAX_DISPLAY_COUNT];
 157     CGDisplayErr err = CGGetActiveDisplayList(MAX_DISPLAY_COUNT, activeDisplays, &displayCount);
 158     if (err != kCGErrorSuccess)
 159     {
 160         NSLog(@"CGGetActiveDisplayList returned error: %d", err);
 161     }
 162     else
 163     {
 164         NSRect nsrect = [self frame];
 165 
 166         for (CGDisplayCount i=0; i<displayCount; i++)
 167         {
 168             CGRect cgrect = CGDisplayBounds(activeDisplays[i]);
 169             if ((nsrect.origin.x == cgrect.origin.x) && (nsrect.origin.y == cgrect.origin.y)
 170                 && (nsrect.size.width == cgrect.size.width) && (nsrect.size.height == cgrect.size.height))
 171             {
 172                 displayID = activeDisplays[i];
 173                 break;
 174             }
 175         }
 176 
 177 #if 0
 178         err = CGDisplayCapture(displayID);
 179 #endif
 180         if (displayID == kCGDirectMainDisplay)
 181         {
 182             [NSMenu setMenuBarVisible:NO];
 183         }
 184 
 185         if (err != kCGErrorSuccess)
 186         {
 187             NSLog(@"CGDisplayCapture returned error: %d", err);
 188             displayID = 0;
 189         }
 190         else
 191         {
 192             if (hide == YES)
 193             {
 194                 CGDisplayHideCursor(displayID);
 195             }
 196         }
 197     }
 198 
 199     return displayID;
 200 }
 201 
 202 - (void)exitFullscreen:(CGDirectDisplayID)displayID
 203 {
 204     if (displayID != 0)
 205     {
 206         if (displayID == kCGDirectMainDisplay)
 207         {
 208             [NSMenu setMenuBarVisible:YES];
 209         }
 210 #if 0
 211         CGDisplayRelease(displayID);
 212 #endif
 213         CGDisplayShowCursor(displayID);
 214     }
 215 }
 216 
 217 @end
 218