diff --git a/src/Avalonia.Native.OSX/.gitignore b/src/Avalonia.Native.OSX/.gitignore new file mode 100644 index 0000000000..cbe26bb13c --- /dev/null +++ b/src/Avalonia.Native.OSX/.gitignore @@ -0,0 +1,4 @@ +build + +Avalonia.Native.OSX.xcodeproj/xcuserdata +Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcuserdata diff --git a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..8d36bf3b8f --- /dev/null +++ b/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 = ""; }; + 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../Avalonia.Native/headers; sourceTree = ""; }; + 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; + 37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; }; + 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; }; + 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; + 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; }; + 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; }; + 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; }; + AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; + AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; + 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 = ""; }; +/* 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 = ""; + }; + 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 = ""; + }; + AB7A61F02147C815003C5833 /* Products */ = { + isa = PBXGroup; + children = ( + AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */, + ); + name = Products; + sourceTree = ""; + }; +/* 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 */; +} diff --git a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..4ea7ed7566 --- /dev/null +++ b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme new file mode 100644 index 0000000000..96588581bf --- /dev/null +++ b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Native.OSX/KeyTransform.h b/src/Avalonia.Native.OSX/KeyTransform.h new file mode 100644 index 0000000000..c4466020c3 --- /dev/null +++ b/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 + +extern std::map s_KeyMap; + +#endif diff --git a/src/Avalonia.Native.OSX/KeyTransform.mm b/src/Avalonia.Native.OSX/KeyTransform.mm new file mode 100644 index 0000000000..7486aaad69 --- /dev/null +++ b/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 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} +}; diff --git a/src/Avalonia.Native.OSX/Screens.mm b/src/Avalonia.Native.OSX/Screens.mm new file mode 100644 index 0000000000..229c598797 --- /dev/null +++ b/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 +{ + 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(); +} diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm new file mode 100644 index 0000000000..55b1abc720 --- /dev/null +++ b/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 +{ +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(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; + } + + return; + } + } + + events->OnCompleted(0, nullptr); + + }; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(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(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; + } + + return; + } + } + + events->OnCompleted(0, nullptr); + + }; + + if(parentWindowHandle != nullptr) + { + auto windowHolder = dynamic_cast(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(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; + } + + return; + } + + events->OnCompleted(0, nullptr); + + }; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + + [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler]; + } + else + { + [panel beginWithCompletionHandler: handler]; + } + } + } + +}; + +extern IAvnSystemDialogs* CreateSystemDialogs() +{ + return new SystemDialogs(); +} diff --git a/src/Avalonia.Native.OSX/clipboard.mm b/src/Avalonia.Native.OSX/clipboard.mm new file mode 100644 index 0000000000..e39b78fc33 --- /dev/null +++ b/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 +{ +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(); +} diff --git a/src/Avalonia.Native.OSX/common.h b/src/Avalonia.Native.OSX/common.h new file mode 100644 index 0000000000..1647569269 --- /dev/null +++ b/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 +#import +#import +#include + +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 diff --git a/src/Avalonia.Native.OSX/cursor.h b/src/Avalonia.Native.OSX/cursor.h new file mode 100644 index 0000000000..a8eb49c0b9 --- /dev/null +++ b/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 + +class Cursor : public ComSingleObject +{ +private: + NSCursor * _native; + +public: + FORWARD_IUNKNOWN() + Cursor(NSCursor * cursor) + { + _native = cursor; + } + + NSCursor* GetNative() + { + return _native; + } +}; + +extern std::map s_cursorMap; +#endif /* cursor_h */ diff --git a/src/Avalonia.Native.OSX/cursor.mm b/src/Avalonia.Native.OSX/cursor.mm new file mode 100644 index 0000000000..6a06918527 --- /dev/null +++ b/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 + +class CursorFactory : public ComSingleObject +{ + 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 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(); + } +} diff --git a/src/Avalonia.Native.OSX/gl.mm b/src/Avalonia.Native.OSX/gl.mm new file mode 100644 index 0000000000..768ff23ceb --- /dev/null +++ b/src/Avalonia.Native.OSX/gl.mm @@ -0,0 +1,255 @@ +#include "common.h" +#include +#include + +template 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 +{ +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 +{ + 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 +{ + 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 +{ + 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 +{ + 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); +} diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm new file mode 100644 index 0000000000..bb32c44918 --- /dev/null +++ b/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 +{ +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 +{ + +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; +} diff --git a/src/Avalonia.Native.OSX/platformthreading.mm b/src/Avalonia.Native.OSX/platformthreading.mm new file mode 100644 index 0000000000..c1ca6b6421 --- /dev/null +++ b/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 _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 +{ +private: + Signaler* _signaler; + + class LoopCancellation : public ComSingleObject + { + public: + FORWARD_IUNKNOWN() + bool Cancelled = 0; + virtual void Cancel() + { + Cancelled = 1; + } + }; + +public: + FORWARD_IUNKNOWN() + ComPtr 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(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* _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(); +} diff --git a/src/Avalonia.Native.OSX/window.h b/src/Avalonia.Native.OSX/window.h new file mode 100644 index 0000000000..34592993c3 --- /dev/null +++ b/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 +-(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 +-(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 */ diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm new file mode 100644 index 0000000000..2e9f7503fb --- /dev/null +++ b/src/Avalonia.Native.OSX/window.mm @@ -0,0 +1,1228 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +#include "common.h" +#include "window.h" +#include "KeyTransform.h" +#include "cursor.h" +#include + +class SoftwareDrawingOperation +{ +public: + void* Data = 0; + AvnFramebuffer Desc; + void Alloc(NSView* view) + { + auto logicalSize = [view frame].size; + auto pixelSize = [view convertSizeToBacking:logicalSize]; + int w = pixelSize.width; + int h = pixelSize.height; + int stride = w * 4; + Data = malloc(h * stride); + Desc = { + .Data = Data, + .Stride = stride, + .Width = w, + .Height = h, + .PixelFormat = kAvnRgba8888, + .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96} + }; + } + + void Dealloc() + { + if(Data != NULL) + { + free(Data); + Data = NULL; + } + } + + ~SoftwareDrawingOperation() + { + Dealloc(); + } +}; + +class WindowBaseImpl : public virtual ComSingleObject, public INSWindowHolder +{ +private: + NSCursor* cursor; + +public: + FORWARD_IUNKNOWN() + virtual ~WindowBaseImpl() + { + NSDebugLog(@"~WindowBaseImpl()"); + View = NULL; + Window = NULL; + } + AvnView* View; + AvnWindow* Window; + ComPtr BaseEvents; + SoftwareDrawingOperation CurrentSwDrawingOperation; + AvnPoint lastPositionSet; + NSString* _lastTitle; + + WindowBaseImpl(IAvnWindowBaseEvents* events) + { + BaseEvents = events; + View = [[AvnView alloc] initWithParent:this]; + + Window = [[AvnWindow alloc] initWithParent:this]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + _lastTitle = @""; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentView: View]; + } + + virtual AvnWindow* GetNSWindow() + { + return Window; + } + + virtual HRESULT Show() + { + @autoreleasepool + { + SetPosition(lastPositionSet); + UpdateStyle(); + + [Window makeKeyAndOrderFront:Window]; + + [Window setTitle:_lastTitle]; + [Window setTitleVisibility:NSWindowTitleVisible]; + + return S_OK; + } + } + + virtual HRESULT Hide () + { + @autoreleasepool + { + if(Window != nullptr) + { + [Window orderOut:Window]; + } + + return S_OK; + } + } + + virtual HRESULT Activate () + { + @autoreleasepool + { + if(Window != nullptr) + { + [Window makeKeyWindow]; + } + } + + return S_OK; + } + + virtual HRESULT SetTopMost (bool value) + { + @autoreleasepool + { + [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + + return S_OK; + } + } + + virtual HRESULT Close() + { + @autoreleasepool + { + [Window close]; + return S_OK; + } + } + + virtual HRESULT GetClientSize(AvnSize* ret) + { + @autoreleasepool + { + if(ret == nullptr) + return E_POINTER; + auto frame = [View frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + return S_OK; + } + } + + virtual HRESULT GetMaxClientSize(AvnSize* ret) + { + @autoreleasepool + { + if(ret == nullptr) + return E_POINTER; + + auto size = [NSScreen.screens objectAtIndex:0].frame.size; + + ret->Height = size.height; + ret->Width = size.width; + + return S_OK; + } + } + + virtual HRESULT GetScaling (double* ret) + { + @autoreleasepool + { + if(ret == nullptr) + return E_POINTER; + + if(Window == nullptr) + { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } + } + + virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) + { + @autoreleasepool + { + [Window setMinSize: ToNSSize(minSize)]; + [Window setMaxSize: ToNSSize(maxSize)]; + + return S_OK; + } + } + + virtual HRESULT Resize(double x, double y) + { + @autoreleasepool + { + [Window setContentSize:NSSize{x, y}]; + + return S_OK; + } + } + + virtual HRESULT Invalidate (AvnRect rect) + { + @autoreleasepool + { + [View setNeedsDisplayInRect:[View frame]]; + + return S_OK; + } + } + + virtual bool TryLock() + { + @autoreleasepool + { + return [View lockFocusIfCanDraw] == YES; + } + } + + virtual void Unlock() + { + @autoreleasepool + { + [View unlockFocus]; + } + } + + virtual HRESULT BeginMoveDrag () + { + @autoreleasepool + { + auto lastEvent = [View lastMouseDownEvent]; + + if(lastEvent == nullptr) + { + return S_OK; + } + + [Window performWindowDragWithEvent:lastEvent]; + + return S_OK; + } + } + + virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) + { + return S_OK; + } + + virtual HRESULT GetPosition (AvnPoint* ret) + { + @autoreleasepool + { + if(ret == nullptr) + { + return E_POINTER; + } + + auto frame = [Window frame]; + + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; + + *ret = ConvertPointY(*ret); + + return S_OK; + } + } + + virtual HRESULT SetPosition (AvnPoint point) + { + @autoreleasepool + { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + + return S_OK; + } + } + + virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) + { + @autoreleasepool + { + if(ret == nullptr) + { + return E_POINTER; + } + + point = ConvertPointY(point); + auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)]; + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } + } + + virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) + { + @autoreleasepool + { + if(ret == nullptr) + { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint]; + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } + } + + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) + { + [View setSwRenderedFrame: fb dispose: dispose]; + return S_OK; + } + + virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) + { + if(![[NSThread currentThread] isMainThread]) + return E_FAIL; + if(CurrentSwDrawingOperation.Data == NULL) + CurrentSwDrawingOperation.Alloc(View); + *ret = CurrentSwDrawingOperation.Desc; + return S_OK; + } + + virtual HRESULT SetCursor(IAvnCursor* cursor) + { + @autoreleasepool + { + Cursor* avnCursor = dynamic_cast(cursor); + this->cursor = avnCursor->GetNative(); + UpdateCursor(); + return S_OK; + } + } + + virtual void UpdateCursor() + { + [View resetCursorRects]; + if (cursor != nil) + { + auto rect = [Window frame]; + [View addCursorRect:rect cursor:cursor]; + [cursor set]; + } + } + + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) + { + if(View == NULL) + return E_FAIL; + *ppv = ::CreateGlRenderTarget(Window, View); + return S_OK; + } + +protected: + virtual NSWindowStyleMask GetStyle() + { + return NSWindowStyleMaskBorderless; + } + + void UpdateStyle() + { + [Window setStyleMask:GetStyle()]; + } + + virtual void OnResized () + { + + } +}; + +class ModalDisposable : public ComUnknownObject +{ + NSModalSession _session; + AvnWindow* _window; + + void Dispose () + { + [_window orderOut:_window]; + [NSApp endModalSession:_session]; + } + +public: + ModalDisposable(AvnWindow* window, NSModalSession session) + { + _session = session; + _window = window; + } + + virtual ~ModalDisposable() + { + Dispose(); + } +}; + +class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged +{ +private: + bool _canResize = true; + bool _hasDecorations = true; + CGRect _lastUndecoratedFrame; + AvnWindowState _lastWindowState; + + FORWARD_IUNKNOWN() + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) + END_INTERFACE_MAP() + virtual ~WindowImpl(){ + NSDebugLog(@"~WindowImpl"); + } + + ComPtr WindowEvents; + WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + { + WindowEvents = events; + [Window setCanBecomeKeyAndMain]; + } + + virtual HRESULT Show () + { + @autoreleasepool + { + WindowBaseImpl::Show(); + + return SetWindowState(_lastWindowState); + } + } + + virtual HRESULT ShowDialog (IUnknown**ppv) + { + @autoreleasepool + { + if(ppv == nullptr) + { + return E_POINTER; + } + + auto session = [NSApp beginModalSessionForWindow:Window]; + auto disposable = new ModalDisposable(Window, session); + *ppv = disposable; + + SetPosition(lastPositionSet); + UpdateStyle(); + + [Window setTitle:_lastTitle]; + [Window setTitleVisibility:NSWindowTitleVisible]; + + [Window pollModalSession:session]; + + return S_OK; + } + } + + void WindowStateChanged () + { + AvnWindowState state; + GetWindowState(&state); + WindowEvents->WindowStateChanged(state); + } + + bool UndecoratedIsMaximized () + { + return CGRectEqualToRect([Window frame], [Window screen].visibleFrame); + } + + bool IsZoomed () + { + return _hasDecorations ? [Window isZoomed] : UndecoratedIsMaximized(); + } + + void DoZoom() + { + if (_hasDecorations) + { + [Window performZoom:Window]; + } + else + { + if (!UndecoratedIsMaximized()) + { + _lastUndecoratedFrame = [Window frame]; + } + + [Window zoom:Window]; + } + } + + virtual HRESULT SetCanResize(bool value) + { + @autoreleasepool + { + _canResize = value; + UpdateStyle(); + return S_OK; + } + } + + virtual HRESULT SetHasDecorations(bool value) + { + @autoreleasepool + { + _hasDecorations = value; + UpdateStyle(); + + return S_OK; + } + } + + virtual HRESULT SetTitle (const char* title) + { + @autoreleasepool + { + _lastTitle = [NSString stringWithUTF8String:title]; + [Window setTitle:_lastTitle]; + [Window setTitleVisibility:NSWindowTitleVisible]; + + return S_OK; + } + } + + virtual HRESULT SetTitleBarColor(AvnColor color) + { + @autoreleasepool + { + float a = (float)color.Alpha / 255.0f; + float r = (float)color.Red / 255.0f; + float g = (float)color.Green / 255.0f; + float b = (float)color.Blue / 255.0f; + + auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + + // Based on the titlebar color we have to choose either light or dark + // OSX doesnt let you set a foreground color for titlebar. + if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f) + { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; + } + else + { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; + } + + [Window setTitlebarAppearsTransparent:true]; + [Window setBackgroundColor:nscolor]; + } + + return S_OK; + } + + virtual HRESULT GetWindowState (AvnWindowState*ret) + { + @autoreleasepool + { + if(ret == nullptr) + { + return E_POINTER; + } + + if([Window isMiniaturized]) + { + *ret = Minimized; + return S_OK; + } + + if([Window isZoomed]) + { + *ret = Maximized; + return S_OK; + } + + *ret = Normal; + + return S_OK; + } + } + + virtual HRESULT SetWindowState (AvnWindowState state) + { + @autoreleasepool + { + _lastWindowState = state; + + switch (state) { + case Maximized: + lastPositionSet.X = 0; + lastPositionSet.Y = 0; + + if([Window isMiniaturized]) + { + [Window deminiaturize:Window]; + } + + if(!IsZoomed()) + { + DoZoom(); + } + break; + + case Minimized: + [Window miniaturize:Window]; + break; + + default: + if([Window isMiniaturized]) + { + [Window deminiaturize:Window]; + } + + if(IsZoomed()) + { + DoZoom(); + } + break; + } + + return S_OK; + } + } + +protected: + virtual void OnResized () + { + auto windowState = [Window isMiniaturized] ? Minimized + : (IsZoomed() ? Maximized : Normal); + + if (windowState != _lastWindowState) + { + _lastWindowState = windowState; + + WindowEvents->WindowStateChanged(windowState); + } + } + + virtual NSWindowStyleMask GetStyle() + { + unsigned long s = NSWindowStyleMaskBorderless; + if(_hasDecorations) + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + if(_canResize) + s = s | NSWindowStyleMaskResizable; + return s; + } +}; + +NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; + +@implementation AvnView +{ + ComPtr _parent; + ComPtr _swRenderedFrame; + AvnFramebuffer _swRenderedFrameBuffer; + bool _queuedDisplayFromThread; + NSTrackingArea* _area; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver; + NSEvent* _lastMouseDownEvent; + bool _lastKeyHandled; +} + +- (void)dealloc +{ + NSDebugLog(@"AvnView dealloc"); +} + + +- (void)onClosed +{ + _parent = NULL; +} + +- (NSEvent*) lastMouseDownEvent +{ + return _lastMouseDownEvent; +} + +-(AvnView*) initWithParent: (WindowBaseImpl*) parent +{ + self = [super init]; + [self setWantsBestResolutionOpenGLSurface:true]; + _parent = parent; + _area = nullptr; + return self; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return true; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return true; +} + +- (BOOL)canBecomeKeyView +{ + return true; +} + +-(void)setFrameSize:(NSSize)newSize +{ + [super setFrameSize:newSize]; + + if(_area != nullptr) + { + [self removeTrackingArea:_area]; + _area = nullptr; + } + + NSRect rect = NSZeroRect; + rect.size = newSize; + + NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingEnabledDuringMouseDrag; + _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; + [self addTrackingArea:_area]; + + _parent->UpdateCursor(); + + _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); +} + +- (void) drawFb: (AvnFramebuffer*) fb +{ + auto colorSpace = CGColorSpaceCreateDeviceRGB(); + auto dataProvider = CGDataProviderCreateWithData(NULL, fb->Data, fb->Height*fb->Stride, NULL); + + + auto image = CGImageCreate(fb->Width, fb->Height, 8, 32, fb->Stride, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, + dataProvider, nullptr, false, kCGRenderingIntentDefault); + + auto ctx = [NSGraphicsContext currentContext]; + + [ctx saveGraphicsState]; + auto cgc = [ctx CGContext]; + + CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image); + CGImageRelease(image); + CGColorSpaceRelease(colorSpace); + CGDataProviderRelease(dataProvider); + + [ctx restoreGraphicsState]; + +} + +- (void)drawRect:(NSRect)dirtyRect +{ + _parent->BaseEvents->RunRenderPriorityJobs(); + @synchronized (self) { + if(_swRenderedFrame != NULL) + { + [self drawFb: &_swRenderedFrameBuffer]; + return; + } + } + + auto swOp = &_parent->CurrentSwDrawingOperation; + _parent->BaseEvents->Paint(); + if(swOp->Data != NULL) + [self drawFb: &swOp->Desc]; + + swOp->Dealloc(); + return; +} + +-(void) redrawSelf +{ + @autoreleasepool + { + @synchronized(self) + { + if(!_queuedDisplayFromThread) + return; + _queuedDisplayFromThread = false; + } + [self setNeedsDisplayInRect:[self frame]]; + [self display]; + + } +} + +-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose +{ + @autoreleasepool { + @synchronized (self) { + _swRenderedFrame = dispose; + _swRenderedFrameBuffer = *fb; + if(!_queuedDisplayFromThread) + { + _queuedDisplayFromThread = true; + [self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes]; + } + } + } +} + +- (AvnPoint) translateLocalPoint:(AvnPoint)pt +{ + pt.Y = [self bounds].size.height - pt.Y; + return pt; +} + +- (AvnPoint)toAvnPoint:(CGPoint)p +{ + AvnPoint result; + + result.X = p.x; + result.Y = p.y; + + return result; +} + +- (void) viewDidChangeBackingProperties +{ + _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); + [super viewDidChangeBackingProperties]; +} + +- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type +{ + [self becomeFirstResponder]; + auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; + auto avnPoint = [self toAvnPoint:localPoint]; + auto point = [self translateLocalPoint:avnPoint]; + AvnVector delta; + + if(type == Wheel) + { + delta.X = [event scrollingDeltaX] / 5; + delta.Y = [event scrollingDeltaY] / 5; + + if(delta.X == 0 && delta.Y == 0) + { + return; + } + } + + auto timestamp = [event timestamp] * 1000; + auto modifiers = [self getModifiers:[event modifierFlags]]; + + [self becomeFirstResponder]; + _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); + [super mouseMoved:event]; +} + +- (void)mouseMoved:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; +} + +- (void)mouseDown:(NSEvent *)event +{ + _isLeftPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:LeftButtonDown]; +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _isMiddlePressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:MiddleButtonDown]; +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _isRightPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:RightButtonDown]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _isLeftPressed = false; + [self mouseEvent:event withType:LeftButtonUp]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + _isMiddlePressed = false; + [self mouseEvent:event withType:MiddleButtonUp]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _isRightPressed = false; + [self mouseEvent:event withType:RightButtonUp]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super mouseDragged:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super otherMouseDragged:event]; +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super rightMouseDragged:event]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + [self mouseEvent:event withType:Wheel]; + [super scrollWheel:event]; +} + +- (void)mouseEntered:(NSEvent *)event +{ + _isMouseOver = true; + [super mouseEntered:event]; +} + +- (void)mouseExited:(NSEvent *)event +{ + _isMouseOver = false; + [self mouseEvent:event withType:LeaveWindow]; + [super mouseExited:event]; +} + +- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type +{ + auto key = s_KeyMap[[event keyCode]]; + + auto timestamp = [event timestamp] * 1000; + auto modifiers = [self getModifiers:[event modifierFlags]]; + + _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + return _lastKeyHandled; +} + +- (void)keyDown:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyDown]; + [[self inputContext] handleEvent:event]; + [super keyDown:event]; +} + +- (void)keyUp:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyUp]; + [super keyUp:event]; +} + +- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod +{ + unsigned int rv = 0; + + if (mod & NSEventModifierFlagControl) + rv |= Control; + if (mod & NSEventModifierFlagShift) + rv |= Shift; + if (mod & NSEventModifierFlagOption) + rv |= Alt; + if (mod & NSEventModifierFlagCommand) + rv |= Windows; + + if (_isLeftPressed) + rv |= LeftMouseButton; + if (_isMiddlePressed) + rv |= MiddleMouseButton; + if (_isRightPressed) + rv |= RightMouseButton; + + return (AvnInputModifiers)rv; +} + +- (BOOL)hasMarkedText +{ + return _lastKeyHandled; +} + +- (NSRange)markedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ + +} + +- (void)unmarkText +{ + +} + +- (NSArray *)validAttributesForMarkedText +{ + return [NSArray new]; +} + +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + return [NSAttributedString new]; +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + if(!_lastKeyHandled) + { + _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); + } +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + CGRect result; + + return result; +} +@end + + +@implementation AvnWindow +{ + ComPtr _parent; + bool _canBecomeKeyAndMain; + bool _closed; +} + +- (void)dealloc +{ + NSDebugLog(@"AvnWindow dealloc"); +} + +- (void)pollModalSession:(nonnull NSModalSession)session +{ + auto response = [NSApp runModalSession:session]; + + if(response == NSModalResponseContinue) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [self pollModalSession:session]; + }); + } + else if (!_closed) + { + [self orderOut:self]; + [NSApp endModalSession:session]; + } +} + +-(void) setCanBecomeKeyAndMain +{ + _canBecomeKeyAndMain = true; +} + +-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent +{ + self = [super init]; + [self setReleasedWhenClosed:false]; + _parent = parent; + [self setDelegate:self]; + return self; +} + +- (BOOL)windowShouldClose:(NSWindow *)sender +{ + auto window = dynamic_cast(_parent.getRaw()); + + if(window != nullptr) + { + return !window->WindowEvents->Closing(); + } + + return true; +} + +- (void)windowWillClose:(NSNotification *)notification +{ + _closed = true; + if(_parent) + { + ComPtr parent = _parent; + _parent = NULL; + parent->BaseEvents->Closed(); + [parent->View onClosed]; + [self setContentView: nil]; + } +} + + +-(BOOL)canBecomeKeyWindow +{ + return _canBecomeKeyAndMain; +} + +-(BOOL)canBecomeMainWindow +{ + return _canBecomeKeyAndMain; +} + +-(void)becomeKeyWindow +{ + _parent->BaseEvents->Activated(); + [super becomeKeyWindow]; +} + +- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame +{ + return true; +} + +-(void)resignKeyWindow +{ + if(_parent) + _parent->BaseEvents->Deactivated(); + [super resignKeyWindow]; +} + +- (void)windowDidMove:(NSNotification *)notification +{ + AvnPoint position; + _parent->GetPosition(&position); + _parent->BaseEvents->PositionChanged(position); +} + +// TODO this breaks resizing. +/*- (void)windowDidResize:(NSNotification *)notification +{ + + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->WindowStateChanged(); + } +}*/ +@end + +class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup +{ +private: + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) + END_INTERFACE_MAP() + virtual ~PopupImpl(){} + ComPtr WindowEvents; + PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + { + WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; + } + +protected: + virtual NSWindowStyleMask GetStyle() + { + return NSWindowStyleMaskBorderless; + } + + virtual HRESULT Resize(double x, double y) + { + @autoreleasepool + { + [Window setContentSize:NSSize{x, y}]; + + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + return S_OK; + } + } +}; + +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events) +{ + @autoreleasepool + { + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events)); + return ptr; + } +} + +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) +{ + @autoreleasepool + { + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events); + return ptr; + } +} diff --git a/src/Avalonia.Native/.gitignore b/src/Avalonia.Native/.gitignore new file mode 100644 index 0000000000..876fdb5f40 --- /dev/null +++ b/src/Avalonia.Native/.gitignore @@ -0,0 +1 @@ +Generated diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj new file mode 100644 index 0000000000..fdcc5afd5c --- /dev/null +++ b/src/Avalonia.Native/Avalonia.Native.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.0 + /usr/local/bin/castxml + true + $(MSBuildThisFileDirectory)/Generated + + + + + + + + + + + + + + + + + + + runtimes/osx/native/libAvaloniaNative.dylib + true + PreserveNewest + + + diff --git a/src/Avalonia.Native/Avalonia.Native.targets b/src/Avalonia.Native/Avalonia.Native.targets new file mode 100644 index 0000000000..07c1e17c53 --- /dev/null +++ b/src/Avalonia.Native/Avalonia.Native.targets @@ -0,0 +1,9 @@ + + + + + libAvalonia.Native.OSX.dylib + PreserveNewest + + + \ No newline at end of file diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs new file mode 100644 index 0000000000..9fc1677611 --- /dev/null +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -0,0 +1,133 @@ +// 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 configure) + { + new AvaloniaNativePlatform(new IAvaloniaNativeFactory(factory)) + .DoInitialize(configure); + } + + delegate IntPtr CreateAvaloniaNativeDelegate(); + + public static void Initialize(string library, Action 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(proc); + + + Initialize(d(), configure); + } + + public static void Initialize(Action configure) + { + Initialize(CreateAvaloniaNative(), configure); + } + + private AvaloniaNativePlatform(IAvaloniaNativeFactory factory) + { + _factory = factory; + } + + void DoInitialize(Action configure) + { + var opts = new AvaloniaNativeOptions(_factory); + configure?.Invoke(opts); + _factory.Initialize(); + + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())) + .Bind().ToConstant(new CursorFactory(_factory.CreateCursorFactory())) + .Bind().ToSingleton() + .Bind().ToConstant(KeyboardDevice) + .Bind().ToConstant(MouseDevice) + .Bind().ToConstant(this) + .Bind().ToConstant(this) + .Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard())) + .Bind().ToConstant(new RenderLoop()) + .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) + .Bind().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())) + .Bind().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); + } + + } +} diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs new file mode 100644 index 0000000000..d455eb47bc --- /dev/null +++ b/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(this T builder, + string libraryPath = null, + Action configure = null) + where T : AppBuilderBase, new() + { + if (libraryPath == null) + { + builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(configure)); + } + else + { + builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(libraryPath, configure)); + } + + return builder; + } + } +} diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs new file mode 100644 index 0000000000..67c383f6ae --- /dev/null +++ b/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() + { + + } + } +} diff --git a/src/Avalonia.Native/ClipboardImpl.cs b/src/Avalonia.Native/ClipboardImpl.cs new file mode 100644 index 0000000000..d54bc95fbb --- /dev/null +++ b/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 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; + } + } +} diff --git a/src/Avalonia.Native/Cursor.cs b/src/Avalonia.Native/Cursor.cs new file mode 100644 index 0000000000..1093eca1a4 --- /dev/null +++ b/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 => ""; + + 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 ); + } + } +} diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs new file mode 100644 index 0000000000..4bee59266c --- /dev/null +++ b/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, bool> _lockWindow; + + public DeferredFramebuffer(Func, 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; + } + } +} diff --git a/src/Avalonia.Native/DeferredRendererProxy.cs b/src/Avalonia.Native/DeferredRendererProxy.cs new file mode 100644 index 0000000000..126a395f73 --- /dev/null +++ b/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().Add(this); + } + + void IRenderLoop.Remove(IRenderLoopTask i) + { + AvaloniaLocator.Current.GetService().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 HitTest(Point p, IVisual root, Func 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(); + } + } + } +} diff --git a/src/Avalonia.Native/DynLoader.cs b/src/Avalonia.Native/DynLoader.cs new file mode 100644 index 0000000000..c9c064fb95 --- /dev/null +++ b/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 DlOpen; + private static Func DlSym; + private static Func 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; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs new file mode 100644 index 0000000000..dbcdd244cd --- /dev/null +++ b/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(); + 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()); + } + } +} diff --git a/src/Avalonia.Native/Helpers.cs b/src/Avalonia.Native/Helpers.cs new file mode 100644 index 0000000000..c9d1256e91 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Avalonia.Native/IconLoader.cs b/src/Avalonia.Native/IconLoader.cs new file mode 100644 index 0000000000..608c24d031 --- /dev/null +++ b/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().LoadBitmap(fileName)); + } + + public IWindowIconImpl LoadIcon(Stream stream) + { + return new IconStub( + AvaloniaLocator.Current.GetService().LoadBitmap(stream)); + } + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + var ms = new MemoryStream(); + bitmap.Save(ms); + ms.Seek(0, SeekOrigin.Begin); + return LoadIcon(ms); + } + } +} diff --git a/src/Avalonia.Native/Mappings.xml b/src/Avalonia.Native/Mappings.xml new file mode 100644 index 0000000000..9e6c6512c7 --- /dev/null +++ b/src/Avalonia.Native/Mappings.xml @@ -0,0 +1,27 @@ + + + Avalonia.Native + Avalonia.Native.Interop + SharpGen.Runtime.COM + $(THIS_CONFIG_PATH)/headers + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs new file mode 100644 index 0000000000..514ddf03a9 --- /dev/null +++ b/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 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); + } + } +} diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs new file mode 100644 index 0000000000..6005f01a60 --- /dev/null +++ b/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) + { + } + } + } +} diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs new file mode 100644 index 0000000000..c956cb09ff --- /dev/null +++ b/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; + } + } +} diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs new file mode 100644 index 0000000000..275c93dc45 --- /dev/null +++ b/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 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 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 _tcs; + + public SystemDialogEvents() + { + _tcs = new TaskCompletionSource(); + } + + public Task 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); + } + } +} diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs new file mode 100644 index 0000000000..5d30408e52 --- /dev/null +++ b/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 WindowStateChanged { get; set; } + + public void ShowTaskbarIcon(bool value) + { + // NO OP On OSX + } + + public void SetIcon(IWindowIconImpl icon) + { + // NO OP on OSX + } + + public Func Closing { get; set; } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs new file mode 100644 index 0000000000..e81e912a0a --- /dev/null +++ b/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(); + + _gpu = opts.UseGpu; + _deferredRendering = opts.UseDeferredRendering; + + _keyboard = AvaloniaLocator.Current.GetService(); + _mouse = AvaloniaLocator.Current.GetService(); + _cursorFactory = AvaloniaLocator.Current.GetService(); + } + + 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 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 Paint { get; set; } + public Action 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 PositionChanged { get; set; } + + public Action Input { get; set; } + + Action ScalingChanged { get; set; } + + Action 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"); + } +} diff --git a/src/Avalonia.Native/headers/avalonia-native-guids.h b/src/Avalonia.Native/headers/avalonia-native-guids.h new file mode 100644 index 0000000000..439008fd4b --- /dev/null +++ b/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" diff --git a/src/Avalonia.Native/headers/avalonia-native.h b/src/Avalonia.Native/headers/avalonia-native.h new file mode 100644 index 0000000000..0c965b7498 --- /dev/null +++ b/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(); diff --git a/src/Avalonia.Native/headers/com.h b/src/Avalonia.Native/headers/com.h new file mode 100644 index 0000000000..22fb4a11a3 --- /dev/null +++ b/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 diff --git a/src/Avalonia.Native/headers/comimpl.h b/src/Avalonia.Native/headers/comimpl.h new file mode 100644 index 0000000000..cea6d2207a --- /dev/null +++ b/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 + +__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 ComSingleObject : public ComObject, public virtual TInterface +{ + BEGIN_INTERFACE_MAP() + INTERFACE_MAP_ENTRY(TInterface, *TIID) + END_INTERFACE_MAP() + +public: + virtual ~ComSingleObject(){} +}; + +template +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 diff --git a/src/Avalonia.Native/headers/key.h b/src/Avalonia.Native/headers/key.h new file mode 100644 index 0000000000..cdc9658e29 --- /dev/null +++ b/src/Avalonia.Native/headers/key.h @@ -0,0 +1,1023 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +#ifndef _KEY_H_ +#define _KEY_H_ + +/// +/// Defines the keys available on a keyboard. +/// +enum AvnKey +{ + /// + /// No key pressed. + /// + AvnKeyNone = 0, + + /// + /// The Cancel key. + /// + AvnKeyCancel = 1, + + /// + /// The Back key. + /// + AvnKeyBack = 2, + + /// + /// The Tab key. + /// + AvnKeyTab = 3, + + /// + /// The Linefeed key. + /// + AvnKeyLineFeed = 4, + + /// + /// The Clear key. + /// + AvnKeyClear = 5, + + /// + /// The Return key. + /// + AvnKeyReturn = 6, + + /// + /// The Enter key. + /// + AvnKeyEnter = 6, + + /// + /// The Pause key. + /// + AvnKeyPause = 7, + + /// + /// The Caps Lock key. + /// + AvnKeyCapsLock = 8, + + /// + /// The Caps Lock key. + /// + AvnKeyCapital = 8, + + /// + /// The IME Hangul mode key. + /// + AvnKeyHangulMode = 9, + + /// + /// The IME Kana mode key. + /// + AvnKeyKanaMode = 9, + + /// + /// The IME Junja mode key. + /// + AvnKeyJunjaMode = 10, + + /// + /// The IME Final mode key. + /// + AvnKeyFinalMode = 11, + + /// + /// The IME Kanji mode key. + /// + AvnKeyKanjiMode = 12, + + /// + /// The IME Hanja mode key. + /// + HanjaMode = 12, + + /// + /// The Escape key. + /// + Escape = 13, + + /// + /// The IME Convert key. + /// + ImeConvert = 14, + + /// + /// The IME NonConvert key. + /// + ImeNonConvert = 15, + + /// + /// The IME Accept key. + /// + ImeAccept = 16, + + /// + /// The IME Mode change key. + /// + ImeModeChange = 17, + + /// + /// The space bar. + /// + Space = 18, + + /// + /// The Page Up key. + /// + PageUp = 19, + + /// + /// The Page Up key. + /// + Prior = 19, + + /// + /// The Page Down key. + /// + PageDown = 20, + + /// + /// The Page Down key. + /// + Next = 20, + + /// + /// The End key. + /// + End = 21, + + /// + /// The Home key. + /// + Home = 22, + + /// + /// The Left arrow key. + /// + Left = 23, + + /// + /// The Up arrow key. + /// + Up = 24, + + /// + /// The Right arrow key. + /// + Right = 25, + + /// + /// The Down arrow key. + /// + Down = 26, + + /// + /// The Select key. + /// + Select = 27, + + /// + /// The Print key. + /// + Print = 28, + + /// + /// The Execute key. + /// + Execute = 29, + + /// + /// The Print Screen key. + /// + Snapshot = 30, + + /// + /// The Print Screen key. + /// + PrintScreen = 30, + + /// + /// The Insert key. + /// + Insert = 31, + + /// + /// The Delete key. + /// + Delete = 32, + + /// + /// The Help key. + /// + Help = 33, + + /// + /// The 0 key. + /// + D0 = 34, + + /// + /// The 1 key. + /// + D1 = 35, + + /// + /// The 2 key. + /// + D2 = 36, + + /// + /// The 3 key. + /// + D3 = 37, + + /// + /// The 4 key. + /// + D4 = 38, + + /// + /// The 5 key. + /// + D5 = 39, + + /// + /// The 6 key. + /// + D6 = 40, + + /// + /// The 7 key. + /// + D7 = 41, + + /// + /// The 8 key. + /// + D8 = 42, + + /// + /// The 9 key. + /// + D9 = 43, + + /// + /// The A key. + /// + A = 44, + + /// + /// The B key. + /// + B = 45, + + /// + /// The C key. + /// + C = 46, + + /// + /// The D key. + /// + D = 47, + + /// + /// The E key. + /// + E = 48, + + /// + /// The F key. + /// + F = 49, + + /// + /// The G key. + /// + G = 50, + + /// + /// The H key. + /// + H = 51, + + /// + /// The I key. + /// + I = 52, + + /// + /// The J key. + /// + J = 53, + + /// + /// The K key. + /// + AvnKeyK = 54, + + /// + /// The L key. + /// + L = 55, + + /// + /// The M key. + /// + M = 56, + + /// + /// The N key. + /// + N = 57, + + /// + /// The O key. + /// + O = 58, + + /// + /// The P key. + /// + P = 59, + + /// + /// The Q key. + /// + Q = 60, + + /// + /// The R key. + /// + R = 61, + + /// + /// The S key. + /// + S = 62, + + /// + /// The T key. + /// + T = 63, + + /// + /// The U key. + /// + U = 64, + + /// + /// The V key. + /// + V = 65, + + /// + /// The W key. + /// + W = 66, + + /// + /// The X key. + /// + X = 67, + + /// + /// The Y key. + /// + Y = 68, + + /// + /// The Z key. + /// + Z = 69, + + /// + /// The left Windows key. + /// + LWin = 70, + + /// + /// The right Windows key. + /// + RWin = 71, + + /// + /// The Application key. + /// + Apps = 72, + + /// + /// The Sleep key. + /// + Sleep = 73, + + /// + /// The 0 key on the numeric keypad. + /// + NumPad0 = 74, + + /// + /// The 1 key on the numeric keypad. + /// + NumPad1 = 75, + + /// + /// The 2 key on the numeric keypad. + /// + NumPad2 = 76, + + /// + /// The 3 key on the numeric keypad. + /// + NumPad3 = 77, + + /// + /// The 4 key on the numeric keypad. + /// + NumPad4 = 78, + + /// + /// The 5 key on the numeric keypad. + /// + NumPad5 = 79, + + /// + /// The 6 key on the numeric keypad. + /// + NumPad6 = 80, + + /// + /// The 7 key on the numeric keypad. + /// + NumPad7 = 81, + + /// + /// The 8 key on the numeric keypad. + /// + NumPad8 = 82, + + /// + /// The 9 key on the numeric keypad. + /// + NumPad9 = 83, + + /// + /// The Multiply key. + /// + Multiply = 84, + + /// + /// The Add key. + /// + Add = 85, + + /// + /// The Separator key. + /// + Separator = 86, + + /// + /// The Subtract key. + /// + Subtract = 87, + + /// + /// The Decimal key. + /// + Decimal = 88, + + /// + /// The Divide key. + /// + Divide = 89, + + /// + /// The F1 key. + /// + F1 = 90, + + /// + /// The F2 key. + /// + F2 = 91, + + /// + /// The F3 key. + /// + F3 = 92, + + /// + /// The F4 key. + /// + F4 = 93, + + /// + /// The F5 key. + /// + F5 = 94, + + /// + /// The F6 key. + /// + F6 = 95, + + /// + /// The F7 key. + /// + F7 = 96, + + /// + /// The F8 key. + /// + F8 = 97, + + /// + /// The F9 key. + /// + F9 = 98, + + /// + /// The F10 key. + /// + F10 = 99, + + /// + /// The F11 key. + /// + F11 = 100, + + /// + /// The F12 key. + /// + F12 = 101, + + /// + /// The F13 key. + /// + F13 = 102, + + /// + /// The F14 key. + /// + F14 = 103, + + /// + /// The F15 key. + /// + F15 = 104, + + /// + /// The F16 key. + /// + F16 = 105, + + /// + /// The F17 key. + /// + F17 = 106, + + /// + /// The F18 key. + /// + F18 = 107, + + /// + /// The F19 key. + /// + F19 = 108, + + /// + /// The F20 key. + /// + F20 = 109, + + /// + /// The F21 key. + /// + F21 = 110, + + /// + /// The F22 key. + /// + F22 = 111, + + /// + /// The F23 key. + /// + F23 = 112, + + /// + /// The F24 key. + /// + F24 = 113, + + /// + /// The Numlock key. + /// + NumLock = 114, + + /// + /// The Scroll key. + /// + Scroll = 115, + + /// + /// The left Shift key. + /// + LeftShift = 116, + + /// + /// The right Shift key. + /// + RightShift = 117, + + /// + /// The left Ctrl key. + /// + LeftCtrl = 118, + + /// + /// The right Ctrl key. + /// + RightCtrl = 119, + + /// + /// The left Alt key. + /// + LeftAlt = 120, + + /// + /// The right Alt key. + /// + RightAlt = 121, + + /// + /// The browser Back key. + /// + BrowserBack = 122, + + /// + /// The browser Forward key. + /// + BrowserForward = 123, + + /// + /// The browser Refresh key. + /// + BrowserRefresh = 124, + + /// + /// The browser Stop key. + /// + BrowserStop = 125, + + /// + /// The browser Search key. + /// + BrowserSearch = 126, + + /// + /// The browser Favorites key. + /// + BrowserFavorites = 127, + + /// + /// The browser Home key. + /// + BrowserHome = 128, + + /// + /// The Volume Mute key. + /// + VolumeMute = 129, + + /// + /// The Volume Down key. + /// + VolumeDown = 130, + + /// + /// The Volume Up key. + /// + VolumeUp = 131, + + /// + /// The media Next Track key. + /// + MediaNextTrack = 132, + + /// + /// The media Previous Track key. + /// + MediaPreviousTrack = 133, + + /// + /// The media Stop key. + /// + MediaStop = 134, + + /// + /// The media Play/Pause key. + /// + MediaPlayPause = 135, + + /// + /// The Launch Mail key. + /// + LaunchMail = 136, + + /// + /// The Select Media key. + /// + SelectMedia = 137, + + /// + /// The Launch Application 1 key. + /// + LaunchApplication1 = 138, + + /// + /// The Launch Application 2 key. + /// + LaunchApplication2 = 139, + + /// + /// The OEM Semicolon key. + /// + OemSemicolon = 140, + + /// + /// The OEM 1 key. + /// + Oem1 = 140, + + /// + /// The OEM Plus key. + /// + OemPlus = 141, + + /// + /// The OEM Comma key. + /// + OemComma = 142, + + /// + /// The OEM Minus key. + /// + OemMinus = 143, + + /// + /// The OEM Period key. + /// + OemPeriod = 144, + + /// + /// The OEM Question Mark key. + /// + OemQuestion = 145, + + /// + /// The OEM 2 key. + /// + Oem2 = 145, + + /// + /// The OEM Tilde key. + /// + OemTilde = 146, + + /// + /// The OEM 3 key. + /// + Oem3 = 146, + + /// + /// The ABNT_C1 (Brazilian) key. + /// + AbntC1 = 147, + + /// + /// The ABNT_C2 (Brazilian) key. + /// + AbntC2 = 148, + + /// + /// The OEM Open Brackets key. + /// + OemOpenBrackets = 149, + + /// + /// The OEM 4 key. + /// + Oem4 = 149, + + /// + /// The OEM Pipe key. + /// + OemPipe = 150, + + /// + /// The OEM 5 key. + /// + Oem5 = 150, + + /// + /// The OEM Close Brackets key. + /// + OemCloseBrackets = 151, + + /// + /// The OEM 6 key. + /// + Oem6 = 151, + + /// + /// The OEM Quotes key. + /// + OemQuotes = 152, + + /// + /// The OEM 7 key. + /// + Oem7 = 152, + + /// + /// The OEM 8 key. + /// + Oem8 = 153, + + /// + /// The OEM Backslash key. + /// + OemBackslash = 154, + + /// + /// The OEM 3 key. + /// + Oem102 = 154, + + /// + /// A special key masking the real key being processed by an IME. + /// + ImeProcessed = 155, + + /// + /// A special key masking the real key being processed as a system key. + /// + System = 156, + + /// + /// The OEM ATTN key. + /// + OemAttn = 157, + + /// + /// The DBE_ALPHANUMERIC key. + /// + DbeAlphanumeric = 157, + + /// + /// The OEM Finish key. + /// + OemFinish = 158, + + /// + /// The DBE_KATAKANA key. + /// + DbeKatakana = 158, + + /// + /// The DBE_HIRAGANA key. + /// + DbeHiragana = 159, + + /// + /// The OEM Copy key. + /// + OemCopy = 159, + + /// + /// The DBE_SBCSCHAR key. + /// + DbeSbcsChar = 160, + + /// + /// The OEM Auto key. + /// + OemAuto = 160, + + /// + /// The DBE_DBCSCHAR key. + /// + DbeDbcsChar = 161, + + /// + /// The OEM ENLW key. + /// + OemEnlw = 161, + + /// + /// The OEM BackTab key. + /// + OemBackTab = 162, + + /// + /// The DBE_ROMAN key. + /// + DbeRoman = 162, + + /// + /// The DBE_NOROMAN key. + /// + DbeNoRoman = 163, + + /// + /// The ATTN key. + /// + Attn = 163, + + /// + /// The CRSEL key. + /// + CrSel = 164, + + /// + /// The DBE_ENTERWORDREGISTERMODE key. + /// + DbeEnterWordRegisterMode = 164, + + /// + /// The EXSEL key. + /// + ExSel = 165, + + /// + /// The DBE_ENTERIMECONFIGMODE key. + /// + DbeEnterImeConfigureMode = 165, + + /// + /// The ERASE EOF Key. + /// + EraseEof = 166, + + /// + /// The DBE_FLUSHSTRING key. + /// + DbeFlushString = 166, + + /// + /// The Play key. + /// + Play = 167, + + /// + /// The DBE_CODEINPUT key. + /// + DbeCodeInput = 167, + + /// + /// The DBE_NOCODEINPUT key. + /// + DbeNoCodeInput = 168, + + /// + /// The Zoom key. + /// + Zoom = 168, + + /// + /// Reserved for future use. + /// + NoName = 169, + + /// + /// The DBE_DETERMINESTRING key. + /// + DbeDetermineString = 169, + + /// + /// The DBE_ENTERDLGCONVERSIONMODE key. + /// + DbeEnterDialogConversionMode = 170, + + /// + /// The PA1 key. + /// + Pa1 = 170, + + /// + /// The OEM Clear key. + /// + OemClear = 171, + + /// + /// The key is used with another key to create a single combined character. + /// + DeadCharProcessed = 172, +}; + +#endif diff --git a/src/Avalonia.Native/regen.sh b/src/Avalonia.Native/regen.sh new file mode 100755 index 0000000000..38f03b7409 --- /dev/null +++ b/src/Avalonia.Native/regen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +dotnet msbuild /t:Clean,GenerateSharpGenBindings