diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index f52bd3ed..ee17f232 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -816,6 +816,11 @@ + + + + + diff --git a/Source/OpenTK/Platform/LegacyInputDriver.cs b/Source/OpenTK/Platform/LegacyInputDriver.cs new file mode 100644 index 00000000..67461587 --- /dev/null +++ b/Source/OpenTK/Platform/LegacyInputDriver.cs @@ -0,0 +1,103 @@ +#region License +// +// LegacyInputDriver.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using OpenTK.Input; + +namespace OpenTK.Platform +{ + // IInputDriver implementation to satisfy INativeWindow + // while reducing code duplication. + class LegacyInputDriver : IInputDriver + { + List dummy_keyboard_list = new List(1); + List dummy_mice_list = new List(1); + + readonly LegacyJoystickDriver JoystickDriver = new LegacyJoystickDriver(); + + internal LegacyInputDriver() + { + dummy_mice_list.Add(new MouseDevice()); + Mouse[0].Description = "Standard Mouse"; + Mouse[0].NumberOfButtons = 3; + Mouse[0].NumberOfWheels = 1; + + dummy_keyboard_list.Add(new KeyboardDevice()); + Keyboard[0].Description = "Standard Keyboard"; + Keyboard[0].NumberOfKeys = 101; + Keyboard[0].NumberOfLeds = 3; + Keyboard[0].NumberOfFunctionKeys = 12; + } + + #region IInputDriver Members + + public void Poll() + { + } + + #endregion + + #region IKeyboardDriver Members + + public IList Keyboard + { + get { return dummy_keyboard_list; } + } + + #endregion + + #region IMouseDriver Members + + public IList Mouse + { + get { return dummy_mice_list; } + } + + #endregion + + #region IJoystickDriver Members + + public IList Joysticks + { + get { return JoystickDriver.Joysticks; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + } + + #endregion + } +} + diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs b/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs index 8cd64eeb..0f0d45f9 100644 --- a/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs +++ b/Source/OpenTK/Platform/MacOS/Cocoa/Cocoa.cs @@ -6,11 +6,16 @@ namespace OpenTK.Platform.MacOS { static class Cocoa { + static readonly IntPtr selUTF8String = Selector.Get("UTF8String"); + internal const string LibObjC = "/usr/lib/libobjc.dylib"; [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, ulong ulong1); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1); @@ -20,11 +25,20 @@ namespace OpenTK.Platform.MacOS [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2, IntPtr intPtr3); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, int int2, bool bool1); [DllImport(LibObjC, EntryPoint="objc_msgSend")] - public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, uint mask, IntPtr intPtr1, IntPtr intPtr2, bool bool1); + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, uint uint1, IntPtr intPtr1, IntPtr intPtr2, bool bool1); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, IntPtr intPtr1, IntPtr intPtr2); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static bool SendBool(IntPtr receiver, IntPtr selector); [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static bool SendBool(IntPtr receiver, IntPtr selector, int int1); @@ -32,12 +46,18 @@ namespace OpenTK.Platform.MacOS [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static void SendVoid(IntPtr receiver, IntPtr selector); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static void SendVoid(IntPtr receiver, IntPtr selector, uint uint1); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static void SendVoid(IntPtr receiver, IntPtr selector, IntPtr intPtr1); [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static void SendVoid(IntPtr receiver, IntPtr selector, IntPtr intPtr1, int int1); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static void SendVoid(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2); + [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static void SendVoid(IntPtr receiver, IntPtr selector, int int1); @@ -47,7 +67,52 @@ namespace OpenTK.Platform.MacOS [DllImport(LibObjC, EntryPoint="objc_msgSend")] public extern static void SendVoid(IntPtr receiver, IntPtr selector, PointF point1); - public static IntPtr ToNative(string str) + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static void SendVoid(IntPtr receiver, IntPtr selector, RectangleF rect1, bool bool1); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static int SendInt(IntPtr receiver, IntPtr selector); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static uint SendUint(IntPtr receiver, IntPtr selector); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static ushort SendUshort(IntPtr receiver, IntPtr selector); + + [DllImport(LibObjC, EntryPoint="objc_msgSend")] + public extern static float SendFloat(IntPtr receiver, IntPtr selector); + + [DllImport (LibObjC, EntryPoint="objc_msgSend_stret")] + extern static void SendRect(out System.Drawing.RectangleF retval, IntPtr receiver, IntPtr selector); + + [DllImport (LibObjC, EntryPoint="objc_msgSend_stret")] + extern static void SendRect(out System.Drawing.RectangleF retval, IntPtr receiver, IntPtr selector, RectangleF rect1); + + [DllImport (LibObjC, EntryPoint="objc_msgSend_stret")] + extern static void SendPoint(out System.Drawing.PointF retval, IntPtr receiver, IntPtr selector); + + public static RectangleF SendRect(IntPtr receiver, IntPtr selector) + { + RectangleF r; + SendRect(out r, receiver, selector); + return r; + } + + public static RectangleF SendRect(IntPtr receiver, IntPtr selector, RectangleF rect1) + { + RectangleF r; + SendRect(out r, receiver, selector, rect1); + return r; + } + + public static PointF SendPoint(IntPtr receiver, IntPtr selector) + { + PointF r; + SendPoint(out r, receiver, selector); + return r; + } + + public static IntPtr ToNSString(string str) { if (str == null) return IntPtr.Zero; @@ -63,6 +128,33 @@ namespace OpenTK.Platform.MacOS } } + public static string FromNSString(IntPtr handle) + { + return Marshal.PtrToStringAuto(SendIntPtr(handle, selUTF8String)); + } + + public static unsafe IntPtr ToNSImage(Image img) + { + using (System.IO.MemoryStream s = new System.IO.MemoryStream()) + { + img.Save(s, System.Drawing.Imaging.ImageFormat.Png); + byte[] b = s.ToArray(); + + fixed (byte* pBytes = b) + { + IntPtr nsData = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSData"), Selector.Alloc), + Selector.Get("initWithBytes:length:"), (IntPtr)pBytes, b.Length), + Selector.Autorelease); + + IntPtr nsImage = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSImage"), Selector.Alloc), + Selector.Get("initWithData:"), nsData), + Selector.Autorelease); + + return nsImage; + } + } + } + public static IntPtr GetStringConstant(IntPtr handle, string symbol) { var indirect = NS.GetSymbol(handle, symbol); @@ -74,7 +166,6 @@ namespace OpenTK.Platform.MacOS return IntPtr.Zero; return actual; - //return (NSString) Runtime.GetNSObject (actual); } public static IntPtr AppKitLibrary; diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/NSApplicationPresentationOptions.cs b/Source/OpenTK/Platform/MacOS/Cocoa/NSApplicationPresentationOptions.cs new file mode 100755 index 00000000..dbc349ba --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/Cocoa/NSApplicationPresentationOptions.cs @@ -0,0 +1,19 @@ +namespace OpenTK.Platform.MacOS +{ + public enum NSApplicationPresentationOptions + { + Default = 0, + AutoHideDock = 1, + HideDock = 2, + AutoHideMenuBar = 4, + HideMenuBar = 8, + DisableAppleMenu = 16, + DisableProcessSwitching = 32, + DisableForceQuit = 64, + DisableSessionTermination = 128, + DisableHideApplication = 256, + DisableMenuBarTransparency = 512, + FullScreen = 1024, + AutoHideToolbar = 2048, + } +} diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/NSEventModifierMask.cs b/Source/OpenTK/Platform/MacOS/Cocoa/NSEventModifierMask.cs new file mode 100755 index 00000000..e889f6c6 --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/Cocoa/NSEventModifierMask.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenTK.Platform.MacOS +{ + [Flags] + public enum NSEventModifierMask : uint + { + AlphaShiftKeyMask = 65536U, + ShiftKeyMask = 131072U, + ControlKeyMask = 262144U, + AlternateKeyMask = 524288U, + CommandKeyMask = 1048576U, + NumericPadKeyMask = 2097152U, + HelpKeyMask = 4194304U, + FunctionKeyMask = 8388608U, + DeviceIndependentModifierFlagsMask = 4294901760U, + } +} diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/NSEventType.cs b/Source/OpenTK/Platform/MacOS/Cocoa/NSEventType.cs new file mode 100755 index 00000000..a96e0186 --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/Cocoa/NSEventType.cs @@ -0,0 +1,37 @@ +namespace OpenTK.Platform.MacOS +{ + public enum NSEventType + { + LeftMouseDown = 1, + LeftMouseUp = 2, + RightMouseDown = 3, + RightMouseUp = 4, + MouseMoved = 5, + LeftMouseDragged = 6, + RightMouseDragged = 7, + MouseEntered = 8, + MouseExited = 9, + KeyDown = 10, + KeyUp = 11, + FlagsChanged = 12, + AppKitDefined = 13, + SystemDefined = 14, + ApplicationDefined = 15, + Periodic = 16, + CursorUpdate = 17, + Rotate = 18, + BeginGesture = 19, + EndGesture = 20, + ScrollWheel = 22, + TabletPoint = 23, + TabletProximity = 24, + OtherMouseDown = 25, + OtherMouseUp = 26, + OtherMouseDragged = 27, + Gesture = 29, + Magnify = 30, + Swipe = 31, + SmartMagnify = 32, + QuickLook = 33, + } +} diff --git a/Source/OpenTK/Platform/MacOS/Cocoa/NSTrackingAreaOptions.cs b/Source/OpenTK/Platform/MacOS/Cocoa/NSTrackingAreaOptions.cs new file mode 100755 index 00000000..7ad420ff --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/Cocoa/NSTrackingAreaOptions.cs @@ -0,0 +1,19 @@ +using System; + +namespace OpenTK.Platform.MacOS +{ + [Flags] + public enum NSTrackingAreaOptions + { + MouseEnteredAndExited = 1, + MouseMoved = 2, + CursorUpdate = 4, + ActiveWhenFirstResponder = 16, + ActiveInKeyWindow = 32, + ActiveInActiveApp = 64, + ActiveAlways = 128, + AssumeInside = 256, + InVisibleRect = 512, + EnabledDuringMouseDrag = 1024, + } +} diff --git a/Source/OpenTK/Platform/MacOS/CocoaContext.cs b/Source/OpenTK/Platform/MacOS/CocoaContext.cs index 45b9f323..d5927ea5 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaContext.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaContext.cs @@ -14,10 +14,10 @@ namespace OpenTK private IntPtr shareContextRef; static readonly IntPtr NSOpenGLContext = Class.Get("NSOpenGLContext"); - static readonly IntPtr currentContext = Selector.Get("currentContext"); - static readonly IntPtr flushBuffer = Selector.Get("flushBuffer"); - static readonly IntPtr makeCurrentContext = Selector.Get("makeCurrentContext"); - static readonly IntPtr update = Selector.Get("update"); + static readonly IntPtr selCurrentContext = Selector.Get("currentContext"); + static readonly IntPtr selFlushBuffer = Selector.Get("flushBuffer"); + static readonly IntPtr selMakeCurrentContext = Selector.Get("makeCurrentContext"); + static readonly IntPtr selUpdate = Selector.Get("update"); static CocoaContext() { @@ -155,12 +155,12 @@ namespace OpenTK public override void SwapBuffers() { - Cocoa.SendVoid(Handle.Handle, flushBuffer); + Cocoa.SendVoid(Handle.Handle, selFlushBuffer); } public override void MakeCurrent(IWindowInfo window) { - Cocoa.SendVoid(Handle.Handle, makeCurrentContext); + Cocoa.SendVoid(Handle.Handle, selMakeCurrentContext); } public override bool IsCurrent @@ -175,7 +175,7 @@ namespace OpenTK { get { - return Cocoa.SendIntPtr(NSOpenGLContext, currentContext); + return Cocoa.SendIntPtr(NSOpenGLContext, selCurrentContext); } } @@ -207,7 +207,7 @@ namespace OpenTK public override void Update(IWindowInfo window) { - Cocoa.SendVoid(Handle.Handle, update); + Cocoa.SendVoid(Handle.Handle, selUpdate); } #region IDisposable Members @@ -230,8 +230,8 @@ namespace OpenTK Debug.Print("Disposing of Cocoa context."); Cocoa.SendVoid(NSOpenGLContext, Selector.Get("clearCurrentContext")); - Cocoa.SendVoid(currentContext, Selector.Get("clearDrawable")); - Cocoa.SendVoid(currentContext, Selector.Get("release")); + Cocoa.SendVoid(Handle.Handle, Selector.Get("clearDrawable")); + Cocoa.SendVoid(Handle.Handle, Selector.Get("release")); Handle = ContextHandle.Zero; diff --git a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs index 2aa15044..95401c57 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaNativeWindow.cs @@ -1,116 +1,389 @@ using System; using OpenTK.Graphics; using System.Drawing; +using System.ComponentModel; +using OpenTK.Input; +using System.Diagnostics; namespace OpenTK.Platform.MacOS { class CocoaNativeWindow : INativeWindow { - public event System.EventHandler Move; - public event System.EventHandler Resize; - public event System.EventHandler Closing; - public event System.EventHandler Closed; - public event System.EventHandler Disposed; - public event System.EventHandler IconChanged; - public event System.EventHandler TitleChanged; - public event System.EventHandler VisibleChanged; - public event System.EventHandler FocusedChanged; - public event System.EventHandler WindowBorderChanged; - public event System.EventHandler WindowStateChanged; - public event System.EventHandler KeyDown; - public event System.EventHandler KeyPress; - public event System.EventHandler KeyUp; - public event System.EventHandler MouseLeave; - public event System.EventHandler MouseEnter; + public event EventHandler Move = delegate { }; + public event EventHandler Resize = delegate { }; + public event EventHandler Closing = delegate { }; + public event EventHandler Closed = delegate { }; + public event EventHandler Disposed = delegate { }; + public event EventHandler IconChanged = delegate { }; + public event EventHandler TitleChanged = delegate { }; + public event EventHandler VisibleChanged = delegate { }; + public event EventHandler FocusedChanged = delegate { }; + public event EventHandler WindowBorderChanged = delegate { }; + public event EventHandler WindowStateChanged = delegate { }; + public event EventHandler KeyDown = delegate { }; + public event EventHandler KeyPress = delegate { }; + public event EventHandler KeyUp = delegate { }; + public event EventHandler MouseLeave = delegate { }; + public event EventHandler MouseEnter = delegate { }; - static readonly IntPtr nextEventMatchingMask = Selector.Get("nextEventMatchingMask:untilDate:inMode:dequeue:"); - static readonly IntPtr sendEvent = Selector.Get("sendEvent:"); - static readonly IntPtr updateWindows = Selector.Get("updateWindows"); - static readonly IntPtr contentView = Selector.Get("contentView"); + static readonly IntPtr selNextEventMatchingMask = Selector.Get("nextEventMatchingMask:untilDate:inMode:dequeue:"); + static readonly IntPtr selSendEvent = Selector.Get("sendEvent:"); + static readonly IntPtr selUpdateWindows = Selector.Get("updateWindows"); + 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 selClose = Selector.Get("close"); + static readonly IntPtr selTitle = Selector.Get("title"); + static readonly IntPtr selSetTitle = Selector.Get("setTitle:"); + static readonly IntPtr selSetApplicationIconImage = Selector.Get("setApplicationIconImage:"); + static readonly IntPtr selIsKeyWindow = Selector.Get("isKeyWindow"); + static readonly IntPtr selIsVisible = Selector.Get("isVisible"); + static readonly IntPtr selSetIsVisible = Selector.Get("setIsVisible:"); + static readonly IntPtr selFrame = Selector.Get("frame"); + static readonly IntPtr selBounds = Selector.Get("bounds"); + static readonly IntPtr selScreen = Selector.Get("screen"); + static readonly IntPtr selSetFrame = Selector.Get("setFrame:display:"); + static readonly IntPtr selConvertRectToBacking = Selector.Get("convertRectToBacking:"); + static readonly IntPtr selConvertRectFromBacking = Selector.Get("convertRectFromBacking:"); + static readonly IntPtr selFrameRectForContentRect = Selector.Get("frameRectForContentRect:"); + static readonly IntPtr selType = Selector.Get("type"); + static readonly IntPtr selKeyCode = Selector.Get("keyCode"); + static readonly IntPtr selModifierFlags = Selector.Get("modifierFlags"); + static readonly IntPtr selIsARepeat = Selector.Get("isARepeat"); + static readonly IntPtr selCharactersIgnoringModifiers = Selector.Get("charactersIgnoringModifiers"); + static readonly IntPtr selAddTrackingArea = Selector.Get("addTrackingArea:"); + static readonly IntPtr selRemoveTrackingArea = Selector.Get("removeTrackingArea:"); + static readonly IntPtr selTrackingArea = Selector.Get("trackingArea"); + static readonly IntPtr selInitWithRect = Selector.Get("initWithRect:options:owner:userInfo:"); + static readonly IntPtr selOwner = Selector.Get("owner"); + static readonly IntPtr selLocationInWindowOwner = Selector.Get("locationInWindow"); + static readonly IntPtr selHide = Selector.Get("hide"); + static readonly IntPtr selUnhide = Selector.Get("unhide"); + 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 NSDefaultRunLoopMode; + static readonly IntPtr NSCursor; static CocoaNativeWindow() { Cocoa.Initialize(); NSDefaultRunLoopMode = Cocoa.GetStringConstant(Cocoa.FoundationLibrary, "NSDefaultRunLoopMode"); + NSCursor = Class.Get("NSCursor"); } private CocoaWindowInfo windowInfo; private IntPtr windowClass; + private IntPtr trackingArea; + private bool disposed = false; + private bool exists = true; + private bool cursorVisible = true; + private System.Drawing.Icon icon; + private LegacyInputDriver inputDriver = new LegacyInputDriver(); + private WindowBorder windowBorder = WindowBorder.Resizable; + private MacOSKeyMap keyMap = new MacOSKeyMap(); + private OpenTK.Input.KeyboardKeyEventArgs keyArgs = new OpenTK.Input.KeyboardKeyEventArgs(); + private KeyPressEventArgs keyPressArgs = new KeyPressEventArgs((char)0); 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 WindowShouldCloseDelegate(WindowShouldClose), "windowShouldClose:", "b@:@"); + Class.RegisterMethod(windowClass, new AcceptsFirstResponderDelegate(AcceptsFirstResponder), "acceptsFirstResponder", "b@:"); + Class.RegisterMethod(windowClass, new CanBecomeKeyWindowDelegate(CanBecomeKeyWindow), "canBecomeKeyWindow", "b@:"); + Class.RegisterMethod(windowClass, new CanBecomeMainWindowDelegate(CanBecomeMainWindow), "canBecomeMainWindow", "b@:"); + Class.RegisterClass(windowClass); // Create window instance var contentRect = new System.Drawing.RectangleF(x, y, width, height); - var style = NSWindowStyle.Titled | NSWindowStyle.Resizable; + var style = GetStyleMask(windowBorder); var bufferingType = NSBackingStore.Buffered; IntPtr windowPtr; windowPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc); windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, false); - windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Autorelease); // 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.ToNative(title)); + Cocoa.SendVoid(windowPtr, Selector.Get("setTitle:"), Cocoa.ToNSString(title)); Cocoa.SendVoid(windowPtr, Selector.Get("makeKeyAndOrderFront:"), IntPtr.Zero); windowInfo = new CocoaWindowInfo(windowPtr); + ResetTrackingArea(); } delegate void WindowDidResizeDelegate(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); + delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd); - void WindowDidResize(IntPtr self, IntPtr cmd, IntPtr notification) + private void WindowDidResize(IntPtr self, IntPtr cmd, IntPtr notification) { + ResetTrackingArea(); GraphicsContext.CurrentContext.Update(windowInfo); } - public static IntPtr GetView(IntPtr windowHandle) + private bool WindowShouldClose(IntPtr self, IntPtr cmd, IntPtr sender) { - return Cocoa.SendIntPtr(windowHandle, contentView); + var cancelArgs = new CancelEventArgs(); + Closing(this, cancelArgs); + + if (!cancelArgs.Cancel) + { + Closed(this, EventArgs.Empty); + return true; + } + + return false; + } + + private bool AcceptsFirstResponder(IntPtr self, IntPtr cmd) + { + return true; + } + + private bool CanBecomeKeyWindow(IntPtr self, IntPtr cmd) + { + return true; + } + + private bool CanBecomeMainWindow(IntPtr self, IntPtr cmd) + { + return true; + } + + private void ResetTrackingArea() + { + var owner = windowInfo.ViewHandle; + if (trackingArea != IntPtr.Zero) + { + Cocoa.SendVoid(owner, selRemoveTrackingArea, trackingArea); + } + + var ownerBounds = Cocoa.SendRect(owner, selBounds); + var options = (int)(NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseMoved); + + trackingArea = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSTrackingArea"), Selector.Alloc), + selInitWithRect, ownerBounds, options, owner, IntPtr.Zero); + + Cocoa.SendVoid(owner, selAddTrackingArea, trackingArea); } public void Close() { - throw new System.NotImplementedException(); + // 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 (WindowShouldClose(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) + { + Cocoa.SendVoid(windowInfo.Handle, selClose); + } + } + else + { + Cocoa.SendVoid(windowInfo.Handle, selPerformClose, windowInfo.Handle); + } + } + + private KeyModifiers GetModifiers(NSEventModifierMask mask) + { + OpenTK.Input.KeyModifiers modifiers = 0; + if ((mask & NSEventModifierMask.ControlKeyMask) != 0) modifiers |= OpenTK.Input.KeyModifiers.Control; + if ((mask & NSEventModifierMask.ShiftKeyMask) != 0) modifiers |= OpenTK.Input.KeyModifiers.Shift; + if ((mask & NSEventModifierMask.AlternateKeyMask) != 0) modifiers |= OpenTK.Input.KeyModifiers.Alt; + return modifiers; + } + + private void GetKey(ushort keyCode, NSEventModifierMask modifierFlags, OpenTK.Input.KeyboardKeyEventArgs args) + { + OpenTK.Input.Key key; + if (!keyMap.TryGetValue((OpenTK.Platform.MacOS.Carbon.MacOSKeyCode)keyCode, out key)) + { + key = OpenTK.Input.Key.Unknown; + } + + args.Key = key; + args.Modifiers = GetModifiers(modifierFlags); + args.ScanCode = (uint)keyCode; + } + + private MouseButton GetMouseButton(int cocoaButtonIndex) + { + if (cocoaButtonIndex == 0) return MouseButton.Left; + if (cocoaButtonIndex == 1) return MouseButton.Right; + if (cocoaButtonIndex == 2) return MouseButton.Middle; + if (cocoaButtonIndex >= (int)MouseButton.LastButton) + return MouseButton.LastButton; + + return (MouseButton)cocoaButtonIndex; } public void ProcessEvents() { - var e = Cocoa.SendIntPtr(NSApplication.Handle, nextEventMatchingMask, uint.MaxValue, IntPtr.Zero, NSDefaultRunLoopMode, true); + var e = Cocoa.SendIntPtr(NSApplication.Handle, selNextEventMatchingMask, uint.MaxValue, IntPtr.Zero, NSDefaultRunLoopMode, true); if (e == IntPtr.Zero) return; - Cocoa.SendVoid(NSApplication.Handle, sendEvent, e); - Cocoa.SendVoid(NSApplication.Handle, updateWindows); + var type = (NSEventType)Cocoa.SendInt(e, selType); + switch (type) + { + case NSEventType.KeyDown: + { + var keyCode = Cocoa.SendUshort(e, selKeyCode); + var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); + var isARepeat = Cocoa.SendBool(e, selIsARepeat); + GetKey(keyCode, modifierFlags, keyArgs); + InputDriver.Keyboard[0].SetKey(keyArgs.Key, keyArgs.ScanCode, true); + + if (!isARepeat || InputDriver.Keyboard[0].KeyRepeat) + { + KeyDown(this, keyArgs); + } + + var s = Cocoa.FromNSString(Cocoa.SendIntPtr(e, selCharactersIgnoringModifiers)); + foreach (var c in s) + { + int intVal = (int)c; + if (!Char.IsControl(c) && (intVal < 63232 || intVal > 63235)) + { + // For some reason, arrow keys (mapped 63232-63235) are seen as non-control characters, so get rid of those. + + keyPressArgs.KeyChar = c; + KeyPress(this, keyPressArgs); + } + } + + // Steal all keydown events to avoid the annoying "bleep" sound. + return; + } + + case NSEventType.KeyUp: + { + var keyCode = Cocoa.SendUshort(e, selKeyCode); + var modifierFlags = (NSEventModifierMask)Cocoa.SendUint(e, selModifierFlags); + + GetKey(keyCode, modifierFlags, keyArgs); + InputDriver.Keyboard[0].SetKey(keyArgs.Key, keyArgs.ScanCode, false); + + KeyUp(this, keyArgs); + } + break; + + case NSEventType.MouseEntered: + { + var eventTrackingArea = Cocoa.SendIntPtr(e, selTrackingArea); + var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner); + if (trackingAreaOwner == windowInfo.ViewHandle) + { + if (!cursorVisible) + { + SetCursorVisible(false); + } + + MouseEnter(this, EventArgs.Empty); + } + } + break; + + case NSEventType.MouseExited: + { + var eventTrackingArea = Cocoa.SendIntPtr(e, selTrackingArea); + var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner); + if (trackingAreaOwner == windowInfo.ViewHandle) + { + if (!cursorVisible) + { + SetCursorVisible(true); + } + + MouseLeave(this, EventArgs.Empty); + } + } + break; + + case NSEventType.MouseMoved: + { + var pf = Cocoa.SendPoint(e, selLocationInWindowOwner); + var p = new Point((int)pf.X, (int)pf.Y); + + var s = ClientSize; + if (p.X < 0) p.X = 0; + if (p.Y < 0) p.Y = 0; + if (p.X > s.Width) p.X = s.Width; + if (p.Y > s.Height) p.Y = s.Height; + p.Y = s.Height - p.Y; + + InputDriver.Mouse[0].Position = p; + } + break; + + case NSEventType.ScrollWheel: + { + var scrollingDelta = Cocoa.SendFloat(e, selScrollingDeltaY); + InputDriver.Mouse[0].WheelPrecise += scrollingDelta; + } + break; + + case NSEventType.LeftMouseDown: + case NSEventType.RightMouseDown: + case NSEventType.OtherMouseDown: + { + var buttonNumber = Cocoa.SendInt(e, selButtonNumber); + InputDriver.Mouse[0][GetMouseButton(buttonNumber)] = true; + } + break; + + case NSEventType.LeftMouseUp: + case NSEventType.RightMouseUp: + case NSEventType.OtherMouseUp: + { + var buttonNumber = Cocoa.SendInt(e, selButtonNumber); + InputDriver.Mouse[0][GetMouseButton(buttonNumber)] = false; + } + break; + } + + Cocoa.SendVoid(NSApplication.Handle, selSendEvent, e); + Cocoa.SendVoid(NSApplication.Handle, selUpdateWindows); } public System.Drawing.Point PointToClient(System.Drawing.Point point) { - throw new System.NotImplementedException(); + var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectFromScreen, new RectangleF(point.X, point.Y, 0, 0)); + return new Point((int)r.X, (int)(GetContentViewFrame().Height - GetCurrentScreenFrame().Height - r.Y)); } public System.Drawing.Point PointToScreen(System.Drawing.Point point) { - throw new System.NotImplementedException(); + var r = Cocoa.SendRect(windowInfo.Handle, selConvertRectToScreen, new RectangleF(point.X, point.Y, 0, 0)); + return new Point((int)r.X, (int)(-GetContentViewFrame().Height + GetCurrentScreenFrame().Height - r.Y)); } public System.Drawing.Icon Icon { - get - { - throw new System.NotImplementedException(); - } + get { return icon; } set { - throw new System.NotImplementedException(); + icon = value; + Cocoa.SendVoid(NSApplication.Handle, selSetApplicationIconImage, Cocoa.ToNSImage(icon.ToBitmap())); + IconChanged(this, EventArgs.Empty); } } @@ -118,11 +391,12 @@ namespace OpenTK.Platform.MacOS { get { - throw new System.NotImplementedException(); + return Cocoa.FromNSString(Cocoa.SendIntPtr(windowInfo.Handle, selTitle)); } set { - throw new System.NotImplementedException(); + Cocoa.SendIntPtr(windowInfo.Handle, selSetTitle, Cocoa.ToNSString(value)); + TitleChanged(this, EventArgs.Empty); } } @@ -130,7 +404,7 @@ namespace OpenTK.Platform.MacOS { get { - throw new System.NotImplementedException(); + return Cocoa.SendBool(windowInfo.Handle, selIsKeyWindow); } } @@ -138,12 +412,12 @@ namespace OpenTK.Platform.MacOS { get { - //throw new System.NotImplementedException(); - return true; + return Cocoa.SendBool(windowInfo.Handle, selIsVisible); } set { - //throw new System.NotImplementedException(); + Cocoa.SendVoid(windowInfo.Handle, selSetIsVisible, value); + VisibleChanged(this, EventArgs.Empty); } } @@ -151,7 +425,7 @@ namespace OpenTK.Platform.MacOS { get { - return true; + return exists; } } @@ -163,75 +437,162 @@ namespace OpenTK.Platform.MacOS } } + private void RestoreWindowState() + { + 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); + } + else if (ws == WindowState.Maximized) + { + Cocoa.SendVoid(windowInfo.Handle, selZoom, windowInfo.Handle); + } + else if (ws == WindowState.Minimized) + { + Cocoa.SendVoid(windowInfo.Handle, selDeminiaturize, windowInfo.Handle); + } + } + public WindowState WindowState { get { - throw new System.NotImplementedException(); + if (Cocoa.SendBool(windowInfo.ViewHandle, selIsInFullScreenMode)) + return WindowState.Fullscreen; + + if (Cocoa.SendBool(windowInfo.Handle, selIsMiniaturized)) + return WindowState.Minimized; + + if (Cocoa.SendBool(windowInfo.Handle, selIsZoomed)) + return WindowState.Maximized; + + return WindowState.Normal; } set { - throw new System.NotImplementedException(); + var oldState = WindowState; + if (oldState == value) + return; + + RestoreWindowState(); + + 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); + } + + if (value == WindowState.Maximized) + { + Cocoa.SendVoid(windowInfo.Handle, selZoom, windowInfo.Handle); + } + + if (value == WindowState.Minimized) + { + Cocoa.SendVoid(windowInfo.Handle, selMiniaturize, windowInfo.Handle); + } + + WindowStateChanged(this, EventArgs.Empty); + WindowDidResize(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } } public WindowBorder WindowBorder { - get - { - throw new System.NotImplementedException(); + get + { + return windowBorder; } set { - throw new System.NotImplementedException(); + windowBorder = value; + Cocoa.SendVoid(windowInfo.Handle, selSetStyleMask, (uint)GetStyleMask(windowBorder)); + WindowBorderChanged(this, EventArgs.Empty); } } + private static NSWindowStyle GetStyleMask(WindowBorder windowBorder) + { + switch (windowBorder) + { + case WindowBorder.Resizable: return NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled | NSWindowStyle.Resizable; + case WindowBorder.Fixed: return NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled; + case WindowBorder.Hidden: return NSWindowStyle.Borderless; + } + + return (NSWindowStyle)0; + } + public System.Drawing.Rectangle Bounds { get { - throw new System.NotImplementedException(); + var r = Cocoa.SendRect(windowInfo.Handle, selFrame); + return new Rectangle((int)r.X, (int)(GetCurrentScreenFrame().Height - r.Y), (int)r.Width, (int)r.Height); } set { - throw new System.NotImplementedException(); + Cocoa.SendVoid(windowInfo.Handle, selSetFrame, new RectangleF(value.X, GetCurrentScreenFrame().Height - value.Y, value.Width, value.Height), true); } } public System.Drawing.Point Location { - get - { - throw new System.NotImplementedException(); + get + { + return Bounds.Location; } set { - throw new System.NotImplementedException(); + var b = Bounds; + b.Location = value; + Bounds = b; } } public System.Drawing.Size Size { - get - { - throw new System.NotImplementedException(); + get + { + return Bounds.Size; } set { - throw new System.NotImplementedException(); + var b = Bounds; + b.Y -= Bounds.Height; + b.Y += value.Height; + b.Size = value; + Bounds = b; } } public int X { - get + get { - throw new System.NotImplementedException(); + return Bounds.X; } set { - throw new System.NotImplementedException(); + var b = Bounds; + b.X = value; + Bounds = b; } } @@ -239,35 +600,35 @@ namespace OpenTK.Platform.MacOS { get { - throw new System.NotImplementedException(); + return Bounds.Y; } set { - throw new System.NotImplementedException(); + var b = Bounds; + b.Y = value; + Bounds = b; } } public int Width { - get - { - throw new System.NotImplementedException(); - } + get { return ClientRectangle.Width; } set { - throw new System.NotImplementedException(); + var s = Size; + s.Width = value; + ClientSize = s; } } public int Height { - get - { - throw new System.NotImplementedException(); - } + get { return ClientRectangle.Height; } set { - throw new System.NotImplementedException(); + var s = Size; + s.Height = value; + ClientSize = s; } } @@ -275,23 +636,27 @@ namespace OpenTK.Platform.MacOS { get { - throw new System.NotImplementedException(); + var contentViewBounds = Cocoa.SendRect(windowInfo.ViewHandle, selBounds); + var bounds = Cocoa.SendRect(windowInfo.Handle, selConvertRectToBacking, contentViewBounds); + return new Rectangle((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height); } - set + set { - throw new System.NotImplementedException(); + ClientSize = value.Size; // Just set size, to be consistent with WinGLNative. } } public System.Drawing.Size ClientSize { - get + get { - throw new System.NotImplementedException(); + return ClientRectangle.Size; } set { - throw new System.NotImplementedException(); + var r_scaled = Cocoa.SendRect(windowInfo.Handle, selConvertRectFromBacking, new RectangleF(PointF.Empty, value)); + var r = Cocoa.SendRect(windowInfo.Handle, selFrameRectForContentRect, r_scaled); + Size = new Size((int)r.Width, (int)r.Height); } } @@ -299,25 +664,87 @@ namespace OpenTK.Platform.MacOS { get { - throw new System.NotImplementedException(); + return inputDriver; } } public bool CursorVisible { - get - { - throw new System.NotImplementedException(); - } + get { return cursorVisible; } set { - throw new System.NotImplementedException(); + cursorVisible = value; + if (value) + { + SetCursorVisible(true); + } + else + { + SetCursorVisible(false); + } } } public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + Debug.Print("Disposing of CocoaNativeWindow."); + + CursorVisible = true; + disposed = true; + exists = false; + + if (disposing) + { + if (trackingArea != IntPtr.Zero) + { + Cocoa.SendVoid(windowInfo.ViewHandle, selRemoveTrackingArea, trackingArea); + Cocoa.SendVoid(trackingArea, Selector.Release); + trackingArea = IntPtr.Zero; + } + + Cocoa.SendVoid(windowInfo.Handle, Selector.Release); + } + + Disposed(this, EventArgs.Empty); + } + + ~CocoaNativeWindow() + { + Dispose(false); + } + + public static IntPtr GetView(IntPtr windowHandle) + { + return Cocoa.SendIntPtr(windowHandle, selContentView); + } + + private RectangleF GetContentViewFrame() + { + return Cocoa.SendRect(windowInfo.ViewHandle, selFrame); + } + + private IntPtr GetCurrentScreen() + { + return Cocoa.SendIntPtr(windowInfo.Handle, selScreen); + } + + private RectangleF GetCurrentScreenFrame() + { + return Cocoa.SendRect(GetCurrentScreen(), selFrame); + } + + private void SetCursorVisible(bool visible) + { + Cocoa.SendVoid(NSCursor, visible ? selUnhide : selHide); } } }