Better fullscreen handling.
authorOlle Håkansson <ollhak@gmail.com>
Sun, 20 Apr 2014 17:56:23 +0000 (19:56 +0200)
committerthefiddler <stapostol@gmail.com>
Thu, 24 Apr 2014 11:45:05 +0000 (13:45 +0200)
Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs
Source/OpenTK/Platform/MacOS/Cocoa/NSApplicationPresentationOptions.cs
Source/OpenTK/Platform/MacOS/Cocoa/NSEventModifierMask.cs
Source/OpenTK/Platform/MacOS/Cocoa/NSEventType.cs
Source/OpenTK/Platform/MacOS/Cocoa/NSTrackingAreaOptions.cs
Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs

index c21d415..1e38c57 100644 (file)
@@ -84,9 +84,18 @@ namespace OpenTK.Platform.MacOS.Carbon
         [DllImport(appServices,EntryPoint="CGDisplayCapture")]
         internal static extern CGDisplayErr DisplayCapture(IntPtr display);
 
+        [DllImport(appServices,EntryPoint="CGCaptureAllDisplays")]
+        internal static extern CGDisplayErr CaptureAllDisplays();
+
+        [DllImport(appServices,EntryPoint="CGShieldingWindowLevel")]
+        internal static extern uint ShieldingWindowLevel();
+
         [DllImport(appServices,EntryPoint="CGDisplayRelease")]
         internal static extern CGDisplayErr DisplayRelease(IntPtr display);
 
+        [DllImport(appServices,EntryPoint="CGReleaseAllDisplays")]
+        internal static extern CGDisplayErr DisplayReleaseAll();
+
         [DllImport(appServices, EntryPoint = "CGDisplayAvailableModes")]
         internal static extern IntPtr DisplayAvailableModes(IntPtr display);
 
index dbc349b..865f2a9 100755 (executable)
@@ -1,6 +1,6 @@
 namespace OpenTK.Platform.MacOS
 {
-    public enum NSApplicationPresentationOptions
+    enum NSApplicationPresentationOptions
     {
         Default = 0,
         AutoHideDock = 1,
index e889f6c..55fc51b 100755 (executable)
@@ -3,7 +3,7 @@
 namespace OpenTK.Platform.MacOS
 {
     [Flags]
-    public enum NSEventModifierMask : uint
+    enum NSEventModifierMask : uint
     {
         AlphaShiftKeyMask = 65536U,
         ShiftKeyMask = 131072U,
index a96e018..28d4eda 100755 (executable)
@@ -1,6 +1,6 @@
 namespace OpenTK.Platform.MacOS
 {
-    public enum NSEventType
+    enum NSEventType
     {
         LeftMouseDown = 1,
         LeftMouseUp = 2,
index 7ad420f..f146acf 100755 (executable)
@@ -3,7 +3,7 @@
 namespace OpenTK.Platform.MacOS
 {
     [Flags]
-    public enum NSTrackingAreaOptions
+    enum NSTrackingAreaOptions
     {
         MouseEnteredAndExited = 1,
         MouseMoved = 2,
index 95401c5..0546e9e 100644 (file)
@@ -32,7 +32,7 @@ namespace OpenTK.Platform.MacOS
         static readonly IntPtr selContentView = Selector.Get("contentView");
         static readonly IntPtr selConvertRectFromScreen = Selector.Get("convertRectFromScreen:");
         static readonly IntPtr selConvertRectToScreen = Selector.Get("convertRectToScreen:");
-        static readonly IntPtr selPerformClose = Selector.Get("performClose:");
+        //static readonly IntPtr selPerformClose = Selector.Get("performClose:");
         static readonly IntPtr selClose = Selector.Get("close");
         static readonly IntPtr selTitle = Selector.Get("title");
         static readonly IntPtr selSetTitle = Selector.Get("setTitle:");
@@ -63,15 +63,19 @@ namespace OpenTK.Platform.MacOS
         static readonly IntPtr selScrollingDeltaY = Selector.Get("scrollingDeltaY");
         static readonly IntPtr selButtonNumber = Selector.Get("buttonNumber");
         static readonly IntPtr selSetStyleMask = Selector.Get("setStyleMask:");
-        static readonly IntPtr selIsInFullScreenMode = Selector.Get("isInFullScreenMode");
         static readonly IntPtr selIsMiniaturized = Selector.Get("isMiniaturized");
         static readonly IntPtr selIsZoomed = Selector.Get("isZoomed");
         static readonly IntPtr selMiniaturize = Selector.Get("miniaturize:");
         static readonly IntPtr selDeminiaturize = Selector.Get("deminiaturize:");
         static readonly IntPtr selZoom = Selector.Get("zoom:");
-        static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
-        static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
-        
+        static readonly IntPtr selLevel = Selector.Get("level");
+        static readonly IntPtr selSetLevel = Selector.Get("setLevel:");
+        static readonly IntPtr selPresentationOptions = Selector.Get("presentationOptions");
+        static readonly IntPtr selSetPresentationOptions = Selector.Get("setPresentationOptions:");
+        //static readonly IntPtr selIsInFullScreenMode = Selector.Get("isInFullScreenMode");
+        //static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
+        //static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
+
         static readonly IntPtr NSDefaultRunLoopMode;
         static readonly IntPtr NSCursor;
 
@@ -95,11 +99,20 @@ namespace OpenTK.Platform.MacOS
         private OpenTK.Input.KeyboardKeyEventArgs keyArgs = new OpenTK.Input.KeyboardKeyEventArgs();
         private KeyPressEventArgs keyPressArgs = new KeyPressEventArgs((char)0);
 
+        bool exclusiveFullscreen = true;
+        bool fullscreenMode;
+        Rectangle preFullscreenSize;
+        int preFullscreenLevel;
+        string preFullscreenTitle;
+
         public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
         {
             // Create the window class
             windowClass = Class.AllocateClass("OpenTKWindow", "NSWindow");
             Class.RegisterMethod(windowClass, new WindowDidResizeDelegate(WindowDidResize), "windowDidResize:", "v@:@");
+            Class.RegisterMethod(windowClass, new WindowDidMoveDelegate(WindowDidMove), "windowDidMove:", "v@:@");
+            Class.RegisterMethod(windowClass, new WindowDidBecomeKeyDelegate(WindowDidBecomeKey), "windowDidBecomeKey:", "v@:@");
+            Class.RegisterMethod(windowClass, new WindowDidResignKeyDelegate(WindowDidResignKey), "windowDidResignKey:", "v@:@");
             Class.RegisterMethod(windowClass, new WindowShouldCloseDelegate(WindowShouldClose), "windowShouldClose:", "b@:@");
             Class.RegisterMethod(windowClass, new AcceptsFirstResponderDelegate(AcceptsFirstResponder), "acceptsFirstResponder", "b@:");
             Class.RegisterMethod(windowClass, new CanBecomeKeyWindowDelegate(CanBecomeKeyWindow), "canBecomeKeyWindow", "b@:");
@@ -115,18 +128,21 @@ namespace OpenTK.Platform.MacOS
             IntPtr windowPtr;
             windowPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc);
             windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, false);
+            windowInfo = new CocoaWindowInfo(windowPtr);
 
             // Set up behavior
             Cocoa.SendIntPtr(windowPtr, Selector.Get("setDelegate:"), windowPtr); // The window class acts as its own delegate 
             Cocoa.SendVoid(windowPtr, Selector.Get("cascadeTopLeftFromPoint:"), new System.Drawing.PointF(20, 20));
-            Cocoa.SendVoid(windowPtr, Selector.Get("setTitle:"), Cocoa.ToNSString(title));
             Cocoa.SendVoid(windowPtr, Selector.Get("makeKeyAndOrderFront:"), IntPtr.Zero);
+            SetTitle(title, false);
 
-            windowInfo = new CocoaWindowInfo(windowPtr);
             ResetTrackingArea();
         }
 
         delegate void WindowDidResizeDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
+        delegate void WindowDidMoveDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
+        delegate void WindowDidBecomeKeyDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
+        delegate void WindowDidResignKeyDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
         delegate bool WindowShouldCloseDelegate(IntPtr self, IntPtr cmd, IntPtr sender);
         delegate bool AcceptsFirstResponderDelegate(IntPtr self, IntPtr cmd);
         delegate bool CanBecomeKeyWindowDelegate(IntPtr self, IntPtr cmd);
@@ -136,6 +152,22 @@ namespace OpenTK.Platform.MacOS
         {
             ResetTrackingArea();
             GraphicsContext.CurrentContext.Update(windowInfo);
+            Resize(this, EventArgs.Empty);
+        }
+
+        private void WindowDidMove(IntPtr self, IntPtr cmd, IntPtr notification)
+        {
+            Move(this, EventArgs.Empty);
+        }
+
+        private void WindowDidBecomeKey(IntPtr self, IntPtr cmd, IntPtr notification)
+        {
+            FocusedChanged(this, EventArgs.Empty);
+        }
+
+        private void WindowDidResignKey(IntPtr self, IntPtr cmd, IntPtr notification)
+        {
+            FocusedChanged(this, EventArgs.Empty);
         }
 
         private bool WindowShouldClose(IntPtr self, IntPtr cmd, IntPtr sender)
@@ -188,17 +220,17 @@ namespace OpenTK.Platform.MacOS
         {
             // PerformClose is equivalent to pressing the close-button, which
             // does not work in a borderless window. Handle this special case.
-            if (WindowBorder == WindowBorder.Hidden)
+            //if (WindowBorder == WindowBorder.Hidden)
             {
                 if (WindowShouldClose(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
                 {
                     Cocoa.SendVoid(windowInfo.Handle, selClose);
                 }
             }
-            else
-            {
-                Cocoa.SendVoid(windowInfo.Handle, selPerformClose, windowInfo.Handle);
-            }
+//            else
+//            {
+//                Cocoa.SendVoid(windowInfo.Handle, selPerformClose, windowInfo.Handle);
+//            }
         }
 
         private KeyModifiers GetModifiers(NSEventModifierMask mask)
@@ -395,8 +427,7 @@ namespace OpenTK.Platform.MacOS
             }
             set
             {
-                Cocoa.SendIntPtr(windowInfo.Handle, selSetTitle, Cocoa.ToNSString(value));
-                TitleChanged(this, EventArgs.Empty);
+                SetTitle(value, true);
             }
         }
 
@@ -442,8 +473,19 @@ namespace OpenTK.Platform.MacOS
             var ws = WindowState;
             if (ws == WindowState.Fullscreen)
             {
-                IntPtr nsDictionary = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSDictionary"), Selector.Alloc), Selector.Init), Selector.Autorelease);
-                Cocoa.SendVoid(windowInfo.ViewHandle, selExitFullScreenModeWithOptions, nsDictionary);
+                //Cocoa.SendVoid(windowInfo.ViewHandle, selExitFullScreenModeWithOptions, IntPtr.Zero);
+
+                SetMenuVisible(true);
+                if (exclusiveFullscreen)
+                {
+                    OpenTK.Platform.MacOS.Carbon.CG.DisplayReleaseAll();
+                    Cocoa.SendVoid(windowInfo.Handle, selSetLevel, preFullscreenLevel);
+                }
+
+                Cocoa.SendVoid(windowInfo.Handle, selSetStyleMask, (uint)GetStyleMask(windowBorder));
+                Bounds = preFullscreenSize;
+                SetTitle(preFullscreenTitle, false); // For some reason, the title is lost
+                fullscreenMode = false;
             }
             else if (ws == WindowState.Maximized)
             {
@@ -459,7 +501,7 @@ namespace OpenTK.Platform.MacOS
         {
             get
             {
-                if (Cocoa.SendBool(windowInfo.ViewHandle, selIsInFullScreenMode))
+                if (fullscreenMode)
                     return WindowState.Fullscreen;
 
                 if (Cocoa.SendBool(windowInfo.Handle, selIsMiniaturized))
@@ -480,30 +522,46 @@ namespace OpenTK.Platform.MacOS
 
                 if (value == WindowState.Fullscreen)
                 {
-                    NSApplicationPresentationOptions options = 
-                        NSApplicationPresentationOptions.DisableAppleMenu |
-                        NSApplicationPresentationOptions.HideMenuBar |
-                        NSApplicationPresentationOptions.HideDock;
-
-                    // "Exclusive fullscreen"?
-                    //NSApplicationPresentationOptions.DisableProcessSwitching;
-
-                    var obj = Cocoa.SendIntPtr(Class.Get("NSNumber"), Selector.Get("numberWithUnsignedLong:"), (ulong)options);
-                    var key = Cocoa.ToNSString("NSFullScreenModeApplicationPresentationOptions");
-                    
-                    var nsDictionary = Cocoa.SendIntPtr(Class.Get("NSDictionary"), Selector.Alloc);
-                    nsDictionary = Cocoa.SendIntPtr(nsDictionary, Selector.Get("initWithObjectsAndKeys:"), obj, key, IntPtr.Zero);
-                    nsDictionary = Cocoa.SendIntPtr(nsDictionary, Selector.Autorelease);
-
-                    Cocoa.SendVoid(windowInfo.ViewHandle, selEnterFullScreenModeWithOptions, GetCurrentScreen(), nsDictionary);
-                }
+//                    NSApplicationPresentationOptions options = 
+//                        NSApplicationPresentationOptions.DisableAppleMenu |
+//                        NSApplicationPresentationOptions.HideMenuBar |
+//                        NSApplicationPresentationOptions.HideDock;
+//
+//                    // "Exclusive fullscreen"?
+//                    //NSApplicationPresentationOptions.DisableProcessSwitching;
+//
+//                    var obj = Cocoa.SendIntPtr(Class.Get("NSNumber"), Selector.Get("numberWithUnsignedLong:"), (ulong)options);
+//                    var key = Cocoa.ToNSString("NSFullScreenModeApplicationPresentationOptions");
+//                    
+//                    var nsDictionary = Cocoa.SendIntPtr(Class.Get("NSDictionary"), Selector.Alloc);
+//                    nsDictionary = Cocoa.SendIntPtr(nsDictionary, Selector.Get("initWithObjectsAndKeys:"), obj, key, IntPtr.Zero);
+//                    nsDictionary = Cocoa.SendIntPtr(nsDictionary, Selector.Autorelease);
+//
+//                    Cocoa.SendVoid(windowInfo.ViewHandle, selEnterFullScreenModeWithOptions, GetCurrentScreen(), nsDictionary);
+
+                    fullscreenMode = true;
+                    preFullscreenSize = Bounds;
+                    preFullscreenTitle = Title;
+                    var screenFrame = GetCurrentScreenFrame();
+
+                    if (exclusiveFullscreen)
+                    {
+                        preFullscreenLevel = Cocoa.SendInt(windowInfo.Handle, selLevel);
+                        var windowLevel = OpenTK.Platform.MacOS.Carbon.CG.ShieldingWindowLevel();
+
+                        OpenTK.Platform.MacOS.Carbon.CG.CaptureAllDisplays();
+                        Cocoa.SendVoid(windowInfo.Handle, selSetLevel, windowLevel);
+                    }
 
-                if (value == WindowState.Maximized)
+                    Cocoa.SendVoid(windowInfo.Handle, selSetStyleMask, (uint)NSWindowStyle.Borderless);
+                    Bounds = new Rectangle((int)screenFrame.X, (int)screenFrame.Y, (int)screenFrame.Width, (int)screenFrame.Height);
+                    SetMenuVisible(false);
+                }
+                else if (value == WindowState.Maximized)
                 {
                     Cocoa.SendVoid(windowInfo.Handle, selZoom, windowInfo.Handle);
                 }
-
-                if (value == WindowState.Minimized)
+                else if (value == WindowState.Minimized)
                 {
                     Cocoa.SendVoid(windowInfo.Handle, selMiniaturize, windowInfo.Handle);
                 }
@@ -544,11 +602,11 @@ namespace OpenTK.Platform.MacOS
             get
             {
                 var r = Cocoa.SendRect(windowInfo.Handle, selFrame);
-                return new Rectangle((int)r.X, (int)(GetCurrentScreenFrame().Height - r.Y), (int)r.Width, (int)r.Height);
+                return new Rectangle((int)r.X, (int)r.Y, (int)r.Width, (int)r.Height);
             }
             set
             {
-                Cocoa.SendVoid(windowInfo.Handle, selSetFrame, new RectangleF(value.X, GetCurrentScreenFrame().Height - value.Y, value.Width, value.Height), true);
+                Cocoa.SendVoid(windowInfo.Handle, selSetFrame, new RectangleF(value.X, value.Y, value.Width, value.Height), true);
             }
         }
 
@@ -746,5 +804,31 @@ namespace OpenTK.Platform.MacOS
         {
             Cocoa.SendVoid(NSCursor, visible ? selUnhide : selHide);
         }
+
+        private void SetMenuVisible(bool visible)
+        {
+            var options = (NSApplicationPresentationOptions)Cocoa.SendInt(NSApplication.Handle, selPresentationOptions);
+            var changedOptions = NSApplicationPresentationOptions.HideMenuBar | NSApplicationPresentationOptions.HideDock;
+
+            if (!visible)
+            {
+                options |= changedOptions;
+            }
+            else
+            {
+                options &= ~changedOptions;
+            }
+
+            Cocoa.SendVoid(NSApplication.Handle, selSetPresentationOptions, (int)options);
+        }
+
+        private void SetTitle(string newTitle, bool callEvent)
+        {
+            Cocoa.SendIntPtr(windowInfo.Handle, selSetTitle, Cocoa.ToNSString(newTitle));
+            if (callEvent)
+            {
+                TitleChanged(this, EventArgs.Empty);
+            }
+        }
     }
 }