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