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 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                                                    "(JIIIIIIIIIIIIIIIFFFF)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         jfloat scale = (jfloat)GetScreenScaleFactor(screen);
  87         jscreen = (jobject)(*env)->NewObject(env, jScreenClass, screenInit,
  88                                              ptr_to_jlong(screen),
  89 
  90                                              (jint)NSBitsPerPixelFromDepth([screen depth]),
  91 
  92                                              (jint)[screen frame].origin.x,
  93                                              (jint)(primaryFrame.size.height - [screen frame].size.height - [screen frame].origin.y),
  94                                              (jint)[screen frame].size.width,
  95                                              (jint)[screen frame].size.height,
  96 
  97                                              (jint)[screen frame].origin.x,
  98                                              (jint)(primaryFrame.size.height - [screen frame].size.height - [screen frame].origin.y),
  99                                              (jint)[screen frame].size.width,
 100                                              (jint)[screen frame].size.height,
 101 
 102                                              (jint)[screen visibleFrame].origin.x,
 103                                              (jint)(primaryFrame.size.height - [screen visibleFrame].size.height - [screen visibleFrame].origin.y),
 104                                              (jint)[screen visibleFrame].size.width,
 105                                              (jint)[screen visibleFrame].size.height,
 106 
 107 
 108                                              (jint)resolution.width,
 109                                              (jint)resolution.height,
 110                                              1.0f, 1.0f,
 111                                              scale, scale);
 112 
 113         GLASS_CHECK_EXCEPTION(env);
 114     }
 115 
 116     return jscreen;
 117 }
 118 
 119 jobjectArray createJavaScreens(JNIEnv* env) {
 120     //Update the Java notion of screens[]
 121     NSArray* screens = [NSScreen screens];
 122 
 123     if (jScreenClass == NULL)
 124     {
 125         jclass jcls = (*env)->FindClass(env, "com/sun/glass/ui/Screen");
 126         GLASS_CHECK_EXCEPTION(env);
 127         jScreenClass = (*env)->NewGlobalRef(env, jcls);
 128     }
 129 
 130     jobjectArray screenArray = (*env)->NewObjectArray(env,
 131                                                       [screens count],
 132                                                       jScreenClass,
 133                                                       NULL);
 134     GLASS_CHECK_EXCEPTION(env);
 135     for (NSUInteger index = 0; index < [screens count]; index++) {
 136         jobject javaScreen = createJavaScreen(env, [screens objectAtIndex:index]);
 137         (*env)->SetObjectArrayElement(env, screenArray, index, javaScreen);
 138         GLASS_CHECK_EXCEPTION(env);
 139     }
 140 
 141     return screenArray;
 142 }
 143 
 144 void GlassScreenDidChangeScreenParameters(JNIEnv *env)
 145 {
 146     if (jScreenNotifySettingsChanged == NULL)
 147     {
 148         jScreenNotifySettingsChanged = (*env)->GetStaticMethodID(env, jScreenClass, "notifySettingsChanged", "()V");
 149         GLASS_CHECK_EXCEPTION(env);
 150     }
 151 
 152     (*env)->CallStaticVoidMethod(env, jScreenClass, jScreenNotifySettingsChanged);
 153     GLASS_CHECK_EXCEPTION(env);
 154 }
 155 
 156 @implementation NSScreen (FullscreenAdditions)
 157 
 158 - (CGDirectDisplayID)enterFullscreenAndHideCursor:(BOOL)hide
 159 {
 160     CGDirectDisplayID displayID = 0;
 161 
 162     CGDisplayCount displayCount = 0;
 163     CGDirectDisplayID activeDisplays[MAX_DISPLAY_COUNT];
 164     CGDisplayErr err = CGGetActiveDisplayList(MAX_DISPLAY_COUNT, activeDisplays, &displayCount);
 165     if (err != kCGErrorSuccess)
 166     {
 167         NSLog(@"CGGetActiveDisplayList returned error: %d", err);
 168     }
 169     else
 170     {
 171         NSRect nsrect = [self frame];
 172 
 173         for (CGDisplayCount i=0; i<displayCount; i++)
 174         {
 175             CGRect cgrect = CGDisplayBounds(activeDisplays[i]);
 176             if ((nsrect.origin.x == cgrect.origin.x) && (nsrect.origin.y == cgrect.origin.y)
 177                 && (nsrect.size.width == cgrect.size.width) && (nsrect.size.height == cgrect.size.height))
 178             {
 179                 displayID = activeDisplays[i];
 180                 break;
 181             }
 182         }
 183 
 184 #if 0
 185         err = CGDisplayCapture(displayID);
 186 #endif
 187         if (displayID == kCGDirectMainDisplay)
 188         {
 189             [NSMenu setMenuBarVisible:NO];
 190         }
 191 
 192         if (err != kCGErrorSuccess)
 193         {
 194             NSLog(@"CGDisplayCapture returned error: %d", err);
 195             displayID = 0;
 196         }
 197         else
 198         {
 199             if (hide == YES)
 200             {
 201                 CGDisplayHideCursor(displayID);
 202             }
 203         }
 204     }
 205 
 206     return displayID;
 207 }
 208 
 209 - (void)exitFullscreen:(CGDirectDisplayID)displayID
 210 {
 211     if (displayID != 0)
 212     {
 213         if (displayID == kCGDirectMainDisplay)
 214         {
 215             [NSMenu setMenuBarVisible:YES];
 216         }
 217 #if 0
 218         CGDisplayRelease(displayID);
 219 #endif
 220         CGDisplayShowCursor(displayID);
 221     }
 222 }
 223 
 224 @end
 225