Browse Source

Merge pull request #1977 from AvaloniaUI/avalonia-native

New ObjC++/COM-based OSX backend
pull/2005/head
danwalmsley 8 years ago
committed by GitHub
parent
commit
a184e27062
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      Avalonia.sln
  2. 37
      build.cake
  3. 1
      dirs.proj
  4. 4
      src/Avalonia.Native.OSX/.gitignore
  5. 328
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  6. 7
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  7. 91
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  8. 12
      src/Avalonia.Native.OSX/KeyTransform.h
  9. 241
      src/Avalonia.Native.OSX/KeyTransform.mm
  10. 51
      src/Avalonia.Native.OSX/Screens.mm
  11. 262
      src/Avalonia.Native.OSX/SystemDialogs.mm
  12. 47
      src/Avalonia.Native.OSX/clipboard.mm
  13. 34
      src/Avalonia.Native.OSX/common.h
  14. 29
      src/Avalonia.Native.OSX/cursor.h
  15. 73
      src/Avalonia.Native.OSX/cursor.mm
  16. 255
      src/Avalonia.Native.OSX/gl.mm
  17. 178
      src/Avalonia.Native.OSX/main.mm
  18. 190
      src/Avalonia.Native.OSX/platformthreading.mm
  19. 33
      src/Avalonia.Native.OSX/window.h
  20. 1228
      src/Avalonia.Native.OSX/window.mm
  21. 1
      src/Avalonia.Native/.gitignore
  22. 22
      src/Avalonia.Native/Avalonia.Native.csproj
  23. 9
      src/Avalonia.Native/Avalonia.Native.targets
  24. 132
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  25. 29
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  26. 80
      src/Avalonia.Native/CallbackBase.cs
  27. 47
      src/Avalonia.Native/ClipboardImpl.cs
  28. 45
      src/Avalonia.Native/Cursor.cs
  29. 87
      src/Avalonia.Native/DeferredFramebuffer.cs
  30. 108
      src/Avalonia.Native/DeferredRendererProxy.cs
  31. 128
      src/Avalonia.Native/DynLoader.cs
  32. 134
      src/Avalonia.Native/GlPlatformFeature.cs
  33. 35
      src/Avalonia.Native/Helpers.cs
  34. 50
      src/Avalonia.Native/IconLoader.cs
  35. 27
      src/Avalonia.Native/Mappings.xml
  36. 101
      src/Avalonia.Native/PlatformThreadingInterface.cs
  37. 39
      src/Avalonia.Native/PopupImpl.cs
  38. 45
      src/Avalonia.Native/ScreenImpl.cs
  39. 90
      src/Avalonia.Native/SystemDialogs.cs
  40. 100
      src/Avalonia.Native/WindowImpl.cs
  41. 351
      src/Avalonia.Native/WindowImplBase.cs
  42. 5
      src/Avalonia.Native/headers/avalonia-native-guids.h
  43. 363
      src/Avalonia.Native/headers/avalonia-native.h
  44. 57
      src/Avalonia.Native/headers/com.h
  45. 183
      src/Avalonia.Native/headers/comimpl.h
  46. 1023
      src/Avalonia.Native/headers/key.h
  47. 2
      src/Avalonia.Native/regen.sh

26
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

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
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
src/Avalonia.Native.OSX/.gitignore

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

328
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
src/Avalonia.Native.OSX/window.mm

File diff suppressed because it is too large

1
src/Avalonia.Native/.gitignore

@ -0,0 +1 @@
Generated

22
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
src/Avalonia.Native/headers/key.h

File diff suppressed because it is too large

2
src/Avalonia.Native/regen.sh

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