소스 검색

Merge pull request #1977 from AvaloniaUI/avalonia-native

New ObjC++/COM-based OSX backend
danwalmsley 7 년 전
부모
커밋
a184e27062
47개의 변경된 파일6383개의 추가작업 그리고 37개의 파일을 삭제
  1. 26 0
      Avalonia.sln
  2. 0 37
      build.cake
  3. 1 0
      dirs.proj
  4. 4 0
      src/Avalonia.Native.OSX/.gitignore
  5. 328 0
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  6. 7 0
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  7. 91 0
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  8. 12 0
      src/Avalonia.Native.OSX/KeyTransform.h
  9. 241 0
      src/Avalonia.Native.OSX/KeyTransform.mm
  10. 51 0
      src/Avalonia.Native.OSX/Screens.mm
  11. 262 0
      src/Avalonia.Native.OSX/SystemDialogs.mm
  12. 47 0
      src/Avalonia.Native.OSX/clipboard.mm
  13. 34 0
      src/Avalonia.Native.OSX/common.h
  14. 29 0
      src/Avalonia.Native.OSX/cursor.h
  15. 73 0
      src/Avalonia.Native.OSX/cursor.mm
  16. 255 0
      src/Avalonia.Native.OSX/gl.mm
  17. 178 0
      src/Avalonia.Native.OSX/main.mm
  18. 190 0
      src/Avalonia.Native.OSX/platformthreading.mm
  19. 33 0
      src/Avalonia.Native.OSX/window.h
  20. 1228 0
      src/Avalonia.Native.OSX/window.mm
  21. 1 0
      src/Avalonia.Native/.gitignore
  22. 22 0
      src/Avalonia.Native/Avalonia.Native.csproj
  23. 9 0
      src/Avalonia.Native/Avalonia.Native.targets
  24. 132 0
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  25. 29 0
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  26. 80 0
      src/Avalonia.Native/CallbackBase.cs
  27. 47 0
      src/Avalonia.Native/ClipboardImpl.cs
  28. 45 0
      src/Avalonia.Native/Cursor.cs
  29. 87 0
      src/Avalonia.Native/DeferredFramebuffer.cs
  30. 108 0
      src/Avalonia.Native/DeferredRendererProxy.cs
  31. 128 0
      src/Avalonia.Native/DynLoader.cs
  32. 134 0
      src/Avalonia.Native/GlPlatformFeature.cs
  33. 35 0
      src/Avalonia.Native/Helpers.cs
  34. 50 0
      src/Avalonia.Native/IconLoader.cs
  35. 27 0
      src/Avalonia.Native/Mappings.xml
  36. 101 0
      src/Avalonia.Native/PlatformThreadingInterface.cs
  37. 39 0
      src/Avalonia.Native/PopupImpl.cs
  38. 45 0
      src/Avalonia.Native/ScreenImpl.cs
  39. 90 0
      src/Avalonia.Native/SystemDialogs.cs
  40. 100 0
      src/Avalonia.Native/WindowImpl.cs
  41. 351 0
      src/Avalonia.Native/WindowImplBase.cs
  42. 5 0
      src/Avalonia.Native/headers/avalonia-native-guids.h
  43. 363 0
      src/Avalonia.Native/headers/avalonia-native.h
  44. 57 0
      src/Avalonia.Native/headers/com.h
  45. 183 0
      src/Avalonia.Native/headers/comimpl.h
  46. 1023 0
      src/Avalonia.Native/headers/key.h
  47. 2 0
      src/Avalonia.Native/regen.sh

+ 26 - 0
Avalonia.sln

@@ -188,6 +188,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", "
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
+EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -1689,6 +1691,30 @@ Global
 		{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU
 		{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.Build.0 = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 0 - 37
build.cake

@@ -311,42 +311,6 @@ Task("Publish-NuGet-Impl")
     Information("Publish-NuGet Task failed, but continuing with next Task...");
 });
 
-Task("Inspect-Impl")
-    .WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
-    .Does(() =>
-{
-    var badIssues = new []{"PossibleNullReferenceException"};
-    var whitelist = new []{"tests", "src\\android", "src\\ios",
-        "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"};
-    Information("Running code inspections");
-    
-    var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"),
-        new ProcessSettings
-        {
-            Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln",
-            RedirectStandardOutput = true
-        });
-
-    Information("Analyzing report");
-    var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml"));
-    var failBuild = false;
-    foreach(var xml in doc.Descendants("Issue"))
-    {
-        var typeId = xml.Attribute("TypeId").Value.ToString();
-        if(badIssues.Contains(typeId))
-        {
-            var file = xml.Attribute("File").Value.ToString().ToLower();
-            if(whitelist.Any(wh => file.StartsWith(wh)))
-                continue;
-            var line = xml.Attribute("Line").Value.ToString();
-            Error(typeId + " - " + file + " on line " + line);
-            failBuild = true;
-        }
-    }
-    if(failBuild)
-        throw new Exception("Issues found");
-});
-
 ///////////////////////////////////////////////////////////////////////////////
 // TARGETS
 ///////////////////////////////////////////////////////////////////////////////
@@ -364,7 +328,6 @@ Task("Run-Tests")
 
 Task("Package")
     .IsDependentOn("Run-Tests")
-    .IsDependentOn("Inspect-Impl")
     .IsDependentOn("Create-NuGet-Packages-Impl");
 
 Task("AppVeyor")

+ 1 - 0
dirs.proj

@@ -5,6 +5,7 @@
     <ProjectReference Include="tests/**/*.*proj" />
     <ProjectReference Remove="**/*.shproj" />
     <ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
+    <ProjectReference Remove="**/*.pbxproj" />
   </ItemGroup>
   <ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">
     <ProjectReference Remove="src/Android/**/*.*proj" />

+ 4 - 0
src/Avalonia.Native.OSX/.gitignore

@@ -0,0 +1,4 @@
+build
+
+Avalonia.Native.OSX.xcodeproj/xcuserdata
+Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcuserdata

+ 328 - 0
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -0,0 +1,328 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
+		37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
+		37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
+		5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
+		5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
+		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
+		AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
+		AB573DC4217605E400D389A2 /* gl.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB573DC3217605E400D389A2 /* gl.mm */; };
+		AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
+		AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
+		AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
+		37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../Avalonia.Native/headers; sourceTree = "<group>"; };
+		37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; };
+		37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = "<group>"; };
+		37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = "<group>"; };
+		37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
+		5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
+		5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
+		5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
+		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
+		AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
+		AB573DC3217605E400D389A2 /* gl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = gl.mm; sourceTree = "<group>"; };
+		AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+		AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = "<group>"; };
+		AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
+		AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+		AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		AB7A61EC2147C814003C5833 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
+				AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		AB661C1C2148230E00291242 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				AB1E522B217613570091CD71 /* OpenGL.framework */,
+				AB661C1D2148230F00291242 /* AppKit.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		AB7A61E62147C814003C5833 = {
+			isa = PBXGroup;
+			children = (
+				37A4E71A2178846A00EACBCD /* headers */,
+				AB573DC3217605E400D389A2 /* gl.mm */,
+				5BF943652167AD1D009CAE35 /* cursor.h */,
+				5B21A981216530F500CEE36E /* cursor.mm */,
+				5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
+				AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
+				AB661C212148288600291242 /* common.h */,
+				379860FE214DA0C000CD0246 /* KeyTransform.h */,
+				37E2330E21583241000CB7E2 /* KeyTransform.mm */,
+				AB661C1F2148286E00291242 /* window.mm */,
+				37C09D8A21581EF2006A6758 /* window.h */,
+				AB00E4F62147CA920032A60A /* main.mm */,
+				37A517B22159597E00FBA241 /* Screens.mm */,
+				37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
+				AB7A61F02147C815003C5833 /* Products */,
+				AB661C1C2148230E00291242 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		AB7A61F02147C815003C5833 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		AB7A61ED2147C814003C5833 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		AB7A61EE2147C814003C5833 /* Avalonia.Native.OSX */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = AB7A61F82147C815003C5833 /* Build configuration list for PBXNativeTarget "Avalonia.Native.OSX" */;
+			buildPhases = (
+				AB7A61EB2147C814003C5833 /* Sources */,
+				AB7A61EC2147C814003C5833 /* Frameworks */,
+				AB7A61ED2147C814003C5833 /* Headers */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Avalonia.Native.OSX;
+			productName = Avalonia.Native.OSX;
+			productReference = AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */;
+			productType = "com.apple.product-type.library.dynamic";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		AB7A61E72147C814003C5833 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1000;
+				ORGANIZATIONNAME = Avalonia;
+				TargetAttributes = {
+					AB7A61EE2147C814003C5833 = {
+						CreatedOnToolsVersion = 8.3.2;
+						ProvisioningStyle = Automatic;
+					};
+				};
+			};
+			buildConfigurationList = AB7A61EA2147C814003C5833 /* Build configuration list for PBXProject "Avalonia.Native.OSX" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = AB7A61E62147C814003C5833;
+			productRefGroup = AB7A61F02147C815003C5833 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				AB7A61EE2147C814003C5833 /* Avalonia.Native.OSX */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		AB7A61EB2147C814003C5833 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */,
+				5B21A982216530F500CEE36E /* cursor.mm in Sources */,
+				AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
+				37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
+				37A517B32159597E00FBA241 /* Screens.mm in Sources */,
+				AB00E4F72147CA920032A60A /* main.mm in Sources */,
+				37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
+				AB573DC4217605E400D389A2 /* gl.mm in Sources */,
+				AB661C202148286E00291242 /* window.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		AB7A61F62147C815003C5833 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		AB7A61F72147C815003C5833 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.12;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		AB7A61F92147C815003C5833 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				HEADER_SEARCH_PATHS = ../Avalonia.Native/headers;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		AB7A61FA2147C815003C5833 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				EXECUTABLE_PREFIX = lib;
+				HEADER_SEARCH_PATHS = ../Avalonia.Native/headers;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		AB7A61EA2147C814003C5833 /* Build configuration list for PBXProject "Avalonia.Native.OSX" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				AB7A61F62147C815003C5833 /* Debug */,
+				AB7A61F72147C815003C5833 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		AB7A61F82147C815003C5833 /* Build configuration list for PBXNativeTarget "Avalonia.Native.OSX" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				AB7A61F92147C815003C5833 /* Debug */,
+				AB7A61FA2147C815003C5833 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = AB7A61E72147C814003C5833 /* Project object */;
+}

+ 7 - 0
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:Avalonia.Native.OSX.xcodeproj">
+   </FileRef>
+</Workspace>

+ 91 - 0
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1000"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "AB7A61EE2147C814003C5833"
+               BuildableName = "libAvalonia.Native.OSX.dylib"
+               BlueprintName = "Avalonia.Native.OSX"
+               ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "YES"
+      customWorkingDirectory = "$PROJECT_DIR/../../samples/ControlCatalog"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <PathRunnable
+         runnableDebuggingMode = "0"
+         FilePath = "/usr/local/share/dotnet/dotnet">
+      </PathRunnable>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "AB7A61EE2147C814003C5833"
+            BuildableName = "libAvalonia.Native.OSX.dylib"
+            BlueprintName = "Avalonia.Native.OSX"
+            ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <CommandLineArguments>
+         <CommandLineArgument
+            argument = "bin/Debug/netcoreapp2.0/ControlCatalog.dll"
+            isEnabled = "YES">
+         </CommandLineArgument>
+      </CommandLineArguments>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "AB7A61EE2147C814003C5833"
+            BuildableName = "libAvalonia.Native.OSX.dylib"
+            BlueprintName = "Avalonia.Native.OSX"
+            ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 12 - 0
src/Avalonia.Native.OSX/KeyTransform.h

@@ -0,0 +1,12 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#ifndef keytransform_h
+#define keytransform_h
+#include "common.h"
+#include "key.h"
+#include <map>
+
+extern std::map<int, AvnKey> s_KeyMap;
+
+#endif

+ 241 - 0
src/Avalonia.Native.OSX/KeyTransform.mm

@@ -0,0 +1,241 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "KeyTransform.h"
+
+const int kVK_ANSI_A = 0x00;
+const int kVK_ANSI_S = 0x01;
+const int kVK_ANSI_D = 0x02;
+const int kVK_ANSI_F = 0x03;
+const int kVK_ANSI_H = 0x04;
+const int kVK_ANSI_G = 0x05;
+const int kVK_ANSI_Z = 0x06;
+const int kVK_ANSI_X = 0x07;
+const int kVK_ANSI_C = 0x08;
+const int kVK_ANSI_V = 0x09;
+const int kVK_ANSI_B = 0x0B;
+const int kVK_ANSI_Q = 0x0C;
+const int kVK_ANSI_W = 0x0D;
+const int kVK_ANSI_E = 0x0E;
+const int kVK_ANSI_R = 0x0F;
+const int kVK_ANSI_Y = 0x10;
+const int kVK_ANSI_T = 0x11;
+const int kVK_ANSI_1 = 0x12;
+const int kVK_ANSI_2 = 0x13;
+const int kVK_ANSI_3 = 0x14;
+const int kVK_ANSI_4 = 0x15;
+const int kVK_ANSI_6 = 0x16;
+const int kVK_ANSI_5 = 0x17;
+//const int kVK_ANSI_Equal = 0x18;
+const int kVK_ANSI_9 = 0x19;
+const int kVK_ANSI_7 = 0x1A;
+const int kVK_ANSI_Minus = 0x1B;
+const int kVK_ANSI_8 = 0x1C;
+const int kVK_ANSI_0 = 0x1D;
+const int kVK_ANSI_RightBracket = 0x1E;
+const int kVK_ANSI_O = 0x1F;
+const int kVK_ANSI_U = 0x20;
+const int kVK_ANSI_LeftBracket = 0x21;
+const int kVK_ANSI_I = 0x22;
+const int kVK_ANSI_P = 0x23;
+const int kVK_ANSI_L = 0x25;
+const int kVK_ANSI_J = 0x26;
+const int kVK_ANSI_Quote = 0x27;
+const int kVK_ANSI_K = 0x28;
+const int kVK_ANSI_Semicolon = 0x29;
+const int kVK_ANSI_Backslash = 0x2A;
+const int kVK_ANSI_Comma = 0x2B;
+//const int kVK_ANSI_Slash = 0x2C;
+const int kVK_ANSI_N = 0x2D;
+const int kVK_ANSI_M = 0x2E;
+const int kVK_ANSI_Period = 0x2F;
+//const int kVK_ANSI_Grave = 0x32;
+const int kVK_ANSI_KeypadDecimal = 0x41;
+const int kVK_ANSI_KeypadMultiply = 0x43;
+const int kVK_ANSI_KeypadPlus = 0x45;
+const int kVK_ANSI_KeypadClear = 0x47;
+const int kVK_ANSI_KeypadDivide = 0x4B;
+const int kVK_ANSI_KeypadEnter = 0x4C;
+const int kVK_ANSI_KeypadMinus = 0x4E;
+//const int kVK_ANSI_KeypadEquals = 0x51;
+const int kVK_ANSI_Keypad0 = 0x52;
+const int kVK_ANSI_Keypad1 = 0x53;
+const int kVK_ANSI_Keypad2 = 0x54;
+const int kVK_ANSI_Keypad3 = 0x55;
+const int kVK_ANSI_Keypad4 = 0x56;
+const int kVK_ANSI_Keypad5 = 0x57;
+const int kVK_ANSI_Keypad6 = 0x58;
+const int kVK_ANSI_Keypad7 = 0x59;
+const int kVK_ANSI_Keypad8 = 0x5B;
+const int kVK_ANSI_Keypad9 = 0x5C;
+const int kVK_Return = 0x24;
+const int kVK_Tab = 0x30;
+const int kVK_Space = 0x31;
+const int kVK_Delete = 0x33;
+const int kVK_Escape = 0x35;
+const int kVK_Command = 0x37;
+const int kVK_Shift = 0x38;
+const int kVK_CapsLock = 0x39;
+const int kVK_Option = 0x3A;
+const int kVK_Control = 0x3B;
+const int kVK_RightCommand = 0x36;
+const int kVK_RightShift = 0x3C;
+const int kVK_RightOption = 0x3D;
+const int kVK_RightControl = 0x3E;
+//const int kVK_Function = 0x3F;
+const int kVK_F17 = 0x40;
+const int kVK_VolumeUp = 0x48;
+const int kVK_VolumeDown = 0x49;
+const int kVK_Mute = 0x4A;
+const int kVK_F18 = 0x4F;
+const int kVK_F19 = 0x50;
+const int kVK_F20 = 0x5A;
+const int kVK_F5 = 0x60;
+const int kVK_F6 = 0x61;
+const int kVK_F7 = 0x62;
+const int kVK_F3 = 0x63;
+const int kVK_F8 = 0x64;
+const int kVK_F9 = 0x65;
+const int kVK_F11 = 0x67;
+const int kVK_F13 = 0x69;
+const int kVK_F16 = 0x6A;
+const int kVK_F14 = 0x6B;
+const int kVK_F10 = 0x6D;
+const int kVK_F12 = 0x6F;
+const int kVK_F15 = 0x71;
+const int kVK_Help = 0x72;
+const int kVK_Home = 0x73;
+const int kVK_PageUp = 0x74;
+const int kVK_ForwardDelete = 0x75;
+const int kVK_F4 = 0x76;
+const int kVK_End = 0x77;
+const int kVK_F2 = 0x78;
+const int kVK_PageDown = 0x79;
+const int kVK_F1 = 0x7A;
+const int kVK_LeftArrow = 0x7B;
+const int kVK_RightArrow = 0x7C;
+const int kVK_DownArrow = 0x7D;
+const int kVK_UpArrow = 0x7E;
+//const int kVK_ISO_Section = 0x0A;
+//const int kVK_JIS_Yen = 0x5D;
+//const int kVK_JIS_Underscore = 0x5E;
+//const int kVK_JIS_KeypadComma = 0x5F;
+//const int kVK_JIS_Eisu = 0x66;
+//const int kVK_JIS_Kana = 0x68;
+
+ std::map<int, AvnKey> s_KeyMap =
+ {
+    {kVK_ANSI_A, A},
+    {kVK_ANSI_S, S},
+    {kVK_ANSI_D, D},
+    {kVK_ANSI_F, F},
+    {kVK_ANSI_H, H},
+    {kVK_ANSI_G, G},
+    {kVK_ANSI_Z, Z},
+    {kVK_ANSI_X, X},
+    {kVK_ANSI_C, C},
+    {kVK_ANSI_V, V},
+    {kVK_ANSI_B, B},
+    {kVK_ANSI_Q, Q},
+    {kVK_ANSI_W, W},
+    {kVK_ANSI_E, E},
+    {kVK_ANSI_R, R},
+    {kVK_ANSI_Y, Y},
+    {kVK_ANSI_T, T},
+    {kVK_ANSI_1, D1},
+    {kVK_ANSI_2, D2},
+    {kVK_ANSI_3, D3},
+    {kVK_ANSI_4, D4},
+    {kVK_ANSI_6, D6},
+    {kVK_ANSI_5, D5},
+    //{kVK_ANSI_Equal, ?},
+    {kVK_ANSI_9, D9},
+    {kVK_ANSI_7, D7},
+    {kVK_ANSI_Minus, OemMinus},
+    {kVK_ANSI_8, D8},
+    {kVK_ANSI_0, D0},
+    {kVK_ANSI_RightBracket, OemCloseBrackets},
+    {kVK_ANSI_O, O},
+    {kVK_ANSI_U, U},
+    {kVK_ANSI_LeftBracket, OemOpenBrackets},
+    {kVK_ANSI_I, I},
+    {kVK_ANSI_P, P},
+    {kVK_ANSI_L, L},
+    {kVK_ANSI_J, J},
+    {kVK_ANSI_Quote, OemQuotes},
+    {kVK_ANSI_K, AvnKeyK},
+    {kVK_ANSI_Semicolon, OemSemicolon},
+    {kVK_ANSI_Backslash, OemBackslash},
+    {kVK_ANSI_Comma, OemComma},
+    //{kVK_ANSI_Slash, ?},
+    {kVK_ANSI_N, N},
+    {kVK_ANSI_M, M},
+    {kVK_ANSI_Period, OemPeriod},
+    //{kVK_ANSI_Grave, ?},
+    {kVK_ANSI_KeypadDecimal, Decimal},
+    {kVK_ANSI_KeypadMultiply, Multiply},
+    {kVK_ANSI_KeypadPlus, OemPlus},
+    {kVK_ANSI_KeypadClear, AvnKeyClear},
+    {kVK_ANSI_KeypadDivide, Divide},
+    {kVK_ANSI_KeypadEnter, AvnKeyEnter},
+    {kVK_ANSI_KeypadMinus, OemMinus},
+    //{kVK_ANSI_KeypadEquals, ?},
+    {kVK_ANSI_Keypad0, NumPad0},
+    {kVK_ANSI_Keypad1, NumPad1},
+    {kVK_ANSI_Keypad2, NumPad2},
+    {kVK_ANSI_Keypad3, NumPad3},
+    {kVK_ANSI_Keypad4, NumPad4},
+    {kVK_ANSI_Keypad5, NumPad5},
+    {kVK_ANSI_Keypad6, NumPad6},
+    {kVK_ANSI_Keypad7, NumPad7},
+    {kVK_ANSI_Keypad8, NumPad8},
+    {kVK_ANSI_Keypad9, NumPad9},
+    {kVK_Return, AvnKeyReturn},
+    {kVK_Tab, AvnKeyTab},
+    {kVK_Space, Space},
+    {kVK_Delete, AvnKeyBack},
+    {kVK_Escape, Escape},
+    {kVK_Command, LWin},
+    {kVK_Shift, LeftShift},
+    {kVK_CapsLock, AvnKeyCapsLock},
+    {kVK_Option, LeftAlt},
+    {kVK_Control, LeftCtrl},
+    {kVK_RightCommand, RWin},
+    {kVK_RightShift, RightShift},
+    {kVK_RightOption, RightAlt},
+    {kVK_RightControl, RightCtrl},
+    //{kVK_Function, ?},
+    {kVK_F17, F17},
+    {kVK_VolumeUp, VolumeUp},
+    {kVK_VolumeDown, VolumeDown},
+    {kVK_Mute, VolumeMute},
+    {kVK_F18, F18},
+    {kVK_F19, F19},
+    {kVK_F20, F20},
+    {kVK_F5, F5},
+    {kVK_F6, F6},
+    {kVK_F7, F7},
+    {kVK_F3, F3},
+    {kVK_F8, F8},
+    {kVK_F9, F9},
+    {kVK_F11, F11},
+    {kVK_F13, F13},
+    {kVK_F16, F16},
+    {kVK_F14, F14},
+    {kVK_F10, F10},
+    {kVK_F12, F12},
+    {kVK_F15, F15},
+    {kVK_Help, Help},
+    {kVK_Home, Home},
+    {kVK_PageUp, PageUp},
+    {kVK_ForwardDelete, Delete},
+    {kVK_F4, F4},
+    {kVK_End, End},
+    {kVK_F2, F2},
+    {kVK_PageDown, PageDown},
+    {kVK_F1, F1},
+    {kVK_LeftArrow, Left},
+    {kVK_RightArrow, Right},
+    {kVK_DownArrow, Down},
+    {kVK_UpArrow, Up}
+};

+ 51 - 0
src/Avalonia.Native.OSX/Screens.mm

@@ -0,0 +1,51 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+
+class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
+{
+    public:
+    FORWARD_IUNKNOWN()
+    virtual HRESULT GetScreenCount (int* ret)
+    {
+        @autoreleasepool
+        {
+            *ret = (int)[NSScreen screens].count;
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT GetScreen (int index, AvnScreen* ret)
+    {
+        @autoreleasepool
+        {
+            if(index < 0 || index >= [NSScreen screens].count)
+            {
+                return E_INVALIDARG;
+            }
+            
+            auto screen = [[NSScreen screens] objectAtIndex:index];
+            
+            ret->Bounds.X = [screen frame].origin.x;
+            ret->Bounds.Y = [screen frame].origin.y;
+            ret->Bounds.Height = [screen frame].size.height;
+            ret->Bounds.Width = [screen frame].size.width;
+            
+            ret->WorkingArea.X = [screen visibleFrame].origin.x;
+            ret->WorkingArea.Y = [screen visibleFrame].origin.y;
+            ret->WorkingArea.Height = [screen visibleFrame].size.height;
+            ret->WorkingArea.Width = [screen visibleFrame].size.width;
+            
+            ret->Primary = index == 0;
+            
+            return S_OK;
+        }
+    }
+};
+
+extern IAvnScreens* CreateScreens()
+{
+    return new Screens();
+}

+ 262 - 0
src/Avalonia.Native.OSX/SystemDialogs.mm

@@ -0,0 +1,262 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+#include "window.h"
+
+class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
+{
+public:
+    FORWARD_IUNKNOWN()
+    virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
+                                     IAvnSystemDialogEvents* events,
+                                     const char* title,
+                                     const char* initialDirectory)
+    {
+        @autoreleasepool
+        {
+            auto panel = [NSOpenPanel openPanel];
+            
+            panel.canChooseDirectories = true;
+            panel.canCreateDirectories = true;
+            panel.canChooseFiles = false;
+            
+            if(title != nullptr)
+            {
+                panel.title = [NSString stringWithUTF8String:title];
+            }
+            
+            if(initialDirectory != nullptr)
+            {
+                auto directoryString = [NSString stringWithUTF8String:initialDirectory];
+                panel.directoryURL = [NSURL fileURLWithPath:directoryString];
+            }
+            
+            auto handler = ^(NSModalResponse result) {
+                if(result == NSFileHandlingPanelOKButton)
+                {
+                    auto urls = [panel URLs];
+                    
+                    if(urls.count > 0)
+                    {
+                        void* strings[urls.count];
+                        
+                        for(int i = 0; i < urls.count; i++)
+                        {
+                            auto url = [urls objectAtIndex:i];
+                            
+                            auto string = [url absoluteString];
+                            string = [string substringFromIndex:7];
+                            
+                            strings[i] = (void*)[string UTF8String];
+                        }
+                        
+                        events->OnCompleted((int)urls.count, &strings[0]);
+                        
+                        [panel orderOut:panel];
+                        
+                        if(parentWindowHandle != nullptr)
+                        {
+                            auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                            [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
+                        }
+                        
+                        return;
+                    }
+                }
+                
+                events->OnCompleted(0, nullptr);
+                
+            };
+            
+            if(parentWindowHandle != nullptr)
+            {
+                auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                
+                [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
+            }
+            else
+            {
+                [panel beginWithCompletionHandler: handler];
+            }
+        }
+    }
+    
+    virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,
+                                 IAvnSystemDialogEvents* events,
+                                 bool allowMultiple,
+                                 const char* title,
+                                 const char* initialDirectory,
+                                 const char* initialFile,
+                                 const char* filters)
+    {
+        @autoreleasepool
+        {
+            auto panel = [NSOpenPanel openPanel];
+            
+            panel.allowsMultipleSelection = allowMultiple;
+            
+            if(title != nullptr)
+            {
+                panel.title = [NSString stringWithUTF8String:title];
+            }
+            
+            if(initialDirectory != nullptr)
+            {
+                auto directoryString = [NSString stringWithUTF8String:initialDirectory];
+                panel.directoryURL = [NSURL fileURLWithPath:directoryString];
+            }
+            
+            if(initialFile != nullptr)
+            {
+                panel.nameFieldStringValue = [NSString stringWithUTF8String:initialFile];
+            }
+            
+            if(filters != nullptr)
+            {
+                auto filtersString = [NSString stringWithUTF8String:filters];
+                
+                if(filtersString.length > 0)
+                {
+                    auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
+                    
+                    panel.allowedFileTypes = allowedTypes;
+                }
+            }
+            
+            auto handler = ^(NSModalResponse result) {
+                if(result == NSFileHandlingPanelOKButton)
+                {
+                    auto urls = [panel URLs];
+                    
+                    if(urls.count > 0)
+                    {
+                        void* strings[urls.count];
+                        
+                        for(int i = 0; i < urls.count; i++)
+                        {
+                            auto url = [urls objectAtIndex:i];
+                            
+                            auto string = [url absoluteString];
+                            string = [string substringFromIndex:7];
+                            
+                            strings[i] = (void*)[string UTF8String];
+                        }
+                        
+                        events->OnCompleted((int)urls.count, &strings[0]);
+                        
+                        [panel orderOut:panel];
+                        
+                        if(parentWindowHandle != nullptr)
+                        {
+                            auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                            [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
+                        }
+                        
+                        return;
+                    }
+                }
+                
+                events->OnCompleted(0, nullptr);
+                
+            };
+            
+            if(parentWindowHandle != nullptr)
+            {
+                auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                
+                [panel beginSheetModalForWindow:windowHolder->GetNSWindow() completionHandler:handler];
+            }
+            else
+            {
+                [panel beginWithCompletionHandler: handler];
+            }
+        }
+    }
+    
+    virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
+                                 IAvnSystemDialogEvents* events,
+                                 const char* title,
+                                 const char* initialDirectory,
+                                 const char* initialFile,
+                                 const char* filters)
+    {
+        @autoreleasepool
+        {
+            auto panel = [NSSavePanel savePanel];
+            
+            if(title != nullptr)
+            {
+                panel.title = [NSString stringWithUTF8String:title];
+            }
+            
+            if(initialDirectory != nullptr)
+            {
+                auto directoryString = [NSString stringWithUTF8String:initialDirectory];
+                panel.directoryURL = [NSURL fileURLWithPath:directoryString];
+            }
+            
+            if(initialFile != nullptr)
+            {
+                panel.nameFieldStringValue = [NSString stringWithUTF8String:initialFile];
+            }
+            
+            if(filters != nullptr)
+            {
+                auto filtersString = [NSString stringWithUTF8String:filters];
+                
+                if(filtersString.length > 0)
+                {
+                    auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
+                    
+                    panel.allowedFileTypes = allowedTypes;
+                }
+            }
+            
+            auto handler = ^(NSModalResponse result) {
+                if(result == NSFileHandlingPanelOKButton)
+                {
+                    void* strings[1];
+                    
+                    auto url = [panel URL];
+                    
+                    auto string = [url absoluteString];
+                    string = [string substringFromIndex:7];     
+                    strings[0] = (void*)[string UTF8String];
+               
+                    events->OnCompleted(1, &strings[0]);
+                    
+                    [panel orderOut:panel];
+                    
+                    if(parentWindowHandle != nullptr)
+                    {
+                        auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                        [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
+                    }
+                    
+                    return;
+                }
+                
+                events->OnCompleted(0, nullptr);
+                
+            };
+            
+            if(parentWindowHandle != nullptr)
+            {
+                auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                
+                [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
+            }
+            else
+            {
+                [panel beginWithCompletionHandler: handler];
+            }
+        }
+    }
+
+};
+
+extern IAvnSystemDialogs* CreateSystemDialogs()
+{
+    return new SystemDialogs();
+}

+ 47 - 0
src/Avalonia.Native.OSX/clipboard.mm

@@ -0,0 +1,47 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+
+class Clipboard : public ComSingleObject<IAvnClipboard, &IID_IAvnClipboard>
+{
+public:
+    FORWARD_IUNKNOWN()
+    virtual HRESULT GetText (void** retOut)
+    {
+        @autoreleasepool
+        {
+            NSString *str = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
+            *retOut = (void *)str.UTF8String;
+        }
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT SetText (char* text)
+    {
+        @autoreleasepool
+        {
+            NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
+            [pasteBoard clearContents];
+            [pasteBoard setString:@(text) forType:NSPasteboardTypeString];
+        }
+        
+        return S_OK;
+    }
+
+    virtual HRESULT Clear()
+    {
+        @autoreleasepool
+        {
+            [[NSPasteboard generalPasteboard] clearContents];
+        }
+        
+        return S_OK;
+    }
+};
+
+extern IAvnClipboard* CreateClipboard()
+{
+    return new Clipboard();
+}

+ 34 - 0
src/Avalonia.Native.OSX/common.h

@@ -0,0 +1,34 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#ifndef common_h
+#define common_h
+#include "comimpl.h"
+#include "avalonia-native.h"
+#include <stdio.h>
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+#include <pthread.h>
+
+extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
+extern IAvnSystemDialogs* CreateSystemDialogs();
+extern IAvnScreens* CreateScreens();
+extern IAvnClipboard* CreateClipboard();
+extern IAvnCursorFactory* CreateCursorFactory();
+extern IAvnGlFeature* GetGlFeature();
+extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view);
+
+extern NSPoint ToNSPoint (AvnPoint p);
+extern AvnPoint ToAvnPoint (NSPoint p);
+extern AvnPoint ConvertPointY (AvnPoint p);
+extern NSSize ToNSSize (AvnSize s);
+
+#ifdef DEBUG
+#define NSDebugLog(...) NSLog(__VA_ARGS__)
+#else
+#define NSDebugLog(...) (void)0
+#endif
+
+#endif

+ 29 - 0
src/Avalonia.Native.OSX/cursor.h

@@ -0,0 +1,29 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#ifndef cursor_h
+#define cursor_h
+
+#include "common.h"
+#include <map>
+
+class Cursor : public ComSingleObject<IAvnCursor, &IID_IAvnCursor>
+{
+private:
+    NSCursor * _native;
+
+public:
+    FORWARD_IUNKNOWN()
+    Cursor(NSCursor * cursor)
+    {
+        _native = cursor;
+    }
+
+    NSCursor* GetNative()
+    {
+        return _native;
+    }
+};
+
+extern std::map<AvnStandardCursorType, Cursor*> s_cursorMap;
+#endif /* cursor_h */

+ 73 - 0
src/Avalonia.Native.OSX/cursor.mm

@@ -0,0 +1,73 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+#include "cursor.h"
+#include <map>
+
+class CursorFactory : public ComSingleObject<IAvnCursorFactory, &IID_IAvnCursorFactory>
+{
+    Cursor* arrowCursor = new Cursor([NSCursor arrowCursor]);
+    Cursor* crossCursor = new Cursor([NSCursor crosshairCursor]);
+    Cursor* resizeUpCursor = new Cursor([NSCursor resizeUpCursor]);
+    Cursor* resizeDownCursor = new Cursor([NSCursor resizeDownCursor]);
+    Cursor* resizeUpDownCursor = new Cursor([NSCursor resizeUpDownCursor]);
+    Cursor* dragCopyCursor = new Cursor([NSCursor dragCopyCursor]);
+    Cursor* dragLinkCursor = new Cursor([NSCursor dragLinkCursor]);
+    Cursor* pointingHandCursor = new Cursor([NSCursor pointingHandCursor]);
+    Cursor* contextualMenuCursor = new Cursor([NSCursor contextualMenuCursor]);
+    Cursor* IBeamCursor = new Cursor([NSCursor IBeamCursor]);
+    Cursor* resizeLeftCursor = new Cursor([NSCursor resizeLeftCursor]);
+    Cursor* resizeRightCursor = new Cursor([NSCursor resizeRightCursor]);
+    Cursor* resizeWestEastCursor = new Cursor([NSCursor resizeLeftRightCursor]);
+    Cursor* operationNotAllowedCursor = new Cursor([NSCursor operationNotAllowedCursor]);
+
+    std::map<AvnStandardCursorType, Cursor*> s_cursorMap =
+    {
+        { CursorArrow, arrowCursor },
+        { CursorAppStarting, arrowCursor },
+        { CursorWait, arrowCursor },
+        { CursorTopLeftCorner, crossCursor },
+        { CursorTopRightCorner, crossCursor },
+        { CursorBottomLeftCorner, crossCursor },
+        { CursorBottomRightCorner, crossCursor },
+        { CursorCross, crossCursor },
+        { CursorSizeAll, crossCursor },
+        { CursorSizeNorthSouth, resizeUpDownCursor},
+        { CursorSizeWestEast, resizeWestEastCursor},
+        { CursorTopSide, resizeUpCursor },
+        { CursorUpArrow, resizeUpCursor },
+        { CursorBottomSize, resizeDownCursor },
+        { CursorDragCopy, dragCopyCursor },
+        { CursorDragMove, dragCopyCursor },
+        { CursorDragLink, dragLinkCursor },
+        { CursorHand, pointingHandCursor },
+        { CursorHelp, contextualMenuCursor },
+        { CursorIbeam, IBeamCursor },
+        { CursorLeftSide, resizeLeftCursor },
+        { CursorRightSide, resizeRightCursor },
+        { CursorNo, operationNotAllowedCursor }
+    };
+
+public:
+    FORWARD_IUNKNOWN()
+    virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut)
+    {
+        *retOut = s_cursorMap[cursorType];
+        
+        if(*retOut != nullptr)
+        {
+            (*retOut)->AddRef();
+        }
+            
+        return S_OK;
+    }
+};
+
+extern IAvnCursorFactory* CreateCursorFactory()
+{
+    @autoreleasepool
+    {
+        return new CursorFactory();
+    }
+}

+ 255 - 0
src/Avalonia.Native.OSX/gl.mm

@@ -0,0 +1,255 @@
+#include "common.h"
+#include <OpenGL/gl.h>
+#include <dlfcn.h>
+
+template <typename T, size_t N> char (&ArrayCounter(T (&a)[N]))[N];
+#define ARRAY_COUNT(a) (sizeof(ArrayCounter(a)))
+
+NSOpenGLPixelFormat* CreateFormat()
+{
+    NSOpenGLPixelFormatAttribute attribs[] =
+    {
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFAStencilSize, 8,
+        NSOpenGLPFADepthSize, 8,
+        0
+    };
+    return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+}
+
+class AvnGlContext : public virtual ComSingleObject<IAvnGlContext, &IID_IAvnGlContext>
+{
+public:
+    FORWARD_IUNKNOWN()
+    NSOpenGLContext* GlContext;
+    GLuint Framebuffer, RenderBuffer, StencilBuffer;
+    AvnGlContext(NSOpenGLContext* gl, bool offscreen)
+    {
+        Framebuffer = 0;
+        RenderBuffer = 0;
+        StencilBuffer = 0;
+        GlContext = gl;
+        if(offscreen)
+        {
+            [GlContext makeCurrentContext];
+
+            glGenFramebuffersEXT(1, &Framebuffer);
+            glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
+            glGenRenderbuffersEXT(1, &RenderBuffer);
+            glGenRenderbuffersEXT(1, &StencilBuffer);
+
+            glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
+            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);
+            glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
+            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
+        }
+        
+    }
+    
+    
+    virtual HRESULT MakeCurrent()
+    {
+        [GlContext makeCurrentContext];/*
+        glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
+        glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
+        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
+        glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
+        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);*/
+        return S_OK;
+    }
+};
+
+class AvnGlDisplay : public virtual ComSingleObject<IAvnGlDisplay, &IID_IAvnGlDisplay>
+{
+    int _sampleCount, _stencilSize;
+    void* _libgl;
+    
+public:
+    FORWARD_IUNKNOWN()
+    
+    AvnGlDisplay(int sampleCount, int stencilSize)
+    {
+        _sampleCount = sampleCount;
+        _stencilSize = stencilSize;
+        _libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY);
+    }
+    
+    virtual HRESULT GetSampleCount(int* ret)
+    {
+        *ret = _sampleCount;
+        return S_OK;
+    }
+    virtual HRESULT GetStencilSize(int* ret)
+    {
+        *ret = _stencilSize;
+        return S_OK;
+    }
+    
+    virtual HRESULT ClearContext()
+    {
+        [NSOpenGLContext clearCurrentContext];
+        return S_OK;
+    }
+    
+    virtual void* GetProcAddress(char* proc)
+    {
+        return dlsym(_libgl, proc);
+    }
+};
+
+
+class GlFeature : public virtual ComSingleObject<IAvnGlFeature, &IID_IAvnGlFeature>
+{
+    IAvnGlDisplay* _display;
+    AvnGlContext *_immediate;
+    NSOpenGLContext* _shared;
+public:
+    FORWARD_IUNKNOWN()
+    NSOpenGLPixelFormat* _format;
+    GlFeature(IAvnGlDisplay* display, AvnGlContext* immediate, NSOpenGLPixelFormat* format)
+    {
+        _display = display;
+        _immediate = immediate;
+        _format = format;
+        _shared = [[NSOpenGLContext alloc] initWithFormat:_format shareContext:_immediate->GlContext];
+    }
+    
+    NSOpenGLContext* CreateContext()
+    {
+        return _shared;
+        //return [[NSOpenGLContext alloc] initWithFormat:_format shareContext:nil];
+    }
+    
+    virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut)
+    {
+        *retOut = _display;
+        _display->AddRef();
+        return S_OK;
+    }
+    
+    virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut)
+    {
+        *retOut = _immediate;
+        _immediate->AddRef();
+        return S_OK;
+    }
+};
+
+static GlFeature* Feature;
+
+GlFeature* CreateGlFeature()
+{
+    auto format = CreateFormat();
+    if(format == nil)
+    {
+        NSLog(@"Unable to choose pixel format");
+        return NULL;
+    }
+    
+    auto immediateContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil];
+    if(immediateContext == nil)
+    {
+        NSLog(@"Unable to create NSOpenGLContext");
+        return NULL;
+    }
+
+    int stencilBits = 0, sampleCount = 0;
+    
+    auto fmt = CGLGetPixelFormat([immediateContext CGLContextObj]);
+    CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &sampleCount);
+    CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &stencilBits);
+    
+    auto offscreen = new AvnGlContext(immediateContext, true);
+    auto display = new AvnGlDisplay(sampleCount, stencilBits);
+    
+    return new GlFeature(display, offscreen, format);
+}
+
+
+static GlFeature* GetFeature()
+{
+    if(Feature == nil)
+        Feature = CreateGlFeature();
+    return Feature;
+}
+
+extern IAvnGlFeature* GetGlFeature()
+{
+    return GetFeature();
+}
+
+class AvnGlRenderingSession : public ComSingleObject<IAvnGlSurfaceRenderingSession, &IID_IAvnGlSurfaceRenderingSession>
+{
+    NSView* _view;
+    NSWindow* _window;
+    NSOpenGLContext* _context;
+public:
+    FORWARD_IUNKNOWN()
+    AvnGlRenderingSession(NSWindow*window, NSView* view, NSOpenGLContext* context)
+    {
+        _context = context;
+        _window = window;
+        _view = view;
+    }
+    
+    virtual HRESULT GetPixelSize(AvnPixelSize* ret)
+    {
+        auto fsize = [_view convertSizeToBacking: [_view frame].size];
+        ret->Width = (int)fsize.width;
+        ret->Height = (int)fsize.height;
+        return S_OK;
+    }
+    virtual HRESULT GetScaling(double* ret)
+    {
+        *ret = [_window backingScaleFactor];
+        return S_OK;
+    }
+    
+    virtual ~AvnGlRenderingSession()
+    {
+        glFlush();
+        [_context flushBuffer];
+        [_context setView:nil];
+        CGLUnlockContext([_context CGLContextObj]);
+        [_view unlockFocus];
+    }
+};
+
+class AvnGlRenderTarget : public ComSingleObject<IAvnGlSurfaceRenderTarget, &IID_IAvnGlSurfaceRenderTarget>
+{
+    NSView* _view;
+    NSWindow* _window;
+    NSOpenGLContext* _context;
+public:
+    FORWARD_IUNKNOWN()
+    AvnGlRenderTarget(NSWindow* window, NSView*view)
+    {
+        _window = window;
+        _view = view;
+        _context = GetFeature()->CreateContext();
+    }
+    
+    virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret)
+    {
+        auto f = GetFeature();
+        if(f == NULL)
+            return E_FAIL;
+        if(![_view lockFocusIfCanDraw])
+            return E_ABORT;
+        
+        auto gl = _context;
+        CGLLockContext([_context CGLContextObj]);
+        [gl setView: _view];
+        [gl makeCurrentContext];
+        auto frame = [_view frame];
+        
+        *ret = new AvnGlRenderingSession(_window, _view, gl);
+        return S_OK;
+    }
+};
+
+extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view)
+{
+    return new AvnGlRenderTarget(window, view);
+}

+ 178 - 0
src/Avalonia.Native.OSX/main.mm

@@ -0,0 +1,178 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+//This file will contain actual IID structures
+#define COM_GUIDS_MATERIALIZE
+#include "common.h"
+
+static BOOL ShowInDock = 1;
+
+static void SetActivationPolicy()
+{
+    [[NSApplication sharedApplication] setActivationPolicy: (ShowInDock ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory)];
+}
+
+class MacOptions : public ComSingleObject<IAvnMacOptions, &IID_IAvnMacOptions>
+{
+public:
+    FORWARD_IUNKNOWN()
+    virtual HRESULT SetShowInDock(int show)
+    {
+        ShowInDock = show;
+        SetActivationPolicy();
+        return S_OK;
+    }
+};
+
+
+
+/// See "Using POSIX Threads in a Cocoa Application" section here:
+/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
+@interface ThreadingInitializer : NSObject
+- (void) do;
+@end
+@implementation ThreadingInitializer
+
+pthread_mutex_t mutex;
+pthread_cond_t cond;
+
+- (void) runOnce
+{
+    pthread_mutex_lock(&mutex);
+    pthread_cond_signal(&cond);
+    pthread_mutex_unlock(&mutex);
+}
+
+- (void) do
+{
+    pthread_mutex_init(&mutex, NULL);
+    pthread_cond_init(&cond, NULL);
+    [[[NSThread alloc] initWithTarget:self selector:@selector(runOnce) object:nil] start];
+    pthread_mutex_lock(&mutex);
+    pthread_cond_wait(&cond, &mutex);
+    pthread_mutex_unlock(&mutex);
+    pthread_cond_destroy(&cond);
+    pthread_mutex_destroy(&mutex);
+}
+
+
+@end
+
+
+class AvaloniaNative : public ComSingleObject<IAvaloniaNativeFactory, &IID_IAvaloniaNativeFactory>
+{
+    
+public:
+    FORWARD_IUNKNOWN()
+    virtual HRESULT Initialize()
+    {
+        @autoreleasepool{
+            [[ThreadingInitializer new] do];
+            return S_OK;
+        }
+    };
+    
+    virtual IAvnMacOptions* GetMacOptions()
+    {
+        return (IAvnMacOptions*)new MacOptions();
+    }
+    
+    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv)
+    {
+        if(cb == nullptr || ppv == nullptr)
+            return E_POINTER;
+        *ppv = CreateAvnWindow(cb);
+        return S_OK;
+    };
+    
+    virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv)
+    {
+        if(cb == nullptr || ppv == nullptr)
+            return E_POINTER;
+        
+        *ppv = CreateAvnPopup(cb);
+        return S_OK;
+    }
+    
+    virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv)
+    {
+        *ppv = CreatePlatformThreading();
+        return S_OK;
+    }
+    
+    virtual HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv)
+    {
+        *ppv = ::CreateSystemDialogs();
+        return  S_OK;
+    }
+    
+    virtual HRESULT CreateScreens (IAvnScreens** ppv)
+    {
+        *ppv = ::CreateScreens ();
+        return S_OK;
+    }
+
+    virtual HRESULT CreateClipboard(IAvnClipboard** ppv)
+    {
+        *ppv = ::CreateClipboard ();
+        return S_OK;
+    }
+
+    virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv)
+    {
+        *ppv = ::CreateCursorFactory();
+        return S_OK;
+    }
+    
+    virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv)
+    {
+        auto rv = ::GetGlFeature();
+        if(rv == NULL)
+            return E_FAIL;
+        rv->AddRef();
+        *ppv = rv;
+        return S_OK;
+    }
+};
+
+extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
+{
+    return new AvaloniaNative();
+};
+
+NSSize ToNSSize (AvnSize s)
+{
+    NSSize result;
+    result.width = s.Width;
+    result.height = s.Height;
+    
+    return result;
+}
+
+NSPoint ToNSPoint (AvnPoint p)
+{
+    NSPoint result;
+    result.x = p.X;
+    result.y = p.Y;
+    
+    return result;
+}
+
+AvnPoint ToAvnPoint (NSPoint p)
+{
+    AvnPoint result;
+    result.X = p.x;
+    result.Y = p.y;
+    
+    return result;
+}
+
+AvnPoint ConvertPointY (AvnPoint p)
+{
+    auto sw = [NSScreen.screens objectAtIndex:0].frame;
+    
+    auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height);
+    p.Y = t - p.Y;
+    
+    return p;
+}

+ 190 - 0
src/Avalonia.Native.OSX/platformthreading.mm

@@ -0,0 +1,190 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+
+class PlatformThreadingInterface;
+@interface Signaler : NSObject
+-(void) setParent: (PlatformThreadingInterface*)parent;
+-(void) signal: (int) priority;
+-(Signaler*) init;
+@end
+
+
+@interface ActionCallback : NSObject
+- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
+- (void) action;
+@end
+
+@implementation ActionCallback
+{
+    ComPtr<IAvnActionCallback> _callback;
+
+}
+- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback
+{
+    _callback = callback;
+    return self;
+}
+
+- (void) action
+{
+    _callback->Run();
+}
+
+
+@end
+
+class TimerWrapper : public ComUnknownObject
+{
+    NSTimer* _timer;
+public:
+    TimerWrapper(IAvnActionCallback* callback, int ms)
+    {
+        auto cb = [[ActionCallback alloc] initWithCallback:callback];
+        _timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(double)ms/1000 target:cb selector:@selector(action) userInfo:nullptr repeats:true];
+    }
+                  
+    virtual ~TimerWrapper()
+    {
+         [_timer invalidate];
+    }
+};
+
+
+
+class PlatformThreadingInterface : public ComSingleObject<IAvnPlatformThreadingInterface, &IID_IAvnPlatformThreadingInterface>
+{
+private:
+    Signaler* _signaler;
+    
+    class LoopCancellation : public ComSingleObject<IAvnLoopCancellation, &IID_IAvnLoopCancellation>
+    {
+    public:
+        FORWARD_IUNKNOWN()
+        bool Cancelled = 0;
+        virtual void Cancel()
+        {
+            Cancelled = 1;
+        }
+    };
+    
+public:
+    FORWARD_IUNKNOWN()
+    ComPtr<IAvnSignaledCallback> SignaledCallback;
+
+    PlatformThreadingInterface()
+    {
+        _signaler = [Signaler new];
+        [_signaler setParent:this];
+    }
+    
+    ~PlatformThreadingInterface()
+    {
+        if(_signaler)
+            [_signaler setParent: NULL];
+        _signaler = NULL;
+    }
+    
+    virtual bool GetCurrentThreadIsLoopThread()
+    {
+        return [[NSThread currentThread] isMainThread];
+    }
+    virtual void SetSignaledCallback(IAvnSignaledCallback* cb)
+    {
+        SignaledCallback = cb;
+    }
+    virtual IAvnLoopCancellation* CreateLoopCancellation()
+    {
+        return new LoopCancellation();
+    }
+    
+    virtual void RunLoop(IAvnLoopCancellation* cancel)
+    {
+        @autoreleasepool {
+            auto can = dynamic_cast<LoopCancellation*>(cancel);
+            [[NSApplication sharedApplication] activateIgnoringOtherApps:true];
+            while(true)
+            {
+                @autoreleasepool
+                {
+                    if(can != NULL && can->Cancelled)
+                        return;
+                    NSEvent* ev = [[NSApplication sharedApplication]
+                                   nextEventMatchingMask:NSEventMaskAny
+                                   untilDate: [NSDate dateWithTimeIntervalSinceNow:1]
+                                   inMode:NSDefaultRunLoopMode
+                                   dequeue:true];
+                    if(can != NULL && can->Cancelled)
+                        return;
+                    if(ev != NULL)
+                        [[NSApplication sharedApplication] sendEvent:ev];
+                }
+            }
+            NSDebugLog(@"RunLoop exited");
+        }
+    }
+    
+    virtual void Signal(int priority)
+    {
+        [_signaler signal:priority];
+    }
+    
+    virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback)
+    {
+        @autoreleasepool {
+            
+            return new TimerWrapper(callback, ms);
+        }
+    }
+};
+
+@implementation Signaler
+
+PlatformThreadingInterface* _parent = 0;
+bool _signaled = 0;
+NSArray<NSString*>* _modes;
+
+-(Signaler*) init
+{
+    if(self = [super init])
+    {
+        _modes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
+    }
+    return self;
+}
+
+-(void) perform
+{
+    @synchronized (self) {
+        _signaled  = false;
+        if(_parent != NULL && _parent->SignaledCallback != NULL)
+            _parent->SignaledCallback->Signaled(0, false);
+    }
+}
+
+-(void) setParent:(PlatformThreadingInterface *)parent
+{
+    @synchronized (self) {
+        _parent = parent;
+    }
+}
+
+-(void) signal: (int) priority
+{
+
+    @synchronized (self) {
+        if(_signaled)
+            return;
+        _signaled = true;
+        [self performSelector:@selector(perform) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes:_modes];
+    }
+    
+}
+@end
+
+
+extern IAvnPlatformThreadingInterface* CreatePlatformThreading()
+{
+    return new PlatformThreadingInterface();
+}

+ 33 - 0
src/Avalonia.Native.OSX/window.h

@@ -0,0 +1,33 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#ifndef window_h
+#define window_h
+
+class WindowBaseImpl;
+
+@interface AvnView : NSView<NSTextInputClient>
+-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
+-(NSEvent* _Nonnull) lastMouseDownEvent;
+-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
+-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
+-(void) onClosed;
+@end
+
+@interface AvnWindow : NSWindow <NSWindowDelegate>
+-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
+-(void) setCanBecomeKeyAndMain;
+-(void) pollModalSession: (NSModalSession _Nonnull) session;
+@end
+
+struct INSWindowHolder
+{
+    virtual AvnWindow* _Nonnull GetNSWindow () = 0;
+};
+
+struct IWindowStateChanged
+{
+    virtual void WindowStateChanged () = 0;
+};
+
+#endif /* window_h */

+ 1228 - 0
src/Avalonia.Native.OSX/window.mm

@@ -0,0 +1,1228 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "common.h"
+#include "window.h"
+#include "KeyTransform.h"
+#include "cursor.h"
+#include <OpenGL/gl.h>
+
+class SoftwareDrawingOperation
+{
+public:
+    void* Data = 0;
+    AvnFramebuffer Desc;
+    void Alloc(NSView* view)
+    {
+        auto logicalSize = [view frame].size;
+        auto pixelSize = [view convertSizeToBacking:logicalSize];
+        int w = pixelSize.width;
+        int h = pixelSize.height;
+        int stride = w * 4;
+        Data = malloc(h * stride);
+        Desc = {
+            .Data = Data,
+            .Stride = stride,
+            .Width = w,
+            .Height = h,
+            .PixelFormat = kAvnRgba8888,
+            .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96}
+        };
+    }
+    
+    void Dealloc()
+    {
+        if(Data != NULL)
+        {
+            free(Data);
+            Data = NULL;
+        }
+    }
+    
+    ~SoftwareDrawingOperation()
+    {
+        Dealloc();
+    }
+};
+
+class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
+{
+private:
+    NSCursor* cursor;
+
+public:
+    FORWARD_IUNKNOWN()
+    virtual ~WindowBaseImpl()
+    {
+        NSDebugLog(@"~WindowBaseImpl()");
+        View = NULL;
+        Window = NULL;
+    }
+    AvnView* View;
+    AvnWindow* Window;
+    ComPtr<IAvnWindowBaseEvents> BaseEvents;
+    SoftwareDrawingOperation CurrentSwDrawingOperation;
+    AvnPoint lastPositionSet;
+    NSString* _lastTitle;
+    
+    WindowBaseImpl(IAvnWindowBaseEvents* events)
+    {
+        BaseEvents = events;
+        View = [[AvnView alloc] initWithParent:this];
+
+        Window = [[AvnWindow alloc] initWithParent:this];
+        
+        lastPositionSet.X = 100;
+        lastPositionSet.Y = 100;
+        _lastTitle = @"";
+        
+        [Window setStyleMask:NSWindowStyleMaskBorderless];
+        [Window setBackingType:NSBackingStoreBuffered];
+        [Window setContentView: View];
+    }
+    
+    virtual AvnWindow* GetNSWindow()
+    {
+        return Window;
+    }
+    
+    virtual HRESULT Show()
+    {
+        @autoreleasepool
+        {
+            SetPosition(lastPositionSet);
+            UpdateStyle();
+            
+            [Window makeKeyAndOrderFront:Window];
+            
+            [Window setTitle:_lastTitle];
+            [Window setTitleVisibility:NSWindowTitleVisible];
+        
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT Hide ()
+    {
+        @autoreleasepool
+        {
+            if(Window != nullptr)
+            {
+                [Window orderOut:Window];
+            }
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT Activate ()
+    {
+        @autoreleasepool
+        {
+            if(Window != nullptr)
+            {
+                [Window makeKeyWindow];
+            }
+        }
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT SetTopMost (bool value)
+    {
+        @autoreleasepool
+        {
+            [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT Close()
+    {
+        @autoreleasepool
+        {
+            [Window close];
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT GetClientSize(AvnSize* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+                return E_POINTER;
+            auto frame = [View frame];
+            ret->Width = frame.size.width;
+            ret->Height = frame.size.height;
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT GetMaxClientSize(AvnSize* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+                return E_POINTER;
+            
+            auto size = [NSScreen.screens objectAtIndex:0].frame.size;
+            
+            ret->Height = size.height;
+            ret->Width = size.width;
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT GetScaling (double* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+                return E_POINTER;
+            
+            if(Window == nullptr)
+            {
+                *ret = 1;
+                return S_OK;
+            }
+            
+            *ret = [Window backingScaleFactor];
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize)
+    {
+        @autoreleasepool
+        {
+            [Window setMinSize: ToNSSize(minSize)];
+            [Window setMaxSize: ToNSSize(maxSize)];
+        
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT Resize(double x, double y)
+    {
+        @autoreleasepool
+        {
+            [Window setContentSize:NSSize{x, y}];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT Invalidate (AvnRect rect)
+    {
+        @autoreleasepool
+        {
+            [View setNeedsDisplayInRect:[View frame]];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual bool TryLock()
+    {
+        @autoreleasepool
+        {
+            return [View lockFocusIfCanDraw] == YES;
+        }
+    }
+    
+    virtual void Unlock()
+    {
+        @autoreleasepool
+        {
+            [View unlockFocus];
+        }
+    }
+    
+    virtual HRESULT BeginMoveDrag ()
+    {
+        @autoreleasepool
+        {
+            auto lastEvent = [View lastMouseDownEvent];
+            
+            if(lastEvent == nullptr)
+            {
+                return S_OK;
+            }
+            
+            [Window performWindowDragWithEvent:lastEvent];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT BeginResizeDrag (AvnWindowEdge edge)
+    {
+        return S_OK;
+    }
+    
+    virtual HRESULT GetPosition (AvnPoint* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+            {
+                return E_POINTER;
+            }
+            
+            auto frame = [Window frame];
+            
+            ret->X = frame.origin.x;
+            ret->Y = frame.origin.y + frame.size.height;
+            
+            *ret = ConvertPointY(*ret);
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetPosition (AvnPoint point)
+    {
+        @autoreleasepool
+        {
+            lastPositionSet = point;
+            [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+            {
+                return E_POINTER;
+            }
+            
+            point = ConvertPointY(point);
+            auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)];
+            
+            *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+            {
+                return E_POINTER;
+            }
+            
+            auto cocoaViewPoint =  ToNSPoint([View translateLocalPoint:point]);
+            auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint];
+            *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose)
+    {
+        [View setSwRenderedFrame: fb dispose: dispose];
+        return S_OK;
+    }
+    
+    virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret)
+    {
+        if(![[NSThread currentThread] isMainThread])
+            return E_FAIL;
+        if(CurrentSwDrawingOperation.Data == NULL)
+            CurrentSwDrawingOperation.Alloc(View);
+        *ret = CurrentSwDrawingOperation.Desc;
+        return S_OK;
+    }
+    
+    virtual HRESULT SetCursor(IAvnCursor* cursor)
+    {
+        @autoreleasepool
+        {
+            Cursor* avnCursor = dynamic_cast<Cursor*>(cursor);
+            this->cursor = avnCursor->GetNative();
+            UpdateCursor();
+            return S_OK;
+        }
+    }
+
+    virtual void UpdateCursor()
+    {
+        [View resetCursorRects];
+        if (cursor != nil)
+        {
+             auto rect = [Window frame];
+             [View addCursorRect:rect cursor:cursor];
+             [cursor set];
+        }
+    }
+    
+    virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv)
+    {
+        if(View == NULL)
+            return E_FAIL;
+        *ppv = ::CreateGlRenderTarget(Window, View);
+        return S_OK;
+    }
+
+protected:
+    virtual NSWindowStyleMask GetStyle()
+    {
+        return NSWindowStyleMaskBorderless;
+    }
+    
+    void UpdateStyle()
+    {
+        [Window setStyleMask:GetStyle()];
+    }
+    
+    virtual void OnResized ()
+    {
+        
+    }
+};
+
+class ModalDisposable : public ComUnknownObject
+{
+    NSModalSession _session;
+    AvnWindow* _window;
+    
+    void Dispose ()
+    {
+        [_window orderOut:_window];
+        [NSApp endModalSession:_session];
+    }
+    
+public:
+    ModalDisposable(AvnWindow* window, NSModalSession session)
+    {
+        _session = session;
+        _window = window;
+    }
+    
+    virtual ~ModalDisposable()
+    {
+        Dispose();
+    }
+};
+
+class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
+{
+private:
+    bool _canResize = true;
+    bool _hasDecorations = true;
+    CGRect _lastUndecoratedFrame;
+    AvnWindowState _lastWindowState;
+    
+    FORWARD_IUNKNOWN()
+    BEGIN_INTERFACE_MAP()
+    INHERIT_INTERFACE_MAP(WindowBaseImpl)
+    INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
+    END_INTERFACE_MAP()
+    virtual ~WindowImpl(){
+        NSDebugLog(@"~WindowImpl");
+    }
+    
+    ComPtr<IAvnWindowEvents> WindowEvents;
+    WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
+    {
+        WindowEvents = events;
+        [Window setCanBecomeKeyAndMain];
+    }
+    
+    virtual HRESULT Show ()
+    {
+        @autoreleasepool
+        {
+            WindowBaseImpl::Show();
+            
+            return SetWindowState(_lastWindowState);
+        }
+    }
+    
+    virtual HRESULT ShowDialog (IUnknown**ppv)
+    {
+        @autoreleasepool
+        {
+            if(ppv == nullptr)
+            {
+                return E_POINTER;
+            }
+            
+            auto session = [NSApp beginModalSessionForWindow:Window];
+            auto disposable = new ModalDisposable(Window, session);
+            *ppv = disposable;
+            
+            SetPosition(lastPositionSet);
+            UpdateStyle();
+            
+            [Window setTitle:_lastTitle];
+            [Window setTitleVisibility:NSWindowTitleVisible];
+            
+            [Window pollModalSession:session];
+            
+            return S_OK;
+        }
+    }
+    
+    void WindowStateChanged ()
+    {
+        AvnWindowState state;
+        GetWindowState(&state);
+        WindowEvents->WindowStateChanged(state);
+    }
+    
+    bool UndecoratedIsMaximized ()
+    {
+        return CGRectEqualToRect([Window frame], [Window screen].visibleFrame);
+    }
+    
+    bool IsZoomed ()
+    {
+        return _hasDecorations ? [Window isZoomed] : UndecoratedIsMaximized();
+    }
+    
+    void DoZoom()
+    {
+        if (_hasDecorations)
+        {
+            [Window performZoom:Window];
+        }
+        else
+        {
+            if (!UndecoratedIsMaximized())
+            {
+                _lastUndecoratedFrame = [Window frame];
+            }
+            
+            [Window zoom:Window];
+        }
+    }
+    
+    virtual HRESULT SetCanResize(bool value)
+    {
+        @autoreleasepool
+        {
+            _canResize = value;
+            UpdateStyle();
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetHasDecorations(bool value)
+    {
+        @autoreleasepool
+        {
+            _hasDecorations = value;
+            UpdateStyle();
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetTitle (const char* title)
+    {
+        @autoreleasepool
+        {
+            _lastTitle = [NSString stringWithUTF8String:title];
+            [Window setTitle:_lastTitle];
+            [Window setTitleVisibility:NSWindowTitleVisible];
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetTitleBarColor(AvnColor color)
+    {
+        @autoreleasepool
+        {
+            float a = (float)color.Alpha / 255.0f;
+            float r = (float)color.Red / 255.0f;
+            float g = (float)color.Green / 255.0f;
+            float b = (float)color.Blue / 255.0f;
+            
+            auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
+            
+            // Based on the titlebar color we have to choose either light or dark
+            // OSX doesnt let you set a foreground color for titlebar.
+            if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f)
+            {
+                [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
+            }
+            else
+            {
+                [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
+            }
+            
+            [Window setTitlebarAppearsTransparent:true];
+            [Window setBackgroundColor:nscolor];
+        }
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT GetWindowState (AvnWindowState*ret)
+    {
+        @autoreleasepool
+        {
+            if(ret == nullptr)
+            {
+                return E_POINTER;
+            }
+            
+            if([Window isMiniaturized])
+            {
+                *ret = Minimized;
+                return S_OK;
+            }
+            
+            if([Window isZoomed])
+            {
+                *ret = Maximized;
+                return S_OK;
+            }
+            
+            *ret = Normal;
+            
+            return S_OK;
+        }
+    }
+    
+    virtual HRESULT SetWindowState (AvnWindowState state)
+    {
+        @autoreleasepool
+        {
+            _lastWindowState = state;
+            
+            switch (state) {
+                case Maximized:
+                    lastPositionSet.X = 0;
+                    lastPositionSet.Y = 0;
+                    
+                    if([Window isMiniaturized])
+                    {
+                        [Window deminiaturize:Window];
+                    }
+                    
+                    if(!IsZoomed())
+                    {
+                        DoZoom();
+                    }
+                    break;
+                    
+                case Minimized:
+                    [Window miniaturize:Window];
+                    break;
+                    
+                default:
+                    if([Window isMiniaturized])
+                    {
+                        [Window deminiaturize:Window];
+                    }
+                    
+                    if(IsZoomed())
+                    {
+                        DoZoom();
+                    }
+                    break;
+            }
+            
+            return S_OK;
+        }
+    }
+    
+protected:
+    virtual void OnResized ()
+    {
+        auto windowState = [Window isMiniaturized] ? Minimized
+        : (IsZoomed() ? Maximized : Normal);
+        
+        if (windowState != _lastWindowState)
+        {
+            _lastWindowState = windowState;
+            
+            WindowEvents->WindowStateChanged(windowState);
+        }
+    }
+    
+    virtual NSWindowStyleMask GetStyle()
+    {
+        unsigned long s = NSWindowStyleMaskBorderless;
+        if(_hasDecorations)
+            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+        if(_canResize)
+            s = s | NSWindowStyleMaskResizable;
+        return s;
+    }
+};
+
+NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
+
+@implementation AvnView
+{
+    ComPtr<WindowBaseImpl> _parent;
+    ComPtr<IUnknown> _swRenderedFrame;
+    AvnFramebuffer _swRenderedFrameBuffer;
+    bool _queuedDisplayFromThread;
+    NSTrackingArea* _area;
+    bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver;
+    NSEvent* _lastMouseDownEvent;
+    bool _lastKeyHandled;
+}
+
+- (void)dealloc
+{
+    NSDebugLog(@"AvnView dealloc");
+}
+
+
+- (void)onClosed
+{
+    _parent = NULL;
+}
+
+- (NSEvent*) lastMouseDownEvent
+{
+    return _lastMouseDownEvent;
+}
+
+-(AvnView*)  initWithParent: (WindowBaseImpl*) parent
+{
+    self = [super init];
+    [self setWantsBestResolutionOpenGLSurface:true];
+    _parent = parent;
+    _area = nullptr;
+    return self;
+}
+
+- (BOOL)isOpaque
+{
+    return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return true;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)event
+{
+    return true;
+}
+
+- (BOOL)canBecomeKeyView
+{
+    return true;
+}
+
+-(void)setFrameSize:(NSSize)newSize
+{
+    [super setFrameSize:newSize];
+    
+    if(_area != nullptr)
+    {
+        [self removeTrackingArea:_area];
+        _area = nullptr;
+    }
+    
+    NSRect rect = NSZeroRect;
+    rect.size = newSize;
+    
+    NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingEnabledDuringMouseDrag;
+    _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr];
+    [self addTrackingArea:_area];
+    
+    _parent->UpdateCursor();
+
+    _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
+}
+
+- (void) drawFb: (AvnFramebuffer*) fb
+{
+    auto colorSpace = CGColorSpaceCreateDeviceRGB();
+    auto dataProvider = CGDataProviderCreateWithData(NULL, fb->Data, fb->Height*fb->Stride, NULL);
+
+    
+    auto image = CGImageCreate(fb->Width, fb->Height, 8, 32, fb->Stride, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast,
+                               dataProvider, nullptr, false, kCGRenderingIntentDefault);
+    
+    auto ctx = [NSGraphicsContext currentContext];
+    
+    [ctx saveGraphicsState];
+    auto cgc = [ctx CGContext];
+    
+    CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image);
+    CGImageRelease(image);
+    CGColorSpaceRelease(colorSpace);
+    CGDataProviderRelease(dataProvider);
+    
+    [ctx restoreGraphicsState];
+
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+    _parent->BaseEvents->RunRenderPriorityJobs();
+    @synchronized (self) {
+        if(_swRenderedFrame != NULL)
+        {
+            [self drawFb: &_swRenderedFrameBuffer];
+            return;
+        }
+    }
+    
+    auto swOp = &_parent->CurrentSwDrawingOperation;
+    _parent->BaseEvents->Paint();
+    if(swOp->Data != NULL)
+        [self drawFb: &swOp->Desc];
+    
+    swOp->Dealloc();
+    return;
+}
+
+-(void) redrawSelf
+{
+    @autoreleasepool
+    {
+        @synchronized(self)
+        {
+            if(!_queuedDisplayFromThread)
+                return;
+            _queuedDisplayFromThread = false;
+        }
+        [self setNeedsDisplayInRect:[self frame]];
+        [self display];
+        
+    }
+}
+
+-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
+{
+    @autoreleasepool {
+        @synchronized (self) {
+            _swRenderedFrame = dispose;
+            _swRenderedFrameBuffer = *fb;
+            if(!_queuedDisplayFromThread)
+            {
+                _queuedDisplayFromThread = true;
+                [self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes];
+            }
+        }
+    }
+}
+
+- (AvnPoint) translateLocalPoint:(AvnPoint)pt
+{
+    pt.Y = [self bounds].size.height - pt.Y;
+    return pt;
+}
+
+- (AvnPoint)toAvnPoint:(CGPoint)p
+{
+    AvnPoint result;
+    
+    result.X = p.x;
+    result.Y = p.y;
+    
+    return result;
+}
+
+- (void) viewDidChangeBackingProperties
+{
+    _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
+    [super viewDidChangeBackingProperties];
+}
+
+- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
+{
+    [self becomeFirstResponder];
+    auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
+    auto avnPoint = [self toAvnPoint:localPoint];
+    auto point = [self translateLocalPoint:avnPoint];
+    AvnVector delta;
+    
+    if(type == Wheel)
+    {
+        delta.X = [event scrollingDeltaX] / 5;
+        delta.Y = [event scrollingDeltaY] / 5;
+        
+        if(delta.X == 0 && delta.Y == 0)
+        {
+            return;
+        }
+    }
+    
+    auto timestamp = [event timestamp] * 1000;
+    auto modifiers = [self getModifiers:[event modifierFlags]];
+    
+    [self becomeFirstResponder];
+    _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
+    [super mouseMoved:event];
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+}
+
+- (void)mouseDown:(NSEvent *)event
+{
+    _isLeftPressed = true;
+    _lastMouseDownEvent = event;
+    [self mouseEvent:event withType:LeftButtonDown];
+}
+
+- (void)otherMouseDown:(NSEvent *)event
+{
+    _isMiddlePressed = true;
+    _lastMouseDownEvent = event;
+    [self mouseEvent:event withType:MiddleButtonDown];
+}
+
+- (void)rightMouseDown:(NSEvent *)event
+{
+    _isRightPressed = true;
+    _lastMouseDownEvent = event;
+    [self mouseEvent:event withType:RightButtonDown];
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+    _isLeftPressed = false;
+    [self mouseEvent:event withType:LeftButtonUp];
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+    _isMiddlePressed = false;
+    [self mouseEvent:event withType:MiddleButtonUp];
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+    _isRightPressed = false;
+    [self mouseEvent:event withType:RightButtonUp];
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super mouseDragged:event];
+}
+
+- (void)otherMouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super otherMouseDragged:event];
+}
+
+- (void)rightMouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super rightMouseDragged:event];
+}
+
+- (void)scrollWheel:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Wheel];
+    [super scrollWheel:event];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+    _isMouseOver = true;
+    [super mouseEntered:event];
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+    _isMouseOver = false;
+    [self mouseEvent:event withType:LeaveWindow];
+    [super mouseExited:event];
+} 
+
+- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
+{ 
+    auto key = s_KeyMap[[event keyCode]];
+    
+    auto timestamp = [event timestamp] * 1000;
+    auto modifiers = [self getModifiers:[event modifierFlags]];
+     
+    _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
+}
+
+- (BOOL)performKeyEquivalent:(NSEvent *)event
+{
+    return _lastKeyHandled;
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+    [self keyboardEvent:event withType:KeyDown];
+    [[self inputContext] handleEvent:event];
+    [super keyDown:event];
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+    [self keyboardEvent:event withType:KeyUp];
+    [super keyUp:event];
+}
+
+- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
+{
+    unsigned int rv = 0;
+    
+    if (mod & NSEventModifierFlagControl)
+        rv |= Control;
+    if (mod & NSEventModifierFlagShift)
+        rv |= Shift;
+    if (mod & NSEventModifierFlagOption)
+        rv |= Alt;
+    if (mod & NSEventModifierFlagCommand)
+        rv |= Windows;
+    
+    if (_isLeftPressed)
+        rv |= LeftMouseButton;
+    if (_isMiddlePressed)
+        rv |= MiddleMouseButton;
+    if (_isRightPressed)
+        rv |= RightMouseButton;
+    
+    return (AvnInputModifiers)rv;
+}
+
+- (BOOL)hasMarkedText
+{
+    return _lastKeyHandled;
+}
+
+- (NSRange)markedRange
+{
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRange)selectedRange
+{
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+{
+    
+}
+
+- (void)unmarkText
+{
+    
+}
+
+- (NSArray<NSString *> *)validAttributesForMarkedText
+{
+    return [NSArray new];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+    return [NSAttributedString new];
+}
+
+- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
+{
+    if(!_lastKeyHandled)
+    {
+        _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
+    }
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)point
+{
+    return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+    CGRect result;
+    
+    return result;
+}
+@end
+
+
+@implementation AvnWindow
+{
+    ComPtr<WindowBaseImpl> _parent;
+    bool _canBecomeKeyAndMain;
+    bool _closed;
+}
+
+- (void)dealloc
+{
+    NSDebugLog(@"AvnWindow dealloc");
+}
+
+- (void)pollModalSession:(nonnull NSModalSession)session
+{
+    auto response = [NSApp runModalSession:session];
+    
+    if(response == NSModalResponseContinue)
+    {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self pollModalSession:session];
+        });
+    }
+    else if (!_closed)
+    {
+        [self orderOut:self];
+        [NSApp endModalSession:session];
+    }
+}
+
+-(void) setCanBecomeKeyAndMain
+{
+    _canBecomeKeyAndMain = true;
+}
+
+-(AvnWindow*)  initWithParent: (WindowBaseImpl*) parent
+{
+    self = [super init];
+    [self setReleasedWhenClosed:false];
+    _parent = parent;
+    [self setDelegate:self];
+    return self;
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender
+{
+    auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
+    
+    if(window != nullptr)
+    {
+        return !window->WindowEvents->Closing();
+    }
+    
+    return true;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+    _closed = true;
+    if(_parent)
+    {
+        ComPtr<WindowBaseImpl> parent = _parent;
+        _parent = NULL;
+        parent->BaseEvents->Closed();
+        [parent->View onClosed];
+        [self setContentView: nil];
+    }
+}
+
+
+-(BOOL)canBecomeKeyWindow
+{
+    return _canBecomeKeyAndMain;
+}
+
+-(BOOL)canBecomeMainWindow
+{
+    return _canBecomeKeyAndMain;
+}
+
+-(void)becomeKeyWindow
+{
+    _parent->BaseEvents->Activated();
+    [super becomeKeyWindow];
+}
+
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+{
+    return true;
+}
+
+-(void)resignKeyWindow
+{
+    if(_parent)
+        _parent->BaseEvents->Deactivated();
+    [super resignKeyWindow];
+}
+
+- (void)windowDidMove:(NSNotification *)notification
+{
+    AvnPoint position;
+    _parent->GetPosition(&position);
+    _parent->BaseEvents->PositionChanged(position);
+}
+
+// TODO this breaks resizing.
+/*- (void)windowDidResize:(NSNotification *)notification
+{
+    
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+    
+    if(parent != nullptr)
+    {
+        parent->WindowStateChanged();
+    }
+}*/
+@end
+
+class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
+{
+private:
+    BEGIN_INTERFACE_MAP()
+    INHERIT_INTERFACE_MAP(WindowBaseImpl)
+    INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup)
+    END_INTERFACE_MAP()
+    virtual ~PopupImpl(){}
+    ComPtr<IAvnWindowEvents> WindowEvents;
+    PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
+    {
+        WindowEvents = events;
+        [Window setLevel:NSPopUpMenuWindowLevel];
+    }
+    
+protected:
+    virtual NSWindowStyleMask GetStyle()
+    {
+        return NSWindowStyleMaskBorderless;
+    }
+    
+    virtual HRESULT Resize(double x, double y)
+    {
+        @autoreleasepool
+        {
+            [Window setContentSize:NSSize{x, y}];
+            
+            [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
+            return S_OK;
+        }
+    }
+};
+
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events)
+{
+    @autoreleasepool
+    {
+        IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events));
+        return ptr;
+    }
+}
+
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
+{
+    @autoreleasepool
+    {
+        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
+        return ptr;
+    }
+}

+ 1 - 0
src/Avalonia.Native/.gitignore

@@ -0,0 +1 @@
+Generated

+ 22 - 0
src/Avalonia.Native/Avalonia.Native.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath>
+    <CastXmlPath Condition="Exists('/usr/local/bin/castxml')">/usr/local/bin/castxml</CastXmlPath>
+    
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <SharpGenGeneratedCodeFolder>$(MSBuildThisFileDirectory)/Generated</SharpGenGeneratedCodeFolder>
+  </PropertyGroup>
+    <ItemGroup>
+        <PackageReference Include="SharpGenTools.Sdk" Version="1.1.2" PrivateAssets="all" />
+        <PackageReference Include="SharpGen.Runtime.Com" Version="1.1.0" />
+        <SharpGenMapping Include="Mappings.xml" />
+      <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
+      <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
+      <ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" />
+      <ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
+      <ProjectReference Include="..\Avalonia.OpenGL\Avalonia.OpenGL.csproj" />
+      <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
+    </ItemGroup>
+</Project>

+ 9 - 0
src/Avalonia.Native/Avalonia.Native.targets

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+    <ItemGroup>
+        <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libAvalonia.Native.OSX.dylib">
+            <Link>libAvalonia.Native.OSX.dylib</Link>
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+</Project>

+ 132 - 0
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -0,0 +1,132 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using Avalonia.Controls.Platform;
+using Avalonia.Input;
+using Avalonia.Input.Platform;
+using Avalonia.Native.Interop;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+
+namespace Avalonia.Native
+{
+    class AvaloniaNativePlatform : IPlatformSettings, IWindowingPlatform
+    {
+        private readonly IAvaloniaNativeFactory _factory;
+
+        [DllImport("libAvaloniaNative")]
+        static extern IntPtr CreateAvaloniaNative();
+
+        internal static readonly MouseDevice MouseDevice = new MouseDevice();
+        internal static readonly KeyboardDevice KeyboardDevice = new KeyboardDevice();
+
+        public Size DoubleClickSize => new Size(4, 4);
+
+        public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500); //TODO
+
+        public static void Initialize(IntPtr factory, Action<AvaloniaNativeOptions> configure)
+        {
+            new AvaloniaNativePlatform(new IAvaloniaNativeFactory(factory))
+                .DoInitialize(configure);
+        }
+
+        delegate IntPtr CreateAvaloniaNativeDelegate();
+
+        public static void Initialize(string library, Action<AvaloniaNativeOptions> configure)
+        {
+            var loader = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+                                           ? (IDynLoader)new Win32Loader() : new UnixLoader();
+            var lib = loader.LoadLibrary(library);
+            var proc = loader.GetProcAddress(lib, "CreateAvaloniaNative", false);
+            var d = Marshal.GetDelegateForFunctionPointer<CreateAvaloniaNativeDelegate>(proc);
+
+
+            Initialize(d(), configure);
+        }
+
+        public static void Initialize(Action<AvaloniaNativeOptions> configure)
+        {
+            Initialize(CreateAvaloniaNative(), configure);
+        }
+
+        private AvaloniaNativePlatform(IAvaloniaNativeFactory factory)
+        {
+            _factory = factory;
+        }
+
+        void DoInitialize(Action<AvaloniaNativeOptions> configure)
+        {
+            var opts = new AvaloniaNativeOptions(_factory);
+            configure?.Invoke(opts);
+            _factory.Initialize();
+
+            AvaloniaLocator.CurrentMutable
+                .Bind<IPlatformThreadingInterface>().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
+                .Bind<IStandardCursorFactory>().ToConstant(new CursorFactory(_factory.CreateCursorFactory()))
+                .Bind<IPlatformIconLoader>().ToSingleton<IconLoader>()
+                .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
+                .Bind<IMouseDevice>().ToConstant(MouseDevice)
+                .Bind<IPlatformSettings>().ToConstant(this)
+                .Bind<IWindowingPlatform>().ToConstant(this)
+                .Bind<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
+                .Bind<IRenderLoop>().ToConstant(new RenderLoop())
+                .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
+                .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
+                .Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
+                .Bind<AvaloniaNativeOptions>().ToConstant(opts);
+        }
+
+        public IWindowImpl CreateWindow()
+        {
+            return new WindowImpl(_factory);
+        }
+
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
+        {
+            throw new NotImplementedException();
+        }
+
+        public IPopupImpl CreatePopup()
+        {
+            return new PopupImpl(_factory);
+        }
+    }
+
+    public class AvaloniaNativeMacOptions
+    {
+        private readonly IAvnMacOptions _opts;
+        private bool _showInDock;
+        internal AvaloniaNativeMacOptions(IAvnMacOptions opts)
+        {
+            _opts = opts;
+            ShowInDock = true;
+        }
+
+        public bool ShowInDock 
+        { 
+            get => _showInDock;
+            set
+            {
+                _showInDock = value;
+                _opts.ShowInDock = value ? 1 : 0;
+            }
+        }
+    }
+
+    public class AvaloniaNativeOptions
+    {
+        public AvaloniaNativeMacOptions MacOptions { get; set; }
+        public bool UseDeferredRendering { get; set; } = true;
+        public bool UseGpu { get; set; } = false;
+        internal AvaloniaNativeOptions(IAvaloniaNativeFactory factory)
+        {
+            var mac = factory.GetMacOptions();
+            if (mac != null)
+                MacOptions = new AvaloniaNativeMacOptions(mac);
+        }
+
+    }
+}

+ 29 - 0
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@@ -0,0 +1,29 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Controls;
+using Avalonia.Native;
+
+namespace Avalonia
+{
+    public static class AvaloniaNativePlatformExtensions
+    {
+        public static T UseAvaloniaNative<T>(this T builder,
+                                             string libraryPath = null,
+                                             Action<AvaloniaNativeOptions> configure = null)
+                                             where T : AppBuilderBase<T>, new()
+        {
+            if (libraryPath == null)
+            {
+                builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(configure));
+            }
+            else
+            {
+                builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(libraryPath, configure));
+            }
+
+            return builder;
+        }
+    }
+}

+ 80 - 0
src/Avalonia.Native/CallbackBase.cs

@@ -0,0 +1,80 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using SharpGen.Runtime;
+
+namespace Avalonia.Native
+{
+    public class CallbackBase : SharpGen.Runtime.IUnknown
+    {
+        private uint _refCount;
+        private bool _disposed;
+        private readonly object _lock = new object();
+        private ShadowContainer _shadow;
+
+        public CallbackBase()
+        {
+            _refCount = 1;
+        }
+
+        public ShadowContainer Shadow
+        {
+            get => _shadow;
+            set
+            {
+                lock (_lock)
+                {
+                    if (_disposed && value != null)
+                    {
+                        throw new ObjectDisposedException("CallbackBase");
+                    }
+
+                    _shadow = value;
+                }
+            }
+        }
+
+        public uint AddRef()
+        {
+            lock (_lock)
+            {
+                return ++_refCount;
+            }
+        }
+
+        public void Dispose()
+        {
+            lock (_lock)
+            {
+                if (!_disposed)
+                {
+                    _disposed = true;
+                    Release();
+                }
+            }
+        }
+
+        public uint Release()
+        {
+            lock (_lock)
+            {
+                _refCount--;
+
+                if (_refCount == 0)
+                {
+                    Shadow?.Dispose();
+                    Shadow = null;
+                    Destroyed();
+                }
+
+                return _refCount;
+            }
+        }
+
+        protected virtual void Destroyed()
+        {
+
+        }
+    }
+}

+ 47 - 0
src/Avalonia.Native/ClipboardImpl.cs

@@ -0,0 +1,47 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+using Avalonia.Input.Platform;
+using Avalonia.Native.Interop;
+
+namespace Avalonia.Native
+{
+    class ClipboardImpl : IClipboard
+    {
+        private IAvnClipboard _native;
+
+        public ClipboardImpl(IAvnClipboard native)
+        {
+            _native = native;
+        }
+
+        public Task ClearAsync()
+        {
+            _native.Clear();
+
+            return Task.CompletedTask;
+        }
+
+        public Task<string> GetTextAsync()
+        {
+            var outPtr = _native.GetText();
+            var text = Marshal.PtrToStringAnsi(outPtr);
+
+            return Task.FromResult(text);
+        }
+
+        public Task SetTextAsync(string text)
+        {
+            _native.Clear();
+
+            if (text != null)
+            {
+                _native.SetText(text);
+            }
+
+            return Task.CompletedTask;
+        }
+    }
+}

+ 45 - 0
src/Avalonia.Native/Cursor.cs

@@ -0,0 +1,45 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Input;
+using Avalonia.Platform;
+using Avalonia.Native.Interop;
+
+namespace Avalonia.Native
+{
+    class AvaloniaNativeCursor : IPlatformHandle, IDisposable
+    {
+        public IAvnCursor Cursor { get; private set; }
+        public IntPtr Handle => IntPtr.Zero;
+
+        public string HandleDescriptor => "<none>";
+
+        public AvaloniaNativeCursor(IAvnCursor cursor)
+        {
+            Cursor = cursor;
+        }
+
+        public void Dispose()
+        {
+            Cursor.Dispose();
+            Cursor = null;
+        }
+    }
+
+    class CursorFactory : IStandardCursorFactory
+    {
+        IAvnCursorFactory _native;
+
+        public CursorFactory(IAvnCursorFactory native)
+        {
+            _native = native;
+        }
+
+        public IPlatformHandle GetCursor(StandardCursorType cursorType)
+        {
+            var cursor = _native.GetCursor((AvnStandardCursorType)cursorType);
+            return new AvaloniaNativeCursor( cursor );
+        }
+    }
+}

+ 87 - 0
src/Avalonia.Native/DeferredFramebuffer.cs

@@ -0,0 +1,87 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+using SharpGen.Runtime;
+
+namespace Avalonia.Native
+{
+    public class DeferredFramebuffer : ILockedFramebuffer
+    {
+        private readonly Func<Action<IAvnWindowBase>, bool> _lockWindow;
+
+        public DeferredFramebuffer(Func<Action<IAvnWindowBase>, bool> lockWindow,
+                                   int width, int height, Vector dpi)
+        {
+            _lockWindow = lockWindow;
+            Address = Marshal.AllocHGlobal(width * height * 4);
+            Width = width;
+            Height = height;
+            RowBytes = width * 4;
+            Dpi = dpi;
+            Format = PixelFormat.Rgba8888;
+        }
+
+        public IntPtr Address { get; set; }
+        public int Width { get; set; }
+        public int Height { get; set; }
+        public int RowBytes { get; set; }
+        public Vector Dpi { get; set; }
+        public PixelFormat Format { get; set; }
+
+        class Disposer : CallbackBase
+        {
+            private IntPtr _ptr;
+
+            public Disposer(IntPtr ptr)
+            {
+                _ptr = ptr;
+            }
+
+            protected override void Destroyed()
+            {
+                if(_ptr != IntPtr.Zero)
+                {
+                    Marshal.FreeHGlobal(_ptr);
+                    _ptr = IntPtr.Zero;
+                }
+            }
+        }
+
+        public void Dispose()
+        {
+            if (Address == IntPtr.Zero)
+                return;
+
+            if (!_lockWindow(win =>
+            {
+                var fb = new AvnFramebuffer
+                {
+                    Data = Address,
+                    Dpi = new AvnVector
+                    {
+                        X = Dpi.X,
+                        Y = Dpi.Y
+                    },
+                    Width = Width,
+                    Height = Height,
+                    PixelFormat = (AvnPixelFormat)Format,
+                    Stride = RowBytes
+                };
+
+                using (var d = new Disposer(Address))
+                {
+                    win.ThreadSafeSetSwRenderedFrame(ref fb, d);
+                }
+            }))
+            {
+                Marshal.FreeHGlobal(Address);
+            }
+
+            Address = IntPtr.Zero;
+        }
+    }
+}

+ 108 - 0
src/Avalonia.Native/DeferredRendererProxy.cs

@@ -0,0 +1,108 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using Avalonia.Native.Interop;
+using Avalonia.Rendering;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Native
+{
+    public class DeferredRendererProxy : IRenderer, IRenderLoopTask, IRenderLoop
+    {
+        public DeferredRendererProxy(IRenderRoot root, IAvnWindowBase window)
+        {
+            if (window != null)
+            {
+                _useLock = true;
+                window.AddRef();
+_window = new IAvnWindowBase(window.NativePointer);
+            }
+            _renderer = new DeferredRenderer(root, this);
+            _rendererTask = (IRenderLoopTask)_renderer;
+        }
+
+        void IRenderLoop.Add(IRenderLoopTask i)
+        {
+            AvaloniaLocator.Current.GetService<IRenderLoop>().Add(this);
+        }
+
+        void IRenderLoop.Remove(IRenderLoopTask i)
+        {
+            AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this);
+        }
+
+        private DeferredRenderer _renderer;
+        private IRenderLoopTask _rendererTask;
+        private IAvnWindowBase _window;
+        private bool _useLock;
+
+        public bool DrawFps{
+            get => _renderer.DrawFps;
+            set => _renderer.DrawFps = value;
+        }
+        public bool DrawDirtyRects 
+        {
+            get => _renderer.DrawDirtyRects;
+            set => _renderer.DrawDirtyRects = value;
+        }
+
+        public bool NeedsUpdate => _rendererTask.NeedsUpdate;
+
+        public void AddDirty(IVisual visual) => _renderer.AddDirty(visual);
+
+        public void Dispose()
+        {
+            _renderer.Dispose();
+            _window?.Dispose();
+            _window = null;
+        }
+        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
+        {
+            return _renderer.HitTest(p, root, filter);
+        }
+
+        public void Paint(Rect rect)
+        {
+            if (NeedsUpdate)
+            {
+                Update(TimeSpan.FromMilliseconds(Environment.TickCount));
+            }
+
+            Render();
+        }
+
+        public void Resized(Size size) => _renderer.Resized(size);
+
+        public void Start() => _renderer.Start();
+
+        public void Stop() => _renderer.Stop();
+
+        public void Update(TimeSpan time)
+        {
+            _rendererTask.Update(time);
+        }
+
+        public void Render()
+        {
+            if(_useLock)
+            {
+                _rendererTask.Render();
+                return;
+            }
+            if (_window == null)
+                return;
+            if (!_window.TryLock())
+                return;
+            try
+            {
+                _rendererTask.Render();
+            }
+            finally
+            {
+                _window.Unlock();
+            }
+        }
+    }
+}

+ 128 - 0
src/Avalonia.Native/DynLoader.cs

@@ -0,0 +1,128 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Runtime.InteropServices;
+
+/*
+ * Source code imported from https://github.com/kekekeks/evhttp-sharp
+ * Source is provided under MIT license for Avalonia project and derived works
+ */
+
+namespace Avalonia.Native
+{
+    internal interface IDynLoader
+    {
+        IntPtr LoadLibrary(string dll);
+        IntPtr GetProcAddress(IntPtr dll, string proc, bool optional);
+
+    }
+
+    class UnixLoader : IDynLoader
+    {
+        // ReSharper disable InconsistentNaming
+        static class LinuxImports
+        {
+            [DllImport("libdl.so.2")]
+            private static extern IntPtr dlopen(string path, int flags);
+
+            [DllImport("libdl.so.2")]
+            private static extern IntPtr dlsym(IntPtr handle, string symbol);
+
+            [DllImport("libdl.so.2")]
+            private static extern IntPtr dlerror();
+
+            public static void Init()
+            {
+                DlOpen = dlopen;
+                DlSym = dlsym;
+                DlError = dlerror;
+            }
+        }
+
+        static class OsXImports
+        {
+            [DllImport("/usr/lib/libSystem.dylib")]
+            private static extern IntPtr dlopen(string path, int flags);
+
+            [DllImport("/usr/lib/libSystem.dylib")]
+            private static extern IntPtr dlsym(IntPtr handle, string symbol);
+
+            [DllImport("/usr/lib/libSystem.dylib")]
+            private static extern IntPtr dlerror();
+
+            public static void Init()
+            {
+                DlOpen = dlopen;
+                DlSym = dlsym;
+                DlError = dlerror;
+            }
+
+        }
+
+        [DllImport("libc")]
+        static extern int uname(IntPtr buf);
+
+        static UnixLoader()
+        {
+            var buffer = Marshal.AllocHGlobal(0x1000);
+            uname(buffer);
+            var unixName = Marshal.PtrToStringAnsi(buffer);
+            Marshal.FreeHGlobal(buffer);
+            if (unixName == "Darwin")
+                OsXImports.Init();
+            else
+                LinuxImports.Init();
+        }
+
+        private static Func<string, int, IntPtr> DlOpen;
+        private static Func<IntPtr, string, IntPtr> DlSym;
+        private static Func<IntPtr> DlError;
+        // ReSharper restore InconsistentNaming
+
+        static string DlErrorString() => Marshal.PtrToStringAnsi(DlError());
+
+        public IntPtr LoadLibrary(string dll)
+        {
+            var handle = DlOpen(dll, 1);
+            if (handle == IntPtr.Zero)
+                throw new Exception(DlErrorString());
+            return handle;
+        }
+
+        public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
+        {
+            var ptr = DlSym(dll, proc);
+            if (ptr == IntPtr.Zero && !optional)
+                throw new Exception(DlErrorString());
+            return ptr;
+        }
+    }
+
+    internal class Win32Loader : IDynLoader
+    {
+        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+        private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+        [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)]
+        private static extern IntPtr LoadLibrary(string lpszLib);
+
+        IntPtr IDynLoader.LoadLibrary(string dll)
+        {
+            var handle = LoadLibrary(dll);
+            if (handle != IntPtr.Zero)
+                return handle;
+            var err = Marshal.GetLastWin32Error();
+
+            throw new Exception("Error loading " + dll + " error " + err);
+        }
+
+        IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc, bool optional)
+        {
+            var ptr = GetProcAddress(dll, proc);
+            if (ptr == IntPtr.Zero && !optional)
+                throw new Exception("Error " + Marshal.GetLastWin32Error());
+            return ptr;
+        }
+    }
+}

+ 134 - 0
src/Avalonia.Native/GlPlatformFeature.cs

@@ -0,0 +1,134 @@
+using System;
+using Avalonia.OpenGL;
+using Avalonia.Native.Interop;
+using System.Drawing;
+using Avalonia.Threading;
+
+namespace Avalonia.Native
+{
+    class GlPlatformFeature : IWindowingPlatformGlFeature
+    {
+
+        public GlPlatformFeature(IAvnGlFeature feature)
+        {
+            Display = new GlDisplay(feature.ObtainDisplay());
+            ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext());
+        }
+
+        public IGlContext ImmediateContext { get; }
+        public GlDisplay Display { get; }
+    }
+
+    class GlDisplay : IGlDisplay
+    {
+        private readonly IAvnGlDisplay _display;
+
+        public GlDisplay(IAvnGlDisplay display)
+        {
+            _display = display;
+            GlInterface = new GlInterface((name, optional) =>
+            {
+                var rv = _display.GetProcAddress(name);
+                if (rv == IntPtr.Zero && !optional)
+                    throw new OpenGlException($"{name} not found in system OpenGL");
+                return rv;
+            });
+        }
+
+        public GlDisplayType Type => GlDisplayType.OpenGL2;
+
+        public GlInterface GlInterface { get; }
+
+        public int SampleCount => _display.GetSampleCount();
+
+        public int StencilSize => _display.GetStencilSize();
+
+        public void ClearContext() => _display.ClearContext();
+    }
+
+    class GlContext : IGlContext
+    {
+        public IAvnGlContext Context { get; }
+
+        public GlContext(GlDisplay display, IAvnGlContext context)
+        {
+            Display = display;
+            Context = context;
+        }
+
+        public IGlDisplay Display { get; }
+
+        public void MakeCurrent(IGlSurface surface)
+        {
+            if (surface != null)
+                throw new ArgumentException(nameof(surface));
+            Context.MakeCurrent();
+        }
+    }
+
+
+    class GlPlatformSurfaceRenderTarget : IGlPlatformSurfaceRenderTarget
+    {
+        private IAvnGlSurfaceRenderTarget _target;
+        public GlPlatformSurfaceRenderTarget(IAvnGlSurfaceRenderTarget target)
+        {
+            _target = target;
+        }
+
+        public IGlPlatformSurfaceRenderingSession BeginDraw()
+        {
+            var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
+            return new GlPlatformSurfaceRenderingSession(feature.Display, _target.BeginDrawing());
+        }
+
+        public void Dispose()
+        {
+            _target?.Dispose();
+            _target = null;
+        }
+    }
+
+    class GlPlatformSurfaceRenderingSession : IGlPlatformSurfaceRenderingSession
+    {
+        private IAvnGlSurfaceRenderingSession _session;
+
+        public GlPlatformSurfaceRenderingSession(GlDisplay display, IAvnGlSurfaceRenderingSession session)
+        {
+            Display = display;
+            _session = session;
+        }
+
+        public IGlDisplay Display { get; }
+
+        public System.Drawing.Size PixelSize
+        {
+            get
+            {
+                var s = _session.GetPixelSize();
+                return new System.Drawing.Size(s.Width, s.Height);
+            }
+        }
+
+        public double Scaling => _session.GetScaling();
+
+        public void Dispose()
+        {
+            _session?.Dispose();
+            _session = null;
+        }
+    }
+
+    class GlPlatformSurface : IGlPlatformSurface
+    {
+        private readonly IAvnWindowBase _window;
+
+        public GlPlatformSurface(IAvnWindowBase window)
+        {
+            _window = window;
+        }
+        public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+        {
+            return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget());
+        }
+    }
+}

+ 35 - 0
src/Avalonia.Native/Helpers.cs

@@ -0,0 +1,35 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Avalonia.Native.Interop;
+
+namespace Avalonia.Native
+{
+    public static class Helpers
+    {
+        public static Point ToAvaloniaPoint (this AvnPoint pt)
+        {
+            return new Point(pt.X, pt.Y);
+        }
+
+        public static AvnPoint ToAvnPoint (this Point pt)
+        {
+            return new AvnPoint { X = pt.X, Y = pt.Y };
+        }
+
+        public static AvnSize ToAvnSize (this Size size)
+        {
+            return new AvnSize { Height = size.Height, Width = size.Width };
+        }
+
+        public static Size ToAvaloniaSize (this AvnSize size)
+        {
+            return new Size(size.Width, size.Height);
+        }
+
+        public static Rect ToAvaloniaRect (this AvnRect rect)
+        {
+            return new Rect(rect.X, rect.Y, rect.Width, rect.Height);
+        }
+    }
+}

+ 50 - 0
src/Avalonia.Native/IconLoader.cs

@@ -0,0 +1,50 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.IO;
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+    // OSX doesn't have a concept of *window* icon. 
+    // Icons in the title bar are only shown if there is 
+    // an opened file (on disk) associated with the current window
+    // see http://stackoverflow.com/a/7038671/2231814
+    class IconLoader : IPlatformIconLoader
+    {
+        class IconStub : IWindowIconImpl
+        {
+            private readonly IBitmapImpl _bitmap;
+
+            public IconStub(IBitmapImpl bitmap)
+            {
+                _bitmap = bitmap;
+            }
+
+            public void Save(Stream outputStream)
+            {
+                _bitmap.Save(outputStream);
+            }
+        }
+
+        public IWindowIconImpl LoadIcon(string fileName)
+        {
+            return new IconStub(
+                AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(fileName));
+        }
+
+        public IWindowIconImpl LoadIcon(Stream stream)
+        {
+            return new IconStub(
+                AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(stream));
+        }
+
+        public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
+        {
+            var ms = new MemoryStream();
+            bitmap.Save(ms);
+            ms.Seek(0, SeekOrigin.Begin);
+            return LoadIcon(ms);
+        }
+    }
+}

+ 27 - 0
src/Avalonia.Native/Mappings.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config id="AvaloniaNative" xmlns="urn:SharpGen.Config">
+    <assembly>Avalonia.Native</assembly>
+    <namespace>Avalonia.Native.Interop</namespace>
+    <depends>SharpGen.Runtime.COM</depends>
+    <include-dir>$(THIS_CONFIG_PATH)/headers</include-dir>
+    <include file="avalonia-native.h" 
+             namespace="Avalonia.Native.Interop"
+             attach="true"
+             pre="#define COM_GUIDS_MATERIALIZE\n"/>
+    <include file="com.h" 
+             namespace="Avalonia.Native.Interop"
+             attach="true"
+             pre="#define COM_GUIDS_MATERIALIZE\n"/>
+    <bindings>
+        <bind from="HRESULT" to="SharpGen.Runtime.Result"/>
+        <bind from="HRESULT" to="SharpGen.Runtime.Result"/>
+    </bindings>
+    <mapping>
+        <remove function=".*" />
+        <map interface="*.Events" callback="true" autogen-shadow="true"/>
+        <map interface="*.Callback" callback="true" autogen-shadow="true"/>
+        <map param=".*::.*::ppv" return="true"/>
+        <map param=".*::.*::ret" return="true"/>
+        <map param=".*::.*::retOut" attribute="out" return="true"/>
+    </mapping>
+</config>

+ 101 - 0
src/Avalonia.Native/PlatformThreadingInterface.cs

@@ -0,0 +1,101 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Threading;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+using Avalonia.Threading;
+using SharpGen.Runtime;
+
+namespace Avalonia.Native
+{
+    public class PlatformThreadingInterface : IPlatformThreadingInterface
+    {
+        class TimerCallback : CallbackBase, IAvnActionCallback
+        {
+            readonly Action _tick;
+
+            public TimerCallback(Action tick)
+            {
+                _tick = tick;
+            }
+
+            public void Run()
+            {
+                _tick();
+            }
+        }
+
+        class SignaledCallback : CallbackBase, IAvnSignaledCallback
+        {
+            readonly PlatformThreadingInterface _parent;
+
+            public SignaledCallback(PlatformThreadingInterface parent)
+            {
+                _parent = parent;
+            }
+
+            public void Signaled(int priority, bool priorityContainsMeaningfulValue)
+            {
+                _parent.Signaled?.Invoke(priorityContainsMeaningfulValue ? (DispatcherPriority?)priority : null);
+            }
+        }
+
+        readonly IAvnPlatformThreadingInterface _native;
+
+        public PlatformThreadingInterface(IAvnPlatformThreadingInterface native)
+        {
+            _native = native;
+            using (var cb = new SignaledCallback(this))
+                _native.SignaledCallback = cb;
+        }
+
+        public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread;
+
+        public event Action<DispatcherPriority?> Signaled;
+
+        public void RunLoop(CancellationToken cancellationToken)
+        {
+            if (cancellationToken.CanBeCanceled == false)
+                _native.RunLoop(null);
+            else
+            {
+                var l = new object();
+                var cancellation = _native.CreateLoopCancellation();
+                cancellationToken.Register(() =>
+                {
+                    lock (l)
+                    {
+                        cancellation?.Cancel();
+                        cancellation?.Dispose();
+                        cancellation = null;
+                    }
+                });
+                try
+                {
+                    _native.RunLoop(cancellation);
+                }
+                finally
+                {
+                    lock(l)
+                    {
+                        cancellation?.Dispose();
+                        cancellation = null;
+                    }
+                }
+            }
+        }
+
+        public void Signal(DispatcherPriority priority)
+        {
+            _native.Signal((int)priority);
+        }
+
+        public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
+        {
+            using (var cb = new TimerCallback(tick))
+                return _native.StartTimer((int)priority, (int)interval.TotalMilliseconds, cb);
+        }
+    }
+}

+ 39 - 0
src/Avalonia.Native/PopupImpl.cs

@@ -0,0 +1,39 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+    public class PopupImpl : WindowBaseImpl, IPopupImpl
+    {
+        public PopupImpl(IAvaloniaNativeFactory factory)
+        {
+            using (var e = new PopupEvents(this))
+            {
+                Init(factory.CreatePopup(e), factory.CreateScreens());
+            }
+        }
+
+        class PopupEvents : WindowBaseEvents, IAvnWindowEvents
+        {
+            readonly PopupImpl _parent;
+
+            public PopupEvents(PopupImpl parent) : base(parent)
+            {
+                _parent = parent;
+            }
+
+            bool IAvnWindowEvents.Closing()
+            {
+                return true;
+            }
+
+            void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
+            {
+            }
+        }
+    }
+}

+ 45 - 0
src/Avalonia.Native/ScreenImpl.cs

@@ -0,0 +1,45 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+    class ScreenImpl : IScreenImpl, IDisposable
+    {
+        private IAvnScreens _native;
+
+        public ScreenImpl(IAvnScreens native)
+        {
+            _native = native;
+        }
+
+        public int ScreenCount => _native.GetScreenCount();
+
+        public Screen[] AllScreens
+        {
+            get
+            {
+                var count = ScreenCount;
+                var result = new Screen[count];
+
+                for(int i = 0; i < count; i++)
+                {
+                    var screen = _native.GetScreen(i);
+
+                    result[i] = new Screen(screen.Bounds.ToAvaloniaRect(), screen.WorkingArea.ToAvaloniaRect(), screen.Primary);
+                }
+
+                return result;
+            }
+        }
+
+        public void Dispose ()
+        {
+            _native.Dispose();
+            _native = null;
+        }
+    }
+}

+ 90 - 0
src/Avalonia.Native/SystemDialogs.cs

@@ -0,0 +1,90 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.Platform;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+    public class SystemDialogs : ISystemDialogImpl
+    {
+        IAvnSystemDialogs _native;
+
+        public SystemDialogs(IAvnSystemDialogs native)
+        {
+            _native = native;
+        }
+
+        public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
+        {
+            var events = new SystemDialogEvents();
+
+            if (dialog is OpenFileDialog ofd)
+            {
+                _native.OpenFileDialog((parent as WindowImpl)?.Native,
+                                        events, ofd.AllowMultiple,
+                                        ofd.Title ?? "",
+                                        ofd.InitialDirectory ?? "",
+                                        ofd.InitialFileName ?? "",
+                                        string.Join(";", dialog.Filters.SelectMany(f => f.Extensions)));
+            }
+            else
+            {
+                _native.SaveFileDialog((parent as WindowImpl)?.Native,
+                                        events,
+                                        dialog.Title ?? "",
+                                        dialog.InitialDirectory ?? "",
+                                        dialog.InitialFileName ?? "",
+                                        string.Join(";", dialog.Filters.SelectMany(f => f.Extensions)));
+            }
+
+            return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; });
+        }
+
+        public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
+        {
+            var events = new SystemDialogEvents();
+
+            _native.SelectFolderDialog((parent as WindowImpl)?.Native, events, dialog.Title ?? "", dialog.InitialDirectory ?? "");
+
+            return events.Task.ContinueWith(t => { events.Dispose(); return t.Result.FirstOrDefault(); });
+        }
+    }
+
+    public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents
+    {
+        private TaskCompletionSource<string[]> _tcs;
+
+        public SystemDialogEvents()
+        {
+            _tcs = new TaskCompletionSource<string[]>();
+        }
+
+        public Task<string[]> Task => _tcs.Task;
+
+        public void OnCompleted(int numResults, IntPtr trFirstResultRef)
+        {
+            string[] results = new string[numResults];
+
+            unsafe
+            {
+                var ptr = (IntPtr*)trFirstResultRef.ToPointer();
+
+                for (int i = 0; i < numResults; i++)
+                {
+                    results[i] = Marshal.PtrToStringAnsi(*ptr);
+
+                    ptr++;
+                }
+            }
+
+            _tcs.SetResult(results);
+        }
+    }
+}

+ 100 - 0
src/Avalonia.Native/WindowImpl.cs

@@ -0,0 +1,100 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Controls;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+    public class WindowImpl : WindowBaseImpl, IWindowImpl
+    {
+        IAvnWindow _native;
+        public WindowImpl(IAvaloniaNativeFactory factory)
+        {
+            using (var e = new WindowEvents(this))
+            {
+                Init(_native = factory.CreateWindow(e), factory.CreateScreens());
+            }
+        }
+
+        class WindowEvents : WindowBaseEvents, IAvnWindowEvents
+        {
+            readonly WindowImpl _parent;
+
+            public WindowEvents(WindowImpl parent) : base(parent)
+            {
+                _parent = parent;
+            }
+
+            bool IAvnWindowEvents.Closing()
+            {
+                if(_parent.Closing != null)
+                {
+                    return _parent.Closing();
+                }
+
+                return true;
+            }
+
+            void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
+            {
+                _parent.WindowStateChanged?.Invoke((WindowState)state);
+            }
+        }
+
+        public IAvnWindow Native => _native;
+
+        public IDisposable ShowDialog()
+        {
+            return _native.ShowDialog();
+        }
+
+        public void CanResize(bool value)
+        {
+            _native.CanResize = value;
+        }
+
+        public void SetSystemDecorations(bool enabled)
+        {
+            _native.HasDecorations = enabled;
+        }
+
+        public void SetTitleBarColor (Avalonia.Media.Color color)
+        {
+            _native.SetTitleBarColor(new AvnColor { Alpha = color.A, Red = color.R, Green = color.G, Blue = color.B });
+        }
+
+        public void SetTitle(string title)
+        {
+            _native.SetTitle(title);
+        }
+
+        public WindowState WindowState
+        {
+            get
+            {
+                return (WindowState)_native.GetWindowState();
+            }
+            set
+            {
+                _native.SetWindowState((AvnWindowState)value);
+            }
+        }
+
+        public Action<WindowState> WindowStateChanged { get; set; }
+
+        public void ShowTaskbarIcon(bool value)
+        {
+            // NO OP On OSX
+        }
+
+        public void SetIcon(IWindowIconImpl icon)
+        {
+            // NO OP on OSX
+        }
+
+        public Func<bool> Closing { get; set; }
+    }
+}

+ 351 - 0
src/Avalonia.Native/WindowImplBase.cs

@@ -0,0 +1,351 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Input;
+using Avalonia.Input.Raw;
+using Avalonia.Native.Interop;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+using Avalonia.Threading;
+
+namespace Avalonia.Native
+{
+    public class WindowBaseImpl : IWindowBaseImpl,
+        IFramebufferPlatformSurface
+    {
+        IInputRoot _inputRoot;
+        IAvnWindowBase _native;
+        private object _syncRoot = new object();
+        private bool _deferredRendering = false;
+        private bool _gpu = false;
+        private readonly IMouseDevice _mouse;
+        private readonly IKeyboardDevice _keyboard;
+        private readonly IStandardCursorFactory _cursorFactory;
+        private Size _savedLogicalSize;
+        private Size _lastRenderedLogicalSize;
+        private double _savedScaling;
+        private GlPlatformSurface _glSurface;
+
+        public WindowBaseImpl()
+        {
+            var opts = AvaloniaLocator.Current.GetService<AvaloniaNativeOptions>();
+
+            _gpu = opts.UseGpu;
+            _deferredRendering = opts.UseDeferredRendering;
+
+            _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
+            _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
+            _cursorFactory = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
+        }
+
+        protected void Init(IAvnWindowBase window, IAvnScreens screens)
+        {
+            _native = window;
+            _glSurface = new GlPlatformSurface(window);
+            Screen = new ScreenImpl(screens);
+            _savedLogicalSize = ClientSize;
+            _savedScaling = Scaling;
+        }
+
+        public Size ClientSize 
+        {
+            get
+            {
+                var s = _native.GetClientSize();
+                return new Size(s.Width, s.Height);
+            }
+        }
+
+        public IEnumerable<object> Surfaces => new[] {
+            (_gpu ? _glSurface : (object)null),
+            this 
+        };
+
+        public ILockedFramebuffer Lock()
+        {
+            if(_deferredRendering)
+            {
+                var w = _savedLogicalSize.Width * _savedScaling;
+                var h = _savedLogicalSize.Height * _savedScaling;
+                var dpi = _savedScaling * 96;
+                return new DeferredFramebuffer(cb =>
+                {
+                    lock (_syncRoot)
+                    {
+                        if (_native == null)
+                            return false;
+                        cb(_native);
+                        _lastRenderedLogicalSize = _savedLogicalSize;
+                        return true;
+                    }
+                }, (int)w, (int)h, new Vector(dpi, dpi));
+           }
+
+            return new FramebufferWrapper(_native.GetSoftwareFramebuffer());
+        }
+
+        public Action<Rect> Paint { get; set; }
+        public Action<Size> Resized { get; set; }
+        public Action Closed { get; set; }
+        public IMouseDevice MouseDevice => AvaloniaNativePlatform.MouseDevice;
+
+
+        class FramebufferWrapper : ILockedFramebuffer
+        {
+            public FramebufferWrapper(AvnFramebuffer fb)
+            {
+                Address = fb.Data;
+                Width = fb.Width;
+                Height = fb.Height;
+                RowBytes = fb.Stride;
+                Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y);
+                Format = (PixelFormat)fb.PixelFormat;
+            }
+            public IntPtr Address { get; set; }
+            public int Width { get; set; }
+            public int Height { get; set; }
+            public int RowBytes {get;set;}
+            public Vector Dpi { get; set; }
+            public PixelFormat Format { get; }
+            public void Dispose()
+            {
+                // Do nothing
+            }
+        }
+
+        protected class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
+        {
+            private readonly WindowBaseImpl _parent;
+
+            public WindowBaseEvents(WindowBaseImpl parent)
+            {
+                _parent = parent;
+            }
+
+            void IAvnWindowBaseEvents.Closed()
+            {
+                var n = _parent._native;
+                _parent._native = null;
+                try
+                {
+                    _parent?.Closed?.Invoke();
+                }
+                finally
+                {
+                    n?.Dispose();
+                }
+            }
+
+            void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
+
+            void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke();
+
+            void IAvnWindowBaseEvents.Paint()
+            {
+                Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
+                var s = _parent.ClientSize;
+                _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height));
+            }
+
+            void IAvnWindowBaseEvents.Resized(AvnSize size)
+            {
+                var s = new Size(size.Width, size.Height);
+                _parent._savedLogicalSize = s;
+                _parent.Resized?.Invoke(s);
+            }
+
+            void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
+            {
+                _parent.PositionChanged?.Invoke(position.ToAvaloniaPoint());
+            }
+
+            void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+            {
+                _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
+            }
+
+            bool IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+            {
+                return _parent.RawKeyEvent(type, timeStamp, modifiers, key);
+            }
+
+            bool IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
+            {
+                return _parent.RawTextInputEvent(timeStamp, text);
+            }
+
+
+            void IAvnWindowBaseEvents.ScalingChanged(double scaling)
+            {
+                _parent._savedScaling = scaling;
+                _parent.ScalingChanged?.Invoke(scaling);
+            }
+
+            void IAvnWindowBaseEvents.RunRenderPriorityJobs()
+            {
+                if (_parent._deferredRendering
+                    && _parent._lastRenderedLogicalSize != _parent.ClientSize)
+                    // Hack to trigger Paint event on the renderer
+                    _parent.Paint?.Invoke(new Rect());
+                Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
+            }
+        }
+
+        public void Activate()
+        {
+            _native.Activate();
+        }
+
+        public bool RawTextInputEvent(uint timeStamp, string text)
+        {
+            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+            var args = new RawTextInputEventArgs(_keyboard, timeStamp, text);
+
+            Input?.Invoke(args);
+
+            return args.Handled;
+        }
+
+        public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+        {
+            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+            var args = new RawKeyEventArgs(_keyboard, timeStamp, (RawKeyEventType)type, (Key)key, (InputModifiers)modifiers);
+
+            Input?.Invoke(args);
+
+            return args.Handled;
+        }
+
+        public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+        {
+            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+            switch (type)
+            {
+                case AvnRawMouseEventType.Wheel:
+                    Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (InputModifiers)modifiers));
+                    break;
+
+                default:
+                    Input?.Invoke(new RawMouseEventArgs(_mouse, timeStamp, _inputRoot, (RawMouseEventType)type, point.ToAvaloniaPoint(), (InputModifiers)modifiers));
+                    break;
+            }
+        }
+
+        public void Resize(Size clientSize)
+        {
+            _native.Resize(clientSize.Width, clientSize.Height);
+        }
+
+        public IRenderer CreateRenderer(IRenderRoot root)
+        {
+            if (_deferredRendering)
+                return new DeferredRendererProxy(root, _gpu ? _native : null);
+            return new ImmediateRenderer(root);
+        }
+
+        public virtual void Dispose()
+        {
+            _native?.Close();
+            _native?.Dispose();
+            _native = null;
+
+            (Screen as ScreenImpl)?.Dispose();
+        }
+
+
+        public void Invalidate(Rect rect)
+        {
+            if (!_deferredRendering && _native != null)
+                _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y });
+        }
+
+        public void SetInputRoot(IInputRoot inputRoot)
+        {
+            _inputRoot = inputRoot;
+        }
+
+
+        public void Show()
+        {
+            _native.Show();
+        }
+
+
+        public Point Position
+        {
+            get => _native.GetPosition().ToAvaloniaPoint();
+            set => _native.SetPosition(value.ToAvnPoint());
+        }
+
+        public Point PointToClient(Point point)
+        {
+            return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
+        }
+
+        public Point PointToScreen(Point point)
+        {
+            return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPoint();
+        }
+
+        public void Hide()
+        {
+            _native.Hide();
+        }
+
+        public void BeginMoveDrag()
+        {
+            _native.BeginMoveDrag();
+        }
+
+        public Size MaxClientSize => _native.GetMaxClientSize().ToAvaloniaSize();
+
+        public void SetTopmost(bool value)
+        {
+            _native.SetTopMost(value);
+        }
+
+        public double Scaling => _native.GetScaling();
+
+        public Action Deactivated { get; set; }
+        public Action Activated { get; set; }
+
+        public void SetCursor(IPlatformHandle cursor)
+        {
+            var newCursor = cursor as AvaloniaNativeCursor;
+            newCursor = newCursor ?? (_cursorFactory.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor);
+            _native.Cursor = newCursor.Cursor;
+        }
+
+        public Action<Point> PositionChanged { get; set; }
+
+        public Action<RawInputEventArgs> Input { get; set; }
+
+        Action<double> ScalingChanged { get; set; }
+
+        Action<double> ITopLevelImpl.ScalingChanged { get; set; }
+
+        public IScreenImpl Screen { get; private set; }
+
+        // TODO
+
+        public void SetMinMaxSize(Size minSize, Size maxSize)
+        {
+            _native.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize());
+        }
+
+        public void BeginResizeDrag(WindowEdge edge)
+        {
+
+        }
+
+        public IPlatformHandle Handle => new PlatformHandle(IntPtr.Zero, "NOT SUPPORTED");
+    }
+}

+ 5 - 0
src/Avalonia.Native/headers/avalonia-native-guids.h

@@ -0,0 +1,5 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#define COM_GUIDS_MATERIALIZE
+#include "avalonia-native.h"

+ 363 - 0
src/Avalonia.Native/headers/avalonia-native.h

@@ -0,0 +1,363 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#include "com.h"
+#include "key.h"
+
+#define AVNCOM(name, id) COMINTERFACE(name, 2e2cda0a, 9ae5, 4f1b, 8e, 20, 08, 1a, 04, 27, 9f, id)
+
+struct IAvnWindowEvents;
+struct IAvnWindow;
+struct IAvnPopup;
+struct IAvnMacOptions;
+struct IAvnPlatformThreadingInterface;
+struct IAvnSystemDialogEvents;
+struct IAvnSystemDialogs;
+struct IAvnScreens;
+struct IAvnClipboard;
+struct IAvnCursor;
+struct IAvnCursorFactory;
+struct IAvnGlFeature;
+struct IAvnGlContext;
+struct IAvnGlDisplay;
+struct IAvnGlSurfaceRenderTarget;
+struct IAvnGlSurfaceRenderingSession;
+
+struct AvnSize
+{
+    double Width, Height;
+};
+
+struct AvnPixelSize
+{
+    int Width, Height;
+};
+
+struct AvnRect
+{
+    double X, Y, Width, Height;
+};
+
+struct AvnVector
+{
+    double X, Y;
+};
+
+struct AvnPoint
+{
+    double X, Y;
+};
+
+struct AvnScreen
+{
+    AvnRect Bounds;
+    AvnRect WorkingArea;
+    bool Primary;
+};
+
+enum AvnPixelFormat
+{
+    kAvnRgb565,
+    kAvnRgba8888,
+    kAvnBgra8888
+};
+
+struct AvnFramebuffer
+{
+    void* Data;
+    int Width;
+    int Height;
+    int Stride;
+    AvnVector Dpi;
+    AvnPixelFormat PixelFormat;
+};
+
+struct AvnColor
+{
+    unsigned char Alpha;
+    unsigned char Red;
+    unsigned char Green;
+    unsigned char Blue;
+};
+
+enum AvnRawMouseEventType
+{
+    LeaveWindow,
+    LeftButtonDown,
+    LeftButtonUp,
+    RightButtonDown,
+    RightButtonUp,
+    MiddleButtonDown,
+    MiddleButtonUp,
+    Move,
+    Wheel,
+    NonClientLeftButtonDown
+};
+
+enum AvnRawKeyEventType
+{
+    KeyDown,
+    KeyUp
+};
+
+enum AvnInputModifiers
+{
+    AvnInputModifiersNone = 0,
+    Alt = 1,
+    Control = 2,
+    Shift = 4,
+    Windows = 8,
+    LeftMouseButton = 16,
+    RightMouseButton = 32,
+    MiddleMouseButton = 64
+};
+
+enum AvnWindowState
+{
+    Normal,
+    Minimized,
+    Maximized,
+};
+
+enum AvnStandardCursorType
+{
+    CursorArrow,
+    CursorIbeam,
+    CursorWait,
+    CursorCross,
+    CursorUpArrow,
+    CursorSizeWestEast,
+    CursorSizeNorthSouth,
+    CursorSizeAll,
+    CursorNo,
+    CursorHand,
+    CursorAppStarting,
+    CursorHelp,
+    CursorTopSide,
+    CursorBottomSize,
+    CursorLeftSide,
+    CursorRightSide,
+    CursorTopLeftCorner,
+    CursorTopRightCorner,
+    CursorBottomLeftCorner,
+    CursorBottomRightCorner,
+    CursorDragMove,
+    CursorDragCopy,
+    CursorDragLink,
+};
+
+enum AvnWindowEdge
+{
+    WindowEdgeNorthWest,
+    WindowEdgeNorth,
+    WindowEdgeNorthEast,
+    WindowEdgeWest,
+    WindowEdgeEast,
+    WindowEdgeSouthWest,
+    WindowEdgeSouth,
+    WindowEdgeSouthEast
+};
+
+AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
+{
+public:
+    virtual HRESULT Initialize() = 0;
+    virtual IAvnMacOptions* GetMacOptions() = 0;
+    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0;
+    virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0;
+    virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0;
+    virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
+    virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
+    virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
+    virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0;
+    virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0;
+};
+
+AVNCOM(IAvnWindowBase, 02) : IUnknown
+{
+    virtual HRESULT Show() = 0;
+    virtual HRESULT Hide () = 0;
+    virtual HRESULT Close() = 0;
+    virtual HRESULT Activate () = 0;
+    virtual HRESULT GetClientSize(AvnSize*ret) = 0;
+    virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0;
+    virtual HRESULT GetScaling(double*ret)=0;
+    virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) = 0;
+    virtual HRESULT Resize(double width, double height) = 0;
+    virtual HRESULT Invalidate (AvnRect rect) = 0;
+    virtual HRESULT BeginMoveDrag () = 0;
+    virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) = 0;
+    virtual HRESULT GetPosition (AvnPoint*ret) = 0;
+    virtual HRESULT SetPosition (AvnPoint point) = 0;
+    virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0;
+    virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0;
+    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0;
+    virtual HRESULT SetTopMost (bool value) = 0;
+    virtual HRESULT SetCursor(IAvnCursor* cursor) = 0;
+    virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0;
+    virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0;
+    virtual bool TryLock() = 0;
+    virtual void Unlock() = 0;
+};
+
+AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
+{
+    
+};
+
+AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
+{
+    virtual HRESULT ShowDialog (IUnknown**ppv) = 0;
+    virtual HRESULT SetCanResize(bool value) = 0;
+    virtual HRESULT SetHasDecorations(bool value) = 0;
+    virtual HRESULT SetTitle (const char* title) = 0;
+    virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
+    virtual HRESULT SetWindowState(AvnWindowState state) = 0;
+    virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
+};
+
+AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
+{
+    virtual HRESULT Paint() = 0;
+    virtual void Closed() = 0;
+    virtual void Activated() = 0;
+    virtual void Deactivated() = 0;
+    virtual void Resized(const AvnSize& size) = 0;
+    virtual void PositionChanged (AvnPoint position) = 0;
+    virtual void RawMouseEvent (AvnRawMouseEventType type,
+                                unsigned int timeStamp,
+                                AvnInputModifiers modifiers,
+                                AvnPoint point,
+                                AvnVector delta) = 0;
+    virtual bool RawKeyEvent (AvnRawKeyEventType type, unsigned int timeStamp, AvnInputModifiers modifiers, unsigned int key) = 0;
+    virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
+    virtual void ScalingChanged(double scaling) = 0;
+    virtual void RunRenderPriorityJobs() = 0;
+};
+
+
+AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
+{
+    /**
+     * Closing Event
+     * Called when the user presses the OS window close button.
+     * return true to allow the close, return false to prevent close.
+     */
+    virtual bool Closing () = 0;
+    
+    virtual void WindowStateChanged (AvnWindowState state) = 0;
+};
+
+AVNCOM(IAvnMacOptions, 07) : IUnknown
+{
+    virtual HRESULT SetShowInDock(int show) = 0;
+};
+
+AVNCOM(IAvnActionCallback, 08) : IUnknown
+{
+    virtual void Run() = 0;
+};
+
+AVNCOM(IAvnSignaledCallback, 09) : IUnknown
+{
+    virtual void Signaled(int priority, bool priorityContainsMeaningfulValue) = 0;
+};
+
+AVNCOM(IAvnLoopCancellation, 0a) : IUnknown
+{
+    virtual void Cancel() = 0;
+};
+
+AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown
+{
+    virtual bool GetCurrentThreadIsLoopThread() = 0;
+    virtual void SetSignaledCallback(IAvnSignaledCallback* cb) = 0;
+    virtual IAvnLoopCancellation* CreateLoopCancellation() = 0;
+    virtual void RunLoop(IAvnLoopCancellation* cancel) = 0;
+    // Can't pass int* to sharpgentools for some reason
+    virtual void Signal(int priority) = 0;
+    virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0;
+};
+
+AVNCOM(IAvnSystemDialogEvents, 0c) : IUnknown
+{
+    virtual void OnCompleted (int numResults, void* ptrFirstResult) = 0;
+};
+
+AVNCOM(IAvnSystemDialogs, 0d) : IUnknown
+{
+    virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
+                                     IAvnSystemDialogEvents* events,
+                                     const char* title,
+                                     const char* initialPath) = 0;
+    
+    virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,
+                                 IAvnSystemDialogEvents* events,
+                                 bool allowMultiple,
+                                 const char* title,
+                                 const char* initialDirectory,
+                                 const char* initialFile,
+                                 const char* filters) = 0;
+    
+    virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
+                                 IAvnSystemDialogEvents* events,
+                                 const char* title,
+                                 const char* initialDirectory,
+                                 const char* initialFile,
+                                 const char* filters) = 0;
+};
+
+AVNCOM(IAvnScreens, 0e) : IUnknown
+{
+    virtual HRESULT GetScreenCount (int* ret) = 0;
+    virtual HRESULT GetScreen (int index, AvnScreen* ret) = 0;
+};
+
+AVNCOM(IAvnClipboard, 0f) : IUnknown
+{
+    virtual HRESULT GetText (void** retOut) = 0;
+    virtual HRESULT SetText (char* text) = 0;
+    virtual HRESULT Clear() = 0;
+};
+
+AVNCOM(IAvnCursor, 10) : IUnknown
+{
+};
+
+AVNCOM(IAvnCursorFactory, 11) : IUnknown
+{
+    virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) = 0;
+};
+
+
+AVNCOM(IAvnGlFeature, 12) : IUnknown
+{
+    virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) = 0;
+    virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) = 0;
+};
+
+AVNCOM(IAvnGlDisplay, 13) : IUnknown
+{
+    virtual HRESULT GetSampleCount(int* ret) = 0;
+    virtual HRESULT GetStencilSize(int* ret) = 0;
+    virtual HRESULT ClearContext() = 0;
+    virtual void* GetProcAddress(char* proc) = 0;
+};
+
+AVNCOM(IAvnGlContext, 14) : IUnknown
+{
+    virtual HRESULT MakeCurrent() = 0;
+};
+
+AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown
+{
+    virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) = 0;
+};
+
+AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown
+{
+    virtual HRESULT GetPixelSize(AvnPixelSize* ret) = 0;
+    virtual HRESULT GetScaling(double* ret) = 0;
+};
+
+extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();

+ 57 - 0
src/Avalonia.Native/headers/com.h

@@ -0,0 +1,57 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
+#ifndef COM_H_INCLUDED
+#define COM_H_INCLUDED
+
+
+typedef struct _GUID {
+    unsigned int  Data1;
+    unsigned short Data2;
+    unsigned short Data3;
+    unsigned char  Data4[ 8 ];
+} GUID;
+typedef GUID IID;
+typedef const IID* REFIID;
+typedef unsigned int HRESULT;
+typedef unsigned int DWORD;
+typedef DWORD ULONG;
+
+#define STDMETHODCALLTYPE
+
+#define S_OK                             0x0L
+
+#define E_NOTIMPL                        0x80004001L
+#define E_NOINTERFACE                    0x80004002L
+#define E_POINTER                        0x80004003L
+#define E_ABORT                          0x80004004L
+#define E_FAIL                           0x80004005L
+#define E_UNEXPECTED                     0x8000FFFFL
+#define E_HANDLE                         0x80070006L
+#define E_INVALIDARG                     0x80070057L
+
+struct IUnknown
+{
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+            REFIID riid,
+            void **ppvObject) = 0;
+
+    virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
+
+    virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
+
+};
+
+#ifdef COM_GUIDS_MATERIALIZE
+#define __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) extern "C" const GUID IID_ ## name = {0x ## d1, 0x ## d2, 0x ## d3, \
+{0x ## d41, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42 } };
+#else
+#define __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) extern "C" const GUID IID_ ## name;
+#endif
+#define COMINTERFACE(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) \
+struct __attribute__((annotate("uuid(" #d1 "-" #d2 "-" #d3 "-" #d41 #d42 "-" #d43 #d44 #d45 #d46 #d47 #d48 ")" ))) name
+
+#endif // COM_H_INCLUDED
+#pragma clang diagnostic pop

+ 183 - 0
src/Avalonia.Native/headers/comimpl.h

@@ -0,0 +1,183 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+#include "com.h"
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
+#ifndef COMIMPL_H_INCLUDED
+#define COMIMPL_H_INCLUDED
+
+#include <cstring>
+
+__IID_DEF(IUnknown, 0, 0, 0, C0, 00, 00, 00, 00, 00, 00, 46);
+
+class ComObject : public virtual IUnknown
+{
+private:
+    unsigned int _refCount;
+public:
+    
+    virtual ULONG AddRef()
+    {
+        _refCount++;
+        return _refCount;
+    }
+    
+    
+    virtual ULONG Release()
+    {
+        _refCount--;
+        ULONG rv = _refCount;
+        if(_refCount == 0)
+            delete(this);
+        return rv;
+    }
+    
+    ComObject()
+    {
+        _refCount = 1;
+        
+    }
+    virtual ~ComObject()
+    {
+    }
+    
+    
+    virtual ::HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject) = 0;
+    
+    virtual ::HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
+                                                     void **ppvObject)
+    {
+        if(0 == memcmp(riid, &IID_IUnknown, sizeof(GUID)))
+            *ppvObject = (IUnknown*)this;
+        else
+        {
+            auto rv = QueryInterfaceImpl(riid, ppvObject);
+            if(rv != S_OK)
+                return rv;
+        }
+        _refCount++;
+        return S_OK;
+    }
+
+};
+
+
+#define FORWARD_IUNKNOWN() \
+virtual ULONG Release(){ \
+return ComObject::Release(); \
+} \
+virtual ULONG AddRef() \
+{ \
+    return ComObject::AddRef(); \
+} \
+virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) \
+{ \
+    return ComObject::QueryInterface(riid, ppvObject); \
+}
+
+#define BEGIN_INTERFACE_MAP() public: virtual HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject){
+#define INTERFACE_MAP_ENTRY(TInterface, IID) if(0 == memcmp(riid, &IID, sizeof(GUID))) { TInterface* casted = this; *ppvObject = casted; return S_OK; }
+#define END_INTERFACE_MAP() return E_NOINTERFACE; }
+#define INHERIT_INTERFACE_MAP(TBase) if(TBase::QueryInterfaceImpl(riid, ppvObject) == S_OK) return S_OK;
+
+
+
+class ComUnknownObject : public ComObject
+{
+public:
+    FORWARD_IUNKNOWN()
+    virtual ::HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject) override
+    {
+        return E_NOINTERFACE;
+    };
+    virtual ~ComUnknownObject(){}
+};
+
+template<class TInterface, GUID const* TIID> class ComSingleObject : public ComObject, public virtual TInterface
+{
+    BEGIN_INTERFACE_MAP()
+    INTERFACE_MAP_ENTRY(TInterface, *TIID)
+    END_INTERFACE_MAP()
+    
+public:
+    virtual ~ComSingleObject(){}
+};
+
+template<class TInterface>
+class ComPtr
+{
+private:
+    TInterface* _obj;
+public:
+    ComPtr()
+    {
+        _obj = 0;
+    }
+    
+    ComPtr(TInterface* pObj)
+    {
+        _obj = 0;
+
+        if (pObj)
+        {
+            _obj = pObj;
+            _obj->AddRef();
+        }
+    }
+    
+    ComPtr(const ComPtr& ptr)
+    {
+        _obj = 0;
+        
+        if (ptr._obj)
+        {
+            _obj = ptr._obj;
+            _obj->AddRef();
+        }
+
+    }
+    
+    ComPtr& operator=(ComPtr other)
+    {
+        if(_obj != NULL)
+            _obj->Release();
+        _obj = other._obj;
+        if(_obj != NULL)
+            _obj->AddRef();
+        return *this;
+    }
+
+    ~ComPtr()
+    {
+        if (_obj)
+        {
+            _obj->Release();
+            _obj = 0;
+        }
+    }
+    
+    TInterface* getRaw()
+    {
+        return _obj;
+    }
+    
+    operator TInterface*() const
+    {
+        return _obj;
+    }
+    TInterface& operator*() const
+    {
+        return *_obj;
+    }
+    TInterface** operator&()
+    {
+        return &_obj;
+    }
+    TInterface* operator->() const
+    {
+        return _obj;
+    }
+};
+
+#endif // COMIMPL_H_INCLUDED
+#pragma clang diagnostic pop

+ 1023 - 0
src/Avalonia.Native/headers/key.h

@@ -0,0 +1,1023 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+#ifndef _KEY_H_
+#define _KEY_H_
+
+/// <summary>
+/// Defines the keys available on a keyboard.
+/// </summary>
+enum AvnKey
+{
+    /// <summary>
+    /// No key pressed.
+    /// </summary>
+    AvnKeyNone = 0,
+    
+    /// <summary>
+    /// The Cancel key.
+    /// </summary>
+    AvnKeyCancel = 1,
+    
+    /// <summary>
+    /// The Back key.
+    /// </summary>
+    AvnKeyBack = 2,
+    
+    /// <summary>
+    /// The Tab key.
+    /// </summary>
+    AvnKeyTab = 3,
+    
+    /// <summary>
+    /// The Linefeed key.
+    /// </summary>
+    AvnKeyLineFeed = 4,
+    
+    /// <summary>
+    /// The Clear key.
+    /// </summary>
+    AvnKeyClear = 5,
+    
+    /// <summary>
+    /// The Return key.
+    /// </summary>
+    AvnKeyReturn = 6,
+    
+    /// <summary>
+    /// The Enter key.
+    /// </summary>
+    AvnKeyEnter = 6,
+    
+    /// <summary>
+    /// The Pause key.
+    /// </summary>
+    AvnKeyPause = 7,
+    
+    /// <summary>
+    /// The Caps Lock key.
+    /// </summary>
+    AvnKeyCapsLock = 8,
+    
+    /// <summary>
+    /// The Caps Lock key.
+    /// </summary>
+    AvnKeyCapital = 8,
+    
+    /// <summary>
+    /// The IME Hangul mode key.
+    /// </summary>
+    AvnKeyHangulMode = 9,
+    
+    /// <summary>
+    /// The IME Kana mode key.
+    /// </summary>
+    AvnKeyKanaMode = 9,
+    
+    /// <summary>
+    /// The IME Junja mode key.
+    /// </summary>
+    AvnKeyJunjaMode = 10,
+    
+    /// <summary>
+    /// The IME Final mode key.
+    /// </summary>
+    AvnKeyFinalMode = 11,
+    
+    /// <summary>
+    /// The IME Kanji mode key.
+    /// </summary>
+    AvnKeyKanjiMode = 12,
+    
+    /// <summary>
+    /// The IME Hanja mode key.
+    /// </summary>
+    HanjaMode = 12,
+    
+    /// <summary>
+    /// The Escape key.
+    /// </summary>
+    Escape = 13,
+    
+    /// <summary>
+    /// The IME Convert key.
+    /// </summary>
+    ImeConvert = 14,
+    
+    /// <summary>
+    /// The IME NonConvert key.
+    /// </summary>
+    ImeNonConvert = 15,
+    
+    /// <summary>
+    /// The IME Accept key.
+    /// </summary>
+    ImeAccept = 16,
+    
+    /// <summary>
+    /// The IME Mode change key.
+    /// </summary>
+    ImeModeChange = 17,
+    
+    /// <summary>
+    /// The space bar.
+    /// </summary>
+    Space = 18,
+    
+    /// <summary>
+    /// The Page Up key.
+    /// </summary>
+    PageUp = 19,
+    
+    /// <summary>
+    /// The Page Up key.
+    /// </summary>
+    Prior = 19,
+    
+    /// <summary>
+    /// The Page Down key.
+    /// </summary>
+    PageDown = 20,
+    
+    /// <summary>
+    /// The Page Down key.
+    /// </summary>
+    Next = 20,
+    
+    /// <summary>
+    /// The End key.
+    /// </summary>
+    End = 21,
+    
+    /// <summary>
+    /// The Home key.
+    /// </summary>
+    Home = 22,
+    
+    /// <summary>
+    /// The Left arrow key.
+    /// </summary>
+    Left = 23,
+    
+    /// <summary>
+    /// The Up arrow key.
+    /// </summary>
+    Up = 24,
+    
+    /// <summary>
+    /// The Right arrow key.
+    /// </summary>
+    Right = 25,
+    
+    /// <summary>
+    /// The Down arrow key.
+    /// </summary>
+    Down = 26,
+    
+    /// <summary>
+    /// The Select key.
+    /// </summary>
+    Select = 27,
+    
+    /// <summary>
+    /// The Print key.
+    /// </summary>
+    Print = 28,
+    
+    /// <summary>
+    /// The Execute key.
+    /// </summary>
+    Execute = 29,
+    
+    /// <summary>
+    /// The Print Screen key.
+    /// </summary>
+    Snapshot = 30,
+    
+    /// <summary>
+    /// The Print Screen key.
+    /// </summary>
+    PrintScreen = 30,
+    
+    /// <summary>
+    /// The Insert key.
+    /// </summary>
+    Insert = 31,
+    
+    /// <summary>
+    /// The Delete key.
+    /// </summary>
+    Delete = 32,
+    
+    /// <summary>
+    /// The Help key.
+    /// </summary>
+    Help = 33,
+    
+    /// <summary>
+    /// The 0 key.
+    /// </summary>
+    D0 = 34,
+    
+    /// <summary>
+    /// The 1 key.
+    /// </summary>
+    D1 = 35,
+    
+    /// <summary>
+    /// The 2 key.
+    /// </summary>
+    D2 = 36,
+    
+    /// <summary>
+    /// The 3 key.
+    /// </summary>
+    D3 = 37,
+    
+    /// <summary>
+    /// The 4 key.
+    /// </summary>
+    D4 = 38,
+    
+    /// <summary>
+    /// The 5 key.
+    /// </summary>
+    D5 = 39,
+    
+    /// <summary>
+    /// The 6 key.
+    /// </summary>
+    D6 = 40,
+    
+    /// <summary>
+    /// The 7 key.
+    /// </summary>
+    D7 = 41,
+    
+    /// <summary>
+    /// The 8 key.
+    /// </summary>
+    D8 = 42,
+    
+    /// <summary>
+    /// The 9 key.
+    /// </summary>
+    D9 = 43,
+    
+    /// <summary>
+    /// The A key.
+    /// </summary>
+    A = 44,
+    
+    /// <summary>
+    /// The B key.
+    /// </summary>
+    B = 45,
+    
+    /// <summary>
+    /// The C key.
+    /// </summary>
+    C = 46,
+    
+    /// <summary>
+    /// The D key.
+    /// </summary>
+    D = 47,
+    
+    /// <summary>
+    /// The E key.
+    /// </summary>
+    E = 48,
+    
+    /// <summary>
+    /// The F key.
+    /// </summary>
+    F = 49,
+    
+    /// <summary>
+    /// The G key.
+    /// </summary>
+    G = 50,
+    
+    /// <summary>
+    /// The H key.
+    /// </summary>
+    H = 51,
+    
+    /// <summary>
+    /// The I key.
+    /// </summary>
+    I = 52,
+    
+    /// <summary>
+    /// The J key.
+    /// </summary>
+    J = 53,
+    
+    /// <summary>
+    /// The K key.
+    /// </summary>
+    AvnKeyK = 54,
+    
+    /// <summary>
+    /// The L key.
+    /// </summary>
+    L = 55,
+    
+    /// <summary>
+    /// The M key.
+    /// </summary>
+    M = 56,
+    
+    /// <summary>
+    /// The N key.
+    /// </summary>
+    N = 57,
+    
+    /// <summary>
+    /// The O key.
+    /// </summary>
+    O = 58,
+    
+    /// <summary>
+    /// The P key.
+    /// </summary>
+    P = 59,
+    
+    /// <summary>
+    /// The Q key.
+    /// </summary>
+    Q = 60,
+    
+    /// <summary>
+    /// The R key.
+    /// </summary>
+    R = 61,
+    
+    /// <summary>
+    /// The S key.
+    /// </summary>
+    S = 62,
+    
+    /// <summary>
+    /// The T key.
+    /// </summary>
+    T = 63,
+    
+    /// <summary>
+    /// The U key.
+    /// </summary>
+    U = 64,
+    
+    /// <summary>
+    /// The V key.
+    /// </summary>
+    V = 65,
+    
+    /// <summary>
+    /// The W key.
+    /// </summary>
+    W = 66,
+    
+    /// <summary>
+    /// The X key.
+    /// </summary>
+    X = 67,
+    
+    /// <summary>
+    /// The Y key.
+    /// </summary>
+    Y = 68,
+    
+    /// <summary>
+    /// The Z key.
+    /// </summary>
+    Z = 69,
+    
+    /// <summary>
+    /// The left Windows key.
+    /// </summary>
+    LWin = 70,
+    
+    /// <summary>
+    /// The right Windows key.
+    /// </summary>
+    RWin = 71,
+    
+    /// <summary>
+    /// The Application key.
+    /// </summary>
+    Apps = 72,
+    
+    /// <summary>
+    /// The Sleep key.
+    /// </summary>
+    Sleep = 73,
+    
+    /// <summary>
+    /// The 0 key on the numeric keypad.
+    /// </summary>
+    NumPad0 = 74,
+    
+    /// <summary>
+    /// The 1 key on the numeric keypad.
+    /// </summary>
+    NumPad1 = 75,
+    
+    /// <summary>
+    /// The 2 key on the numeric keypad.
+    /// </summary>
+    NumPad2 = 76,
+    
+    /// <summary>
+    /// The 3 key on the numeric keypad.
+    /// </summary>
+    NumPad3 = 77,
+    
+    /// <summary>
+    /// The 4 key on the numeric keypad.
+    /// </summary>
+    NumPad4 = 78,
+    
+    /// <summary>
+    /// The 5 key on the numeric keypad.
+    /// </summary>
+    NumPad5 = 79,
+    
+    /// <summary>
+    /// The 6 key on the numeric keypad.
+    /// </summary>
+    NumPad6 = 80,
+    
+    /// <summary>
+    /// The 7 key on the numeric keypad.
+    /// </summary>
+    NumPad7 = 81,
+    
+    /// <summary>
+    /// The 8 key on the numeric keypad.
+    /// </summary>
+    NumPad8 = 82,
+    
+    /// <summary>
+    /// The 9 key on the numeric keypad.
+    /// </summary>
+    NumPad9 = 83,
+    
+    /// <summary>
+    /// The Multiply key.
+    /// </summary>
+    Multiply = 84,
+    
+    /// <summary>
+    /// The Add key.
+    /// </summary>
+    Add = 85,
+    
+    /// <summary>
+    /// The Separator key.
+    /// </summary>
+    Separator = 86,
+    
+    /// <summary>
+    /// The Subtract key.
+    /// </summary>
+    Subtract = 87,
+    
+    /// <summary>
+    /// The Decimal key.
+    /// </summary>
+    Decimal = 88,
+    
+    /// <summary>
+    /// The Divide key.
+    /// </summary>
+    Divide = 89,
+    
+    /// <summary>
+    /// The F1 key.
+    /// </summary>
+    F1 = 90,
+    
+    /// <summary>
+    /// The F2 key.
+    /// </summary>
+    F2 = 91,
+    
+    /// <summary>
+    /// The F3 key.
+    /// </summary>
+    F3 = 92,
+    
+    /// <summary>
+    /// The F4 key.
+    /// </summary>
+    F4 = 93,
+    
+    /// <summary>
+    /// The F5 key.
+    /// </summary>
+    F5 = 94,
+    
+    /// <summary>
+    /// The F6 key.
+    /// </summary>
+    F6 = 95,
+    
+    /// <summary>
+    /// The F7 key.
+    /// </summary>
+    F7 = 96,
+    
+    /// <summary>
+    /// The F8 key.
+    /// </summary>
+    F8 = 97,
+    
+    /// <summary>
+    /// The F9 key.
+    /// </summary>
+    F9 = 98,
+    
+    /// <summary>
+    /// The F10 key.
+    /// </summary>
+    F10 = 99,
+    
+    /// <summary>
+    /// The F11 key.
+    /// </summary>
+    F11 = 100,
+    
+    /// <summary>
+    /// The F12 key.
+    /// </summary>
+    F12 = 101,
+    
+    /// <summary>
+    /// The F13 key.
+    /// </summary>
+    F13 = 102,
+    
+    /// <summary>
+    /// The F14 key.
+    /// </summary>
+    F14 = 103,
+    
+    /// <summary>
+    /// The F15 key.
+    /// </summary>
+    F15 = 104,
+    
+    /// <summary>
+    /// The F16 key.
+    /// </summary>
+    F16 = 105,
+    
+    /// <summary>
+    /// The F17 key.
+    /// </summary>
+    F17 = 106,
+    
+    /// <summary>
+    /// The F18 key.
+    /// </summary>
+    F18 = 107,
+    
+    /// <summary>
+    /// The F19 key.
+    /// </summary>
+    F19 = 108,
+    
+    /// <summary>
+    /// The F20 key.
+    /// </summary>
+    F20 = 109,
+    
+    /// <summary>
+    /// The F21 key.
+    /// </summary>
+    F21 = 110,
+    
+    /// <summary>
+    /// The F22 key.
+    /// </summary>
+    F22 = 111,
+    
+    /// <summary>
+    /// The F23 key.
+    /// </summary>
+    F23 = 112,
+    
+    /// <summary>
+    /// The F24 key.
+    /// </summary>
+    F24 = 113,
+    
+    /// <summary>
+    /// The Numlock key.
+    /// </summary>
+    NumLock = 114,
+    
+    /// <summary>
+    /// The Scroll key.
+    /// </summary>
+    Scroll = 115,
+    
+    /// <summary>
+    /// The left Shift key.
+    /// </summary>
+    LeftShift = 116,
+    
+    /// <summary>
+    /// The right Shift key.
+    /// </summary>
+    RightShift = 117,
+    
+    /// <summary>
+    /// The left Ctrl key.
+    /// </summary>
+    LeftCtrl = 118,
+    
+    /// <summary>
+    /// The right Ctrl key.
+    /// </summary>
+    RightCtrl = 119,
+    
+    /// <summary>
+    /// The left Alt key.
+    /// </summary>
+    LeftAlt = 120,
+    
+    /// <summary>
+    /// The right Alt key.
+    /// </summary>
+    RightAlt = 121,
+    
+    /// <summary>
+    /// The browser Back key.
+    /// </summary>
+    BrowserBack = 122,
+    
+    /// <summary>
+    /// The browser Forward key.
+    /// </summary>
+    BrowserForward = 123,
+    
+    /// <summary>
+    /// The browser Refresh key.
+    /// </summary>
+    BrowserRefresh = 124,
+    
+    /// <summary>
+    /// The browser Stop key.
+    /// </summary>
+    BrowserStop = 125,
+    
+    /// <summary>
+    /// The browser Search key.
+    /// </summary>
+    BrowserSearch = 126,
+    
+    /// <summary>
+    /// The browser Favorites key.
+    /// </summary>
+    BrowserFavorites = 127,
+    
+    /// <summary>
+    /// The browser Home key.
+    /// </summary>
+    BrowserHome = 128,
+    
+    /// <summary>
+    /// The Volume Mute key.
+    /// </summary>
+    VolumeMute = 129,
+    
+    /// <summary>
+    /// The Volume Down key.
+    /// </summary>
+    VolumeDown = 130,
+    
+    /// <summary>
+    /// The Volume Up key.
+    /// </summary>
+    VolumeUp = 131,
+    
+    /// <summary>
+    /// The media Next Track key.
+    /// </summary>
+    MediaNextTrack = 132,
+    
+    /// <summary>
+    /// The media Previous Track key.
+    /// </summary>
+    MediaPreviousTrack = 133,
+    
+    /// <summary>
+    /// The media Stop key.
+    /// </summary>
+    MediaStop = 134,
+    
+    /// <summary>
+    /// The media Play/Pause key.
+    /// </summary>
+    MediaPlayPause = 135,
+    
+    /// <summary>
+    /// The Launch Mail key.
+    /// </summary>
+    LaunchMail = 136,
+    
+    /// <summary>
+    /// The Select Media key.
+    /// </summary>
+    SelectMedia = 137,
+    
+    /// <summary>
+    /// The Launch Application 1 key.
+    /// </summary>
+    LaunchApplication1 = 138,
+    
+    /// <summary>
+    /// The Launch Application 2 key.
+    /// </summary>
+    LaunchApplication2 = 139,
+    
+    /// <summary>
+    /// The OEM Semicolon key.
+    /// </summary>
+    OemSemicolon = 140,
+    
+    /// <summary>
+    /// The OEM 1 key.
+    /// </summary>
+    Oem1 = 140,
+    
+    /// <summary>
+    /// The OEM Plus key.
+    /// </summary>
+    OemPlus = 141,
+    
+    /// <summary>
+    /// The OEM Comma key.
+    /// </summary>
+    OemComma = 142,
+    
+    /// <summary>
+    /// The OEM Minus key.
+    /// </summary>
+    OemMinus = 143,
+    
+    /// <summary>
+    /// The OEM Period key.
+    /// </summary>
+    OemPeriod = 144,
+    
+    /// <summary>
+    /// The OEM Question Mark key.
+    /// </summary>
+    OemQuestion = 145,
+    
+    /// <summary>
+    /// The OEM 2 key.
+    /// </summary>
+    Oem2 = 145,
+    
+    /// <summary>
+    /// The OEM Tilde key.
+    /// </summary>
+    OemTilde = 146,
+    
+    /// <summary>
+    /// The OEM 3 key.
+    /// </summary>
+    Oem3 = 146,
+    
+    /// <summary>
+    /// The ABNT_C1 (Brazilian) key.
+    /// </summary>
+    AbntC1 = 147,
+    
+    /// <summary>
+    /// The ABNT_C2 (Brazilian) key.
+    /// </summary>
+    AbntC2 = 148,
+    
+    /// <summary>
+    /// The OEM Open Brackets key.
+    /// </summary>
+    OemOpenBrackets = 149,
+    
+    /// <summary>
+    /// The OEM 4 key.
+    /// </summary>
+    Oem4 = 149,
+    
+    /// <summary>
+    /// The OEM Pipe key.
+    /// </summary>
+    OemPipe = 150,
+    
+    /// <summary>
+    /// The OEM 5 key.
+    /// </summary>
+    Oem5 = 150,
+    
+    /// <summary>
+    /// The OEM Close Brackets key.
+    /// </summary>
+    OemCloseBrackets = 151,
+    
+    /// <summary>
+    /// The OEM 6 key.
+    /// </summary>
+    Oem6 = 151,
+    
+    /// <summary>
+    /// The OEM Quotes key.
+    /// </summary>
+    OemQuotes = 152,
+    
+    /// <summary>
+    /// The OEM 7 key.
+    /// </summary>
+    Oem7 = 152,
+    
+    /// <summary>
+    /// The OEM 8 key.
+    /// </summary>
+    Oem8 = 153,
+    
+    /// <summary>
+    /// The OEM Backslash key.
+    /// </summary>
+    OemBackslash = 154,
+    
+    /// <summary>
+    /// The OEM 3 key.
+    /// </summary>
+    Oem102 = 154,
+    
+    /// <summary>
+    /// A special key masking the real key being processed by an IME.
+    /// </summary>
+    ImeProcessed = 155,
+    
+    /// <summary>
+    /// A special key masking the real key being processed as a system key.
+    /// </summary>
+    System = 156,
+    
+    /// <summary>
+    /// The OEM ATTN key.
+    /// </summary>
+    OemAttn = 157,
+    
+    /// <summary>
+    /// The DBE_ALPHANUMERIC key.
+    /// </summary>
+    DbeAlphanumeric = 157,
+    
+    /// <summary>
+    /// The OEM Finish key.
+    /// </summary>
+    OemFinish = 158,
+    
+    /// <summary>
+    /// The DBE_KATAKANA key.
+    /// </summary>
+    DbeKatakana = 158,
+    
+    /// <summary>
+    /// The DBE_HIRAGANA key.
+    /// </summary>
+    DbeHiragana = 159,
+    
+    /// <summary>
+    /// The OEM Copy key.
+    /// </summary>
+    OemCopy = 159,
+    
+    /// <summary>
+    /// The DBE_SBCSCHAR key.
+    /// </summary>
+    DbeSbcsChar = 160,
+    
+    /// <summary>
+    /// The OEM Auto key.
+    /// </summary>
+    OemAuto = 160,
+    
+    /// <summary>
+    /// The DBE_DBCSCHAR key.
+    /// </summary>
+    DbeDbcsChar = 161,
+    
+    /// <summary>
+    /// The OEM ENLW key.
+    /// </summary>
+    OemEnlw = 161,
+    
+    /// <summary>
+    /// The OEM BackTab key.
+    /// </summary>
+    OemBackTab = 162,
+    
+    /// <summary>
+    /// The DBE_ROMAN key.
+    /// </summary>
+    DbeRoman = 162,
+    
+    /// <summary>
+    /// The DBE_NOROMAN key.
+    /// </summary>
+    DbeNoRoman = 163,
+    
+    /// <summary>
+    /// The ATTN key.
+    /// </summary>
+    Attn = 163,
+    
+    /// <summary>
+    /// The CRSEL key.
+    /// </summary>
+    CrSel = 164,
+    
+    /// <summary>
+    /// The DBE_ENTERWORDREGISTERMODE key.
+    /// </summary>
+    DbeEnterWordRegisterMode = 164,
+    
+    /// <summary>
+    /// The EXSEL key.
+    /// </summary>
+    ExSel = 165,
+    
+    /// <summary>
+    /// The DBE_ENTERIMECONFIGMODE key.
+    /// </summary>
+    DbeEnterImeConfigureMode = 165,
+    
+    /// <summary>
+    /// The ERASE EOF Key.
+    /// </summary>
+    EraseEof = 166,
+    
+    /// <summary>
+    /// The DBE_FLUSHSTRING key.
+    /// </summary>
+    DbeFlushString = 166,
+    
+    /// <summary>
+    /// The Play key.
+    /// </summary>
+    Play = 167,
+    
+    /// <summary>
+    /// The DBE_CODEINPUT key.
+    /// </summary>
+    DbeCodeInput = 167,
+    
+    /// <summary>
+    /// The DBE_NOCODEINPUT key.
+    /// </summary>
+    DbeNoCodeInput = 168,
+    
+    /// <summary>
+    /// The Zoom key.
+    /// </summary>
+    Zoom = 168,
+    
+    /// <summary>
+    /// Reserved for future use.
+    /// </summary>
+    NoName = 169,
+    
+    /// <summary>
+    /// The DBE_DETERMINESTRING key.
+    /// </summary>
+    DbeDetermineString = 169,
+    
+    /// <summary>
+    /// The DBE_ENTERDLGCONVERSIONMODE key.
+    /// </summary>
+    DbeEnterDialogConversionMode = 170,
+    
+    /// <summary>
+    /// The PA1 key.
+    /// </summary>
+    Pa1 = 170,
+    
+    /// <summary>
+    /// The OEM Clear key.
+    /// </summary>
+    OemClear = 171,
+    
+    /// <summary>
+    /// The key is used with another key to create a single combined character.
+    /// </summary>
+    DeadCharProcessed = 172,
+};
+
+#endif

+ 2 - 0
src/Avalonia.Native/regen.sh

@@ -0,0 +1,2 @@
+#!/bin/sh
+dotnet msbuild /t:Clean,GenerateSharpGenBindings