Detect KeyDown and MouseDown Events from ScriptComponent

Hmpf. I’m not experienced with .NET Windows programming. In Delphi everything was so simple… :slight_smile:

Well, I guess I have to dig into it, reluctantly. Thanks anyway.

// Rolf

You can subscribe the mouse events of canvas control (mouseDown, mouseUp, mouseMove) to check when is dragging and KeyDown event to check is ctrl is pressed.

Also you can use the Microsoft.VisualBasic.Devices.Keyboard class to check keyboard special buttons as Ctrl.

Dani is right, you can also access this one level higher.
Its just that using user32.dll requires some learning, but once you get it, its not that complicated anymore. Its the p/invoke process, but its actually quite useful when working with unmanaged libraries. Actually you find a lot of documentation on Stack overflow for C#. Simply copy and paste. You will not be the first one asking that particular question about winapi.

In VS 2015 it seems that namespace isn’t accessible. It fails on Devices.

bild

Googling.

// Rolf

Add reference to Microsoft.VisualBasic.dll

Ah, that’s what happens when you haven’t slept for 48 hours. :sleeping: It works just fine.

// Rolf

Now I’ve been searching the Grasshopper SDK for six-seven hours, and I just can’t find such documentation which helps me hook the GH mouse events correctly.

I had no problem finding and hooking up to Rhino’s Keyboard events (and Mouse events too) but that was easy probably just because the Rhino Mouse was not what I wanted.

public class TestMouseCallback: Rhino.UI.MouseCallback

Anyway, the following two variants is the only thing I got to compile, after so many hours of searching and browsing and trying:

  private void RunScript(ref object A)
  {
    var canvas = Grasshopper.Instances.ActiveCanvas;
    if (canvas == null)
      return;

    canvas.DocumentObjectMouseDown += new GH_Canvas.DocumentObjectMouseDownEventHandler(DOBJ_Callback);

    canvas.MouseDown += new System.Windows.Forms.MouseEventHandler(Forms_Callback);
  }

//

  public void DOBJ_Callback(Object sender, GH_CanvasObjectMouseDownEventArgs e)
  {
    Print("DOBJ MouseDown: Piiip!");
  }

  public void Forms_Callback(object sender, MouseEventArgs e)
  {
    Print("Forms MouseDown: Piiip!");
  }

however, neither of them fires any events when clicking around on the Canvas. It’s probably something trivial I’m missing, but sleepy as I am I just don’t find the right way of doing this.

But now it’s time to hit the sack.

// Rolf

GH canvas is a system.windows.forms.control:
https://msdn.microsoft.com/es-es/library/system.windows.forms.control(v=vs.110).aspx

Before you subscribe to an event, you must make sure you unsubscribe it before reassigning it to the same instance.

private void RunScript(bool x, object y, ref object A)
{
  if(x) Grasshopper.Instances.ActiveCanvas.MouseDown += MyMouseDown;
  else Grasshopper.Instances.ActiveCanvas.MouseDown -= MyMouseDown;
}

public void MyMouseDown(object sender, MouseEventArgs e){
  RhinoApp.WriteLine(e.Button.ToString() + " button clicked");
}

You can also do the same for KeyDown event.

you can always unsubscribe before subscribing. That doesn’t yield any error and works
Grasshopper.Instances.ActiveCanvas.MouseDown -= MyMouseDown;
Grasshopper.Instances.ActiveCanvas.MouseDown += MyMouseDown;

However as said: I would not doing this in scriptcomponents, because even if you change a whitespace inside your code, you will create a different script instance, impossible to unsubscribe the MyMouseDown from that other script instance (except closing Rhino).

So doNotKnowTheCorrectNameOfThisInstance1.MyMouseDown is different to
doNotKnowTheCorrectNameOfThisInstance2.MyMouseDown.

1 Like

Yes but well, just unsubscribe it before you change the code,
change the code > x = true > do something > x = false > change the code.

yeah in this particular case it works, just saying this because I didn’t know this before I was doing Eventhandling in script components for the first time. I had to deal with really weird behaviour and it “wasted” a lot of time and energy :slight_smile:

Hi @Dani_Abalde,
Thank you for your help. Yes, I noticed that there were a difference between the two events I subscribed to, but I don’t really understand why my event din’t fire because it looks like I already had it hooked (the second alternative):

Oh well. Remains the problem pointed out by @TomTom.

Back in the days of Delphi, which didn’t (doesn’t) have GarbageCollection I used a dirty trick utilizing Interfaces to do the “GC” for me, by embedding variables in a interface (calling them Guard), then when the code would go out of scope (leaving a method) any local variables based on this interface would be disposed of, due to the reference counting mechanism. Running it would look like pictured below (if I had the correct hook to the GH_Component, that is)

In this case I cannot really “hook” any such variable to the GH_Component class (since @DavidRutten didn’t provide any “OnDispose” hook… but he sure will…?), so to illustrate the idea I made a separate class to host the event, but the idea is the same; When the class is being disposed of, the Dispose method would unregister (unhook) the MouseDown delegate.

The code:

  private void RunScript(object x, ref object A)
  {
    var canvas = Canvas;

    if (canvas == null)
      return;

    if (m_gcevents == null)
    {
      m_gcevents = new GuardedMouseDownEvent(); // implements IDisposable
      m_gcevents.m_owner = this;
      m_gcevents.MouseDown_Subscriber(true);
    }

    Reflect(m_gcevents); // Just some blah-blah code

    System.Threading.Thread.Sleep(500);

    // Garbage collector should take care of unsubscribing, but...
    m_gcevents.Dispose(); 
    // ...sigh. Calling Dispose is only my dirty thing here, since the GC doesn't 
    // seem to be polite enough to actually Dispose of the object.
    m_gcevents = null;
  }

The implementations :

  // Still in Script_Instance...
  private GuardedMouseDownEvent m_gcevents;

  public GH_Canvas Canvas {
    get  { return Grasshopper.Instances.ActiveCanvas;  }
  }

and on the follwing lines the class dealing with MouseDown event and, in the next section, implementing the IDisposable, the dirty trick of sorts:

  // ========================================
  // CLASS GuardedMouseDownEvent
  // ========================================
  public class GuardedMouseDownEvent: IDisposable
  {
    public Script_Instance m_owner; // back reference to the script component

    public GH_Canvas Canvas {
      get { return Grasshopper.Instances.ActiveCanvas; } // for convenience
    }

    public void MouseDown_Subscriber(bool subscribe) {
      if (subscribe)
      {
        Rhino.RhinoApp.WriteLine("Registering GC_MouseDown event");
        Canvas.MouseDown += GC_MouseDown;
      } else {
        Rhino.RhinoApp.WriteLine("Unregistering GC_MouseDown event (poor mouse is now very dead... )");
        Canvas.MouseDown -= GC_MouseDown;
      }
    }

    // Some dummy action on MouseDown
    public void GC_MouseDown(object sender, MouseEventArgs e) {
      Rhino.RhinoApp.WriteLine("GCEvents.GH_MouseDown: Alive & Kicking!");
    }

… and here the IDisposable implementation

    // ==================================
    // IMPLEMENTING INTERFACE IDISPOSABLE
    // ==================================
    bool disposed = false;
    System.Runtime.InteropServices.SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposed)  return;

      if (disposing) { // only managed objects
        handle.Dispose();        
      }

      // Unmanaged stuff, for example hooked events
      Rhino.RhinoApp.WriteLine(string.Format("(Dispose({0}))", disposing));
      MouseDown_Subscriber(false);   // = "unsubscribe"

      disposed = true;
    }
  } // end class  
} // </Custom additional code> 

Me and the .NET GC have not become freinds as of yet, so the GC doesn’t really dispose of the “GuardedMouseDownEvent” instance, but you guys may know better how to force .NET do dump the object without resorting to that ugly call to Dispose();

In any case, @DavidRutten should consider adding more hooks to GH so we can have more fun. Cough, cough.

// Rolf

If any of you .NET experts can make the “Guarded Event” class to actually get disposed of, here’s the component:

GH_MouseTest.gh (4.5 KB)

// Rolf

This looks more complicated than it should be. Or I don’t understand what you’re trying to do.
.Net is in charge of releasing the memory that is no longer needed.
If you want to unsubscribe the event with m_gcevents = null, (which seems like a whim to me) you can declare a deconstructor (and you will need a constructor too) and unsubscribe the event there:

public GuardedMouseDownEvent(){}
~GuardedMouseDownEvent(){ //this will be called before complete m_gcevents = null;
  MouseDown_Subscriber(false);
}

As you have structured the code, you do not allow invoking the mouse event, because before the code is executed (and then the user can launch an event) you are already unsubscribing the event.

The idea is to have a mechanism that unsubscribes when a variable goes out of scope, meaning, “Create and forget”.

This approach is as perfect a solution as reference counting is perfect (well, that has been providing a poor man’s “pseudo Garbage Collection” for decades in non-GC languages). I used this technique in Delphi Pascal a lot.

One would of course make a generic “Guard” class which takes any System.Object (or delegate) as an argument in the constructor so as to not have to design such a class for each use case.

// Rolf

I’m sorry, I still don’t understand. If you need to specify to release the memory where a variable is pointed, using m_gcevents = null or dispose, just to unsubscribe the event, why not replace m_gcevents = null with a call to the unsubscribe method, directly? .Net will be in charge of freeing up the resources when it deems it appropriate. By replacing a line you avoid superfluous (at least in my opinion) code.

This wasn’t meant to be working, it was meant to illustrate the Guard concept.

Quoting myself:

Edit:

Problem is of course that Script_Instance is the wrong class to deal with the lifetime of the event. It should follow the liftime of the GH_Component (hence the poking on @DavidRutten).

// Rolf

actually the problem could be solved quite easily if there would be a possibility to define a method outside the local scope, being a true global method. You can do that in CPython. I also do alot with VRED Professional and in there I script eventbased. The difference to Grasshopper is that my functionality can be declared global. So whenever my script changes, the method is overridden. However this still can produce weird behaviour, because I have to make sure that every object gets destroyed after rerunning the solution. This could work with ease if I wouldn‘t access unmanaged c++ objects in there. So I encountered really hard to debug behaviour. I did a lot of manual disposing, but as long there is one referenced living object, you cannot kill an instance. You even need to kill in the right order.So what happends is, that there are multiple instances, with different implentations running at the same time. You can see this by printing all active objects out. So I think its not that you cannot work with events in a script context, its just that you really need be aware of whats going on and being very careful. This requires knowledge of how the gc works, and I bet the casual scripter here doesn‘t know anything about it except that it manages memory for you.So thats why I keep saying:Don‘t do this dave, unless you really know whats going on. For .net I don‘t know a good workaround, except to ensure unsubscribing before loosing reference to the subscriping method.

1 Like

BeforeCompile & AfterCompile

Yes @TomTom, you’re 100% right. Although the GC would eventually get rid of even an interface guard the risk is that in the meantime, before being destructed, events would fire who knows where and when. Guarding passive data is one thing, but not unregistering delegates before they become inaccessible can end up in disaster.

I think there’s only one way to deal with this in a safe way in the Script component, and that is if @DavidRutten intoduces a BeforeCompile method (or event) and of course, an AfterCompile method (or event) on the Script_Instance class (the GH_Component can’t be overridden in this context anyway).

And as we have seen, the existing methods BeforeRunScript and AfterRunScript doesn’t help becaue it’s not related to neither mouse events nor to the compile command and so any delegates becomes inaccessible before having a chance to clean up any eventhandlers.

Being able to set up delegates/event subscribers is a crucial step in modern development. Prototyping with the script components is also extremely useful an efficient, so I can’t think of a good reason not to introduce those two methods (or hooks). Even if adding event hooks also on the GH_Component for this end, one would still need the extra Pre-Post-methods on the ScriptComponent in order to capture the case in which compiling renders any old event subscribers inaccessible.

I really think that the script component should become a first class member in the development tool box of grasshopper. Only the editor can remain simpler for simplicity’s sake.

// Rolf

This should be solved by creating a destructor for Script_Instance, however it seems that .Net calls it whenever it wants, sometimes before creating a new instance and sometimes much later.
I suppose this could be corrected by simply forcing the finalization of the instance within the Scripting component before reassigning it.

  private void RunScript(object x, object y, ref object A){
    RhinoApp.WriteLine("RunScript");
  }

  ~Script_Instance(){
    RhinoApp.WriteLine("***Destructor***"); 
  }

Rhino ouput:

RunScript
Destructor
RunScript
RunScript
RunScript
RunScript
Destructor
RunScript
Destructor
RunScript
Destructor
RunScript
Destructor
RunScript
Destructor
Destructor
Destructor
RunScript