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