diff --git a/src/CodeGenerator/CodeGenerator.csproj b/src/CodeGenerator/CodeGenerator.csproj
index 6c2760d..c15d0c0 100644
--- a/src/CodeGenerator/CodeGenerator.csproj
+++ b/src/CodeGenerator/CodeGenerator.csproj
@@ -25,5 +25,6 @@
+
diff --git a/src/CodeGenerator/ImguiDefinitions.cs b/src/CodeGenerator/ImguiDefinitions.cs
index 92e7d74..c9cc936 100644
--- a/src/CodeGenerator/ImguiDefinitions.cs
+++ b/src/CodeGenerator/ImguiDefinitions.cs
@@ -21,7 +21,7 @@ namespace CodeGenerator
if (v == null) return 0;
return v.ToObject();
}
- public void LoadFrom(string directory)
+ public void LoadFrom(string directory, bool excludeInternals = true)
{
JObject typesJson;
@@ -65,23 +65,27 @@ namespace CodeGenerator
{
JProperty jp = (JProperty)jt;
string name = jp.Name;
- if (typeLocations?[jp.Name]?.Value().Contains("internal") ?? false) {
+ bool isInternal = typeLocations?[jp.Name]?.Value().Contains("internal") ?? false;
+
+ if (excludeInternals && isInternal)
return null;
- }
+
EnumMember[] elements = jp.Values().Select(v =>
{
return new EnumMember(v["name"].ToString(), v["calc_value"].ToString());
}).ToArray();
- return new EnumDefinition(name, elements);
+ return new EnumDefinition(name, elements, isInternal);
}).Where(x => x != null).ToArray();
Types = typesJson["structs"].Select(jt =>
{
JProperty jp = (JProperty)jt;
string name = jp.Name;
- if (typeLocations?[jp.Name]?.Value().Contains("internal") ?? false) {
+ bool isInternal = typeLocations?[jp.Name]?.Value().Contains("internal") ?? false;
+
+ if (excludeInternals && isInternal)
return null;
- }
+
TypeReference[] fields = jp.Values().Select(v =>
{
if (v["type"].ToString().Contains("static")) { return null; }
@@ -94,7 +98,7 @@ namespace CodeGenerator
v["template_type"]?.ToString(),
Enums);
}).Where(tr => tr != null).ToArray();
- return new TypeDefinition(name, fields);
+ return new TypeDefinition(name, fields, isInternal);
}).Where(x => x != null).ToArray();
Functions = functionsJson.Children().Select(jt =>
@@ -120,7 +124,9 @@ namespace CodeGenerator
}
}
if (friendlyName == null) { return null; }
- if (val["location"]?.ToString().Contains("internal") ?? false) return null;
+ bool isInternal = val["location"]?.ToString().Contains("internal") ?? false;
+ if (excludeInternals && isInternal)
+ return null;
string exportedName = ov_cimguiname;
if (exportedName == null)
@@ -184,7 +190,8 @@ namespace CodeGenerator
structName,
comment,
isConstructor,
- isDestructor);
+ isDestructor,
+ isInternal);
}).Where(od => od != null).ToArray();
if(overloads.Length == 0) return null;
return new FunctionDefinition(name, overloads, Enums);
@@ -231,8 +238,9 @@ namespace CodeGenerator
public string Name { get; }
public string FriendlyName { get; }
public EnumMember[] Members { get; }
+ public bool IsInternal { get; }
- public EnumDefinition(string name, EnumMember[] elements)
+ public EnumDefinition(string name, EnumMember[] elements, bool isInternal)
{
Name = name;
if (Name.EndsWith('_'))
@@ -250,6 +258,7 @@ namespace CodeGenerator
{
_sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name));
}
+ IsInternal = isInternal;
}
public string SanitizeNames(string text)
@@ -302,11 +311,13 @@ namespace CodeGenerator
{
public string Name { get; }
public TypeReference[] Fields { get; }
+ public bool IsInternal { get; }
- public TypeDefinition(string name, TypeReference[] fields)
+ public TypeDefinition(string name, TypeReference[] fields, bool isInternal)
{
Name = name;
Fields = fields;
+ IsInternal = isInternal;
}
}
@@ -496,6 +507,7 @@ namespace CodeGenerator
public string Comment { get; }
public bool IsConstructor { get; }
public bool IsDestructor { get; }
+ public bool IsInternal { get; }
public OverloadDefinition(
string exportedName,
@@ -506,7 +518,8 @@ namespace CodeGenerator
string structName,
string comment,
bool isConstructor,
- bool isDestructor)
+ bool isDestructor,
+ bool isInternal)
{
ExportedName = exportedName;
FriendlyName = friendlyName;
@@ -518,11 +531,12 @@ namespace CodeGenerator
Comment = comment;
IsConstructor = isConstructor;
IsDestructor = isDestructor;
+ IsInternal = isInternal;
}
public OverloadDefinition WithParameters(TypeReference[] parameters)
{
- return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor);
+ return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor, IsInternal);
}
}
}
diff --git a/src/CodeGenerator/Program.cs b/src/CodeGenerator/Program.cs
index d85f7c2..fb39deb 100644
--- a/src/CodeGenerator/Program.cs
+++ b/src/CodeGenerator/Program.cs
@@ -8,37 +8,85 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using System.CommandLine;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
namespace CodeGenerator
{
internal static class Program
{
- static void Main(string[] args)
+ private const string InternalNamespace = ".Internal";
+
+ static async Task Main(string[] args)
{
- string outputPath;
- if (args.Length > 0)
- {
- outputPath = args[0];
- }
- else
- {
- outputPath = AppContext.BaseDirectory;
- }
+ // internal vars for command line results used by the rest of the program.
+ bool runApp = false;
+ string outputPath = string.Empty;
+ string libraryName = string.Empty;
+ bool useInternals = false;
- if (!Directory.Exists(outputPath))
- {
- Directory.CreateDirectory(outputPath);
- }
+ #region Command line handler
+ var optionOutputPath = new Option(
+ aliases: new[] { "--outputDir", "-o" },
+ description: "The directory to place generated code files.",
+ parseArgument: result =>
+ {
+ if (result.Tokens.Count == 0)
+ return new DirectoryInfo(AppContext.BaseDirectory);
- string libraryName;
- if (args.Length > 1)
- {
- libraryName = args[1];
- }
- else
+ string value = result.Tokens.Single().Value;
+
+ try { return Directory.CreateDirectory(value); }
+ catch (Exception) { result.ErrorMessage = $"Unable to create directory: {value}"; return null; }
+ },
+ isDefault: true);
+
+ var optionLibraryname = new Option(
+ aliases: new[] { "--library", "-l" },
+ description: "The library to read parse.",
+ getDefaultValue: () => "cimgui")
+ .FromAmong("cimgui", "cimplot", "cimnodes", "cimguizmo");
+
+ var optionInternal = new Option(
+ name: "--internal",
+ description: "When set to true, includes the internal header file.",
+ parseArgument: result =>
+ {
+ // Using parse with isDefault: false, instead of the normal validation, allows us to use "--internal" without specifying true to mean true.
+ if (result.Tokens.Count == 0)
+ return true;
+
+ if (bool.TryParse(result.Tokens.Single().Value, out var value))
+ return value;
+
+ result.ErrorMessage = "Invalid option for --internal. Value must be true or false.";
+ return false; // ignored because of error message.
+ },
+ isDefault: false);
+
+ var rootCommand = new RootCommand("Generates code for the ImGui.NET libraries based on the cimgui definition files.");
+
+ rootCommand.AddOption(optionInternal);
+ rootCommand.AddOption(optionOutputPath);
+ rootCommand.AddOption(optionLibraryname);
+
+ rootCommand.SetHandler((outputPathValue, libNameValue, useInternalValue) =>
{
- libraryName = "cimgui";
- }
+ outputPath = outputPathValue.FullName;
+ libraryName = libNameValue;
+ useInternals = useInternalValue;
+
+ runApp = true;
+
+ }, optionOutputPath, optionLibraryname, optionInternal);
+
+ var commandResult = await rootCommand.InvokeAsync(args);
+
+ if (!runApp)
+ return commandResult;
+
+ #endregion
string projectNamespace = libraryName switch
{
@@ -78,7 +126,7 @@ namespace CodeGenerator
string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName);
var defs = new ImguiDefinitions();
- defs.LoadFrom(definitionsPath);
+ defs.LoadFrom(definitionsPath, !useInternals);
Console.WriteLine($"Outputting generated code files to {outputPath}.");
@@ -118,7 +166,7 @@ namespace CodeGenerator
writer.Using("ImGuiNET");
}
writer.WriteLine(string.Empty);
- writer.PushBlock($"namespace {projectNamespace}");
+ writer.PushBlock($"namespace {projectNamespace}{(td.IsInternal ? InternalNamespace : string.Empty)}");
writer.PushBlock($"public unsafe partial struct {td.Name}");
foreach (TypeReference field in td.Fields)
@@ -284,6 +332,10 @@ namespace CodeGenerator
{
writer.Using("ImGuiNET");
}
+ if (useInternals)
+ {
+ writer.Using($"{projectNamespace}{InternalNamespace}");
+ }
writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}Native");
@@ -347,6 +399,7 @@ namespace CodeGenerator
writer.PopBlock();
}
+ // Root ImGui* class items - Noninternal
using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs")))
{
writer.Using("System");
@@ -360,52 +413,65 @@ namespace CodeGenerator
writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}");
- foreach (FunctionDefinition fd in defs.Functions)
+ EmitFunctions(false);
+ writer.PopBlock();
+ writer.PopBlock();
+
+ if (useInternals)
{
- if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; }
+ writer.PushBlock($"namespace {projectNamespace}{InternalNamespace}");
+ writer.PushBlock($"public static unsafe partial class {classPrefix}");
+ EmitFunctions(true);
+ writer.PopBlock();
+ writer.PopBlock();
+ }
- foreach (OverloadDefinition overload in fd.Overloads)
+ void EmitFunctions(bool isInternal)
+ {
+ foreach (FunctionDefinition fd in defs.Functions)
{
- string exportedName = overload.ExportedName;
- if (exportedName.StartsWith("ig"))
- {
- exportedName = exportedName.Substring(2, exportedName.Length - 2);
- }
- if (exportedName.Contains("~")) { continue; }
- if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters.
+ if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; }
- bool hasVaList = false;
- for (int i = 0; i < overload.Parameters.Length; i++)
+ foreach (OverloadDefinition overload in fd.Overloads.Where(o => !o.IsMemberFunction && o.IsInternal == isInternal))
{
- TypeReference p = overload.Parameters[i];
- string paramType = GetTypeString(p.Type, p.IsFunctionPointer);
- if (p.Name == "...") { continue; }
+ string exportedName = overload.ExportedName;
+ if (exportedName.StartsWith("ig"))
+ {
+ exportedName = exportedName.Substring(2, exportedName.Length - 2);
+ }
+ if (exportedName.Contains("~")) { continue; }
+ if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters.
- if (paramType == "va_list")
+ bool hasVaList = false;
+ for (int i = 0; i < overload.Parameters.Length; i++)
{
- hasVaList = true;
- break;
+ TypeReference p = overload.Parameters[i];
+ string paramType = GetTypeString(p.Type, p.IsFunctionPointer);
+ if (p.Name == "...") { continue; }
+
+ if (paramType == "va_list")
+ {
+ hasVaList = true;
+ break;
+ }
}
- }
- if (hasVaList) { continue; }
+ if (hasVaList) { continue; }
- KeyValuePair[] orderedDefaults = overload.DefaultValues.OrderByDescending(
- kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();
+ KeyValuePair[] orderedDefaults = overload.DefaultValues.OrderByDescending(
+ kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();
- for (int i = overload.DefaultValues.Count; i >= 0; i--)
- {
- if (overload.IsMemberFunction) { continue; }
- Dictionary defaults = new Dictionary();
- for (int j = 0; j < i; j++)
+ for (int i = overload.DefaultValues.Count; i >= 0; i--)
{
- defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
+ Dictionary defaults = new Dictionary();
+ for (int j = 0; j < i; j++)
+ {
+ defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
+ }
+ EmitOverload(writer, overload, defaults, null, classPrefix);
}
- EmitOverload(writer, overload, defaults, null, classPrefix);
}
}
}
- writer.PopBlock();
- writer.PopBlock();
}
foreach (var method in defs.Variants)
@@ -415,6 +481,8 @@ namespace CodeGenerator
if (!variant.Used) Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}.");
}
}
+
+ return 0;
}
private static bool IsStringFieldName(string name)
diff --git a/src/CodeGenerator/TypeInfo.cs b/src/CodeGenerator/TypeInfo.cs
index 2f37689..034e66e 100644
--- a/src/CodeGenerator/TypeInfo.cs
+++ b/src/CodeGenerator/TypeInfo.cs
@@ -55,6 +55,17 @@ namespace CodeGenerator
{ "ImPlotFormatter", "IntPtr" },
{ "ImPlotGetter", "IntPtr" },
{ "ImPlotTransform", "IntPtr" },
+ // internals
+ { "char[5]", "byte*"},
+ { "ImGuiDir*", "IntPtr" },
+ //{ "ImGuiStoragePair", "IntPtr" },
+ { "ImGuiDockRequest", "IntPtr" },
+ { "ImGuiDockNodeSettings", "IntPtr" },
+ { "ImGuiTableColumnIdx", "sbyte" },
+ { "ImGuiTableDrawChannelIdx", "byte"},
+ { "ImGuiContextHookCallback", "IntPtr" },
+ { "ImGuiErrorLogCallback", "IntPtr" },
+ //{ "ImGuiSizeCallback", "IntPtr"}
};
public static readonly List WellKnownEnums = new List()
@@ -73,6 +84,7 @@ namespace CodeGenerator
"ImVec2",
"ImVec4",
"ImGuiStoragePair",
+ "ImGuiStyleMod",
};
public static readonly Dictionary WellKnownDefaultValues = new Dictionary()