From 471d439654d8f65c258f600f09fc2bd8edf81b1e Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Fri, 10 Feb 2017 11:13:45 -0800 Subject: [PATCH] [C#][iOS] Fix callbacks on AOT Based on the idea of #386 --- csharp/Facebook.Yoga/Native.cs | 37 +++++++++++++++--- csharp/Facebook.Yoga/YogaLogger.cs | 34 ++++++++++------- csharp/Facebook.Yoga/YogaNode.cs | 60 +++++++++++++++++++++++++++--- csharp/Yoga/YGInterop.cpp | 49 +++++++++++++++++++++--- csharp/Yoga/YGInterop.h | 2 + yoga/Yoga.c | 2 + yoga/Yoga.h | 1 + 7 files changed, 154 insertions(+), 31 deletions(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index 2c0d25ac..57c08027 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -58,6 +58,11 @@ namespace Facebook.Yoga [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern int YGNodeGetInstanceCount(); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGNodeSetManaged(IntPtr managed); +#endif + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGSetExperimentalFeatureEnabled( YogaExperimentalFeature feature, @@ -68,16 +73,20 @@ namespace Facebook.Yoga YogaExperimentalFeature feature); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeInsertChild(YGNodeHandle node, YGNodeHandle child, uint index); + public static extern void YGNodeInsertChild( + YGNodeHandle node, + YGNodeHandle child, + uint index); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeRemoveChild(YGNodeHandle node, YGNodeHandle child); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeCalculateLayout(YGNodeHandle node, - float availableWidth, - float availableHeight, - YogaDirection parentDirection); + public static extern void YGNodeCalculateLayout( + YGNodeHandle node, + float availableWidth, + float availableHeight, + YogaDirection parentDirection); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeMarkDirty(YGNodeHandle node); @@ -99,13 +108,29 @@ namespace Facebook.Yoga YGNodeHandle node, [MarshalAs(UnmanagedType.FunctionPtr)] YogaMeasureFunc measureFunc); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGInteropSetMeasureFunc( + YGNodeHandle node, + [MarshalAs(UnmanagedType.FunctionPtr)] YogaMeasureFunc measureFunc); +#endif + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeSetBaselineFunc( YGNodeHandle node, [MarshalAs(UnmanagedType.FunctionPtr)] YogaBaselineFunc baselineFunc); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeSetHasNewLayout(YGNodeHandle node, [MarshalAs(UnmanagedType.I1)] bool hasNewLayout); + public static extern void YGInteropSetBaselineFunc( + YGNodeHandle node, + [MarshalAs(UnmanagedType.FunctionPtr)] YogaBaselineFunc baselineFunc); +#endif + + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGNodeSetHasNewLayout( + YGNodeHandle node, + [MarshalAs(UnmanagedType.I1)] bool hasNewLayout); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] diff --git a/csharp/Facebook.Yoga/YogaLogger.cs b/csharp/Facebook.Yoga/YogaLogger.cs index e5d79b79..bd3af941 100644 --- a/csharp/Facebook.Yoga/YogaLogger.cs +++ b/csharp/Facebook.Yoga/YogaLogger.cs @@ -10,6 +10,10 @@ using System; using System.Runtime.InteropServices; +#if __IOS__ +using ObjCRuntime; +#endif + namespace Facebook.Yoga { internal static class YogaLogger @@ -18,26 +22,30 @@ namespace Facebook.Yoga public delegate void Func(YogaLogLevel level, string message); private static bool _initialized; - private static Func _managedLogger = null; public static Func Logger = null; +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(Func))] +#endif + public static void LoggerInternal(YogaLogLevel level, string message) + { + if (Logger != null) + { + Logger(level, message); + } + + if (level == YogaLogLevel.Error) + { + throw new InvalidOperationException(message); + } + } + public static void Initialize() { if (!_initialized) { - _managedLogger = (level, message) => { - if (Logger != null) - { - Logger(level, message); - } - - if (level == YogaLogLevel.Error) - { - throw new InvalidOperationException(message); - } - }; - Native.YGInteropSetLogger(_managedLogger); + Native.YGInteropSetLogger(LoggerInternal); _initialized = true; } } diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 59b6ae08..90e28de4 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -12,6 +12,10 @@ using System.Collections; using System.Collections.Generic; using System.Text; +#if __IOS__ +using ObjCRuntime; +#endif + namespace Facebook.Yoga { public partial class YogaNode : IEnumerable @@ -20,9 +24,7 @@ namespace Facebook.Yoga private WeakReference _parent; private List _children; private MeasureFunction _measureFunction; - private YogaMeasureFunc _ygMeasureFunc; private BaselineFunction _baselineFunction; - private YogaBaselineFunc _ygBaselineFunc; private object _data; public YogaNode() @@ -34,6 +36,11 @@ namespace Facebook.Yoga { throw new InvalidOperationException("Failed to allocate native memory"); } + +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + var managed = GCHandle.Alloc(this, GCHandleType.Weak); + Native.YGNodeSetManaged(GCHandle.ToIntPtr(managed)); +#endif } public YogaNode(YogaNode srcNode) @@ -545,15 +552,23 @@ namespace Facebook.Yoga public void SetMeasureFunction(MeasureFunction measureFunction) { _measureFunction = measureFunction; - _ygMeasureFunc = measureFunction != null ? MeasureInternal : (YogaMeasureFunc)null; - Native.YGNodeSetMeasureFunc(_ygNode, _ygMeasureFunc); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + Native.YGInteropNodeSetMeasureFunc(_ygNode, MeasureInternalIOS); +#else + Native.YGNodeSetMeasureFunc(_ygNode, + measureFunction != null ? MeasureInternal : (YogaMeasureFunc)null); +#endif } public void SetBaselineFunction(BaselineFunction baselineFunction) { _baselineFunction = baselineFunction; - _ygBaselineFunc = baselineFunction != null ? BaselineInternal : (YogaBaselineFunc)null; - Native.YGNodeSetBaselineFunc(_ygNode, _ygBaselineFunc); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + Native.YGInteropNodeSetBaselineFunc(_ygNode, BaselineInternalIOS); +#else + Native.YGNodeSetBaselineFunc(_ygNode, + baselineFunction != null ? BaselineInternal : (YogaBaselineFunc)null); +#endif } public void CalculateLayout() @@ -565,6 +580,25 @@ namespace Facebook.Yoga Native.YGNodeStyleGetDirection(_ygNode)); } +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(YogaMeasureFunc))] + private static YogaSize MeasureInternalIOS( + IntPtr managed, + float width, + YogaMeasureMode widthMode, + float height, + YogaMeasureMode heightMode) + { + var node = GCHandle.FromIntPtr(managed); + if (node == null) + { + throw new InvalidOperationException("YogaNode is already deallocated"); + } + + node.MeasureInternal(IntPtr.Zero, width, widthMode, height, heightMode); + } +#endif + private YogaSize MeasureInternal( IntPtr node, float width, @@ -580,6 +614,20 @@ namespace Facebook.Yoga return _measureFunction(this, width, widthMode, height, heightMode); } +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(YogaBaselineFunc))] + private static float BaselineInternalIOS(IntPtr managed, float width, float height) + { + var node = GCHandle.FromIntPtr(managed); + if (node == null) + { + throw new InvalidOperationException("YogaNode is already deallocated"); + } + + node.BaselineInternal(IntPtr.Zero, width, height); + } +#endif + private float BaselineInternal(IntPtr node, float width, float height) { if (_baselineFunction == null) diff --git a/csharp/Yoga/YGInterop.cpp b/csharp/Yoga/YGInterop.cpp index fc2043dc..bc6262ff 100644 --- a/csharp/Yoga/YGInterop.cpp +++ b/csharp/Yoga/YGInterop.cpp @@ -9,19 +9,56 @@ #include "YGInterop.h" -static YGInteropLoggerFunc gManagedFunc; +typedef YGSize (*YGInteropMeasureFunc)(void *managed, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); +typedef float (*YGInteropBaselineFunc)(void *managed, const float width, const float height); -static int unmanagedLogger(YGLogLevel level, const char *format, va_list args) { +static YGInteropLoggerFunc gManagedLoggerFunc; +static YGInteropMeasureFunc gManagedMeasureFunc; +static YGInteropBaselineFunc gManagedBaselineFunc; + +static int unmanagedLoggerFunc(YGLogLevel level, const char *format, va_list args) { int result = 0; - if (gManagedFunc) { + if (gManagedLoggerFunc) { char buffer[256]; result = vsnprintf(buffer, sizeof(buffer), format, args); - (*gManagedFunc)(level, buffer); + (*gManagedLoggerFunc)(level, buffer); } return result; } void YGInteropSetLogger(YGInteropLoggerFunc managedFunc) { - gManagedFunc = managedFunc; - YGSetLogger(&unmanagedLogger); + gManagedLoggerFunc = managedFunc; + YGSetLogger(&unmanagedLoggerFunc); +} + +static YGSize unmanagedMeasureFunc(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + YG_ASSERT(gManagedMeasureFunc, "Expect to set managed measure function"); + void *managed = YGNodeGetManaged(node); + YG_ASSERT(managed, "Expect to set managed in node"); + return (*gManagedMeasureFunc)(managed, width, widthMode, height, heightMode); +} + +void YGInteropNodeSetMeasureFunc(YGNodeRef node, YGInteropMeasureFunc managedFunc) { + gManagedMeasureFunc = managedFunc; + YGNodeSetMeasureFunc(node, &unmanagedMeasureFunc); +} + +static float unmanagedBaselineFunc(YGNodeRef node, const float width, const float height) { + YG_ASSERT(gManagedBaselineFunc, "Expect to set managed baseline function"); + void *managed = YGNodeGetManaged(node); + YG_ASSERT(managed, "Expect to set managed in node"); + return (*gManagedBaselineFunc)(managed, width, height); +} + +void YGInteropNodeSetBaselineFunc(YGNodeRef node, YGInteropBaselineFunc managedFunc) { + gManagedBaselineFunc = managedFunc; + YGNodeSetBaselineFunc(node, &unmanagedBaselineFunc); } diff --git a/csharp/Yoga/YGInterop.h b/csharp/Yoga/YGInterop.h index 7494977f..6353678c 100644 --- a/csharp/Yoga/YGInterop.h +++ b/csharp/Yoga/YGInterop.h @@ -16,5 +16,7 @@ YG_EXTERN_C_BEGIN typedef void (*YGInteropLoggerFunc)(YGLogLevel level, const char *message); WIN_EXPORT void YGInteropSetLogger(YGInteropLoggerFunc managedFunc); +WIN_EXPORT void YGInteropNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); +WIN_EXPORT void YGInteropNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc); YG_EXTERN_C_END diff --git a/yoga/Yoga.c b/yoga/Yoga.c index 3bd71471..da5617cf 100644 --- a/yoga/Yoga.c +++ b/yoga/Yoga.c @@ -108,6 +108,7 @@ typedef struct YGNode { YGBaselineFunc baseline; YGPrintFunc print; void *context; + void *managed; bool isDirty; bool hasNewLayout; @@ -565,6 +566,7 @@ void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) { } YG_NODE_PROPERTY_IMPL(void *, Context, context, context); +YG_NODE_PROPERTY_IMPL(void *, Managed, managed, managed); YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print); YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout); diff --git a/yoga/Yoga.h b/yoga/Yoga.h index bf9bd3e8..8b0e00ab 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -143,6 +143,7 @@ WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge); YG_NODE_PROPERTY(void *, Context, context); +YG_NODE_PROPERTY(void *, Managed, managed); YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc); YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc) YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc);