diff --git a/.github/workflows/nuget.org-linux.yml b/.github/workflows/nuget.org-linux.yml
new file mode 100644
index 00000000..cd3844b0
--- /dev/null
+++ b/.github/workflows/nuget.org-linux.yml
@@ -0,0 +1,34 @@
+# This is a basic workflow to help you get started with Actions
+
+name: nuget.org-linux
+
+on:
+ push: { branches: [ master ] }
+ pull_request: { branches: [ master ] }
+ release: { types: [published] } # runs on “Publish release” button
+ workflow_dispatch: # lets you run it by hand
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ name: Checkout Code
+ with:
+ fetch-depth: 0
+
+ # Necessary for running neato
+ - name: Install libgts
+ run: sudo apt-get update && sudo apt-get install -y libgts-0.7-5
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Setup Just
+ uses: extractions/setup-just@v3
+
+ - name: Build and test
+ run: just test-nugetorg
diff --git a/.github/workflows/nuget.org-windows.yml b/.github/workflows/nuget.org-windows.yml
new file mode 100644
index 00000000..6b9c71c9
--- /dev/null
+++ b/.github/workflows/nuget.org-windows.yml
@@ -0,0 +1,30 @@
+# This is a basic workflow to help you get started with Actions
+
+name: nuget.org-windows
+
+on:
+ push: { branches: [ master ] }
+ pull_request: { branches: [ master ] }
+ release: { types: [published] } # runs on “Publish release” button
+ workflow_dispatch: # lets you run it by hand
+
+jobs:
+ build:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ name: Checkout Code
+ with:
+ fetch-depth: 0
+
+ - name: Add msbuild to PATH
+ uses: microsoft/setup-msbuild@v1.0.2
+
+ - name: Setup NuGet.exe for use with actions
+ uses: NuGet/setup-nuget@v1.0.5
+
+ - name: Setup Just
+ uses: extractions/setup-just@v3
+
+ - name: Build and test
+ run: just test-nugetorg
diff --git a/How-to-release.txt b/How-to-release.txt
index 41f1ebeb..23023374 100644
--- a/How-to-release.txt
+++ b/How-to-release.txt
@@ -1,12 +1,10 @@
-- Bump the version number in Rubjerg.Graphviz.csproj to the version to be released.
-- Make a PR and grab the nuget package from the CI artifacts.
+- Make a prerelease on github (and add a new tag there). This will trigger the CI.
+- Grab the nuget package from the CI artifacts.
- Upload the .nupkg file to nuget.org.
- Upload to https://www.nuget.org/packages/manage/upload
+ - Mark as unlisted
- Make a cup of coffee
- Update the version of the referenced Nuget package in the NugetOrgTest project to the package that was just uploaded and run the test.
- - dotnet add Rubjerg.Graphviz.NugetOrgTest\Rubjerg.Graphviz.NugetOrgTest.csproj package Rubjerg.Graphviz
-- If the build and test succeeds we are ready to release. Merge the PR, add the tag, and push this to github.
- - Merge PR on github
- - git tag vx.y.z
- - git push github --tags
-- In GitHub, add a new release of the just pushed commit at https://github.com/Rubjerg/Graphviz.NetWrapper/releases.
+ - dotnet add NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj package Rubjerg.Graphviz
+ - Make a PR for this and merge when this succeeds.
+- Mark prerelease as latest release on github and nuget.org
diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj
index df767f28..dfcef5fb 100644
--- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj
+++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj
@@ -5,16 +5,20 @@
net8.0
x64
x64
- Chiel ten Brinke
- Rubjerg
..\packages
true
+ latest
embedded
+
+
+
+
+ true
- 2.0.2
+ 3.0.0
3.18.3
diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj
index eccd2c9a..cdebedd1 100644
--- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj
+++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj
@@ -5,13 +5,16 @@
net8.0
x64
x64
- Chiel ten Brinke
- Rubjerg
..\packages
true
+ latest
embedded
+
+ true
+
+
diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs
index eb1aec2e..35d168c6 100644
--- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs
+++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs
@@ -1,5 +1,4 @@
using NUnit.Framework;
-using Rubjerg.Graphviz.Test;
namespace Rubjerg.Graphviz.NugetOrgTest
{
diff --git a/Rubjerg.Graphviz.Test/Reproductions.cs b/Rubjerg.Graphviz.Test/Reproductions.cs
index de87676a..e935b019 100644
--- a/Rubjerg.Graphviz.Test/Reproductions.cs
+++ b/Rubjerg.Graphviz.Test/Reproductions.cs
@@ -3,7 +3,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz.Test;
diff --git a/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj b/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj
index ae9652cb..c81c2133 100644
--- a/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj
+++ b/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj
@@ -5,8 +5,6 @@
net8.0
x64
x64
- Chiel ten Brinke
- Rubjerg
..\packages
true
latest
diff --git a/Rubjerg.Graphviz.Test/TestInterop.cs b/Rubjerg.Graphviz.Test/TestInterop.cs
index a4960be4..193cc3c5 100644
--- a/Rubjerg.Graphviz.Test/TestInterop.cs
+++ b/Rubjerg.Graphviz.Test/TestInterop.cs
@@ -1,7 +1,8 @@
using NUnit.Framework;
using System.IO;
using System;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
+using static Rubjerg.Graphviz.FFI.TestLib;
namespace Rubjerg.Graphviz.Test;
diff --git a/Rubjerg.Graphviz.Test/TestXDotLayout.cs b/Rubjerg.Graphviz.Test/TestXDotLayout.cs
index 6b98160f..8ab2be4e 100644
--- a/Rubjerg.Graphviz.Test/TestXDotLayout.cs
+++ b/Rubjerg.Graphviz.Test/TestXDotLayout.cs
@@ -4,6 +4,8 @@
namespace Rubjerg.Graphviz.Test;
+using FFI;
+
[TestFixture()]
public class TestXDotLayout
{
diff --git a/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj b/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj
index 4b172ff3..a160b68b 100644
--- a/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj
+++ b/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj
@@ -5,14 +5,16 @@
net8.0
x64
x64
- Chiel ten Brinke
- Rubjerg
..\packages
true
latest
embedded
+
+ true
+
+
3.18.3
diff --git a/Rubjerg.Graphviz/CGraphThing.cs b/Rubjerg.Graphviz/CGraphThing.cs
index 846a277e..3c34fe1f 100644
--- a/Rubjerg.Graphviz/CGraphThing.cs
+++ b/Rubjerg.Graphviz/CGraphThing.cs
@@ -2,8 +2,9 @@
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
using System.Globalization;
+using Rubjerg.Graphviz.FFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/Constants.cs b/Rubjerg.Graphviz/Constants.cs
deleted file mode 100644
index c3a6930b..00000000
--- a/Rubjerg.Graphviz/Constants.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Rubjerg.Graphviz;
-
-internal static class Constants
-{
-#if _WINDOWS
- public const string GvcLib = "gvc.dll";
- public const string CGraphLib = "cgraph.dll";
- public const string XDotLib = "xdot.dll";
- public const string GraphvizWrapperLib = "GraphvizWrapper.dll";
-#else
- public const string GvcLib = "libgvc.so.6";
- public const string CGraphLib = "libcgraph.so.6";
- public const string XDotLib = "libxdot.so.4";
- public const string GraphvizWrapperLib = "libGraphvizWrapper.so";
-#endif
-}
diff --git a/Rubjerg.Graphviz/Edge.cs b/Rubjerg.Graphviz/Edge.cs
index bb6cc76a..9a22a526 100644
--- a/Rubjerg.Graphviz/Edge.cs
+++ b/Rubjerg.Graphviz/Edge.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/FFI/Constants.cs b/Rubjerg.Graphviz/FFI/Constants.cs
new file mode 100644
index 00000000..37a8e98a
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/Constants.cs
@@ -0,0 +1,14 @@
+namespace Rubjerg.Graphviz.FFI;
+
+internal static class Constants
+{
+ public const string GvcLibNameWindows = "gvc";
+ public const string CGraphLibNameWindows = "cgraph";
+ public const string XDotLibNameWindows = "xdot";
+
+ public const string GvcLibNameLinux = "libgvc.so.6";
+ public const string CGraphLibNameLinux = "libcgraph.so.6";
+ public const string XDotLibNameLinux = "libxdot.so.4";
+
+ public const string GraphvizWrapperLibName = "GraphvizWrapper";
+}
diff --git a/Rubjerg.Graphviz/FFI/GraphvizFFI.cs b/Rubjerg.Graphviz/FFI/GraphvizFFI.cs
new file mode 100644
index 00000000..71ec6fdc
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/GraphvizFFI.cs
@@ -0,0 +1,563 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Marshaling;
+using static Platform;
+
+///
+/// Graphviz is thread unsafe, so we wrap all function calls inside a lock to make sure we don't run into
+/// issues caused by multiple threads accessing the graphviz datastructures (like the GC executing a destructor).
+///
+internal static class GraphvizFFI
+{
+ private static readonly object _mutex = new object();
+
+ public static IntPtr GvContext()
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.gvContext() : GraphvizLibLinux.gvContext();
+ }
+ }
+ public static int GvFreeContext(IntPtr gvc)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.gvFreeContext(gvc) : GraphvizLibLinux.gvFreeContext(gvc);
+ }
+ }
+ public static int GvLayout(IntPtr gvc, IntPtr graph, string engine)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(engine, enginePtr => IsWindows ? GraphvizLibWindows.gvLayout(gvc, graph, enginePtr) : GraphvizLibLinux.gvLayout(gvc, graph, enginePtr));
+ }
+ }
+ public static int GvFreeLayout(IntPtr gvc, IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.gvFreeLayout(gvc, graph) : GraphvizLibLinux.gvFreeLayout(gvc, graph);
+ }
+ }
+ public static int GvRender(IntPtr gvc, IntPtr graph, string? format, IntPtr @out)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(format, formatPtr => IsWindows ? GraphvizLibWindows.gvRender(gvc, graph, formatPtr, @out) : GraphvizLibLinux.gvRender(gvc, graph, formatPtr, @out));
+ }
+ }
+ public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string? format, string? filename)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(format, formatPtr => MarshalToUtf8(filename, filenamePtr => IsWindows ? GraphvizLibWindows.gvRenderFilename(gvc, graph, formatPtr, filenamePtr) : GraphvizLibLinux.gvRenderFilename(gvc, graph, formatPtr, filenamePtr)));
+ }
+ }
+ public static IntPtr Agnode(IntPtr graph, string? name, int create)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agnode(graph, namePtr, create) : GraphvizLibLinux.agnode(graph, namePtr, create));
+ }
+ }
+ public static int Agdegree(IntPtr graph, IntPtr node, int inset, int outset)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agdegree(graph, node, inset, outset) : GraphvizLibLinux.agdegree(graph, node, inset, outset);
+ }
+ }
+ public static IntPtr Agfstout(IntPtr graph, IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agfstout(graph, node) : GraphvizLibLinux.agfstout(graph, node);
+ }
+ }
+ public static IntPtr Agnxtout(IntPtr graph, IntPtr edge)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtout(graph, edge) : GraphvizLibLinux.agnxtout(graph, edge);
+ }
+ }
+ public static IntPtr Agfstin(IntPtr graph, IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agfstin(graph, node) : GraphvizLibLinux.agfstin(graph, node);
+ }
+ }
+ public static IntPtr Agnxtin(IntPtr graph, IntPtr edge)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtin(graph, edge) : GraphvizLibLinux.agnxtin(graph, edge);
+ }
+ }
+ public static IntPtr Agfstedge(IntPtr graph, IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agfstedge(graph, node) : GraphvizLibLinux.agfstedge(graph, node);
+ }
+ }
+ public static IntPtr Agnxtedge(IntPtr graph, IntPtr edge, IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtedge(graph, edge, node) : GraphvizLibLinux.agnxtedge(graph, edge, node);
+ }
+ }
+ public static void Agattr(IntPtr graph, int type, string name, string deflt)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(deflt, defltPtr =>
+ MarshalToUtf8(name, namePtr =>
+ {
+ if (IsWindows)
+ GraphvizLibWindows.agattr(graph, type, namePtr, defltPtr);
+ else
+ GraphvizLibLinux.agattr(graph, type, namePtr, defltPtr);
+ }));
+ }
+ }
+ public static void AgattrHtml(IntPtr graph, int type, string name, string deflt)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(name, namePtr =>
+ MarshalToUtf8(deflt, defltPtr =>
+ {
+ if (IsWindows)
+ {
+ var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(graph), defltPtr);
+ GraphvizLibWindows.agattr(graph, type, namePtr, htmlPtr);
+ }
+ else
+ {
+ var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(graph), defltPtr);
+ GraphvizLibLinux.agattr(graph, type, namePtr, htmlPtr);
+ }
+ }));
+ }
+ }
+
+ public static void Agset(IntPtr obj, string name, string value)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(name, namePtr =>
+ MarshalToUtf8(value, valuePtr =>
+ {
+ if (IsWindows)
+ GraphvizLibWindows.agset(obj, namePtr, valuePtr);
+ else
+ GraphvizLibLinux.agset(obj, namePtr, valuePtr);
+ }));
+ }
+ }
+
+ public static void AgsetHtml(IntPtr obj, string name, string value)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(name, namePtr =>
+ MarshalToUtf8(value, valuePtr =>
+ {
+ if (IsWindows)
+ {
+ var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(obj), valuePtr);
+ GraphvizLibWindows.agset(obj, namePtr, htmlPtr);
+ }
+ else
+ {
+ var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(obj), valuePtr);
+ GraphvizLibLinux.agset(obj, namePtr, htmlPtr);
+ }
+ }));
+ }
+ }
+
+ public static void Agsafeset(IntPtr obj, string name, string? val, string? deflt)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(name, namePtr => MarshalToUtf8(val, valPtr => MarshalToUtf8(deflt, defltPtr =>
+ {
+ if (IsWindows)
+ GraphvizLibWindows.agsafeset(obj, namePtr, valPtr, defltPtr);
+ else
+ GraphvizLibLinux.agsafeset(obj, namePtr, valPtr, defltPtr);
+ })));
+ }
+ }
+ public static void AgsafesetHtml(IntPtr obj, string name, string? val, string? deflt)
+ {
+ lock (_mutex)
+ {
+ MarshalToUtf8(name, namePtr => MarshalToUtf8(val, valPtr => MarshalToUtf8(deflt, defltPtr =>
+ {
+ if (IsWindows)
+ {
+ var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(obj), defltPtr);
+ GraphvizLibWindows.agsafeset(obj, namePtr, valPtr, htmlPtr);
+ }
+ else
+ {
+ var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(obj), defltPtr);
+ GraphvizLibLinux.agsafeset(obj, namePtr, valPtr, htmlPtr);
+ }
+ })));
+ }
+ }
+ public static IntPtr Agroot(IntPtr obj)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agroot(obj) : GraphvizLibLinux.agroot(obj);
+ }
+ }
+ public static IntPtr Agnxtattr(IntPtr obj, int kind, IntPtr attribute)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtattr(obj, kind, attribute) : GraphvizLibLinux.agnxtattr(obj, kind, attribute);
+ }
+ }
+ public static int Agcopyattr(IntPtr from, IntPtr to)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agcopyattr(from, to) : GraphvizLibLinux.agcopyattr(from, to);
+ }
+ }
+ public static bool Ageqedge(IntPtr edge1, IntPtr edge2)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.rj_ageqedge(edge1, edge2);
+ }
+ }
+ public static IntPtr Agtail(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.rj_agtail(node);
+ }
+ }
+ public static IntPtr Aghead(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.rj_aghead(node);
+ }
+ }
+ public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string? name, int create)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agedge(graph, tail, head, namePtr, create) : GraphvizLibLinux.agedge(graph, tail, head, namePtr, create));
+ }
+ }
+ public static IntPtr Agmkin(IntPtr edge)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.rj_agmkin(edge);
+ }
+ }
+ public static IntPtr Agmkout(IntPtr edge)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.rj_agmkout(edge);
+ }
+ }
+ public static IntPtr Agparent(IntPtr obj)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agparent(obj) : GraphvizLibLinux.agparent(obj);
+ }
+ }
+ public static int Agclose(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agclose(graph) : GraphvizLibLinux.agclose(graph);
+ }
+ }
+ public static int Agdelete(IntPtr graph, IntPtr item)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agdelete(graph, item) : GraphvizLibLinux.agdelete(graph, item);
+ }
+ }
+ public static IntPtr Agfstnode(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agfstnode(graph) : GraphvizLibLinux.agfstnode(graph);
+ }
+ }
+ public static IntPtr Agnxtnode(IntPtr graph, IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtnode(graph, node) : GraphvizLibLinux.agnxtnode(graph, node);
+ }
+ }
+ public static int Agcontains(IntPtr graph, IntPtr obj)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agcontains(graph, obj) : GraphvizLibLinux.agcontains(graph, obj);
+ }
+ }
+ public static IntPtr Agsubg(IntPtr graph, string? name, int create)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agsubg(graph, namePtr, create) : GraphvizLibLinux.agsubg(graph, namePtr, create));
+ }
+ }
+ public static IntPtr Agfstsubg(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agfstsubg(graph) : GraphvizLibLinux.agfstsubg(graph);
+ }
+ }
+ public static IntPtr Agnxtsubg(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agnxtsubg(graph) : GraphvizLibLinux.agnxtsubg(graph);
+ }
+ }
+ public static int Agisstrict(IntPtr ptr)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agisstrict(ptr) : GraphvizLibLinux.agisstrict(ptr);
+ }
+ }
+ public static int Agisdirected(IntPtr ptr)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agisdirected(ptr) : GraphvizLibLinux.agisdirected(ptr);
+ }
+ }
+ public static int Agisundirected(IntPtr ptr)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agisundirected(ptr) : GraphvizLibLinux.agisundirected(ptr);
+ }
+ }
+ public static IntPtr Agsubedge(IntPtr graph, IntPtr edge, int create)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agsubedge(graph, edge, create) : GraphvizLibLinux.agsubedge(graph, edge, create);
+ }
+ }
+ public static IntPtr Agsubnode(IntPtr graph, IntPtr node, int create)
+ {
+ lock (_mutex)
+ {
+ return IsWindows ? GraphvizLibWindows.agsubnode(graph, node, create) : GraphvizLibLinux.agsubnode(graph, node, create);
+ }
+ }
+ public static IntPtr EdgeLabel(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.edge_label(node);
+ }
+ }
+ public static string? Rjagmemwrite(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ var strPtr = GraphvizWrapperLib.rj_agmemwrite(graph);
+ return MarshalFromUtf8(strPtr, true);
+ }
+ }
+ public static IntPtr GraphLabel(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.graph_label(node);
+ }
+ }
+ public static string? Agget(IntPtr obj, string name)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(name, namePtr => MarshalFromUtf8(IsWindows ? GraphvizLibWindows.agget(obj, namePtr) : GraphvizLibLinux.agget(obj, namePtr), false));
+ }
+ }
+ public static string? Rjagnameof(IntPtr obj)
+ {
+ lock (_mutex)
+ {
+ return MarshalFromUtf8(IsWindows ? GraphvizLibWindows.agnameof(obj) : GraphvizLibLinux.agnameof(obj), false);
+ }
+ }
+ public static void CloneAttributeDeclarations(IntPtr graphfrom, IntPtr graphto)
+ {
+ lock (_mutex)
+ {
+ GraphvizWrapperLib.clone_attribute_declarations(graphfrom, graphto);
+ }
+ }
+ public static string? ImsymKey(IntPtr sym)
+ {
+ lock (_mutex)
+ {
+ return MarshalFromUtf8(GraphvizWrapperLib.rj_sym_key(sym), false);
+ }
+ }
+ public static double LabelX(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.label_x(label);
+ }
+ }
+ public static double LabelY(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.label_y(label);
+ }
+ }
+ public static double LabelWidth(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.label_width(label);
+ }
+ }
+ public static double LabelHeight(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.label_height(label);
+ }
+ }
+ public static string? LabelText(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return MarshalFromUtf8(GraphvizWrapperLib.label_text(label), false);
+ }
+ }
+ public static double LabelFontsize(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.label_fontsize(label);
+ }
+ }
+ public static string? LabelFontname(IntPtr label)
+ {
+ lock (_mutex)
+ {
+ return MarshalFromUtf8(GraphvizWrapperLib.label_fontname(label), false);
+ }
+ }
+ public static double NodeX(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.node_x(node);
+ }
+ }
+ public static double NodeY(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.node_y(node);
+ }
+ }
+ public static double NodeWidth(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.node_width(node);
+ }
+ }
+ public static double NodeHeight(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.node_height(node);
+ }
+ }
+ public static IntPtr NodeLabel(IntPtr node)
+ {
+ lock (_mutex)
+ {
+ return GraphvizWrapperLib.node_label(node);
+ }
+ }
+ public static void ConvertToUndirected(IntPtr graph)
+ {
+ lock (_mutex)
+ {
+ GraphvizWrapperLib.convert_to_undirected(graph);
+ }
+ }
+ public static IntPtr Rjagmemread(string input)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(input, GraphvizWrapperLib.rj_agmemread);
+ }
+ }
+ public static IntPtr Rjagopen(string? name, int graphtype)
+ {
+ lock (_mutex)
+ {
+ return MarshalToUtf8(name, namePtr => GraphvizWrapperLib.rj_agopen(namePtr, graphtype));
+ }
+ }
+
+
+ ///
+ /// A GraphvizContext is used to store various layout
+ /// information that is independent of a particular graph and
+ /// its attributes. It holds the data associated with plugins,
+ /// parsed - command lines, script engines, and anything else
+ /// with a scope potentially larger than one graph, up to the
+ /// scope of the application. In addition, it maintains lists of
+ /// the available layout algorithms and renderers; it also
+ /// records the most recent layout algorithm applied to a graph.
+ /// It can be used to specify multiple renderings of a given
+ /// graph layout into different associated files.It is also used
+ /// to store various global information used during rendering.
+ /// There should be just one GVC created for the entire
+ /// duration of an application. A single GVC value can be used
+ /// with multiple graphs, though with only one graph at a
+ /// time. In addition, if gvLayout() was invoked for a graph and
+ /// GVC, then gvFreeLayout() should be called before using
+ /// gvLayout() again, even on the same graph.
+ ///
+ public static IntPtr GVC { get; private set; }
+ static GraphvizFFI()
+ {
+ // We initialize the gvc here before interacting with graphviz
+ // https://gitlab.com/graphviz/graphviz/-/issues/2434
+ GVC = GvContext();
+ }
+
+}
diff --git a/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs b/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs
new file mode 100644
index 00000000..c6e7c5e7
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Constants;
+
+internal static class GraphvizLibLinux
+{
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agclose(IntPtr graph);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agcontains(IntPtr graph, IntPtr obj);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agcopyattr(IntPtr from, IntPtr to);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agdelete(IntPtr graph, IntPtr item);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstedge(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstin(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstnode(IntPtr graph);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstout(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstsubg(IntPtr graph);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agget(IntPtr obj, IntPtr name);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisdirected(IntPtr ptr);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisstrict(IntPtr ptr);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisundirected(IntPtr ptr);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnameof(IntPtr obj);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnode(IntPtr graph, IntPtr name, int create);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtin(IntPtr graph, IntPtr edge);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtnode(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtout(IntPtr graph, IntPtr edge);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtsubg(IntPtr graph);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agparent(IntPtr obj);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agroot(IntPtr obj);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agset(IntPtr obj, IntPtr name, IntPtr value);
+ [DllImport(CGraphLibNameLinux, SetLastError = false, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create);
+
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create);
+ [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create);
+
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr gvContext();
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvFreeContext(IntPtr gvc);
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvFreeLayout(IntPtr gvc, IntPtr graph);
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine);
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out);
+ [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename);
+}
\ No newline at end of file
diff --git a/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs b/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs
new file mode 100644
index 00000000..8717ca30
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Constants;
+
+internal static class GraphvizLibWindows
+{
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agclose(IntPtr graph);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agcontains(IntPtr graph, IntPtr obj);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agcopyattr(IntPtr from, IntPtr to);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agdelete(IntPtr graph, IntPtr item);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstedge(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstin(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstnode(IntPtr graph);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstout(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agfstsubg(IntPtr graph);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agget(IntPtr obj, IntPtr name);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisdirected(IntPtr ptr);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisstrict(IntPtr ptr);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int agisundirected(IntPtr ptr);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnameof(IntPtr obj);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnode(IntPtr graph, IntPtr name, int create);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtin(IntPtr graph, IntPtr edge);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtnode(IntPtr graph, IntPtr node);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtout(IntPtr graph, IntPtr edge);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agnxtsubg(IntPtr graph);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agparent(IntPtr obj);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agroot(IntPtr obj);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void agset(IntPtr obj, IntPtr name, IntPtr value);
+ [DllImport(CGraphLibNameWindows, SetLastError = false, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create);
+
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create);
+ [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create);
+
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr gvContext();
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvFreeContext(IntPtr gvc);
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvFreeLayout(IntPtr gvc, IntPtr graph);
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine);
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out);
+ [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename);
+}
\ No newline at end of file
diff --git a/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs b/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs
new file mode 100644
index 00000000..53478c23
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Marshaling;
+using static Constants;
+
+internal static class GraphvizWrapperLib
+{
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void clone_attribute_declarations(IntPtr graphfrom, IntPtr graphto);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void convert_to_undirected(IntPtr graph);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr edge_label(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr graph_label(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr label_fontname(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double label_fontsize(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double label_height(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr label_text(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double label_width(IntPtr label);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double label_x(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double label_y(IntPtr label);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double node_height(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr node_label(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double node_width(IntPtr node);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double node_x(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double node_y(IntPtr node);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static extern bool rj_ageqedge(IntPtr edge1, IntPtr edge2);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_aghead(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agmemread(IntPtr input);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agmemwrite(IntPtr graph);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agmkin(IntPtr edge);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agmkout(IntPtr edge);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agtail(IntPtr node);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_sym_key(IntPtr sym);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr rj_agopen(IntPtr name, int graphtype);
+
+ // Accessors for xdot
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern UIntPtr get_cnt(IntPtr xdot);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_ops(IntPtr xdot);
+
+ // Accessors for xdot_image
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_name_image(IntPtr img);
+ public static string? GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_pos(IntPtr img);
+
+ // Accessors for xdot_font
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_size(IntPtr font);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_name_font(IntPtr font);
+ public static string? GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false);
+
+ // Accessors for xdot_op
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern XDotKind get_kind(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_ellipse(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_polygon(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_polyline(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_bezier(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_text(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_image(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_color(IntPtr op);
+ public static string? GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_grad_color(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_font(IntPtr op);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_style(IntPtr op);
+ public static string? GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern uint get_fontchar(IntPtr op);
+
+ // Accessors for xdot_color
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern XDotGradType get_type(IntPtr clr);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_clr(IntPtr clr);
+ public static string? GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_ling(IntPtr clr);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_ring(IntPtr clr);
+
+ // Accessors for xdot_text
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x_text(IntPtr txt);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y_text(IntPtr txt);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern TextAlign get_align(IntPtr txt);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_width(IntPtr txt);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_text_str(IntPtr txt);
+ public static string? GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false);
+
+ // Accessors for xdot_linear_grad
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x0_ling(IntPtr ling);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y0_ling(IntPtr ling);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x1_ling(IntPtr ling);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y1_ling(IntPtr ling);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int get_n_stops_ling(IntPtr ling);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_stops_ling(IntPtr ling);
+
+ // Accessors for xdot_radial_grad
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x0_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y0_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_r0_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x1_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y1_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_r1_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int get_n_stops_ring(IntPtr ring);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_stops_ring(IntPtr ring);
+
+ // Accessors for xdot_color_stop
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern float get_frac(IntPtr stop);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr get_color_stop(IntPtr stop);
+ public static string? GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false);
+
+ // Accessors for xdot_polyline
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern UIntPtr get_cnt_polyline(IntPtr polyline);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_pts_polyline(IntPtr polyline);
+
+ // Accessors for xdot_point
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x_point(IntPtr point);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y_point(IntPtr point);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_z_point(IntPtr point);
+
+ // Accessors for xdot_rect
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_x_rect(IntPtr rect);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_y_rect(IntPtr rect);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_w_rect(IntPtr rect);
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern double get_h_rect(IntPtr rect);
+
+ // Index function for xdot_color_stop array
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_color_stop_at_index(IntPtr stops, int index);
+
+ // Index function for xdot_op array
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_op_at_index(IntPtr ops, int index);
+
+ // Index function for xdot_pt array
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr get_pt_at_index(IntPtr pts, int index);
+}
\ No newline at end of file
diff --git a/Rubjerg.Graphviz/Marshaling.cs b/Rubjerg.Graphviz/FFI/Marshaling.cs
similarity index 88%
rename from Rubjerg.Graphviz/Marshaling.cs
rename to Rubjerg.Graphviz/FFI/Marshaling.cs
index 8a8c6eab..73b1cfe0 100644
--- a/Rubjerg.Graphviz/Marshaling.cs
+++ b/Rubjerg.Graphviz/FFI/Marshaling.cs
@@ -3,7 +3,7 @@
using System.Runtime.InteropServices;
using System.Text;
-namespace Rubjerg.Graphviz;
+namespace Rubjerg.Graphviz.FFI;
using static Constants;
@@ -89,8 +89,6 @@ public static T MarshalToUtf8(string? s, Func continuation, bool f
return byteArray;
}
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
private static extern void free_str(IntPtr ptr);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void agstrfree(IntPtr root, IntPtr str);
}
diff --git a/Rubjerg.Graphviz/NativeMethods.cs b/Rubjerg.Graphviz/FFI/NativeMethods.cs
similarity index 93%
rename from Rubjerg.Graphviz/NativeMethods.cs
rename to Rubjerg.Graphviz/FFI/NativeMethods.cs
index 1516af79..78561ec4 100644
--- a/Rubjerg.Graphviz/NativeMethods.cs
+++ b/Rubjerg.Graphviz/FFI/NativeMethods.cs
@@ -2,7 +2,7 @@
using System.IO;
using System.Runtime.InteropServices;
-namespace Rubjerg.Graphviz;
+namespace Rubjerg.Graphviz.FFI;
public static class NativeMethods
{
diff --git a/Rubjerg.Graphviz/FFI/Platform.cs b/Rubjerg.Graphviz/FFI/Platform.cs
new file mode 100644
index 00000000..a2a4791c
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/Platform.cs
@@ -0,0 +1,9 @@
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+public static class Platform
+{
+ public static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ public static bool IsLinux { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+}
diff --git a/Rubjerg.Graphviz/FFI/TestLib.cs b/Rubjerg.Graphviz/FFI/TestLib.cs
new file mode 100644
index 00000000..17217f96
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/TestLib.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Marshaling;
+using static Constants;
+
+internal static class TestLib
+{
+ public enum TestEnum
+ {
+ Val1, Val2, Val3, Val4, Val5
+ }
+
+ // .NET uses UnmanagedType.Bool by default for P/Invoke, but our C++ code uses UnmanagedType.U1
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static extern bool echobool([MarshalAs(UnmanagedType.U1)] bool arg);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int echoint(int arg);
+
+ public static string? EchoString(string? str)
+ {
+ return MarshalToUtf8(str, ptr =>
+ {
+ var returnPtr = TestLib.echo_string(ptr);
+ // echo_string gives us ownership over the string, which means that we have to free it.
+ return MarshalFromUtf8(returnPtr, true);
+ });
+ }
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern TestEnum echo_enum(TestEnum e);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int return1();
+ public static string? ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false);
+ public static string? ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false);
+ public static string? ReturnHello() => MarshalFromUtf8(return_hello(), false);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int return_1();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern TestEnum return_enum1();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern TestEnum return_enum2();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern TestEnum return_enum5();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static extern bool return_false();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static extern bool return_true();
+
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr echo_string(IntPtr str);
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr return_copyright();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr return_empty_string();
+ [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr return_hello();
+}
\ No newline at end of file
diff --git a/Rubjerg.Graphviz/FFI/XDotFFI.cs b/Rubjerg.Graphviz/FFI/XDotFFI.cs
new file mode 100644
index 00000000..ea4d6b37
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/XDotFFI.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Marshaling;
+using static Platform;
+
+internal static class XDotFFI
+{
+ public static IntPtr ParseXDot(string xdotString)
+ {
+ return MarshalToUtf8(xdotString, s => IsWindows ? XDotLibWindows.parseXDot(s) : XDotLibLinux.parseXDot(s));
+ }
+
+ public static void FreeXDot(IntPtr xdotptr)
+ {
+ if (IsWindows)
+ XDotLibWindows.freeXDot(xdotptr);
+ else
+ XDotLibLinux.freeXDot(xdotptr);
+ }
+}
diff --git a/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs b/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs
new file mode 100644
index 00000000..cbf22a67
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Constants;
+
+///
+/// See https://graphviz.org/docs/outputs/canon/#xdot
+///
+internal static class XDotLibLinux
+{
+ [DllImport(XDotLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr parseXDot(IntPtr xdotString);
+
+ [DllImport(XDotLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void freeXDot(IntPtr xdotptr);
+}
diff --git a/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs b/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs
new file mode 100644
index 00000000..7aeda9fa
--- /dev/null
+++ b/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Rubjerg.Graphviz.FFI;
+
+using static Constants;
+
+///
+/// See https://graphviz.org/docs/outputs/canon/#xdot
+///
+internal static class XDotLibWindows
+{
+ [DllImport(XDotLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr parseXDot(IntPtr xdotString);
+
+ [DllImport(XDotLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void freeXDot(IntPtr xdotptr);
+}
diff --git a/Rubjerg.Graphviz/XDotParser.cs b/Rubjerg.Graphviz/FFI/XDotParser.cs
similarity index 61%
rename from Rubjerg.Graphviz/XDotParser.cs
rename to Rubjerg.Graphviz/FFI/XDotParser.cs
index b8b9ea95..a29159ff 100644
--- a/Rubjerg.Graphviz/XDotParser.cs
+++ b/Rubjerg.Graphviz/FFI/XDotParser.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
-namespace Rubjerg.Graphviz;
+namespace Rubjerg.Graphviz.FFI;
// These internal types are only used for marshaling
// We replace them with more idiomatic types
@@ -43,7 +43,7 @@ public static List ParseXDot(string xdotString, CoordinateSystem coordin
{
if (xdot != IntPtr.Zero)
{
- XDotFFI.freeXDot(xdot);
+ XDotFFI.FreeXDot(xdot);
}
}
}
@@ -55,78 +55,78 @@ internal static List TranslateXDot(IntPtr xdotPtr, CoordinateSystem coor
XDot xdot = new XDot
{
- Count = (int)XDotFFI.get_cnt(xdotPtr)
+ Count = (int)GraphvizWrapperLib.get_cnt(xdotPtr)
};
// Translate the array of XDotOps
int count = xdot.Count;
xdot.Ops = new XDotOp[count];
- var opsPtr = XDotFFI.get_ops(xdotPtr);
+ var opsPtr = GraphvizWrapperLib.get_ops(xdotPtr);
var activeFont = Font.Default;
var activeFontChar = FontChar.None;
for (int i = 0; i < count; ++i)
{
- IntPtr xdotOpPtr = XDotFFI.get_op_at_index(opsPtr, i);
- var kind = XDotFFI.get_kind(xdotOpPtr);
+ IntPtr xdotOpPtr = GraphvizWrapperLib.get_op_at_index(opsPtr, i);
+ var kind = GraphvizWrapperLib.get_kind(xdotOpPtr);
switch (kind)
{
case XDotKind.FilledEllipse:
- xdot.Ops[i] = new XDotOp.FilledEllipse(TranslateEllipse(XDotFFI.get_ellipse(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.FilledEllipse(TranslateEllipse(GraphvizWrapperLib.get_ellipse(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.UnfilledEllipse:
- xdot.Ops[i] = new XDotOp.UnfilledEllipse(TranslateEllipse(XDotFFI.get_ellipse(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.UnfilledEllipse(TranslateEllipse(GraphvizWrapperLib.get_ellipse(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.FilledPolygon:
- xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.UnfilledPolygon:
- xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.FilledBezier:
- xdot.Ops[i] = new XDotOp.FilledBezier(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.FilledBezier(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.UnfilledBezier:
- xdot.Ops[i] = new XDotOp.UnfilledBezier(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.UnfilledBezier(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.Polyline:
- xdot.Ops[i] = new XDotOp.PolyLine(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.PolyLine(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.Text:
- xdot.Ops[i] = new XDotOp.Text(TranslateText(XDotFFI.get_text(xdotOpPtr), activeFont, activeFontChar)
+ xdot.Ops[i] = new XDotOp.Text(TranslateText(GraphvizWrapperLib.get_text(xdotOpPtr), activeFont, activeFontChar)
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.FillColor:
- xdot.Ops[i] = new XDotOp.FillColor(new Color.Uniform(XDotFFI.GetColor(xdotOpPtr)!));
+ xdot.Ops[i] = new XDotOp.FillColor(new Color.Uniform(GraphvizWrapperLib.GetColor(xdotOpPtr)!));
break;
case XDotKind.PenColor:
- xdot.Ops[i] = new XDotOp.PenColor(new Color.Uniform(XDotFFI.GetColor(xdotOpPtr)!));
+ xdot.Ops[i] = new XDotOp.PenColor(new Color.Uniform(GraphvizWrapperLib.GetColor(xdotOpPtr)!));
break;
case XDotKind.GradFillColor:
- xdot.Ops[i] = new XDotOp.FillColor(TranslateGradColor(XDotFFI.get_grad_color(xdotOpPtr)));
+ xdot.Ops[i] = new XDotOp.FillColor(TranslateGradColor(GraphvizWrapperLib.get_grad_color(xdotOpPtr)));
break;
case XDotKind.GradPenColor:
- xdot.Ops[i] = new XDotOp.PenColor(TranslateGradColor(XDotFFI.get_grad_color(xdotOpPtr)));
+ xdot.Ops[i] = new XDotOp.PenColor(TranslateGradColor(GraphvizWrapperLib.get_grad_color(xdotOpPtr)));
break;
case XDotKind.Font:
- activeFont = TranslateFont(XDotFFI.get_font(xdotOpPtr));
+ activeFont = TranslateFont(GraphvizWrapperLib.get_font(xdotOpPtr));
break;
case XDotKind.Style:
- xdot.Ops[i] = new XDotOp.Style(XDotFFI.GetStyle(xdotOpPtr)!);
+ xdot.Ops[i] = new XDotOp.Style(GraphvizWrapperLib.GetStyle(xdotOpPtr)!);
break;
case XDotKind.Image:
- xdot.Ops[i] = new XDotOp.Image(TranslateImage(XDotFFI.get_image(xdotOpPtr))
+ xdot.Ops[i] = new XDotOp.Image(TranslateImage(GraphvizWrapperLib.get_image(xdotOpPtr))
.ForCoordSystem(coordinateSystem, maxY));
break;
case XDotKind.FontChar:
- activeFontChar = TranslateFontChar(XDotFFI.get_fontchar(xdotOpPtr));
+ activeFontChar = TranslateFontChar(GraphvizWrapperLib.get_fontchar(xdotOpPtr));
break;
default:
throw new ArgumentException($"Unexpected XDotOp.Kind: {kind}");
@@ -145,8 +145,8 @@ private static ImageInfo TranslateImage(IntPtr imagePtr)
{
ImageInfo image = new ImageInfo
(
- Position: TranslateRect(XDotFFI.get_pos(imagePtr)),
- Name: XDotFFI.GetNameImage(imagePtr)
+ Position: TranslateRect(GraphvizWrapperLib.get_pos(imagePtr)),
+ Name: GraphvizWrapperLib.GetNameImage(imagePtr)
);
return image;
@@ -156,8 +156,8 @@ private static Font TranslateFont(IntPtr fontPtr)
{
Font font = new Font
(
- Size: XDotFFI.get_size(fontPtr),
- Name: XDotFFI.GetNameFont(fontPtr)!
+ Size: GraphvizWrapperLib.get_size(fontPtr),
+ Name: GraphvizWrapperLib.GetNameFont(fontPtr)!
);
return font;
@@ -167,10 +167,10 @@ private static RectangleD TranslateEllipse(IntPtr ellipsePtr)
{
RectangleD ellipse = RectangleD.Create
(
- XDotFFI.get_x_rect(ellipsePtr),
- XDotFFI.get_y_rect(ellipsePtr),
- XDotFFI.get_w_rect(ellipsePtr),
- XDotFFI.get_h_rect(ellipsePtr)
+ GraphvizWrapperLib.get_x_rect(ellipsePtr),
+ GraphvizWrapperLib.get_y_rect(ellipsePtr),
+ GraphvizWrapperLib.get_w_rect(ellipsePtr),
+ GraphvizWrapperLib.get_h_rect(ellipsePtr)
);
return ellipse;
@@ -178,15 +178,15 @@ private static RectangleD TranslateEllipse(IntPtr ellipsePtr)
private static Color TranslateGradColor(IntPtr colorPtr)
{
- var type = XDotFFI.get_type(colorPtr);
+ var type = GraphvizWrapperLib.get_type(colorPtr);
switch (type)
{
case XDotGradType.None:
- return new Color.Uniform(XDotFFI.GetClr(colorPtr)!);
+ return new Color.Uniform(GraphvizWrapperLib.GetClr(colorPtr)!);
case XDotGradType.Linear:
- return new Color.Linear(TranslateLinearGrad(XDotFFI.get_ling(colorPtr)));
+ return new Color.Linear(TranslateLinearGrad(GraphvizWrapperLib.get_ling(colorPtr)));
case XDotGradType.Radial:
- return new Color.Radial(TranslateRadialGrad(XDotFFI.get_ring(colorPtr)));
+ return new Color.Radial(TranslateRadialGrad(GraphvizWrapperLib.get_ring(colorPtr)));
default:
throw new ArgumentException($"Unexpected XDotColor.Type: {type}");
}
@@ -194,19 +194,19 @@ private static Color TranslateGradColor(IntPtr colorPtr)
private static LinearGradient TranslateLinearGrad(IntPtr lingPtr)
{
- int count = XDotFFI.get_n_stops_ling(lingPtr);
+ int count = GraphvizWrapperLib.get_n_stops_ling(lingPtr);
LinearGradient linearGrad = new LinearGradient
(
- Point0: new PointD(XDotFFI.get_x0_ling(lingPtr), XDotFFI.get_y0_ling(lingPtr)),
- Point1: new PointD(XDotFFI.get_x1_ling(lingPtr), XDotFFI.get_y1_ling(lingPtr)),
+ Point0: new PointD(GraphvizWrapperLib.get_x0_ling(lingPtr), GraphvizWrapperLib.get_y0_ling(lingPtr)),
+ Point1: new PointD(GraphvizWrapperLib.get_x1_ling(lingPtr), GraphvizWrapperLib.get_y1_ling(lingPtr)),
Stops: new ColorStop[count]
);
// Translate the array of ColorStops
- var stopsPtr = XDotFFI.get_stops_ling(lingPtr);
+ var stopsPtr = GraphvizWrapperLib.get_stops_ling(lingPtr);
for (int i = 0; i < count; ++i)
{
- IntPtr colorStopPtr = XDotFFI.get_color_stop_at_index(stopsPtr, i);
+ IntPtr colorStopPtr = GraphvizWrapperLib.get_color_stop_at_index(stopsPtr, i);
linearGrad.Stops[i] = TranslateColorStop(colorStopPtr);
}
@@ -215,21 +215,21 @@ private static LinearGradient TranslateLinearGrad(IntPtr lingPtr)
private static RadialGradient TranslateRadialGrad(IntPtr ringPtr)
{
- int count = XDotFFI.get_n_stops_ring(ringPtr);
+ int count = GraphvizWrapperLib.get_n_stops_ring(ringPtr);
RadialGradient radialGrad = new RadialGradient
(
- Point0: new PointD(XDotFFI.get_x0_ring(ringPtr), XDotFFI.get_y0_ring(ringPtr)),
- Point1: new PointD(XDotFFI.get_x1_ring(ringPtr), XDotFFI.get_y1_ring(ringPtr)),
- Radius0: XDotFFI.get_r0_ring(ringPtr),
- Radius1: XDotFFI.get_r1_ring(ringPtr),
+ Point0: new PointD(GraphvizWrapperLib.get_x0_ring(ringPtr), GraphvizWrapperLib.get_y0_ring(ringPtr)),
+ Point1: new PointD(GraphvizWrapperLib.get_x1_ring(ringPtr), GraphvizWrapperLib.get_y1_ring(ringPtr)),
+ Radius0: GraphvizWrapperLib.get_r0_ring(ringPtr),
+ Radius1: GraphvizWrapperLib.get_r1_ring(ringPtr),
Stops: new ColorStop[count]
);
// Translate the array of ColorStops
- var stopsPtr = XDotFFI.get_stops_ring(ringPtr);
+ var stopsPtr = GraphvizWrapperLib.get_stops_ring(ringPtr);
for (int i = 0; i < count; ++i)
{
- IntPtr colorStopPtr = XDotFFI.get_color_stop_at_index(stopsPtr, i);
+ IntPtr colorStopPtr = GraphvizWrapperLib.get_color_stop_at_index(stopsPtr, i);
radialGrad.Stops[i] = TranslateColorStop(colorStopPtr);
}
@@ -240,8 +240,8 @@ private static ColorStop TranslateColorStop(IntPtr stopPtr)
{
ColorStop colorStop = new ColorStop
(
- Frac: XDotFFI.get_frac(stopPtr),
- HtmlColor: XDotFFI.GetColorStop(stopPtr)!
+ Frac: GraphvizWrapperLib.get_frac(stopPtr),
+ HtmlColor: GraphvizWrapperLib.GetColorStop(stopPtr)!
);
return colorStop;
@@ -249,14 +249,14 @@ private static ColorStop TranslateColorStop(IntPtr stopPtr)
private static PointD[] TranslatePolyline(IntPtr polylinePtr)
{
- int count = (int)XDotFFI.get_cnt_polyline(polylinePtr);
+ int count = (int)GraphvizWrapperLib.get_cnt_polyline(polylinePtr);
var points = new PointD[count];
// Translate the array of Points
- var pointsPtr = XDotFFI.get_pts_polyline(polylinePtr);
+ var pointsPtr = GraphvizWrapperLib.get_pts_polyline(polylinePtr);
for (int i = 0; i < count; ++i)
{
- IntPtr pointPtr = XDotFFI.get_pt_at_index(pointsPtr, i);
+ IntPtr pointPtr = GraphvizWrapperLib.get_pt_at_index(pointsPtr, i);
points[i] = TranslatePoint(pointPtr);
}
@@ -267,8 +267,8 @@ private static PointD TranslatePoint(IntPtr pointPtr)
{
var point = new PointD
(
- X: XDotFFI.get_x_point(pointPtr),
- Y: XDotFFI.get_y_point(pointPtr)
+ X: GraphvizWrapperLib.get_x_point(pointPtr),
+ Y: GraphvizWrapperLib.get_y_point(pointPtr)
);
return point;
@@ -278,10 +278,10 @@ private static RectangleD TranslateRect(IntPtr rectPtr)
{
var rect = RectangleD.Create
(
- x: XDotFFI.get_x_rect(rectPtr),
- y: XDotFFI.get_y_rect(rectPtr),
- width: XDotFFI.get_w_rect(rectPtr),
- height: XDotFFI.get_h_rect(rectPtr)
+ x: GraphvizWrapperLib.get_x_rect(rectPtr),
+ y: GraphvizWrapperLib.get_y_rect(rectPtr),
+ width: GraphvizWrapperLib.get_w_rect(rectPtr),
+ height: GraphvizWrapperLib.get_h_rect(rectPtr)
);
return rect;
@@ -291,10 +291,10 @@ private static TextInfo TranslateText(IntPtr txtPtr, Font activeFont, FontChar a
{
TextInfo text = new TextInfo
(
- new PointD(XDotFFI.get_x_text(txtPtr), XDotFFI.get_y_text(txtPtr)),
- XDotFFI.get_align(txtPtr),
- XDotFFI.get_width(txtPtr),
- XDotFFI.GetTextStr(txtPtr)!,
+ new PointD(GraphvizWrapperLib.get_x_text(txtPtr), GraphvizWrapperLib.get_y_text(txtPtr)),
+ GraphvizWrapperLib.get_align(txtPtr),
+ GraphvizWrapperLib.get_width(txtPtr),
+ GraphvizWrapperLib.GetTextStr(txtPtr)!,
activeFont,
activeFontChar,
CoordinateSystem.BottomLeft
diff --git a/Rubjerg.Graphviz/ForeignFunctionInterface.cs b/Rubjerg.Graphviz/ForeignFunctionInterface.cs
deleted file mode 100644
index fd15473f..00000000
--- a/Rubjerg.Graphviz/ForeignFunctionInterface.cs
+++ /dev/null
@@ -1,725 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Rubjerg.Graphviz;
-
-using static Marshaling;
-using static Constants;
-
-///
-/// Graphviz is thread unsafe, so we wrap all function calls inside a lock to make sure we don't run into
-/// issues caused by multiple threads accessing the graphviz datastructures (like the GC executing a destructor).
-///
-internal static class ForeignFunctionInterface
-{
- private static readonly object _mutex = new object();
-
- public static IntPtr GvContext()
- {
- lock (_mutex)
- {
- return gvContext();
- }
- }
- public static int GvFreeContext(IntPtr gvc)
- {
- lock (_mutex)
- {
- return gvFreeContext(gvc);
- }
- }
- public static int GvLayout(IntPtr gvc, IntPtr graph, string engine)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(engine, enginePtr => gvLayout(gvc, graph, enginePtr));
- }
- }
- public static int GvFreeLayout(IntPtr gvc, IntPtr graph)
- {
- lock (_mutex)
- {
- return gvFreeLayout(gvc, graph);
- }
- }
- public static int GvRender(IntPtr gvc, IntPtr graph, string? format, IntPtr @out)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(format, formatPtr => gvRender(gvc, graph, formatPtr, @out));
- }
- }
- public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string? format, string? filename)
- {
- lock (_mutex)
- {
- return
- MarshalToUtf8(format, formatPtr =>
- MarshalToUtf8(filename, filenamePtr =>
- gvRenderFilename(gvc, graph, formatPtr, filenamePtr)));
- }
- }
- public static IntPtr Agnode(IntPtr graph, string? name, int create)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(name, namePtr => agnode(graph, namePtr, create));
- }
- }
- public static int Agdegree(IntPtr graph, IntPtr node, int inset, int outset)
- {
- lock (_mutex)
- {
- return agdegree(graph, node, inset, outset);
- }
- }
- public static IntPtr Agfstout(IntPtr graph, IntPtr node)
- {
- lock (_mutex)
- {
- return agfstout(graph, node);
- }
- }
- public static IntPtr Agnxtout(IntPtr graph, IntPtr edge)
- {
- lock (_mutex)
- {
- return agnxtout(graph, edge);
- }
- }
- public static IntPtr Agfstin(IntPtr graph, IntPtr node)
- {
- lock (_mutex)
- {
- return agfstin(graph, node);
- }
- }
- public static IntPtr Agnxtin(IntPtr graph, IntPtr edge)
- {
- lock (_mutex)
- {
- return agnxtin(graph, edge);
- }
- }
- public static IntPtr Agfstedge(IntPtr graph, IntPtr node)
- {
- lock (_mutex)
- {
- return agfstedge(graph, node);
- }
- }
- public static IntPtr Agnxtedge(IntPtr graph, IntPtr edge, IntPtr node)
- {
- lock (_mutex)
- {
- return agnxtedge(graph, edge, node);
- }
- }
- public static void Agattr(IntPtr graph, int type, string name, string deflt)
- {
- lock (_mutex)
- {
- MarshalToUtf8(deflt, defltPtr =>
- MarshalToUtf8(name, namePtr =>
- agattr(graph, type, namePtr, defltPtr)));
- }
- }
- public static void AgattrHtml(IntPtr graph, int type, string name, string deflt)
- {
- lock (_mutex)
- {
- MarshalToUtf8(name, namePtr =>
- MarshalToUtf8(deflt, defltPtr =>
- {
- var htmlPtr = agstrdup_html(agroot(graph), defltPtr);
- agattr(graph, type, namePtr, htmlPtr);
- }));
- }
- }
-
- public static void Agset(IntPtr obj, string name, string value)
- {
- lock (_mutex)
- {
- MarshalToUtf8(name, namePtr =>
- MarshalToUtf8(value, valuePtr =>
- agset(obj, namePtr, valuePtr)));
- }
- }
-
- public static void AgsetHtml(IntPtr obj, string name, string value)
- {
- lock (_mutex)
- {
- MarshalToUtf8(name, namePtr =>
- MarshalToUtf8(value, valuePtr =>
- {
- var htmlPtr = agstrdup_html(agroot(obj), valuePtr);
- agset(obj, namePtr, htmlPtr);
- }));
- }
- }
-
- public static void Agsafeset(IntPtr obj, string name, string? val, string? deflt)
- {
- lock (_mutex)
- {
- MarshalToUtf8(name, namePtr =>
- MarshalToUtf8(val, valPtr =>
- MarshalToUtf8(deflt, defltPtr =>
- agsafeset(obj, namePtr, valPtr, defltPtr))));
- }
- }
- public static void AgsafesetHtml(IntPtr obj, string name, string? val, string? deflt)
- {
- lock (_mutex)
- {
- MarshalToUtf8(name, namePtr =>
- MarshalToUtf8(val, valPtr =>
- MarshalToUtf8(deflt, defltPtr =>
- {
- var htmlPtr = agstrdup_html(agroot(obj), defltPtr);
- agsafeset(obj, namePtr, valPtr, htmlPtr);
- })));
- }
- }
- public static IntPtr Agroot(IntPtr obj)
- {
- lock (_mutex)
- {
- return agroot(obj);
- }
- }
- public static IntPtr Agnxtattr(IntPtr obj, int kind, IntPtr attribute)
- {
- lock (_mutex)
- {
- return agnxtattr(obj, kind, attribute);
- }
- }
- public static int Agcopyattr(IntPtr from, IntPtr to)
- {
- lock (_mutex)
- {
- return agcopyattr(from, to);
- }
- }
- public static bool Ageqedge(IntPtr edge1, IntPtr edge2)
- {
- lock (_mutex)
- {
- return rj_ageqedge(edge1, edge2);
- }
- }
- public static IntPtr Agtail(IntPtr node)
- {
- lock (_mutex)
- {
- return rj_agtail(node);
- }
- }
- public static IntPtr Aghead(IntPtr node)
- {
- lock (_mutex)
- {
- return rj_aghead(node);
- }
- }
- public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string? name, int create)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(name, namePtr => agedge(graph, tail, head, namePtr, create));
- }
- }
- public static IntPtr Agmkin(IntPtr edge)
- {
- lock (_mutex)
- {
- return rj_agmkin(edge);
- }
- }
- public static IntPtr Agmkout(IntPtr edge)
- {
- lock (_mutex)
- {
- return rj_agmkout(edge);
- }
- }
- public static IntPtr Agparent(IntPtr obj)
- {
- lock (_mutex)
- {
- return agparent(obj);
- }
- }
- public static int Agclose(IntPtr graph)
- {
- lock (_mutex)
- {
- return agclose(graph);
- }
- }
- public static int Agdelete(IntPtr graph, IntPtr item)
- {
- lock (_mutex)
- {
- return agdelete(graph, item);
- }
- }
- public static IntPtr Agfstnode(IntPtr graph)
- {
- lock (_mutex)
- {
- return agfstnode(graph);
- }
- }
- public static IntPtr Agnxtnode(IntPtr graph, IntPtr node)
- {
- lock (_mutex)
- {
- return agnxtnode(graph, node);
- }
- }
- public static int Agcontains(IntPtr graph, IntPtr obj)
- {
- lock (_mutex)
- {
- return agcontains(graph, obj);
- }
- }
- public static IntPtr Agsubg(IntPtr graph, string? name, int create)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(name, namePtr => agsubg(graph, namePtr, create));
- }
- }
- public static IntPtr Agfstsubg(IntPtr graph)
- {
- lock (_mutex)
- {
- return agfstsubg(graph);
- }
- }
- public static IntPtr Agnxtsubg(IntPtr graph)
- {
- lock (_mutex)
- {
- return agnxtsubg(graph);
- }
- }
- public static int Agisstrict(IntPtr ptr)
- {
- lock (_mutex)
- {
- return agisstrict(ptr);
- }
- }
- public static int Agisdirected(IntPtr ptr)
- {
- lock (_mutex)
- {
- return agisdirected(ptr);
- }
- }
- public static int Agisundirected(IntPtr ptr)
- {
- lock (_mutex)
- {
- return agisundirected(ptr);
- }
- }
- public static IntPtr Agsubedge(IntPtr graph, IntPtr edge, int create)
- {
- lock (_mutex)
- {
- return agsubedge(graph, edge, create);
- }
- }
- public static IntPtr Agsubnode(IntPtr graph, IntPtr node, int create)
- {
- lock (_mutex)
- {
- return agsubnode(graph, node, create);
- }
- }
- public static IntPtr EdgeLabel(IntPtr node)
- {
- lock (_mutex)
- {
- return edge_label(node);
- }
- }
- public static string? Rjagmemwrite(IntPtr graph)
- {
- lock (_mutex)
- {
- var strPtr = rj_agmemwrite(graph);
- return MarshalFromUtf8(strPtr, true);
- }
- }
- public static IntPtr GraphLabel(IntPtr node)
- {
- lock (_mutex)
- {
- return graph_label(node);
- }
- }
- public static string? Agget(IntPtr obj, string name)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(name, namePtr => MarshalFromUtf8(agget(obj, namePtr), false));
- }
- }
- public static string? Rjagnameof(IntPtr obj)
- {
- lock (_mutex)
- {
- return MarshalFromUtf8(agnameof(obj), false);
- }
- }
- public static void CloneAttributeDeclarations(IntPtr graphfrom, IntPtr graphto)
- {
- lock (_mutex)
- {
- clone_attribute_declarations(graphfrom, graphto);
- }
- }
- public static string? ImsymKey(IntPtr sym)
- {
- lock (_mutex)
- {
- return MarshalFromUtf8(rj_sym_key(sym), false);
- }
- }
- public static double LabelX(IntPtr label)
- {
- lock (_mutex)
- {
- return label_x(label);
- }
- }
- public static double LabelY(IntPtr label)
- {
- lock (_mutex)
- {
- return label_y(label);
- }
- }
- public static double LabelWidth(IntPtr label)
- {
- lock (_mutex)
- {
- return label_width(label);
- }
- }
- public static double LabelHeight(IntPtr label)
- {
- lock (_mutex)
- {
- return label_height(label);
- }
- }
- public static string? LabelText(IntPtr label)
- {
- lock (_mutex)
- {
- return MarshalFromUtf8(label_text(label), false);
- }
- }
- public static double LabelFontsize(IntPtr label)
- {
- lock (_mutex)
- {
- return label_fontsize(label);
- }
- }
- public static string? LabelFontname(IntPtr label)
- {
- lock (_mutex)
- {
- return MarshalFromUtf8(label_fontname(label), false);
- }
- }
- public static double NodeX(IntPtr node)
- {
- lock (_mutex)
- {
- return node_x(node);
- }
- }
- public static double NodeY(IntPtr node)
- {
- lock (_mutex)
- {
- return node_y(node);
- }
- }
- public static double NodeWidth(IntPtr node)
- {
- lock (_mutex)
- {
- return node_width(node);
- }
- }
- public static double NodeHeight(IntPtr node)
- {
- lock (_mutex)
- {
- return node_height(node);
- }
- }
- public static IntPtr NodeLabel(IntPtr node)
- {
- lock (_mutex)
- {
- return node_label(node);
- }
- }
- public static void ConvertToUndirected(IntPtr graph)
- {
- lock (_mutex)
- {
- convert_to_undirected(graph);
- }
- }
- public static IntPtr Rjagmemread(string input)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(input, rj_agmemread);
- }
- }
- public static IntPtr Rjagopen(string? name, int graphtype)
- {
- lock (_mutex)
- {
- return MarshalToUtf8(name, namePtr => rj_agopen(namePtr, graphtype));
- }
- }
-
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr gvContext();
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int gvFreeContext(IntPtr gvc);
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine);
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int gvFreeLayout(IntPtr gvc, IntPtr graph);
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out);
- [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnode(IntPtr graph, IntPtr name, int create);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agfstout(IntPtr graph, IntPtr node);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtout(IntPtr graph, IntPtr edge);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agfstin(IntPtr graph, IntPtr node);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtin(IntPtr graph, IntPtr edge);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agfstedge(IntPtr graph, IntPtr node);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void agset(IntPtr obj, IntPtr name, IntPtr value);
- [DllImport(CGraphLib, SetLastError = false, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agroot(IntPtr obj);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agcopyattr(IntPtr from, IntPtr to);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agparent(IntPtr obj);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agclose(IntPtr graph);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agdelete(IntPtr graph, IntPtr item);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agfstnode(IntPtr graph);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtnode(IntPtr graph, IntPtr node);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agcontains(IntPtr graph, IntPtr obj);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agfstsubg(IntPtr graph);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnxtsubg(IntPtr graph);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agisstrict(IntPtr ptr);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agisdirected(IntPtr ptr);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern int agisundirected(IntPtr ptr);
-
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agget(IntPtr obj, IntPtr name);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agnameof(IntPtr obj);
- [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.U1)]
- private static extern bool rj_ageqedge(IntPtr edge1, IntPtr edge2);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agtail(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_aghead(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agmkin(IntPtr edge);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agmkout(IntPtr edge);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr edge_label(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agmemwrite(IntPtr graph);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr graph_label(IntPtr node);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void clone_attribute_declarations(IntPtr graphfrom, IntPtr graphto);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_sym_key(IntPtr sym);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double label_x(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double label_y(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double label_width(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double label_height(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr label_text(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double label_fontsize(IntPtr label);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr label_fontname(IntPtr label);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double node_x(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double node_y(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double node_width(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern double node_height(IntPtr node);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr node_label(IntPtr node);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern void convert_to_undirected(IntPtr graph);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agmemread(IntPtr input);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr rj_agopen(IntPtr name, int graphtype);
-
-
- ///
- /// A GraphvizContext is used to store various layout
- /// information that is independent of a particular graph and
- /// its attributes. It holds the data associated with plugins,
- /// parsed - command lines, script engines, and anything else
- /// with a scope potentially larger than one graph, up to the
- /// scope of the application. In addition, it maintains lists of
- /// the available layout algorithms and renderers; it also
- /// records the most recent layout algorithm applied to a graph.
- /// It can be used to specify multiple renderings of a given
- /// graph layout into different associated files.It is also used
- /// to store various global information used during rendering.
- /// There should be just one GVC created for the entire
- /// duration of an application. A single GVC value can be used
- /// with multiple graphs, though with only one graph at a
- /// time. In addition, if gvLayout() was invoked for a graph and
- /// GVC, then gvFreeLayout() should be called before using
- /// gvLayout() again, even on the same graph.
- ///
- public static IntPtr GVC { get; private set; }
- static ForeignFunctionInterface()
- {
- // We initialize the gvc here before interacting with graphviz
- // https://gitlab.com/graphviz/graphviz/-/issues/2434
- GVC = GvContext();
- }
-
- #region debugging and testing
-
- // .NET uses UnmanagedType.Bool by default for P/Invoke, but our C++ code uses UnmanagedType.U1
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.U1)]
- public static extern bool echobool([MarshalAs(UnmanagedType.U1)] bool arg);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.U1)]
- public static extern bool return_true();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.U1)]
- public static extern bool return_false();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern int echoint(int arg);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern int return1();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern int return_1();
-
- public enum TestEnum
- {
- Val1, Val2, Val3, Val4, Val5
- }
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern TestEnum return_enum1();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern TestEnum return_enum2();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern TestEnum return_enum5();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern TestEnum echo_enum(TestEnum e);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr echo_string(IntPtr str);
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr return_empty_string();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr return_hello();
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr return_copyright();
-
- public static string? EchoString(string? str)
- {
- return MarshalToUtf8(str, ptr =>
- {
- var returnPtr = echo_string(ptr);
- // echo_string gives us ownership over the string, which means that we have to free it.
- return MarshalFromUtf8(returnPtr, true);
- });
- }
- public static string? ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false);
- public static string? ReturnHello() => MarshalFromUtf8(return_hello(), false);
- public static string? ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false);
- #endregion
-}
diff --git a/Rubjerg.Graphviz/Graph.cs b/Rubjerg.Graphviz/Graph.cs
index 5c01880e..4e605b86 100644
--- a/Rubjerg.Graphviz/Graph.cs
+++ b/Rubjerg.Graphviz/Graph.cs
@@ -3,7 +3,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/Node.cs b/Rubjerg.Graphviz/Node.cs
index 1c7ac280..ab4e1476 100644
--- a/Rubjerg.Graphviz/Node.cs
+++ b/Rubjerg.Graphviz/Node.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/RootGraph.cs b/Rubjerg.Graphviz/RootGraph.cs
index cd9d1b10..79161cc5 100644
--- a/Rubjerg.Graphviz/RootGraph.cs
+++ b/Rubjerg.Graphviz/RootGraph.cs
@@ -1,7 +1,7 @@
using System;
using System.IO;
using System.Linq;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
index 3bfc0b4a..fe31a840 100644
--- a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
+++ b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
@@ -22,7 +22,7 @@
-
+
true
@@ -81,33 +81,33 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Rubjerg.Graphviz/SubGraph.cs b/Rubjerg.Graphviz/SubGraph.cs
index 82240497..c06acd46 100644
--- a/Rubjerg.Graphviz/SubGraph.cs
+++ b/Rubjerg.Graphviz/SubGraph.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using static Rubjerg.Graphviz.ForeignFunctionInterface;
+using static Rubjerg.Graphviz.FFI.GraphvizFFI;
namespace Rubjerg.Graphviz;
diff --git a/Rubjerg.Graphviz/XDotFFI.cs b/Rubjerg.Graphviz/XDotFFI.cs
deleted file mode 100644
index b4e85a95..00000000
--- a/Rubjerg.Graphviz/XDotFFI.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Rubjerg.Graphviz;
-
-using static Marshaling;
-using static Constants;
-
-///
-/// See https://graphviz.org/docs/outputs/canon/#xdot
-///
-internal static class XDotFFI
-{
- [DllImport(XDotLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr parseXDot(IntPtr xdotString);
- public static IntPtr ParseXDot(string xdotString) => MarshalToUtf8(xdotString, parseXDot);
-
- [DllImport(XDotLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern void freeXDot(IntPtr xdotptr);
-
- // Accessors for xdot
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern UIntPtr get_cnt(IntPtr xdot);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_ops(IntPtr xdot);
-
- // Accessors for xdot_image
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_name_image(IntPtr img);
- public static string? GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_pos(IntPtr img);
-
- // Accessors for xdot_font
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_size(IntPtr font);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_name_font(IntPtr font);
- public static string? GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false);
-
- // Accessors for xdot_op
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern XDotKind get_kind(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_ellipse(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_polygon(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_polyline(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_bezier(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_text(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_image(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_color(IntPtr op);
- public static string? GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_grad_color(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_font(IntPtr op);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_style(IntPtr op);
- public static string? GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern uint get_fontchar(IntPtr op);
-
- // Accessors for xdot_color
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern XDotGradType get_type(IntPtr clr);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_clr(IntPtr clr);
- public static string? GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_ling(IntPtr clr);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_ring(IntPtr clr);
-
- // Accessors for xdot_text
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x_text(IntPtr txt);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y_text(IntPtr txt);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern TextAlign get_align(IntPtr txt);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_width(IntPtr txt);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_text_str(IntPtr txt);
- public static string? GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false);
-
- // Accessors for xdot_linear_grad
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x0_ling(IntPtr ling);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y0_ling(IntPtr ling);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x1_ling(IntPtr ling);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y1_ling(IntPtr ling);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern int get_n_stops_ling(IntPtr ling);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_stops_ling(IntPtr ling);
-
- // Accessors for xdot_radial_grad
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x0_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y0_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_r0_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x1_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y1_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_r1_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern int get_n_stops_ring(IntPtr ring);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_stops_ring(IntPtr ring);
-
- // Accessors for xdot_color_stop
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern float get_frac(IntPtr stop);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr get_color_stop(IntPtr stop);
- public static string? GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false);
-
- // Accessors for xdot_polyline
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern UIntPtr get_cnt_polyline(IntPtr polyline);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_pts_polyline(IntPtr polyline);
-
- // Accessors for xdot_point
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x_point(IntPtr point);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y_point(IntPtr point);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_z_point(IntPtr point);
-
- // Accessors for xdot_rect
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_x_rect(IntPtr rect);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_y_rect(IntPtr rect);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_w_rect(IntPtr rect);
-
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern double get_h_rect(IntPtr rect);
-
- // Index function for xdot_color_stop array
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_color_stop_at_index(IntPtr stops, int index);
-
- // Index function for xdot_op array
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_op_at_index(IntPtr ops, int index);
-
- // Index function for xdot_pt array
- [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr get_pt_at_index(IntPtr pts, int index);
-
-}
diff --git a/justfile b/justfile
index eaa7a5fa..5819696a 100644
--- a/justfile
+++ b/justfile
@@ -7,33 +7,29 @@ default: test-all
find-msbuild:
powershell -NoProfile -Command '& { & "${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\\**\\Bin\\MSBuild.exe }'
-# Restore .NET tools
-restore-tools:
+# Restore .NET tools and main solution packages
+restore:
dotnet tool restore
-
-# Restore main solution packages
-restore: restore-tools
dotnet restore Rubjerg.Graphviz.sln
-# Build main app
-build-package: restore
+build SOLUTION:
if {{is-windows}}; then \
- MSBuild.exe Rubjerg.Graphviz.sln //p:Configuration=Release; \
+ MSBuild.exe {{SOLUTION}} //p:Configuration=Release; \
else \
- dotnet build Rubjerg.Graphviz.sln --configuration Release --no-restore; \
+ dotnet build {{SOLUTION}} --configuration Release --no-restore; \
fi
+# Build nuget package
+build-package: restore
+ just build Rubjerg.Graphviz.sln
+
# Restore test project packages
restore-tests: build-package
dotnet restore Rubjerg.Graphviz.Tests.sln
# Build test projects
build-tests: restore-tests
- if {{is-windows}}; then \
- MSBuild.exe Rubjerg.Graphviz.Tests.sln //p:Configuration=Release; \
- else \
- dotnet build Rubjerg.Graphviz.Tests.sln --configuration Release --no-restore; \
- fi
+ just build Rubjerg.Graphviz.Tests.sln
# Run unit tests for a project
test PROJECT:
@@ -50,6 +46,14 @@ test-all: build-tests
just test Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj
just test Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj
+# Run the nuget.org tests
+test-nugetorg:
+ dotnet tool restore
+ dotnet restore NugetOrgTests/Rubjerg.Graphviz.NugetOrgTests.sln
+ just build NugetOrgTests/Rubjerg.Graphviz.NugetOrgTests.sln
+ just test NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj
+ just test NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj
+
locate-nupkg GITHUB_OUTPUT:
echo "package=$(find . -name "Rubjerg.Graphviz.*.nupkg" | head -1)" >> "{{GITHUB_OUTPUT}}"