[vtk-developers] Some Event handling ideas

David Gobbi david.gobbi at gmail.com
Mon Jul 10 17:23:53 EDT 2017


Something else. Ideally handleEvent() should be a generic dispatcher that
simply forwards the events to other methods that handle the specific event
types.

E.g. handleEvent(vtkEventData *) would forward button events to

  handleButtonEvent(vtkButtonEventData *)

So the question is, how to do this dispatching generically without using an
"if" for each event type?


I use a method like the following to add callbacks:

  template<class T, class E>
  void EventDispatcher::Bind(int type, T* observer, void (T::*callback)(E
*e));

Each event class E has a static member E::TypeBase that can be used to
check that class "E" is allowed to handle "type".

The job of the this Bind() method is to build a binding table, which is
basically a map of (type, callback) pairs.


The trick here is that the "callback" stored in the table is polymorphic:

class EventCallback
{
public:
  virtual void operator()(Event *event) const = 0;
};


And the Bind() creates subclasses on the fly, thanks to the magic of
templates.  The operator() method is responsible for performing the
downcast:

template<class T, class E>
class EventCallbackTE : public EventCallback
{
public:
  EventCallbackTE(T *object, void (T::*method)(E *event)) :
    Object(object), Method(method) {}

  void operator()(Event *event) const override
  {
    (this->Object->*(this->Method))(static_cast<E *>(event));
  }

private:
  T *Object;
  void (T::*Method)(E *event);
};


So, basically, the "if" statements are replaced by a lookup table that can
grow as needed.  At least some of the ideas are from the book "Modern C++
Design" (unfortunately I don't have my own copy, so I can't locate pages or
chapters right now).

My own implementation adds things like weak pointers and dispatching based
on more than just the event type, e.g. dispatching also considers which
button is held down, what modifiers are held down, and other relevant state.

 - David


On Mon, Jul 10, 2017 at 2:00 PM, Ken Martin <ken.martin at kitware.com> wrote:

> I like that too :-) So what are your thoughts on down casting to access
> the properties? e.g.
>
> void handleEvent(vtkEventData *event)
> {
>   if (event->GetType() == Button)
>   {
>     vtkButtonEvent *be = static_cast<vtkButtonEvent *>(event);
>     be->GetButtonId()
>   }
>
> or something else?
>
>
>
>
> On Mon, Jul 10, 2017 at 2:59 PM, David Gobbi <david.gobbi at gmail.com>
> wrote:
>
>> Hi Ken,
>>
>> Why not use polymorphism here?  I use an event hierarchy like so:
>>
>>   Event <- InputEvent <- PointEvent, KeyEvent
>>
>> I've never used a union; the code that generates the event knows what
>> type of event object to instantiate, and the observer methods just get a
>> pointer to the event object.  Qt does things similarly.
>>
>>  - David
>>
>>
>> On Mon, Jul 10, 2017 at 9:15 AM, Ken Martin <ken.martin at kitware.com>
>> wrote:
>>
>>>
>>> Lucas and I have been working on VTK's VR support and one issue we
>>> bumped into was event handing. VTK's event handling tends to be data light
>>> where most events follow this approach
>>>
>>> 1) event happens
>>>
>>> 2) we set properties on the interactor such as alt/shift/control key
>>> state, mouse position, etc
>>>
>>> 3) fire the event (with little or no calldata)
>>>
>>> 4) event handlers query needed properties from the interactor
>>>
>>> The challenge here is that as more event types are supported, the
>>> interactor's state and structure is required to grow to match them.  For
>>> example with multitouch events, the interactor now has to support multiple
>>> mouse positions and pointer indexes. Now add in multi controller VR and we
>>> have to also store arrays of world coordinate positions and orientations
>>> and what controller the current event corresponds to.
>>>
>>> Many systems use a different approach where they define an event data
>>> structure or class that get passed with the event (or associated with the
>>> event). Then whatever is handling the event receives that data structure
>>> which contains most of the key properties of the event.  Such an event
>>> structure can also be used for filtering. For example currently in VTK you
>>> can map keyboard events on a widget using the vtkWidgetCallbackMapper method
>>>
>>> void SetCallbackMethod(unsigned long VTKEvent, int modifiers, char
>>> keyCode,
>>>                          int repeatCount, const char* keySym,
>>>                          unsigned long widgetEvent,
>>>                          vtkAbstractWidget *w, CallbackType f);
>>>
>>> Note that this signature is designed for keyboard events as it contains
>>> keyboard event specific parameters. To add a mapping for multitouch, or VR
>>> controller events would require more event specific signatures. In contrast
>>> if we had a formal vtkEventData structure then the method could use a
>>> vtkEventData as a filter and handle many different event types as long as
>>> the EventData has an equality operator on it (or maybe a filtering specific
>>> equality).
>>>
>>> Lucas and I have tried this out for VR and it has made event handling
>>> reasonable and fairly clean and I would like to get some feedback on the
>>> idea/approach/etc.  Right now our vtkEventData is included below. It uses
>>> the approach of a union of event type structs and what has been fleshed out
>>> is targeted at VR but it should be easy to see how keyboard or mouse
>>> devices/events could get added as well.
>>>
>>> Thanks!
>>> Ken
>>>
>>>
>>>
>>> enum class vtkEventDataDevice {
>>>   Unknown = -1,
>>>   HeadMountedDisplay,
>>>   RightController,
>>>   LeftController,
>>>   NumberOfDevices
>>> };
>>>
>>> const int vtkEventDataNumberOfDevices =
>>>   static_cast<int>(vtkEventDataDevice::NumberOfDevices);
>>>
>>> enum class vtkEventDataDeviceInput {
>>>   Unknown = -1,
>>>   Trigger,
>>>   TrackPad,
>>>   Grip,
>>>   ApplicationMenu,
>>>   NumberOfInputs
>>> };
>>>
>>> const int vtkEventDataNumberOfInputs =
>>>   static_cast<int>(vtkEventDataDeviceInput::NumberOfInputs);
>>>
>>> enum class vtkEventDataAction {
>>>   Unknown = -1,
>>>   Press,
>>>   Release,
>>>   NumberOfActions
>>> };
>>>
>>> struct vtkEventDataButton3D
>>> {
>>>   vtkEventDataDevice Device;
>>>   vtkEventDataDeviceInput Input;
>>>   vtkEventDataAction Action;
>>>   double WorldPosition[3];
>>>   double WorldOrientation[4];
>>>
>>>   bool operator==(const vtkEventDataButton3D& rh)
>>>   {
>>>     return (Device == rh.Device && Input == rh.Input && Action ==
>>> rh.Action);
>>>   }
>>>   void SetWorldPosition(const double p[3])
>>>   {
>>>     WorldPosition[0] = p[0];
>>>     WorldPosition[1] = p[1];
>>>     WorldPosition[2] = p[2];
>>>   }
>>>   void SetWorldOrientation(const double p[4])
>>>   {
>>>     WorldOrientation[0] = p[0];
>>>     WorldOrientation[1] = p[1];
>>>     WorldOrientation[2] = p[2];
>>>     WorldOrientation[3] = p[3];
>>>   }
>>> };
>>>
>>> struct vtkEventDataMove3D
>>> {
>>>   vtkEventDataDevice Device;
>>>   double WorldPosition[3];
>>>   double WorldOrientation[4];
>>>
>>>   bool operator==(const vtkEventDataMove3D& rh)
>>>   {
>>>     return (Device == rh.Device);
>>>   }
>>>   void SetWorldPosition(const double p[3])
>>>   {
>>>     WorldPosition[0] = p[0];
>>>     WorldPosition[1] = p[1];
>>>     WorldPosition[2] = p[2];
>>>   }
>>>   void SetWorldOrientation(const double p[4])
>>>   {
>>>     WorldOrientation[0] = p[0];
>>>     WorldOrientation[1] = p[1];
>>>     WorldOrientation[2] = p[2];
>>>     WorldOrientation[3] = p[3];
>>>   }
>>> };
>>>
>>> typedef union
>>> {
>>>   vtkEventDataButton3D Button;
>>>   vtkEventDataMove3D Move;
>>> } vtkEventDataUnion;
>>>
>>> struct vtkEventData
>>> {
>>>   int Type; // see vtkCommand.h
>>>   vtkEventDataUnion Data;
>>>
>>>   bool operator==(const vtkEventData& rh)
>>>   {
>>>     if (Type != rh.Type)
>>>     {
>>>       return false;
>>>     }
>>>     switch (Type)
>>>     {
>>>       case vtkCommand::ButtonEvent3D:
>>>         return Data.Button == rh.Data.Button;
>>>       case vtkCommand::MoveEvent3D:
>>>         return Data.Move == rh.Data.Move;
>>>     }
>>>     return false;
>>>   }
>>>
>>>   bool GetDevice(vtkEventDataDevice &val)
>>>   {
>>>     switch (Type)
>>>     {
>>>       case vtkCommand::ButtonEvent3D:
>>>         val = Data.Button.Device;
>>>         return true;
>>>       case vtkCommand::MoveEvent3D:
>>>         val = Data.Move.Device;
>>>         return true;
>>>     }
>>>     return false;
>>>   }
>>>
>>>   bool GetWorldPosition(double *&pos)
>>>   {
>>>     switch (Type)
>>>     {
>>>       case vtkCommand::ButtonEvent3D:
>>>         pos = Data.Button.WorldPosition;
>>>         return true;
>>>       case vtkCommand::MoveEvent3D:
>>>         pos = Data.Move.WorldPosition;
>>>         return true;
>>>     }
>>>     return false;
>>>   }
>>>
>>>   bool GetWorldOrientation(double *&val)
>>>   {
>>>     switch (Type)
>>>     {
>>>       case vtkCommand::ButtonEvent3D:
>>>         val = Data.Button.WorldOrientation;
>>>         return true;
>>>       case vtkCommand::MoveEvent3D:
>>>         val = Data.Move.WorldOrientation;
>>>         return true;
>>>     }
>>>     return false;
>>>   }
>>> };
>>>
>>>
>>>
>>> --
>>> Ken Martin PhD
>>> Distinguished Engineer
>>> Kitware Inc.
>>> 28 Corporate Drive
>>> Clifton Park NY 12065
>>>
>>> This communication, including all attachments, contains confidential and
>>> legally privileged information, and it is intended only for the use of the
>>> addressee.  Access to this email by anyone else is unauthorized. If you are
>>> not the intended recipient, any disclosure, copying, distribution or any
>>> action taken in reliance on it is prohibited and may be unlawful. If you
>>> received this communication in error please notify us immediately and
>>> destroy the original message.  Thank you.
>>>
>>
>>
>
>
> --
> Ken Martin PhD
> Distinguished Engineer
> Kitware Inc.
> 28 Corporate Drive
> Clifton Park NY 12065
>
> This communication, including all attachments, contains confidential and
> legally privileged information, and it is intended only for the use of the
> addressee.  Access to this email by anyone else is unauthorized. If you are
> not the intended recipient, any disclosure, copying, distribution or any
> action taken in reliance on it is prohibited and may be unlawful. If you
> received this communication in error please notify us immediately and
> destroy the original message.  Thank you.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/vtk-developers/attachments/20170710/c72310f9/attachment.html>


More information about the vtk-developers mailing list