Browse Source

CGlue almost working

Krzysztof Krysiński 1 year ago
parent
commit
76c70816e4
34 changed files with 1397 additions and 176 deletions
  1. 18 0
      src/CGlueTestLib/CGlueTestLib.csproj
  2. 16 0
      src/CGlueTestLib/Exports.cs
  3. 9 0
      src/CGlueTestLib/Imports.cs
  4. 10 0
      src/CGlueTestLib/Program.cs
  5. 81 0
      src/PixiEditor.Api.CGlueMSBuild.Tests/CApiGeneratorTests.cs
  6. 34 0
      src/PixiEditor.Api.CGlueMSBuild.Tests/PixiEditor.Api.CGlueMSBuild.Tests.csproj
  7. BIN
      src/PixiEditor.Api.CGlueMSBuild.Tests/TestAssets/CGlueTestLib.dll
  8. BIN
      src/PixiEditor.Api.CGlueMSBuild.Tests/TestAssets/PixiEditor.Extensions.Wasm.dll
  9. 305 0
      src/PixiEditor.Api.CGlueMSBuild/CApiGenerator.cs
  10. 67 0
      src/PixiEditor.Api.CGlueMSBuild/GenerateCGlueTask.cs
  11. 22 0
      src/PixiEditor.Api.CGlueMSBuild/PixiEditor.Api.CGlueMSBuild.csproj
  12. 135 0
      src/PixiEditor.Api.CGlueMSBuild/TypeMapper.cs
  13. 228 0
      src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.deps.json
  14. BIN
      src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.dll
  15. 1 1
      src/PixiEditor.Extensions.Wasm/Api/Logging/Logger.cs
  16. 4 4
      src/PixiEditor.Extensions.Wasm/Api/Window/PopupWindow.cs
  17. 3 3
      src/PixiEditor.Extensions.Wasm/Api/Window/WindowProvider.cs
  18. 11 0
      src/PixiEditor.Extensions.Wasm/ApiExportAttribute.cs
  19. 11 10
      src/PixiEditor.Extensions.Wasm/Interop.Windowing.cs
  20. 19 7
      src/PixiEditor.Extensions.Wasm/Interop.cs
  21. 2 4
      src/PixiEditor.Extensions.Wasm/PixiEditor.Extensions.Wasm.csproj
  22. BIN
      src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Mdb.dll
  23. BIN
      src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Pdb.dll
  24. BIN
      src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Rocks.dll
  25. BIN
      src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.dll
  26. 256 0
      src/PixiEditor.Extensions.Wasm/build/PixiEditor.Api.CGlueMSBuild.deps.json
  27. BIN
      src/PixiEditor.Extensions.Wasm/build/PixiEditor.Api.CGlueMSBuild.dll
  28. 9 4
      src/PixiEditor.Extensions.Wasm/build/PixiEditor.Extensions.Wasm.targets
  29. 0 7
      src/PixiEditor.Extensions.Wasm/native/api.h
  30. 21 19
      src/PixiEditor.Extensions.Wasm/native/interop.c
  31. 0 37
      src/PixiEditor.Extensions.Wasm/native/layout_builder_api.c
  32. 0 19
      src/PixiEditor.Extensions.Wasm/native/logger_api.c
  33. 0 61
      src/PixiEditor.Extensions.Wasm/native/window_api.c
  34. 135 0
      src/PixiEditor.sln

+ 18 - 0
src/CGlueTestLib/CGlueTestLib.csproj

@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
+    <OutputType>Exe</OutputType>
+    <PublishTrimmed>true</PublishTrimmed>
+    <WasmSingleFileBundle>true</WasmSingleFileBundle>
+  </PropertyGroup>
+
+  <Import Project="..\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.targets"/>
+  <Import Project="..\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.props"/>
+
+    <ItemGroup>
+      <ProjectReference Include="..\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
+    </ItemGroup>
+
+</Project>

+ 16 - 0
src/CGlueTestLib/Exports.cs

@@ -0,0 +1,16 @@
+using PixiEditor.Extensions.CommonApi.FlyUI;
+using PixiEditor.Extensions.CommonApi.FlyUI.Events;
+using PixiEditor.Extensions.Wasm;
+using PixiEditor.Extensions.Wasm.Api.FlyUI;
+
+namespace CGlueTestLib;
+
+internal static class Exports
+{
+    [ApiExport("testExport")]
+    internal static void TestExport()
+    {
+
+    }
+
+}

+ 9 - 0
src/CGlueTestLib/Imports.cs

@@ -0,0 +1,9 @@
+using System.Runtime.InteropServices;
+
+namespace CGlueTestLib;
+
+internal static class Imports
+{
+    [DllImport("pixieditor")]
+    internal static extern void test();
+}

+ 10 - 0
src/CGlueTestLib/Program.cs

@@ -0,0 +1,10 @@
+namespace CGlueTestLib;
+
+public static class Program
+{
+    public static void Main()
+    {
+        Imports.test();
+        Exports.TestExport();
+    }
+}

+ 81 - 0
src/PixiEditor.Api.CGlueMSBuild.Tests/CApiGeneratorTests.cs

@@ -0,0 +1,81 @@
+using Mono.Cecil;
+
+namespace PixiEditor.Api.CGlueMSBuild.Tests;
+
+public class CApiGeneratorTests
+{
+    [Fact]
+    public void TestThatLoadAssemblies()
+    {
+        CApiGenerator apiGenerator = new CApiGenerator("", (message) => { });
+        AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("TestAssets/CGlueTestLib.dll");
+        var assemblies = apiGenerator.LoadAssemblies(assembly, "TestAssets");
+
+        Assert.Equal(2, assemblies.Count);
+    }
+
+    [Fact]
+    public void TestThatImportedMethodsAreExtractedCorrectly()
+    {
+        CApiGenerator apiGenerator = new CApiGenerator("", (message) => { });
+        AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("TestAssets/CGlueTestLib.dll");
+        var assemblies = apiGenerator.LoadAssemblies(assembly, "TestAssets");
+
+        var importedMethods = CApiGenerator.GetImportedMethods(assemblies.SelectMany(a => a.MainModule.Types).ToArray());
+
+        Assert.True(importedMethods.Length > 0);
+    }
+
+    [Fact]
+    public void TestThatGenerateImportsGeneratesCorrectImports()
+    {
+        CApiGenerator apiGenerator = new CApiGenerator("", (message) => { });
+        AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("TestAssets/CGlueTestLib.dll");
+        var assemblies = apiGenerator.LoadAssemblies(assembly, "TestAssets");
+
+        var importedMethods = CApiGenerator.GetImportedMethods(assemblies.SelectMany(a => a.MainModule.Types).ToArray());
+        string imports = apiGenerator.GenerateImports(importedMethods);
+
+        string sanitizedImports = imports.Replace("\n", "").Replace("\r", "");
+
+        Assert.Contains("__attribute__((import_name(\"subscribe_to_event\")))", sanitizedImports);
+        Assert.Contains("void subscribe_to_event(int32_t internalControlId, char* eventName, int32_t eventNameLength);", sanitizedImports);
+    }
+
+    [Fact]
+    public void TestThatGenerateExportsGeneratesCorrectExports()
+    {
+        CApiGenerator apiGenerator = new CApiGenerator("", (message) => { });
+        AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("TestAssets/CGlueTestLib.dll");
+        var assemblies = apiGenerator.LoadAssemblies(assembly, "TestAssets");
+
+        var exportedMethods = CApiGenerator.GetExportedMethods(assemblies.SelectMany(a => a.MainModule.Types).ToArray());
+        string exports = apiGenerator.GenerateExports(exportedMethods);
+
+        string sanitizedExports = exports.Replace("\n", "").Replace("\r", "");
+
+        Assert.Contains("__attribute__((export_name(\"raise_element_event\")))", sanitizedExports);
+        Assert.Contains("void raise_element_event(int32_t internalControlId, char* eventName)", sanitizedExports);
+        Assert.Contains("MonoMethod* method = lookup_interop_method(\"EventRaised\");", sanitizedExports);
+        Assert.Contains("MonoString* mono_eventName = mono_string_new(mono_domain_get(), eventName);", sanitizedExports);
+        Assert.Contains("void* args[] = {&internalControlId, mono_eventName};", sanitizedExports);
+        Assert.Contains("invoke_interop_method(method, args);", sanitizedExports);
+        Assert.Contains("free(method);", sanitizedExports);
+    }
+
+    [Fact]
+    public void TestThatAttachImportFunctionsGenerateProperly()
+    {
+        CApiGenerator apiGenerator = new CApiGenerator("", (message) => { });
+        AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("TestAssets/CGlueTestLib.dll");
+        var assemblies = apiGenerator.LoadAssemblies(assembly, "TestAssets");
+
+        var importedMethods = CApiGenerator.GetImportedMethods(assemblies.SelectMany(a => a.MainModule.Types).ToArray());
+        string attachCode = apiGenerator.GenerateAttachImportedFunctions(importedMethods);
+
+        string sanitizedImports = attachCode.Replace("\n", "").Replace("\r", "");
+
+        Assert.Contains("void attach_imported_functions()", sanitizedImports);
+        Assert.Contains("mono_add_internal_call(\"PixiEditor.Extensions.Wasm.Interop::subscribe_to_event\", internal_subscribe_to_event);", sanitizedImports);
+    }
+}

+ 34 - 0
src/PixiEditor.Api.CGlueMSBuild.Tests/PixiEditor.Api.CGlueMSBuild.Tests.csproj

@@ -0,0 +1,34 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <IsPackable>false</IsPackable>
+    <IsTestProject>true</IsTestProject>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="coverlet.collector" Version="6.0.0"/>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
+    <PackageReference Include="Mono.Cecil" Version="0.11.5"/>
+    <PackageReference Include="xunit" Version="2.5.3"/>
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
+  </ItemGroup>
+
+
+  <ItemGroup>
+    <Using Include="Xunit"/>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\PixiEditor.Api.CGlueMSBuild\PixiEditor.Api.CGlueMSBuild.csproj"/>
+  </ItemGroup>
+
+    <ItemGroup>
+      <None Update="TestAssets\*">
+        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      </None>
+    </ItemGroup>
+
+</Project>

BIN
src/PixiEditor.Api.CGlueMSBuild.Tests/TestAssets/CGlueTestLib.dll


BIN
src/PixiEditor.Api.CGlueMSBuild.Tests/TestAssets/PixiEditor.Extensions.Wasm.dll


+ 305 - 0
src/PixiEditor.Api.CGlueMSBuild/CApiGenerator.cs

@@ -0,0 +1,305 @@
+using System.Runtime.InteropServices;
+using System.Text;
+using Mono.Cecil;
+using Mono.Collections.Generic;
+
+namespace PixiEditor.Api.CGlueMSBuild;
+
+public class CApiGenerator
+{
+    private string InteropCContent { get; }
+    private Action<string> Log { get; }
+    public CApiGenerator(string interopCContent, Action<string> log)
+    {
+        InteropCContent = interopCContent;
+        Log = log;
+    }
+
+    public string Generate(AssemblyDefinition assembly, string directory)
+    {
+        Log($"Reference assembly: {assembly.FullName}");
+        var assemblies = LoadAssemblies(assembly, directory);
+
+        var types = assemblies.SelectMany(a => a.MainModule.Types).ToArray();
+
+        var importedMethods = GetImportedMethods(types);
+
+        var exportedMethods = GetExportedMethods(types);
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.AppendLine();
+        sb.AppendLine("/* ----AUTOGENERATED---- */");
+        sb.AppendLine();
+
+        sb.Append(GenerateImports(importedMethods));
+        sb.AppendLine(GenerateExports(exportedMethods));
+
+        sb.AppendLine(GenerateAttachImportedFunctions(importedMethods));
+
+        return InteropCContent.Replace("void attach_imported_functions(){}", sb.ToString());
+    }
+
+    public static MethodDefinition[] GetExportedMethods(TypeDefinition[] types)
+    {
+        var exportedMethods = types
+            .SelectMany(t => t.Methods)
+            .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "PixiEditor.Extensions.Wasm.ApiExportAttribute"))
+            .ToArray();
+        return exportedMethods;
+    }
+
+    public static MethodDefinition[] GetImportedMethods(TypeDefinition[] types)
+    {
+        var importedMethods = types
+            .SelectMany(t => t.Methods)
+            .Where(m => m.IsStatic && m.HasPInvokeInfo)
+            .ToArray();
+        return importedMethods;
+    }
+
+    public List<AssemblyDefinition> LoadAssemblies(AssemblyDefinition assembly, string directory)
+    {
+        var assemblies = assembly.MainModule.AssemblyReferences
+            .Where(r => !r.Name.StartsWith("System") && !r.Name.StartsWith("Microsoft"))
+            .Select(x =>
+            {
+                Log($"Loading assembly from {directory}/{x.Name}.dll");
+                return AssemblyDefinition.ReadAssembly(Path.Combine(directory, x.Name + ".dll"));
+            })
+            .ToList();
+
+        assemblies.Add(assembly);
+        return assemblies;
+    }
+
+    public string GenerateImports(MethodDefinition[] importedMethods)
+    {
+        StringBuilder sb = new StringBuilder();
+        foreach (var method in importedMethods)
+        {
+            sb.AppendLine(BuildImportFunction(method));
+            sb.AppendLine(BuildAttachedFunction(method));
+        }
+
+        return sb.ToString();
+    }
+
+    public string GenerateExports(MethodDefinition[] exportedMethods)
+    {
+        StringBuilder sb = new StringBuilder();
+        foreach (var method in exportedMethods)
+        {
+            sb.AppendLine(BuildExportFunction(method));
+        }
+
+        return sb.ToString();
+    }
+
+    public string GenerateAttachImportedFunctions(MethodDefinition[] importedMethods)
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.Append("void attach_imported_functions()");
+        sb.AppendLine("{");
+        foreach (var method in importedMethods)
+        {
+            sb.AppendLine(GenerateAttachImportedFunction(method));
+        }
+
+        sb.AppendLine("}");
+
+        return sb.ToString();
+    }
+
+    private string BuildExportFunction(MethodDefinition method)
+    {
+        string exportName = method.CustomAttributes.First(a => a.AttributeType.FullName == "PixiEditor.Extensions.Wasm.ApiExportAttribute").ConstructorArguments[0].Value.ToString();
+        StringBuilder sb = new StringBuilder();
+        sb.Append($"__attribute__((export_name(\"{exportName}\")))");
+        sb.AppendLine();
+        BuildMethodDeclaration(method.ReturnType, sb, exportName, method.Parameters, false);
+        sb.AppendLine("{");
+        sb.AppendLine($"MonoMethod* method = lookup_interop_method(\"{method.Name}\");");
+        string[] paramsToPass = CParamsToMonoVars(method.Parameters, sb);
+        if (paramsToPass.Length > 0)
+        {
+            sb.AppendLine($"void* args[] = {{{string.Join(", ", paramsToPass)}}};");
+            sb.AppendLine("invoke_interop_method(method, args);");
+        }
+        else
+        {
+            sb.AppendLine("invoke_interop_method(method, NULL);");
+        }
+
+        sb.AppendLine("free(method);");
+        sb.AppendLine("}");
+
+        return sb.ToString();
+    }
+
+    private string BuildImportFunction(MethodDefinition method)
+    {
+        string functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint;
+        var returnType = method.ReturnType;
+        var parameters = method.Parameters;
+
+        StringBuilder sb = new StringBuilder();
+        sb.AppendLine($@"__attribute__((import_name(""{functionName}"")))");
+
+        BuildMethodDeclaration(returnType, sb, functionName, parameters, true);
+
+        sb.Append(";");
+
+        return sb.ToString();
+    }
+
+    private static void BuildMethodDeclaration(TypeReference returnType, StringBuilder sb, string functionName,
+        Collection<ParameterDefinition> parameters, bool extractLength)
+    {
+        string returnTypeMapped = TypeMapper.MapToCType(returnType);
+        sb.Append($"{returnTypeMapped} {functionName}(");
+
+        for (int i = 0; i < parameters.Count; i++)
+        {
+            var parameter = parameters[i];
+            string parameterTypeMapped = string.Join(", ", TypeMapper.MapToCTypeParam(parameter.ParameterType, parameter.Name, extractLength));
+            sb.Append(parameterTypeMapped);
+            if (i < parameters.Count - 1)
+            {
+                sb.Append(", ");
+            }
+        }
+
+        sb.AppendLine(")");
+    }
+
+    private string BuildAttachedFunction(MethodDefinition method)
+    {
+        StringBuilder sb = new StringBuilder();
+        string functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint;
+        var returnType = method.ReturnType;
+        var parameters = method.Parameters;
+
+        string returnTypeMapped = TypeMapper.MapToCType(returnType);
+        sb.Append($"{returnTypeMapped} internal_{functionName}(");
+
+        BuildMonoParams(parameters, sb);
+        sb.AppendLine(")");
+        sb.AppendLine("{");
+        BuildCBody(method, sb);
+        sb.AppendLine("}");
+
+        return sb.ToString();
+    }
+
+    private static void BuildMonoParams(Collection<ParameterDefinition> parameters, StringBuilder sb)
+    {
+        for (int i = 0; i < parameters.Count; i++)
+        {
+            var parameter = parameters[i];
+            string parameterTypeMapped = TypeMapper.MapToMonoTypeParam(parameter.ParameterType, parameter.Name);
+            sb.Append(parameterTypeMapped);
+            if (i < parameters.Count - 1)
+            {
+                sb.Append(", ");
+            }
+        }
+    }
+
+    private static void BuildCBody(MethodDefinition method, StringBuilder sb)
+    {
+        string[] paramsToPass = MonoParamsToCVars(method.Parameters, sb);
+        BuildInvokeImport(method, sb, paramsToPass);
+    }
+
+    private static string[] MonoParamsToCVars(Collection<ParameterDefinition> methodParameters, StringBuilder sb)
+    {
+        List<string> cVars = new List<string>();
+        for (int i = 0; i < methodParameters.Count; i++)
+        {
+            var parameter = methodParameters[i];
+            if(TypeMapper.RequiresConversion(parameter.ParameterType))
+            {
+                string varName = $"c_{parameter.Name}";
+                ConvertedParam[] convertedParams = TypeMapper.ConvertMonoToCType(parameter.ParameterType, parameter.Name, varName);
+
+                foreach (var convertedParam in convertedParams)
+                {
+                    sb.AppendLine(convertedParam.FullExpression);
+                    cVars.Add(convertedParam.VarName);
+                }
+            }
+            else
+            {
+                cVars.Add(parameter.Name);
+            }
+        }
+
+        return cVars.ToArray();
+    }
+
+    private string[] CParamsToMonoVars(Collection<ParameterDefinition> methodParameters, StringBuilder sb)
+    {
+        List<string> monoVars = new List<string>();
+        for (int i = 0; i < methodParameters.Count; i++)
+        {
+            var parameter = methodParameters[i];
+            if(TypeMapper.RequiresConversion(parameter.ParameterType))
+            {
+                string varName = $"mono_{parameter.Name}";
+                ConvertedParam[] convertedParams = TypeMapper.CToMonoType(parameter.ParameterType, parameter.Name, varName);
+
+                foreach (var convertedParam in convertedParams)
+                {
+                    sb.AppendLine(convertedParam.FullExpression);
+                    string varToPass = !convertedParam.IsPointer ? $"&{convertedParam.VarName}" : convertedParam.VarName;
+                    monoVars.Add(varToPass);
+                }
+            }
+            else
+            {
+                monoVars.Add($"&{parameter.Name}"); // TODO: make sure appending pointer is always correct
+            }
+        }
+
+        return monoVars.ToArray();
+    }
+
+    private static void BuildInvokeImport(MethodDefinition method, StringBuilder sb, string[] paramsToPass)
+    {
+        string functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint;
+        if (method.ReturnType.FullName != "System.Void")
+        {
+            sb.Append("return ");
+        }
+        sb.Append($"{functionName}(");
+        sb.Append(string.Join(", ", paramsToPass));
+        sb.AppendLine(");");
+    }
+
+    private string GenerateAttachImportedFunction(MethodDefinition method)
+    {
+        StringBuilder sb = new StringBuilder();
+        string functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint;
+        string funcNamespace = method.DeclaringType.FullName;
+        sb.Append($"mono_add_internal_call(\"{funcNamespace}::{functionName}\", internal_{functionName});");
+        return sb.ToString();
+    }
+}
+
+public class ConvertedParam
+{
+    public string VarName { get; set; }
+    public string VarType { get; set; }
+    public string ConversionString { get; set; }
+    public string FullExpression => $"{VarType} {VarName} = {ConversionString};";
+
+    public bool IsPointer => VarType.EndsWith("*");
+
+    public ConvertedParam(string varName, string varType, string conversionString)
+    {
+        VarName = varName;
+        VarType = varType;
+        ConversionString = conversionString;
+    }
+}

+ 67 - 0
src/PixiEditor.Api.CGlueMSBuild/GenerateCGlueTask.cs

@@ -0,0 +1,67 @@
+using Microsoft.Build.Framework;
+using Mono.Cecil;
+
+namespace PixiEditor.Api.CGlueMSBuild
+{
+    public class GenerateCGlueTask : Microsoft.Build.Utilities.Task
+    {
+        /// <summary>
+        /// Path of extension assembly.
+        /// </summary>
+        [Required]
+        public string AssemblyPath { get; set; } = default!;
+
+        /// <summary>
+        /// Path where output C files should be placed.
+        /// </summary>
+        [Required]
+        public string OutputPath { get; set; } = default!;
+
+        /// <summary>
+        /// Path of main interop C file
+        /// </summary>
+        [Required]
+        public string InteropCFilePath { get; set; } = default!;
+
+        public override bool Execute()
+        {
+            try
+            {
+                var assemblyFileName = Path.GetFileName(AssemblyPath);
+                var assembly = AssemblyDefinition.ReadAssembly(AssemblyPath);
+
+                if (!Directory.Exists(OutputPath))
+                {
+                    Directory.CreateDirectory(OutputPath);
+                }
+                else
+                {
+                    foreach (var file in Directory.GetFiles(OutputPath, "*.c"))
+                    {
+                        if (file == InteropCFilePath)
+                        {
+                            continue;
+                        }
+
+                        File.Delete(file);
+                    }
+                }
+
+                var generator = new CApiGenerator(File.ReadAllText(InteropCFilePath), (message) => Log.LogMessage(MessageImportance.High, message));
+
+                string directory = Path.GetDirectoryName(AssemblyPath)!;
+
+                string generated = generator.Generate(assembly, directory);
+                Log.LogMessage(MessageImportance.High, $"Generated C API: {Path.GetFullPath(Path.Combine(OutputPath, "interop.c"))}");
+                File.WriteAllText(Path.Combine(OutputPath, "interop.c"), generated);
+
+                return true;
+            }
+            catch (Exception ex)
+            {
+                Log.LogErrorFromException(ex);
+                return false;
+            }
+        }
+    }
+}

+ 22 - 0
src/PixiEditor.Api.CGlueMSBuild/PixiEditor.Api.CGlueMSBuild.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <LangVersion>latest</LangVersion>
+    <OutputType>Library</OutputType>
+    <OutputPath>../PixiEditor.Extensions.Wasm/build/</OutputPath>
+    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.9.5" ExcludeAssets="Runtime" />
+    <PackageReference Include="Mono.Cecil" Version="0.11.5">
+      <PrivateAssets>All</PrivateAssets>
+      <IncludeAssets>All</IncludeAssets>
+    </PackageReference>
+  </ItemGroup>
+
+</Project>

+ 135 - 0
src/PixiEditor.Api.CGlueMSBuild/TypeMapper.cs

@@ -0,0 +1,135 @@
+using Mono.Cecil;
+
+namespace PixiEditor.Api.CGlueMSBuild;
+
+public static class TypeMapper
+{
+    private static Dictionary<string, string> cTypeMap = new Dictionary<string, string>
+    {
+        { "void", "void" },
+        { "boolean", "bool" },
+        { "byte", "uint8_t" },
+        { "sbyte", "int8_t" },
+        { "int16", "int16_t" },
+        { "uint16", "uint16_t" },
+        { "int32", "int32_t" },
+        { "uint32", "uint32_t" },
+        { "int64", "int64_t" },
+        { "uint64", "uint64_t" },
+        { "single", "float" },
+        { "double", "double" },
+        { "char", "char" },
+        { "string", "char*" },
+        { "intptr", "uint8_t*" } // byte array
+    };
+
+    private static Dictionary<string, string> monoTypeMap = new Dictionary<string, string>
+    {
+        { "void", "void" },
+        { "boolean", "bool" },
+        { "byte", "uint8_t" },
+        { "sbyte", "int8_t" },
+        { "int16", "int16_t" },
+        { "uint16", "uint16_t" },
+        { "int32", "int32_t" },
+        { "uint32", "uint32_t" },
+        { "int64", "int64_t" },
+        { "uint64", "uint64_t" },
+        { "single", "float" },
+        { "double", "double" },
+        { "char", "char" },
+        { "string", "MonoString*" },
+        { "intptr", "uint8_t*" } // byte array
+    };
+
+    private static Dictionary<string, string[]> monoToCConversionMap = new Dictionary<string, string[]>
+    {
+        { "MonoString*", ["mono_string_to_utf8({0})", "strlen({0})"] },
+    };
+
+    private static Dictionary<string, string[]> cToMonoConversionMap = new Dictionary<string, string[]>
+    {
+        { "char*", ["mono_string_new(mono_domain_get(), {0})"] }
+    };
+
+    public static string MapToCType(TypeReference type)
+    {
+        if(cTypeMap.TryGetValue(type.Name.ToLower(), out var mapType))
+        {
+            return mapType;
+        }
+
+        throw new NotSupportedException($"Type {type.FullName} is not supported.");
+    }
+
+    public static string MapToMonoType(TypeReference type)
+    {
+        if(monoTypeMap.TryGetValue(type.Name.ToLower(), out var mapType))
+        {
+            return mapType;
+        }
+
+        throw new NotSupportedException($"Type {type.FullName} is not supported.");
+    }
+
+    public static string[] MapToCTypeParam(TypeReference type, string name, bool extractLength = true)
+    {
+        if (extractLength && IsLengthType(type))
+        {
+            return [$"{MapToCType(type)} {name}", $"int32_t {name}Length"];
+        }
+
+        return [$"{MapToCType(type)} {name}"];
+    }
+
+    public static string MapToMonoTypeParam(TypeReference parameterParameterType, string parameterName)
+    {
+        return $"{MapToMonoType(parameterParameterType)} {parameterName}";
+    }
+
+    private static bool IsLengthType(TypeReference type)
+    {
+        return type.Name.Equals("string", StringComparison.InvariantCultureIgnoreCase) || type.IsArray;
+    }
+
+    public static bool RequiresConversion(TypeReference parameterParameterType)
+    {
+        return IsLengthType(parameterParameterType);
+    }
+
+    public static ConvertedParam[] ConvertMonoToCType(TypeReference parameterParameterType, string inputVarName, string outputVarName)
+    {
+        if (IsLengthType(parameterParameterType))
+        {
+            string monoType = MapToMonoType(parameterParameterType);
+            string[] converted = monoToCConversionMap[monoType];
+            return
+            [
+                new ConvertedParam($"{outputVarName}_data", MapToCType(parameterParameterType), ToConversion(converted[0], inputVarName)),
+                new ConvertedParam($"{outputVarName}_length", "int32_t", ToConversion(converted[1], $"{outputVarName}_data"))
+            ];
+        }
+
+        return [];
+    }
+
+    public static ConvertedParam[] CToMonoType(TypeReference parameterParameterType, string inputVarName, string outputVarName)
+    {
+        if (IsLengthType(parameterParameterType))
+        {
+            string cType = MapToCType(parameterParameterType);
+            string[] converted = cToMonoConversionMap[cType];
+            return
+            [
+                new ConvertedParam(outputVarName, MapToMonoType(parameterParameterType), ToConversion(converted[0], inputVarName))
+            ];
+        }
+
+        return [];
+    }
+
+    private static string ToConversion(string method, string varName)
+    {
+        return string.Format(method, varName);
+    }
+}

+ 228 - 0
src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.deps.json

@@ -0,0 +1,228 @@
+{
+  "runtimeTarget": {
+    "name": ".NETStandard,Version=v2.0/",
+    "signature": ""
+  },
+  "compilationOptions": {},
+  "targets": {
+    ".NETStandard,Version=v2.0": {},
+    ".NETStandard,Version=v2.0/": {
+      "PixiEditor.Api.CGlueMSBuild/1.0.0": {
+        "dependencies": {
+          "Microsoft.Build.Utilities.Core": "17.9.5",
+          "NETStandard.Library": "2.0.3",
+          "StyleCop.Analyzers": "1.1.118"
+        },
+        "runtime": {
+          "PixiEditor.Api.CGlueMSBuild.dll": {}
+        }
+      },
+      "Microsoft.Build.Framework/17.9.5": {
+        "dependencies": {
+          "Microsoft.Win32.Registry": "5.0.0",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "Microsoft.Build.Utilities.Core/17.9.5": {
+        "dependencies": {
+          "Microsoft.Build.Framework": "17.9.5",
+          "Microsoft.NET.StringTools": "17.9.5",
+          "Microsoft.Win32.Registry": "5.0.0",
+          "System.Collections.Immutable": "8.0.0",
+          "System.Configuration.ConfigurationManager": "8.0.0",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Security.Principal.Windows": "5.0.0",
+          "System.Text.Encoding.CodePages": "7.0.0"
+        }
+      },
+      "Microsoft.NET.StringTools/17.9.5": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.NETCore.Platforms/1.1.0": {},
+      "Microsoft.Win32.Registry/5.0.0": {
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Security.AccessControl": "5.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "NETStandard.Library/2.0.3": {
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers/1.1.118": {},
+      "System.Buffers/4.5.1": {},
+      "System.Collections.Immutable/8.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Configuration.ConfigurationManager/8.0.0": {
+        "dependencies": {
+          "System.Security.Cryptography.ProtectedData": "8.0.0"
+        }
+      },
+      "System.Memory/4.5.5": {
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Numerics.Vectors/4.4.0": {},
+      "System.Runtime.CompilerServices.Unsafe/6.0.0": {},
+      "System.Security.AccessControl/5.0.0": {
+        "dependencies": {
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "System.Security.Cryptography.ProtectedData/8.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Security.Principal.Windows/5.0.0": {},
+      "System.Text.Encoding.CodePages/7.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      }
+    }
+  },
+  "libraries": {
+    "PixiEditor.Api.CGlueMSBuild/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    },
+    "Microsoft.Build.Framework/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-CjRmqu9Wv2fyC1d7NKOuBDXcNMI8+GiXGM6izygB+skGGu4Vf0cBcoPq7AFqZCcMpn5DtZ+y7RpaLpB2qrzanQ==",
+      "path": "microsoft.build.framework/17.9.5",
+      "hashPath": "microsoft.build.framework.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.Build.Utilities.Core/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-H2hpVdm7cX/uGJD1HOfab3RKgD5tlnvzQkFqvsrAqGHRi0sqb2w1+hRwERFm23witCjmERnqNgiQjYks6/ds8A==",
+      "path": "microsoft.build.utilities.core/17.9.5",
+      "hashPath": "microsoft.build.utilities.core.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.NET.StringTools/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-C/oPRnjcIZBRzcpl1V06R1eEMCxOGt6mIm+8ioyblELgJEXLM8XjUPuCwljMO52VetsHw54xMcYwU8UEeHEIEg==",
+      "path": "microsoft.net.stringtools/17.9.5",
+      "hashPath": "microsoft.net.stringtools.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.NETCore.Platforms/1.1.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
+      "path": "microsoft.netcore.platforms/1.1.0",
+      "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
+    },
+    "Microsoft.Win32.Registry/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+      "path": "microsoft.win32.registry/5.0.0",
+      "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
+    },
+    "NETStandard.Library/2.0.3": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+      "path": "netstandard.library/2.0.3",
+      "hashPath": "netstandard.library.2.0.3.nupkg.sha512"
+    },
+    "StyleCop.Analyzers/1.1.118": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==",
+      "path": "stylecop.analyzers/1.1.118",
+      "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512"
+    },
+    "System.Buffers/4.5.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
+      "path": "system.buffers/4.5.1",
+      "hashPath": "system.buffers.4.5.1.nupkg.sha512"
+    },
+    "System.Collections.Immutable/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
+      "path": "system.collections.immutable/8.0.0",
+      "hashPath": "system.collections.immutable.8.0.0.nupkg.sha512"
+    },
+    "System.Configuration.ConfigurationManager/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
+      "path": "system.configuration.configurationmanager/8.0.0",
+      "hashPath": "system.configuration.configurationmanager.8.0.0.nupkg.sha512"
+    },
+    "System.Memory/4.5.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+      "path": "system.memory/4.5.5",
+      "hashPath": "system.memory.4.5.5.nupkg.sha512"
+    },
+    "System.Numerics.Vectors/4.4.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==",
+      "path": "system.numerics.vectors/4.4.0",
+      "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512"
+    },
+    "System.Runtime.CompilerServices.Unsafe/6.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
+      "path": "system.runtime.compilerservices.unsafe/6.0.0",
+      "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
+    },
+    "System.Security.AccessControl/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+      "path": "system.security.accesscontrol/5.0.0",
+      "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
+    },
+    "System.Security.Cryptography.ProtectedData/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==",
+      "path": "system.security.cryptography.protecteddata/8.0.0",
+      "hashPath": "system.security.cryptography.protecteddata.8.0.0.nupkg.sha512"
+    },
+    "System.Security.Principal.Windows/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
+      "path": "system.security.principal.windows/5.0.0",
+      "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
+    },
+    "System.Text.Encoding.CodePages/7.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==",
+      "path": "system.text.encoding.codepages/7.0.0",
+      "hashPath": "system.text.encoding.codepages.7.0.0.nupkg.sha512"
+    }
+  }
+}

BIN
src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.dll


+ 1 - 1
src/PixiEditor.Extensions.Wasm/Api/Logging/Logger.cs

@@ -21,6 +21,6 @@ public class Logger : ILogger
 
 
     private void InvokeApiLog(string message)
     private void InvokeApiLog(string message)
     {
     {
-        Interop.LogMessage(message);
+        Interop.log_message(message);
     }
     }
 }
 }

+ 4 - 4
src/PixiEditor.Extensions.Wasm/Api/Window/PopupWindow.cs

@@ -13,18 +13,18 @@ public class PopupWindow : IPopupWindow
 
 
     public string Title
     public string Title
     {
     {
-        get => Interop.GetWindowTitle(windowHandle);
-        set => Interop.SetWindowTitle(windowHandle, value);
+        get => Interop.get_window_title(windowHandle);
+        set => Interop.set_window_title(windowHandle, value);
     }
     }
 
 
     public void Show()
     public void Show()
     {
     {
-        Interop.ShowWindow(windowHandle);
+        Interop.show_window(windowHandle);
     }
     }
 
 
     public void Close()
     public void Close()
     {
     {
-        Interop.CloseWindow(windowHandle);
+        Interop.close_window(windowHandle);
     }
     }
 
 
     public Task<bool?> ShowDialog()
     public Task<bool?> ShowDialog()

+ 3 - 3
src/PixiEditor.Extensions.Wasm/Api/Window/WindowProvider.cs

@@ -12,7 +12,7 @@ public class WindowProvider : IWindowProvider
         byte[] bytes = compiledControl.Serialize().ToArray();
         byte[] bytes = compiledControl.Serialize().ToArray();
         IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
         IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
         Marshal.Copy(bytes, 0, ptr, bytes.Length);
         Marshal.Copy(bytes, 0, ptr, bytes.Length);
-        int handle = Interop.CreatePopupWindow(title, ptr, bytes.Length);
+        int handle = Interop.create_popup_window(title, ptr, bytes.Length);
         Marshal.FreeHGlobal(ptr);
         Marshal.FreeHGlobal(ptr);
 
 
         SubscribeToEvents(compiledControl);
         SubscribeToEvents(compiledControl);
@@ -25,7 +25,7 @@ public class WindowProvider : IWindowProvider
         byte[] bytes = newLayout.Serialize().ToArray();
         byte[] bytes = newLayout.Serialize().ToArray();
         IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
         IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
         Marshal.Copy(bytes, 0, ptr, bytes.Length);
         Marshal.Copy(bytes, 0, ptr, bytes.Length);
-        Interop.StateChanged(uniqueId, ptr, bytes.Length);
+        Interop.state_changed(uniqueId, ptr, bytes.Length);
         Marshal.FreeHGlobal(ptr);
         Marshal.FreeHGlobal(ptr);
 
 
         SubscribeToEvents(newLayout);
         SubscribeToEvents(newLayout);
@@ -40,7 +40,7 @@ public class WindowProvider : IWindowProvider
 
 
         foreach (var queuedEvent in body.QueuedEvents)
         foreach (var queuedEvent in body.QueuedEvents)
         {
         {
-            Interop.SubscribeToEvent(body.UniqueId, queuedEvent);
+            Interop.subscribe_to_event(body.UniqueId, queuedEvent);
         }
         }
     }
     }
 
 

+ 11 - 0
src/PixiEditor.Extensions.Wasm/ApiExportAttribute.cs

@@ -0,0 +1,11 @@
+namespace PixiEditor.Extensions.Wasm;
+
+public class ApiExportAttribute : Attribute
+{
+    public string ExportName { get; }
+
+    public ApiExportAttribute(string exportName)
+    {
+        ExportName = exportName;
+    }
+}

+ 11 - 10
src/PixiEditor.Extensions.Wasm/Interop.Windowing.cs

@@ -1,21 +1,22 @@
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 
 namespace PixiEditor.Extensions.Wasm;
 namespace PixiEditor.Extensions.Wasm;
 
 
 internal static partial class Interop
 internal static partial class Interop
 {
 {
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern int CreatePopupWindow(string title, IntPtr data, int length);
+    [DllImport("interop")]
+    internal static extern int create_popup_window(string title, IntPtr data, int length);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void SetWindowTitle(int windowHandle, string title);
+    [DllImport("interop")]
+    internal static extern void set_window_title(int windowHandle, string title);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern string GetWindowTitle(int windowHandle);
+    [DllImport("interop")]
+    internal static extern string get_window_title(int windowHandle);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void ShowWindow(int windowHandle);
+    [DllImport("interop")]
+    internal static extern void show_window(int windowHandle);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void CloseWindow(int windowHandle);
+    [DllImport("interop")]
+    internal static extern void close_window(int windowHandle);
 }
 }

+ 19 - 7
src/PixiEditor.Extensions.Wasm/Interop.cs

@@ -1,24 +1,34 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Reflection;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using PixiEditor.Extensions.CommonApi.FlyUI;
 using PixiEditor.Extensions.CommonApi.FlyUI;
 using PixiEditor.Extensions.CommonApi.FlyUI.Events;
 using PixiEditor.Extensions.CommonApi.FlyUI.Events;
 using PixiEditor.Extensions.Wasm.Api.FlyUI;
 using PixiEditor.Extensions.Wasm.Api.FlyUI;
 
 
 namespace PixiEditor.Extensions.Wasm;
 namespace PixiEditor.Extensions.Wasm;
 
 
+internal static class Program
+{
+    internal static void Main() {
+
+    }
+}
+
 internal static partial class Interop
 internal static partial class Interop
 {
 {
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void LogMessage(string message);
+    [DllImport("interop")]
+    internal static extern void log_message(string message);
+
 
 
+    [DllImport("interop")]
+    internal static extern unsafe void subscribe_to_event(int internalControlId, string eventName);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void SubscribeToEvent(int internalControlId, string eventName);
+    [DllImport("interop")]
+    internal static extern void state_changed(int uniqueId, IntPtr data, int length);
 
 
-    [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void StateChanged(int uniqueId, IntPtr data, int length);
 
 
+    // No need for [ApiExport] since this is a part of built-in C interop file.
     internal static void Load()
     internal static void Load()
     {
     {
         Type extensionType = Assembly.GetEntryAssembly().ExportedTypes
         Type extensionType = Assembly.GetEntryAssembly().ExportedTypes
@@ -26,18 +36,20 @@ internal static partial class Interop
 
 
         Debug.Assert(extensionType != null, "extensionType != null");
         Debug.Assert(extensionType != null, "extensionType != null");
 
 
-        LogMessage($"Loading extension {extensionType.FullName}");
+        log_message($"Loading extension {extensionType.FullName}");
 
 
         WasmExtension extension = (WasmExtension)Activator.CreateInstance(extensionType);
         WasmExtension extension = (WasmExtension)Activator.CreateInstance(extensionType);
         ExtensionContext.Active = extension;
         ExtensionContext.Active = extension;
         extension.OnLoaded();
         extension.OnLoaded();
     }
     }
 
 
+    // No need for [ApiExport] since this is a part of built-in C interop file.
     internal static void Initialize()
     internal static void Initialize()
     {
     {
         ExtensionContext.Active.OnInitialized();
         ExtensionContext.Active.OnInitialized();
     }
     }
 
 
+    [ApiExport("raise_element_event")]
     internal static void EventRaised(int internalControlId, string eventName) //TOOD: Args
     internal static void EventRaised(int internalControlId, string eventName) //TOOD: Args
     {
     {
         if (LayoutElementsStore.LayoutElements.TryGetValue((int)internalControlId, out ILayoutElement<CompiledControl> element))
         if (LayoutElementsStore.LayoutElements.TryGetValue((int)internalControlId, out ILayoutElement<CompiledControl> element))

+ 2 - 4
src/PixiEditor.Extensions.Wasm/PixiEditor.Extensions.Wasm.csproj

@@ -2,19 +2,17 @@
 
 
     <PropertyGroup>
     <PropertyGroup>
         <TargetFramework>net8.0</TargetFramework>
         <TargetFramework>net8.0</TargetFramework>
+       <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
         <ImplicitUsings>enable</ImplicitUsings>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>disable</Nullable>
         <Nullable>disable</Nullable>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+      <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi\PixiEditor.Extensions.CommonApi.csproj" />
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi\PixiEditor.Extensions.CommonApi.csproj" />
     </ItemGroup>
     </ItemGroup>
 
 
-    <ItemGroup>
-      <ClCompile Include="native\layout_builder_api.c" />
-    </ItemGroup>
-
     <ItemGroup>
     <ItemGroup>
       <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3" />
       <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3" />
     </ItemGroup>
     </ItemGroup>

BIN
src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Mdb.dll


BIN
src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Pdb.dll


BIN
src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.Rocks.dll


BIN
src/PixiEditor.Extensions.Wasm/build/Mono.Cecil.dll


+ 256 - 0
src/PixiEditor.Extensions.Wasm/build/PixiEditor.Api.CGlueMSBuild.deps.json

@@ -0,0 +1,256 @@
+{
+  "runtimeTarget": {
+    "name": ".NETStandard,Version=v2.0/",
+    "signature": ""
+  },
+  "compilationOptions": {},
+  "targets": {
+    ".NETStandard,Version=v2.0": {},
+    ".NETStandard,Version=v2.0/": {
+      "PixiEditor.Api.CGlueMSBuild/1.0.0": {
+        "dependencies": {
+          "Microsoft.Build.Utilities.Core": "17.9.5",
+          "Mono.Cecil": "0.11.5",
+          "NETStandard.Library": "2.0.3",
+          "StyleCop.Analyzers": "1.1.118"
+        },
+        "runtime": {
+          "PixiEditor.Api.CGlueMSBuild.dll": {}
+        }
+      },
+      "Microsoft.Build.Framework/17.9.5": {
+        "dependencies": {
+          "Microsoft.Win32.Registry": "5.0.0",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "Microsoft.Build.Utilities.Core/17.9.5": {
+        "dependencies": {
+          "Microsoft.Build.Framework": "17.9.5",
+          "Microsoft.NET.StringTools": "17.9.5",
+          "Microsoft.Win32.Registry": "5.0.0",
+          "System.Collections.Immutable": "8.0.0",
+          "System.Configuration.ConfigurationManager": "8.0.0",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Security.Principal.Windows": "5.0.0",
+          "System.Text.Encoding.CodePages": "7.0.0"
+        }
+      },
+      "Microsoft.NET.StringTools/17.9.5": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.NETCore.Platforms/1.1.0": {},
+      "Microsoft.Win32.Registry/5.0.0": {
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Security.AccessControl": "5.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "Mono.Cecil/0.11.5": {
+        "runtime": {
+          "lib/netstandard2.0/Mono.Cecil.Mdb.dll": {
+            "assemblyVersion": "0.11.5.0",
+            "fileVersion": "0.11.5.0"
+          },
+          "lib/netstandard2.0/Mono.Cecil.Pdb.dll": {
+            "assemblyVersion": "0.11.5.0",
+            "fileVersion": "0.11.5.0"
+          },
+          "lib/netstandard2.0/Mono.Cecil.Rocks.dll": {
+            "assemblyVersion": "0.11.5.0",
+            "fileVersion": "0.11.5.0"
+          },
+          "lib/netstandard2.0/Mono.Cecil.dll": {
+            "assemblyVersion": "0.11.5.0",
+            "fileVersion": "0.11.5.0"
+          }
+        }
+      },
+      "NETStandard.Library/2.0.3": {
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers/1.1.118": {},
+      "System.Buffers/4.5.1": {},
+      "System.Collections.Immutable/8.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Configuration.ConfigurationManager/8.0.0": {
+        "dependencies": {
+          "System.Security.Cryptography.ProtectedData": "8.0.0"
+        }
+      },
+      "System.Memory/4.5.5": {
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Numerics.Vectors/4.4.0": {},
+      "System.Runtime.CompilerServices.Unsafe/6.0.0": {},
+      "System.Security.AccessControl/5.0.0": {
+        "dependencies": {
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "System.Security.Cryptography.ProtectedData/8.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Security.Principal.Windows/5.0.0": {},
+      "System.Text.Encoding.CodePages/7.0.0": {
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      }
+    }
+  },
+  "libraries": {
+    "PixiEditor.Api.CGlueMSBuild/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    },
+    "Microsoft.Build.Framework/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-CjRmqu9Wv2fyC1d7NKOuBDXcNMI8+GiXGM6izygB+skGGu4Vf0cBcoPq7AFqZCcMpn5DtZ+y7RpaLpB2qrzanQ==",
+      "path": "microsoft.build.framework/17.9.5",
+      "hashPath": "microsoft.build.framework.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.Build.Utilities.Core/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-H2hpVdm7cX/uGJD1HOfab3RKgD5tlnvzQkFqvsrAqGHRi0sqb2w1+hRwERFm23witCjmERnqNgiQjYks6/ds8A==",
+      "path": "microsoft.build.utilities.core/17.9.5",
+      "hashPath": "microsoft.build.utilities.core.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.NET.StringTools/17.9.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-C/oPRnjcIZBRzcpl1V06R1eEMCxOGt6mIm+8ioyblELgJEXLM8XjUPuCwljMO52VetsHw54xMcYwU8UEeHEIEg==",
+      "path": "microsoft.net.stringtools/17.9.5",
+      "hashPath": "microsoft.net.stringtools.17.9.5.nupkg.sha512"
+    },
+    "Microsoft.NETCore.Platforms/1.1.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
+      "path": "microsoft.netcore.platforms/1.1.0",
+      "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
+    },
+    "Microsoft.Win32.Registry/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+      "path": "microsoft.win32.registry/5.0.0",
+      "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
+    },
+    "Mono.Cecil/0.11.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==",
+      "path": "mono.cecil/0.11.5",
+      "hashPath": "mono.cecil.0.11.5.nupkg.sha512"
+    },
+    "NETStandard.Library/2.0.3": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+      "path": "netstandard.library/2.0.3",
+      "hashPath": "netstandard.library.2.0.3.nupkg.sha512"
+    },
+    "StyleCop.Analyzers/1.1.118": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==",
+      "path": "stylecop.analyzers/1.1.118",
+      "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512"
+    },
+    "System.Buffers/4.5.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
+      "path": "system.buffers/4.5.1",
+      "hashPath": "system.buffers.4.5.1.nupkg.sha512"
+    },
+    "System.Collections.Immutable/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
+      "path": "system.collections.immutable/8.0.0",
+      "hashPath": "system.collections.immutable.8.0.0.nupkg.sha512"
+    },
+    "System.Configuration.ConfigurationManager/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
+      "path": "system.configuration.configurationmanager/8.0.0",
+      "hashPath": "system.configuration.configurationmanager.8.0.0.nupkg.sha512"
+    },
+    "System.Memory/4.5.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+      "path": "system.memory/4.5.5",
+      "hashPath": "system.memory.4.5.5.nupkg.sha512"
+    },
+    "System.Numerics.Vectors/4.4.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==",
+      "path": "system.numerics.vectors/4.4.0",
+      "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512"
+    },
+    "System.Runtime.CompilerServices.Unsafe/6.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
+      "path": "system.runtime.compilerservices.unsafe/6.0.0",
+      "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
+    },
+    "System.Security.AccessControl/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+      "path": "system.security.accesscontrol/5.0.0",
+      "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
+    },
+    "System.Security.Cryptography.ProtectedData/8.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==",
+      "path": "system.security.cryptography.protecteddata/8.0.0",
+      "hashPath": "system.security.cryptography.protecteddata.8.0.0.nupkg.sha512"
+    },
+    "System.Security.Principal.Windows/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
+      "path": "system.security.principal.windows/5.0.0",
+      "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
+    },
+    "System.Text.Encoding.CodePages/7.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==",
+      "path": "system.text.encoding.codepages/7.0.0",
+      "hashPath": "system.text.encoding.codepages.7.0.0.nupkg.sha512"
+    }
+  }
+}

BIN
src/PixiEditor.Extensions.Wasm/build/PixiEditor.Api.CGlueMSBuild.dll


+ 9 - 4
src/PixiEditor.Extensions.Wasm/build/PixiEditor.Extensions.Wasm.targets

@@ -1,11 +1,16 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <_WasiFilePathForFixup Include="$(MSBuildThisFileDirectory)..\native\*.c" />
-  </ItemGroup>
-
  <!-- <Target Name="GenerateLayouts" AfterTargets="Build" Inputs="bin\Debug\$(TargetFramework)\PixiEditor.Extensions.Wasm.dll" Outputs="$(RootFolder)\Layouts\">
  <!-- <Target Name="GenerateLayouts" AfterTargets="Build" Inputs="bin\Debug\$(TargetFramework)\PixiEditor.Extensions.Wasm.dll" Outputs="$(RootFolder)\Layouts\">
     <Exec  ConsoleToMSBuild="true"
     <Exec  ConsoleToMSBuild="true"
           Command="..\PixiEditor.Extensions.MSBuildLayoutCompiler\bin\Debug\net8.0\PixiEditor.Extensions.MSBuildLayoutCompiler.exe $(OutputPath)\$(MSBuildProjectName).dll $(OutputPath)\Layouts\"/>
           Command="..\PixiEditor.Extensions.MSBuildLayoutCompiler\bin\Debug\net8.0\PixiEditor.Extensions.MSBuildLayoutCompiler.exe $(OutputPath)\$(MSBuildProjectName).dll $(OutputPath)\Layouts\"/>
   </Target>-->
   </Target>-->
 
 
+  <UsingTask TaskName="GenerateCGlueTask"
+             AssemblyFile="$(MSBuildThisFileDirectory)PixiEditor.Api.CGlueMSBuild.dll" Condition="'$(RuntimeIdentifier)' == 'wasi-wasm'"/>
+  <Target Name="GenerateGlueCode" AfterTargets="Build" BeforeTargets="_BeforeWasmBuildApp" Condition="'$(RuntimeIdentifier)' == 'wasi-wasm'">
+    <GenerateCGlueTask AssemblyPath="$(TargetPath)" OutputPath="$(IntermediateOutputPath)native" InteropCFilePath="$(MSBuildThisFileDirectory)..\native\interop.c" />
+    <ItemGroup>
+      <NativeFileReference Include="$(IntermediateOutputPath)native\*.c" />
+      <_WasmNativeFileForLinking Include="@(NativeFileReference)" />
+    </ItemGroup>
+  </Target>
 </Project>
 </Project>

+ 0 - 7
src/PixiEditor.Extensions.Wasm/native/api.h

@@ -1,7 +0,0 @@
-void attach_logger_calls();
-void attach_window_calls();
-void attach_layout_builder_calls();
-void logger_log_message(MonoString* message);
-void log_message(const char* message, int32_t messageLength);
-MonoMethod* lookup_interop_method(const char* method_name);
-void invoke_interop_method(MonoMethod* method, void* params);

+ 21 - 19
src/PixiEditor.Extensions.Wasm/native/api_interop.c → src/PixiEditor.Extensions.Wasm/native/interop.c

@@ -1,29 +1,12 @@
 #include <assert.h>
 #include <assert.h>
 #include <driver.h>
 #include <driver.h>
+#include <string.h>
 #include <mono/metadata/object.h>
 #include <mono/metadata/object.h>
 #include <mono/metadata/exception.h>
 #include <mono/metadata/exception.h>
-#include "api.h"
+#include <mono/metadata/appdomain.h>
 
 
 extern void _start(void);
 extern void _start(void);
 
 
-void attach_internal_calls()
-{
-    attach_logger_calls();
-    attach_window_calls();
-    attach_layout_builder_calls();
-}
-
-void initialize_runtime(void)
-{
-    static int runtime_initialized = 0;
-
-    if (runtime_initialized == 0) {
-        _start();
-        attach_internal_calls();
-        runtime_initialized = 1;
-    }
-}
-
 MonoMethod* lookup_interop_method(const char* method_name)
 MonoMethod* lookup_interop_method(const char* method_name)
 {
 {
     MonoMethod* method = NULL;
     MonoMethod* method = NULL;
@@ -46,6 +29,25 @@ void invoke_interop_method(MonoMethod* method, void* params)
     free(method);
     free(method);
 }
 }
 
 
+// Content is autogenerated
+void attach_imported_functions(){}
+
+void attach_internal_calls()
+{
+    attach_imported_functions();
+}
+
+void initialize_runtime(void)
+{
+    static int runtime_initialized = 0;
+
+    if (runtime_initialized == 0) {
+        _start();
+        attach_internal_calls();
+        runtime_initialized = 1;
+    }
+}
+
 __attribute((export_name("load")))
 __attribute((export_name("load")))
 void load()
 void load()
 {
 {

+ 0 - 37
src/PixiEditor.Extensions.Wasm/native/layout_builder_api.c

@@ -1,37 +0,0 @@
-#include <assert.h>
-#include <driver.h>
-#include <string.h>
-#include <mono/metadata/object.h>
-#include <mono/metadata/exception.h>
-#include <mono/metadata/appdomain.h>
-
-#include "api.h"
-
-__attribute((import_name("subscribe_to_event")))
-void subscribe_to_event(int32_t elementId, char* eventName, int32_t length);
-
-__attribute__((import_name("state_changed")))
-void state_changed(int32_t elementId, uint8_t* data, int32_t length);
-
-void internal_subscribe_to_event(int32_t elementId, MonoString* eventName)
-{
-    char* eventNameString = mono_string_to_utf8(eventName);
-    subscribe_to_event(elementId, eventNameString, strlen(eventNameString));
-}
-
-__attribute((export_name("raise_element_event")))
-void raise_element_event(int32_t elementId, const char* eventName)
-{
-    MonoMethod* method = lookup_interop_method("EventRaised");
-    MonoString* monoEventName = mono_string_new(mono_domain_get(), eventName);
-    void* args[] = { &elementId, monoEventName };
-    invoke_interop_method(method, args);
-
-    free(method);
-}
-
-void attach_layout_builder_calls()
-{
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::SubscribeToEvent", internal_subscribe_to_event);
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::StateChanged", state_changed);
-}

+ 0 - 19
src/PixiEditor.Extensions.Wasm/native/logger_api.c

@@ -1,19 +0,0 @@
-#include <assert.h>
-#include <driver.h>
-#include <mono/metadata/object.h>
-#include <string.h>
-#include <mono/metadata/exception.h>
-
-__attribute__((import_name("log_message")))
-void log_message(const char* message, int32_t messageLength);
-
-void logger_log_message(MonoString* message)
-{
-    char* message_utf8 = mono_string_to_utf8(message);
-    log_message(message_utf8, strlen(message_utf8));
-}
-
-void attach_logger_calls()
-{
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::LogMessage", logger_log_message);
-}

+ 0 - 61
src/PixiEditor.Extensions.Wasm/native/window_api.c

@@ -1,61 +0,0 @@
-#include <assert.h>
-#include <driver.h>
-#include <string.h>
-#include <mono/metadata/object.h>
-#include <mono/metadata/exception.h>
-#include <mono/metadata/appdomain.h>
-
-#include "api.h"
-
-__attribute__((import_name("create_popup_window")))
-int32_t create_popup_window(const char* title, int32_t titleLength, uint8_t* data, int32_t length);
-
-// content is byte[] from C#
-int32_t internal_create_popup_window(MonoString* title, uint8_t* data, int32_t length)
-{
-    char* title_utf8 = mono_string_to_utf8(title);
-    return create_popup_window(title_utf8, strlen(title_utf8), data, length);
-}
-
-__attribute__((import_name("set_window_title")))
-void set_window_title(int32_t windowHandle, const char* title, int32_t titleLength);
-
-void internal_set_window_title(int32_t windowHandle, MonoString* title)
-{
-    char* title_utf8 = mono_string_to_utf8(title);
-    set_window_title(windowHandle, title_utf8, strlen(title_utf8));
-}
-
-__attribute__((import_name("get_window_title")))
-char* get_window_title(int32_t windowHandle);
-
-MonoString* internal_get_window_title(int32_t windowHandle)
-{
-    MonoString* str = mono_string_new(mono_get_root_domain(), get_window_title(windowHandle));
-    return str;
-}
-
-__attribute__((import_name("show_window")))
-void show_window(int32_t windowHandle);
-
-void internal_show_window(int32_t windowHandle)
-{
-    show_window(windowHandle);
-}
-
-__attribute__((import_name("close_window")))
-void close_window(int32_t windowHandle);
-
-void internal_close_window(int32_t windowHandle)
-{
-    close_window(windowHandle);
-}
-
-void attach_window_calls()
-{
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::CreatePopupWindow", internal_create_popup_window);
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::SetWindowTitle", internal_set_window_title);
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::GetWindowTitle", internal_get_window_title);
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::ShowWindow", internal_show_window);
-    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::CloseWindow", internal_close_window);
-}

+ 135 - 0
src/PixiEditor.sln

@@ -112,6 +112,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Numerics", "Pixi
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.WasmApi.Gen", "PixiEditor.WasmApi.Gen\PixiEditor.WasmApi.Gen.csproj", "{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.WasmApi.Gen", "PixiEditor.WasmApi.Gen\PixiEditor.WasmApi.Gen.csproj", "{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Api.CGlueMSBuild", "PixiEditor.Api.CGlueMSBuild\PixiEditor.Api.CGlueMSBuild.csproj", "{9FCCD0CF-FF76-4638-A712-803EFBBC641F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Api.CGlueMSBuild.Tests", "PixiEditor.Api.CGlueMSBuild.Tests\PixiEditor.Api.CGlueMSBuild.Tests.csproj", "{3476109D-9E5C-48FC-99C6-59407F59EEE4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CGlueTestLib", "CGlueTestLib\CGlueTestLib.csproj", "{317B661B-23E8-43FE-A0D6-D3234DF155B1}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -1919,6 +1925,132 @@ Global
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x64.Build.0 = Debug|Any CPU
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x64.Build.0 = Debug|Any CPU
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x86.Build.0 = Debug|Any CPU
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A}.Steam|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Debug|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevRelease|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.DevSteam|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX Debug|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.MSIX|x86.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|x64.ActiveCfg = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|x64.Build.0 = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|x86.ActiveCfg = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Release|x86.Build.0 = Release|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|Any CPU.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|x64.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|x64.Build.0 = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|x86.ActiveCfg = Debug|Any CPU
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F}.Steam|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Debug|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevRelease|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.DevSteam|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX Debug|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.MSIX|x86.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|x64.ActiveCfg = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|x64.Build.0 = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|x86.ActiveCfg = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Release|x86.Build.0 = Release|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|Any CPU.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|x64.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|x64.Build.0 = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|x86.ActiveCfg = Debug|Any CPU
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4}.Steam|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Debug|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevRelease|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.DevSteam|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX Debug|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.MSIX|x86.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|x64.ActiveCfg = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|x64.Build.0 = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|x86.ActiveCfg = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Release|x86.Build.0 = Release|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|Any CPU.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|x64.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|x64.Build.0 = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|x86.ActiveCfg = Debug|Any CPU
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1}.Steam|x86.Build.0 = Debug|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -1966,6 +2098,9 @@ Global
 		{174E2684-9C49-460C-A9F1-A6920F00C036} = {13DD041C-EE2D-4AF8-B43E-D7BFC7415E4D}
 		{174E2684-9C49-460C-A9F1-A6920F00C036} = {13DD041C-EE2D-4AF8-B43E-D7BFC7415E4D}
 		{FDA53E74-24B0-4304-8BC7-1A580E3CE7B4} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{FDA53E74-24B0-4304-8BC7-1A580E3CE7B4} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A} = {13DD041C-EE2D-4AF8-B43E-D7BFC7415E4D}
 		{5E8F82CF-F48A-40B2-99E3-9BBB8725866A} = {13DD041C-EE2D-4AF8-B43E-D7BFC7415E4D}
+		{9FCCD0CF-FF76-4638-A712-803EFBBC641F} = {13DD041C-EE2D-4AF8-B43E-D7BFC7415E4D}
+		{3476109D-9E5C-48FC-99C6-59407F59EEE4} = {F1FDFA82-0C74-446A-AD7D-DE17EC2CF1E8}
+		{317B661B-23E8-43FE-A0D6-D3234DF155B1} = {F1FDFA82-0C74-446A-AD7D-DE17EC2CF1E8}
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {D04B4AB0-CA33-42FD-A909-79966F9255C5}
 		SolutionGuid = {D04B4AB0-CA33-42FD-A909-79966F9255C5}