void
myMouseCB(void *userData, SoEventCallback *eventCB)
{
   const SoEvent *event = eventCB->getEvent();
   SoSeparator * scenegraph = (SoSeparator*) userData;

   if (SO_MOUSE_PRESS_EVENT(event, BUTTON2)) 
   {
      std::cout << "mouse button 2" << std::endl;

      if (global_render_manager)
      {
         // attempting raypick in the event_cb() callback method
         SoRayPickAction rp( global_render_manager->getViewportRegion() );
         rp.setPoint(event->getPosition());
         rp.setPickAll(true);

         std::cout << "event position: " << event->getPosition()[0] << " " << event->getPosition()[1]
            << std::endl;
         
         rp.apply(global_render_manager->getSceneGraph());

         const SoPickedPointList& pickedPoints = rp.getPickedPointList();
         std::cout << "# of picked points: " << pickedPoints.getLength() << std::endl;
         for (int i=0; i<pickedPoints.getLength(); ++i)
         {
            SoPickedPoint * pickedPoint = pickedPoints[i];
            SbVec3f point = pickedPoint->getPoint();
            std::cout << "point " << i << " : " << point[0] << " " << point[1] << " " <<
               point[2] << std::endl;
         }
      }

      eventCB->setHandled();
   }
   else if (SO_MOUSE_PRESS_EVENT(event, BUTTON1)) 
   {
      //std::cout << "mouse button 1" << std::endl;
      eventCB->setHandled();
   }
   else if (SO_MOUSE_PRESS_EVENT(event, BUTTON3)) 
   {
      //std::cout << "mouse button 3" << std::endl;
      eventCB->setHandled();
   }
}
SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::seekToPoint(const SbVec2s screenpos)
{

    SoRayPickAction rpaction(getSoRenderManager()->getViewportRegion());
    rpaction.setPoint(screenpos);
    rpaction.setRadius(2);
    rpaction.apply(getSoRenderManager()->getSceneGraph());

    SoPickedPoint* picked = rpaction.getPickedPoint();

    if(!picked) {
        this->interactiveCountInc(); // decremented in setSeekMode(FALSE)
        this->setSeekMode(FALSE);
        return FALSE;
    }

    SbVec3f hitpoint;
    hitpoint = picked->getPoint();

    this->seekToPoint(hitpoint);
    return TRUE;
}
/*!
  The string returned from this function is only valid until the next variable is
  requested.
*/
const char *
SoScXMLStateMachine::getVariable(const char * key) const
{
  if (strncmp(key, "_event.", 7) == 0) {
    // printf("scan for key '%s'\n", key);
    const char * subkey = key + 7;
    const ScXMLEvent * ev = this->getCurrentEvent();
    if (ev->isOfType(SoScXMLEvent::getClassTypeId())) {
      const SoScXMLEvent * soev = static_cast<const SoScXMLEvent *>(ev);
      const SoEvent * coinev = soev->getSoEvent();

      if (strcmp(subkey, "getTime()") == 0) {
        SbTime timeval = coinev->getTime();
        double doubletime = timeval.getValue();
        PRIVATE(this)->varstring = SbStringConvert::toString(doubletime);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getPosition().x") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getPosition().y") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getPosition()") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(pos);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getNormalizedPosition().x") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getNormalizedPosition().y") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getNormalizedPosition()") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(pos);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasShiftDown()") == 0) {
        SbBool wasdown = coinev->wasShiftDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasCtrlDown()") == 0) {
        SbBool wasdown = coinev->wasCtrlDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasAltDown()") == 0) {
        SbBool wasdown = coinev->wasAltDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getState()") == 0 && coinev->isOfType(SoButtonEvent::getClassTypeId())) {
        const SoButtonEvent * bevent = coin_assert_cast<const SoButtonEvent *>(coinev);
        SbString enumname;
        SoButtonEvent::enumToString(bevent->getState(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getKey()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
        const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
        SbString enumname;
        SoKeyboardEvent::enumToString(kbevent->getKey(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getPrintableCharacter()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
        const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
        char printable = kbevent->getPrintableCharacter();
        PRIVATE(this)->varstring.sprintf("'%c'", printable);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoMouseButtonEvent::getClassTypeId())) {
        const SoMouseButtonEvent * mbevent = coin_assert_cast<const SoMouseButtonEvent *>(coinev);
        SbString enumname;
        SoMouseButtonEvent::enumToString(mbevent->getButton(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoSpaceballButtonEvent::getClassTypeId())) {
        const SoSpaceballButtonEvent * mbevent = coin_assert_cast<const SoSpaceballButtonEvent *>(coinev);
        SbString enumname;
        SoSpaceballButtonEvent::enumToString(mbevent->getButton(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: x., .y, .z
      else if (strcmp(subkey, "getTranslation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
        const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
        SbVec3f translation = m3event->getTranslation();
        PRIVATE(this)->varstring = SbStringConvert::toString(translation);
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: .angle, .axis
      else if (strcmp(subkey, "getRotation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
        const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
        SbRotation rotation = m3event->getRotation();
        PRIVATE(this)->varstring = SbStringConvert::toString(rotation);
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: make this into a evaluator-level RayPick(SbVec2f) function instead
      else if (strcmp(key + 7, "pickposition3") == 0) {
        SbVec2s location2 = coinev->getPosition();
        SoRayPickAction rpa(this->getViewportRegion());
        rpa.setPoint(location2);
        rpa.apply(this->getSceneGraphRoot());
        SoPickedPoint * pp = rpa.getPickedPoint();
        if (pp) {
          SbVec3f pickpos = pp->getPoint();
          PRIVATE(this)->varstring = SbStringConvert::toString(pickpos);
        } else {
          PRIVATE(this)->varstring.sprintf("FALSE"); // need a valid undefined-value
        }
        return PRIVATE(this)->varstring.getString();
      }
    }
  }

  else if (strncmp(key, "coin:", 5) == 0) {
    const char * subkey = key + 5;
    if (strncmp(subkey, "camera.", 7) == 0) {
      SoCamera * camera = this->getActiveCamera();
      if (!camera) {
        SoDebugError::post("SoScXMLStateMachine::getVariable",
                           "queried for camera, but no camera is set.");
        return NULL;
      }
      const char * detail = subkey + 7;
      if (strcmp(detail, "getTypeId()") == 0) {
        PRIVATE(this)->varstring.sprintf("'%s'", camera->getTypeId().getName().getString());
        return PRIVATE(this)->varstring.getString();
      }
    }

    // get generic field access working and intercept for more So-specific stuff
    // coin:viewport
    // coin:camera
    // coin:scene
  }

  //else {
  //}

  // couldn't resolve the symbol - try parent class to get '_data' and other '_event'
  // locations resolved
  return inherited::getVariable(key);
}