11.1. Creating an Event

This section describes creating a new event and offers background information on translating an event. For information on creating a new device, see Section 11.3, “Creating a Device”. The device's main responsibility is translating events, which is described in more detail in Section 11.2, “Dispatching Events”.


The file SoSubEvent.h contains the macros for defining new event classes. The SO_EVENT_HEADER() macro declares type identifier and naming variables and methods that all event classes must support. The SO_EVENT_SOURCE() macro defines the static variables and methods declared in the SO_EVENT_HEADER() macro.

Creating a new event requires these steps:

  1. Select a name for the new event class and determine what class it is derived from.

  2. Define an initClass() method to initialize the type information. Use the SO_EVENT_INIT_CLASS() macro. The application needs to call the event's initClass() method immediately after SoXt::init.

  3. Define a constructor.

  4. Define a destructor.

  5. Implement set() and get() methods for the additional information your event provides. For example, the dial event needs to include information on which dial was turned, and what its value is.

  6. Write convenience routines for the event to perform common queries and tasks (optional step). For the dial box, the convenience routine is isDialEvent() .

  7. Write convenience macros for the event (optional step). These are static functions that are used in event callback functions. For the dial, the macro is DIAL_EVENT(). Note that it uses the convenience routines defined in step 6.

Translating Events

When an event is dispatched, the event translator creates an Inventor event from an X event and sets its values (see Section 11.2, “Dispatching Events”). It provides all the information about the event, including the following:

  • Time the event occurred

  • Position of the locator when the event occurred

  • State of the modifier keys (Shift, Control, Alt) when the event occurred

  • Any additional information required by the event (for example, if a keyboard key is pressed, which key is it?)

Inventor includes three subclasses of SoEvent. SoButtonEvent includes additional information about the button state (is it up or down?). Subclasses of SoButtonEvent SoButtonEvent SoButtonEvent provide information about which button was pressed. SoMotion3Event SoMotion3Event SoMotion3Event includes information on translation and rotation values generated by an input device such as the spaceball. SoLocation2Event SoLocation2Event SoLocation2Event includes information on the absolute location of the cursor in window coordinates.

A value such as the event's time or position is read-only during event traversal because the event is passed as a const pointer. Only the creator of an event can set its values.

Creating a Dial Event

The dial and button input device generates two X events that need to be translated into Inventor events and handled by the database:


provides value changes of the eight dials


provides information about the state of the device's 32 buttons

The information provided by XDeviceMotionEvent is translated into a DialEvent . The XDeviceButtonEvent is translated into a ButtonBoxEvent , which is subclassed from SoButtonEvent SoButtonEvent SoButtonEvent and has button information specific to the button box.

This section discusses the code for the DialEvent, which describes the state of any of the eight dials. Note, however, that you could instead choose to create a more generic event that could be used for other devices in addition to the dial box. For example, you could create a ResetToHomePositionEvent that would be used when the user presses a button box button, clicks on a Home button on the screen, or performs some other designated action.

Be sure to call initClass() on the event after initializing Inventor.

Example 11.1, “ DialEvent.h shows the code for the dial event include file.

Example 11.1.  DialEvent.h

#include <Inventor/SbBasic.h>
#include <Inventor/events/SoEvent.h>
#include <Inventor/events/SoSubEvent.h>

// Convenience macro for determining if an event matches
   (DialEvent::isDialEvent(EVENT, WHICH))

class DialEvent : public SoEvent {

   // Constructor
   // Which dial generated the event, 1-8
   void     setDial(int d)   { dial = d; }
   int      getDial() const  { return dial; }
   // Value of the dial turned
   void     setValue(int v)  { value = v; }
   int      getValue() const { return value; }
   // Convenience routines to see if an SoEvent is a turn of
   // the passed dial. Passing -1 matches any button.
   static SbBool        isDialEvent(const SoEvent *e, 
                                   int which = -1);
     static void          initClass();
   int                  dial;               // Which dial
   int                  value;              // Value of dial

Example 11.2, “ DialEvent.c++ shows the complete source code for the dial event.

Example 11.2.  DialEvent.c++

#include "DialEvent.h"


// Class initialization

   SO_EVENT_INIT_CLASS(DialEvent, SoEvent);

// Constructor
   dial = 0;
   value = 0;

// Convenience routine - this returns TRUE if the event is a
// dial turn event matching the passed dial.
DialEvent::isDialEvent(const SoEvent *e, int whichDial)
   SbBool isMatch = FALSE;
   // is it a dial event?
   if (e->isOfType(DialEvent::getClassTypeId())) {
     const DialEvent *de = (const DialEvent *) e;
     // did the caller want any dial turn? or do they match?
     if ((whichDial == -1) ||
        (de->getDial() == whichDial))
       isMatch = TRUE;
   return isMatch;