diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index f498b546..c8c0aea3 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -216,6 +216,14 @@ namespace OpenTK.Graphics #endregion + /// + /// Hack for Mac OS full screen support. + /// + internal IGraphicsContext Implementation + { + get { return implementation; } + } + #region --- IGraphicsContext Members --- /// diff --git a/Source/OpenTK/Platform/MacOS/AglContext.cs b/Source/OpenTK/Platform/MacOS/AglContext.cs index 816fe5c3..8b7821dd 100644 --- a/Source/OpenTK/Platform/MacOS/AglContext.cs +++ b/Source/OpenTK/Platform/MacOS/AglContext.cs @@ -89,7 +89,7 @@ namespace OpenTK.Platform.MacOS AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_ACCUM_BLUE_SIZE, mode.AccumulatorFormat.Blue); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_ACCUM_ALPHA_SIZE, mode.AccumulatorFormat.Alpha); } - //AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_FULLSCREEN); + AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_FULLSCREEN); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_NONE); Debug.Unindent(); @@ -103,7 +103,9 @@ namespace OpenTK.Platform.MacOS IntPtr shareContextRef = IntPtr.Zero; // Choose a pixel format with the attributes we specified. - myAGLPixelFormat = Agl.aglChoosePixelFormat(IntPtr.Zero, 0, aglAttributes.ToArray()); + myAGLPixelFormat = Agl.aglChoosePixelFormat(QuartzDisplayDeviceDriver.MainDisplay, + 0, aglAttributes.ToArray()); + MyAGLReportError("aglChoosePixelFormat"); if (shareContext != null) @@ -212,6 +214,16 @@ namespace OpenTK.Platform.MacOS return Agl.aglGetCurrentContext(); } + internal void SetFullScreen() + { + Agl.aglSetFullScreen(contextRef, 0, 0, 0, 0); + } + internal void UnsetFullScreen() + { + SetDrawable(carbonWindow); + } + + #region IGraphicsContext Members bool first = false; public void SwapBuffers() diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs index bfd22492..067fcc2c 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs @@ -21,6 +21,12 @@ namespace OpenTK.Platform.MacOS.Carbon { internal short V; internal short H; + + public Point(int x, int y) + { + V = (short)x; + H = (short)y; + } } [StructLayout(LayoutKind.Sequential)] @@ -346,6 +352,24 @@ namespace OpenTK.Platform.MacOS.Carbon internal delegate OSStatus MacOSEventHandler(IntPtr inCaller, IntPtr inEvent, IntPtr userData); + internal enum WindowPartCode : short + { + inDesk = 0, + inNoWindow = 0, + inMenuBar = 1, + inSysWindow = 2, + inContent = 3, + inDrag = 4, + inGrow = 5, + inGoAway = 6, + inZoomIn = 7, + inZoomOut = 8, + inCollapseBox = 11, + inProxyIcon = 12, + inToolbarButton = 13, + inStructure = 15, + } + #endregion #region --- Carbon API Methods --- @@ -778,6 +802,51 @@ namespace OpenTK.Platform.MacOS.Carbon #endregion + [DllImport(carbon)] + internal static extern bool IsWindowCollapsed(IntPtr windowRef); + + [DllImport(carbon, EntryPoint = "CollapseWindow")] + static extern OSStatus _CollapseWindow(IntPtr windowRef, bool collapse); + + internal static void CollapseWindow(IntPtr windowRef, bool collapse) + { + OSStatus error = _CollapseWindow(windowRef, collapse); + + if (error != OSStatus.NoError) + { + throw new MacOSException(error); + } + } + + [DllImport(carbon, EntryPoint="IsWindowInStandardState")] + static extern bool _IsWindowInStandardState(IntPtr windowRef, IntPtr inIdealSize, IntPtr outIdealStandardState); + + internal static bool IsWindowInStandardState(IntPtr windowRef) + { + return _IsWindowInStandardState(windowRef, IntPtr.Zero, IntPtr.Zero); + } + + [DllImport(carbon, EntryPoint = "ZoomWindowIdeal")] + unsafe static extern OSStatus _ZoomWindowIdeal(IntPtr windowRef, short inPartCode, IntPtr toIdealSize); + + internal static void ZoomWindowIdeal(IntPtr windowRef, WindowPartCode inPartCode, ref Point toIdealSize) + { + Point pt = toIdealSize; + OSStatus error ; + IntPtr handle = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Point))); + Marshal.StructureToPtr(toIdealSize, handle, false); + + error = _ZoomWindowIdeal(windowRef, (short)inPartCode, handle); + + toIdealSize = (Point)Marshal.PtrToStructure(handle,typeof(Point)); + + Marshal.FreeHGlobal(handle); + + if (error != OSStatus.NoError) + { + throw new MacOSException(error); + } + } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs index 45c48e92..59c95c94 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs @@ -44,5 +44,8 @@ namespace OpenTK.Platform.MacOS.Carbon [DllImport(appServices, EntryPoint = "CGDisplayAvailableModes")] internal static extern IntPtr DisplayAvailableModes(IntPtr display); + [DllImport(appServices, EntryPoint = "CGDisplaySwitchToMode")] + internal static extern IntPtr DisplaySwitchToMode(IntPtr display, IntPtr displayMode); + } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index 5054cc8f..e4fdffc3 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -18,8 +18,8 @@ namespace OpenTK.Platform.MacOS using Carbon; using Graphics; - class CarbonGLNative : INativeGLWindow - { + class CarbonGLNative : INativeGLWindow + { CarbonWindowInfo window; CarbonInput mInputDriver; GraphicsContext context; @@ -45,9 +45,9 @@ namespace OpenTK.Platform.MacOS Application.Initialize(); } public CarbonGLNative() - : this(WindowClass.Document, - WindowAttributes.StandardDocument | - WindowAttributes.StandardHandler | + : this(WindowClass.Document, + WindowAttributes.StandardDocument | + WindowAttributes.StandardHandler | WindowAttributes.InWindowMenu | WindowAttributes.LiveResize) { @@ -58,7 +58,7 @@ namespace OpenTK.Platform.MacOS mWindowClass = @class; mWindowAttrib = attrib; - + } ~CarbonGLNative() { @@ -81,12 +81,12 @@ namespace OpenTK.Platform.MacOS if (disposing) { - mWindows.Remove(window.WindowRef); - - window.Dispose(); + mWindows.Remove(window.WindowRef); + + window.Dispose(); window = null; } - + DisposeUPP(); } @@ -131,12 +131,6 @@ namespace OpenTK.Platform.MacOS System.Diagnostics.Debug.Print("Attached window events."); } - - public void Activate() - { - API.SelectWindow(window.WindowRef); - } - void ConnectEvents() { mInputDriver = new CarbonInput(); @@ -167,6 +161,7 @@ namespace OpenTK.Platform.MacOS API.InstallWindowEventHandler(window.WindowRef, uppHandler, eventTypes, window.WindowRef, IntPtr.Zero); } + public string Title { get @@ -180,6 +175,10 @@ namespace OpenTK.Platform.MacOS } } + public void Activate() + { + API.SelectWindow(window.WindowRef); + } public void Show() { IntPtr parent = IntPtr.Zero; @@ -195,8 +194,8 @@ namespace OpenTK.Platform.MacOS public bool Visible { get { return API.IsWindowVisible(window.WindowRef); } - set - { + set + { if (value && Visible == false) Show(); else @@ -208,7 +207,6 @@ namespace OpenTK.Platform.MacOS get { return mIsDisposed; } } - public WindowPositionMethod WindowPositionMethod { get { return mPositionMethod; } @@ -232,7 +230,7 @@ namespace OpenTK.Platform.MacOS EventInfo evt = new EventInfo(inEvent); CarbonGLNative window = (CarbonGLNative)reference.Target; - + //Debug.Print("Processing {0} event for {1}.", evt, window.window); if (window == null) @@ -241,7 +239,7 @@ namespace OpenTK.Platform.MacOS return OSStatus.EventNotHandled; } - switch(evt.EventClass) + switch (evt.EventClass) { case EventClass.Window: return window.ProcessWindowEvent(inCaller, inEvent, evt, userData); @@ -282,7 +280,7 @@ namespace OpenTK.Platform.MacOS case KeyboardEventKind.RawKeyUp: GetCharCodes(inEvent, out code, out charCode); InputDriver.Keyboard[0][Keymap[code]] = false; - + return OSStatus.EventNotHandled; case KeyboardEventKind.RawKeyModifiersChanged: @@ -295,12 +293,11 @@ namespace OpenTK.Platform.MacOS } - private OSStatus ProcessWindowEvent(IntPtr inCaller, IntPtr inEvent, EventInfo evt, IntPtr userData) { System.Diagnostics.Debug.Assert(evt.EventClass == EventClass.Window); - switch(evt.WindowEventKind) + switch (evt.WindowEventKind) { case WindowEventKind.WindowClose: CancelEventArgs cancel = new CancelEventArgs(); @@ -343,7 +340,7 @@ namespace OpenTK.Platform.MacOS (int)pt.X, (int)(pt.Y - mTitlebarHeight)); - switch(evt.MouseEventKind) + switch (evt.MouseEventKind) { case MouseEventKind.MouseDown: button = API.GetEventMouseButton(inEvent); @@ -365,7 +362,7 @@ namespace OpenTK.Platform.MacOS break; - + case MouseEventKind.MouseUp: switch (button) { @@ -381,14 +378,14 @@ namespace OpenTK.Platform.MacOS InputDriver.Mouse[0][OpenTK.Input.MouseButton.Middle] = false; break; } - + button = API.GetEventMouseButton(inEvent); break; - + case MouseEventKind.MouseMoved: case MouseEventKind.MouseDragged: - + //Debug.Print("MouseMoved: {0}", InputDriver.Mouse[0].Position); return OSStatus.EventNotHandled; @@ -399,10 +396,9 @@ namespace OpenTK.Platform.MacOS return OSStatus.EventNotHandled; } - return OSStatus.EventNotHandled; + return OSStatus.EventNotHandled; } - private static void GetCharCodes(IntPtr inEvent, out MacOSKeyCode code, out char charCode) { code = API.GetEventKeyboardKeyCode(inEvent); @@ -439,7 +435,6 @@ namespace OpenTK.Platform.MacOS } - Rect GetRegion() { Rect retval = API.GetWindowBounds(window.WindowRef, WindowRegionCode.ContentRegion); @@ -527,14 +522,13 @@ namespace OpenTK.Platform.MacOS this.context.MakeCurrent(window); context = this.context; - + } public void DestroyWindow() { Dispose(); } - public void ProcessEvents() { Application.ProcessEvents(); @@ -544,7 +538,6 @@ namespace OpenTK.Platform.MacOS { throw new Exception("The method or operation is not implemented."); } - public void PointToScreen(ref System.Drawing.Point p) { throw new Exception("The method or operation is not implemented."); @@ -567,7 +560,7 @@ namespace OpenTK.Platform.MacOS public OpenTK.Input.IInputDriver InputDriver { - get + get { return mInputDriver; } @@ -586,7 +579,6 @@ namespace OpenTK.Platform.MacOS } public event CreateEvent Create; - public event DestroyEvent Destroy; #endregion @@ -604,11 +596,65 @@ namespace OpenTK.Platform.MacOS { get { - return windowState; + if (windowState == WindowState.Fullscreen) + return WindowState.Fullscreen; + + if (Carbon.API.IsWindowCollapsed(window.WindowRef)) + return WindowState.Minimized; + + + if (Carbon.API.IsWindowInStandardState(window.WindowRef)) + { + return WindowState.Maximized; + } + + return WindowState.Normal; } set { + if (value == WindowState) + return; + + Debug.Print("Switching window state from {0} to {1}", WindowState, value); + + if (WindowState == WindowState.Fullscreen) + { + ((AglContext)context.Implementation).UnsetFullScreen(); + } + if (WindowState == WindowState.Minimized) + { + API.CollapseWindow(window.WindowRef, false); + } + Point idealSize; + + switch (value) + { + case WindowState.Fullscreen: + ((AglContext)context.Implementation).SetFullScreen(); + context.Update(WindowInfo); + break; + + case WindowState.Maximized: + idealSize = new Point(9000, 9000); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomOut, ref idealSize); + break; + + case WindowState.Normal: + if (WindowState == WindowState.Maximized) + { + idealSize = new Point(); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomIn, ref idealSize); + } + break; + + case WindowState.Minimized: + API.CollapseWindow(window.WindowRef, true); + + break; + } + windowState = value; + } } @@ -621,16 +667,16 @@ namespace OpenTK.Platform.MacOS set { windowBorder = value; - + if (windowBorder == WindowBorder.Resizable) { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, - WindowAttributes.NoAttributes); + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, + WindowAttributes.NoAttributes); } else if (windowBorder == WindowBorder.Fixed) { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, - WindowAttributes.Resizable | WindowAttributes.FullZoom); + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, + WindowAttributes.Resizable | WindowAttributes.FullZoom); } } } diff --git a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs index 3ce26174..58756cc7 100644 --- a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs +++ b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs @@ -11,7 +11,13 @@ namespace OpenTK.Platform.MacOS class QuartzDisplayDeviceDriver : IDisplayDeviceDriver { static object display_lock = new object(); - + + static Dictionary displayMap = + new Dictionary(); + + static IntPtr mainDisplay; + internal static IntPtr MainDisplay { get { return mainDisplay; } } + static QuartzDisplayDeviceDriver() { //lock (display_lock) @@ -55,6 +61,9 @@ namespace OpenTK.Platform.MacOS // main display. bool primary = (i == 0); + if (primary) + mainDisplay = currentDisplay; + // gets current settings int currentWidth = CG.DisplayPixelsWide(currentDisplay); int currentHeight = CG.DisplayPixelsHigh(currentDisplay); @@ -94,6 +103,8 @@ namespace OpenTK.Platform.MacOS OpenTK.Graphics.DisplayDevice opentk_dev = new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res); + + displayMap.Add(opentk_dev, currentDisplay); } Debug.Unindent(); @@ -102,14 +113,55 @@ namespace OpenTK.Platform.MacOS #region IDisplayDeviceDriver Members + Dictionary storedModes = new Dictionary(); + + public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) { - + IntPtr display = displayMap[device]; + IntPtr currentModePtr = CG.DisplayCurrentMode(display); + + if (storedModes.ContainsKey(display) == false) + { + storedModes.Add(display, currentModePtr); + } + + IntPtr displayModesPtr = CG.DisplayAvailableModes(display); + CFArray displayModes = new CFArray(displayModesPtr); + + for (int j = 0; j < displayModes.Count; j++) + { + CFDictionary dict = new CFDictionary(displayModes[j]); + + int width = (int)dict.GetNumberValue("Width"); + int height = (int)dict.GetNumberValue("Height"); + int bpp = (int)dict.GetNumberValue("BitsPerPixel"); + double freq = dict.GetNumberValue("RefreshRate"); + + if (width == resolution.Width && + height == resolution.Height && + bpp == resolution.BitsPerPixel && + freq == resolution.RefreshRate) + { + CG.DisplaySwitchToMode(display, displayModes[j]); + + return true; + } + + } return false; } public bool TryRestoreResolution(DisplayDevice device) { + IntPtr display = displayMap[device]; + + if (storedModes.ContainsKey(display)) + { + CG.DisplaySwitchToMode(display, storedModes[display]); + return true; + } + return false; }