diff --git a/WireEventImplementor/Properties/launchSettings.json b/WireEventImplementor/Properties/launchSettings.json
new file mode 100644
index 0000000..ff47e2d
--- /dev/null
+++ b/WireEventImplementor/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "profiles": {
+ "WireEventImplementor": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Rhino 7\\System\\Rhino.exe",
+ "commandLineArgs": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/WireEventImplementor/WireEventImplementor.csproj b/WireEventImplementor/WireEventImplementor.csproj
new file mode 100644
index 0000000..0047f87
--- /dev/null
+++ b/WireEventImplementor/WireEventImplementor.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net48
+ 1.0
+ WireEventImplementor
+ Description of WireEventImplementor
+ .dll
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WireEventImplementor/WireInstances.cs b/WireEventImplementor/WireInstances.cs
new file mode 100644
index 0000000..ebe1c5a
--- /dev/null
+++ b/WireEventImplementor/WireInstances.cs
@@ -0,0 +1,79 @@
+using Grasshopper.GUI.Canvas;
+using Grasshopper.GUI.Canvas.Interaction;
+using Grasshopper.Kernel;
+using System;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace WireEventImplementor
+{
+ public static class WireInstances
+ {
+
+ public delegate void PreWiredEventHandler(WireStatus wireStatus);
+
+ public delegate void WiringEventHandler(WireStatus wireStatus);
+
+ public delegate void PostWiredEventHandler(WireStatus wireStatus);
+
+ ///
+ /// Invoked when your wiring cursor is floating.
+ ///
+ public static event WiringEventHandler Wiring;
+
+ ///
+ /// Invoked when your wiring cursor is on the grip.
+ ///
+ public static event PreWiredEventHandler PreWired;
+
+ ///
+ /// Invoked when your wire connects grips.
+ ///
+ public static event PostWiredEventHandler PostWired;
+
+ private static TaskCompletionSource m_source = new TaskCompletionSource();
+
+ ///
+ /// Call when canvas created.
+ ///
+ /// Applied canvas.
+ public static void SetUp(GH_Canvas canvas)
+ {
+ canvas.MouseMove += Canvas_MouseMove;
+ canvas.MouseUp += Canvas_MouseUp;
+ }
+
+ private static void Canvas_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (sender is GH_Canvas canvas && canvas.ActiveInteraction is GH_WireInteraction wireInteraction)
+ {
+ wireInteraction.GetWireParameters(out WireStatus status);
+
+ if (e.Button == MouseButtons.Left)
+ {
+ m_source = new TaskCompletionSource();
+
+ if (status.WireTarget == null)
+ Wiring?.Invoke(status);
+ else
+ {
+ PreWired?.Invoke(status);
+ m_source.SetResult(status);
+ }
+ }
+
+ canvas.Refresh();
+ }
+ }
+
+ private static async void Canvas_MouseUp(object sender, MouseEventArgs e)
+ {
+ WireStatus status = await m_source.Task;
+ m_source = new TaskCompletionSource();
+
+ PostWired?.Invoke(status);
+
+ (sender as GH_Canvas).Refresh();
+ }
+ }
+}
diff --git a/WireEventImplementor/WireStatus.cs b/WireEventImplementor/WireStatus.cs
new file mode 100644
index 0000000..e2ce356
--- /dev/null
+++ b/WireEventImplementor/WireStatus.cs
@@ -0,0 +1,31 @@
+using Grasshopper.Kernel;
+
+namespace WireEventImplementor
+{
+ public class WireStatus
+ {
+ public IGH_Param WireSource { get; }
+
+ public IGH_Param WireTarget { get; }
+
+ public LinkMode? LinkMode { get; }
+
+ public bool? IsDragFromInput { get; }
+
+ public IGH_Param PreviousSideParam => (bool)IsDragFromInput ? WireTarget : WireSource;
+
+ public IGH_Param SubsequentSideParam => (bool)IsDragFromInput ? WireSource : WireTarget;
+
+ private WireStatus()
+ {
+ }
+
+ internal WireStatus(IGH_Param wireSource, IGH_Param wireTarget, LinkMode? linkMode, bool? isDragFromInput)
+ {
+ WireSource = wireSource;
+ WireTarget = wireTarget;
+ LinkMode = linkMode;
+ IsDragFromInput = isDragFromInput;
+ }
+ }
+}
diff --git a/WireEventImplementor/WireUtil.cs b/WireEventImplementor/WireUtil.cs
new file mode 100644
index 0000000..59b768b
--- /dev/null
+++ b/WireEventImplementor/WireUtil.cs
@@ -0,0 +1,29 @@
+using Grasshopper.GUI.Canvas.Interaction;
+using Grasshopper.Kernel;
+using System;
+using System.Reflection;
+
+namespace WireEventImplementor
+{
+ public enum LinkMode
+ {
+ Replace,
+ Add,
+ Remove
+ }
+
+ public static class WireUtil
+ {
+ internal static void GetWireParameters(this GH_WireInteraction self, out WireStatus wireStatus)
+ {
+ Func getPrivateParam = (name) => typeof(GH_WireInteraction).GetField(name, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(self);
+
+ IGH_Param source = getPrivateParam("m_source") as IGH_Param;
+ IGH_Param target = getPrivateParam("m_target") as IGH_Param;
+ LinkMode mode = (LinkMode)Enum.Parse(typeof(LinkMode), getPrivateParam("m_mode").ToString());
+ bool dragFromInput = (bool)getPrivateParam("m_dragfrominput");
+
+ wireStatus = new WireStatus(source, target, mode, dragFromInput);
+ }
+ }
+}