Browse Source

more pgraph data nodes

David Rose 24 years ago
parent
commit
872a3ecd0b
34 changed files with 4466 additions and 67 deletions
  1. 34 10
      panda/src/device/Sources.pp
  2. 10 0
      panda/src/device/config_device.cxx
  3. 5 0
      panda/src/device/device_composite2.cxx
  4. 3 3
      panda/src/device/mouseAndKeyboard.cxx
  5. 141 0
      panda/src/device/qpanalogNode.I
  6. 121 0
      panda/src/device/qpanalogNode.cxx
  7. 113 0
      panda/src/device/qpanalogNode.h
  8. 114 0
      panda/src/device/qpbuttonNode.I
  9. 131 0
      panda/src/device/qpbuttonNode.cxx
  10. 97 0
      panda/src/device/qpbuttonNode.h
  11. 75 0
      panda/src/device/qpdialNode.I
  12. 86 0
      panda/src/device/qpdialNode.cxx
  13. 86 0
      panda/src/device/qpdialNode.h
  14. 118 0
      panda/src/device/qptrackerNode.I
  15. 104 0
      panda/src/device/qptrackerNode.cxx
  16. 94 0
      panda/src/device/qptrackerNode.h
  17. 144 0
      panda/src/device/qpvirtualMouse.cxx
  18. 89 0
      panda/src/device/qpvirtualMouse.h
  19. 49 40
      panda/src/dgraph/qpdataGraphTraverser.cxx
  20. 2 1
      panda/src/dgraph/qpdataGraphTraverser.h
  21. 17 7
      panda/src/tform/Sources.pp
  22. 4 0
      panda/src/tform/config_tform.cxx
  23. 1 0
      panda/src/tform/mouseWatcherGroup.h
  24. 2 2
      panda/src/tform/qpbuttonThrower.cxx
  25. 526 0
      panda/src/tform/qpdriveInterface.I
  26. 482 0
      panda/src/tform/qpdriveInterface.cxx
  27. 199 0
      panda/src/tform/qpdriveInterface.h
  28. 437 0
      panda/src/tform/qpmouseWatcher.I
  29. 962 0
      panda/src/tform/qpmouseWatcher.cxx
  30. 214 0
      panda/src/tform/qpmouseWatcher.h
  31. 3 3
      panda/src/tform/qptrackball.cxx
  32. 1 1
      panda/src/tform/qptransform2sg.cxx
  33. 1 0
      panda/src/tform/tform_composite1.cxx
  34. 1 0
      panda/src/tform/tform_composite2.cxx

+ 34 - 10
panda/src/device/Sources.pp

@@ -9,29 +9,49 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 
 
   #define SOURCES \
   #define SOURCES \
-    analogNode.I analogNode.h buttonNode.I buttonNode.h  \
+    analogNode.I analogNode.h \
+    qpanalogNode.I qpanalogNode.h \
+    buttonNode.I buttonNode.h  \
+    qpbuttonNode.I qpbuttonNode.h  \
     clientAnalogDevice.I clientAnalogDevice.h clientBase.I  \
     clientAnalogDevice.I clientAnalogDevice.h clientBase.I  \
     clientBase.h clientButtonDevice.I clientButtonDevice.h  \
     clientBase.h clientButtonDevice.I clientButtonDevice.h  \
     clientDevice.I clientDevice.h clientDialDevice.I  \
     clientDevice.I clientDevice.h clientDialDevice.I  \
     clientDialDevice.h clientTrackerDevice.I  \
     clientDialDevice.h clientTrackerDevice.I  \
-    clientTrackerDevice.h config_device.h dialNode.I dialNode.h  \
+    clientTrackerDevice.h config_device.h \
+    dialNode.I dialNode.h  \
+    qpdialNode.I qpdialNode.h  \
     mouse.h \
     mouse.h \
     mouseAndKeyboard.h \
     mouseAndKeyboard.h \
-    trackerData.I trackerData.h trackerNode.I  \
-    trackerNode.h virtualMouse.h
+    trackerData.I trackerData.h \
+    trackerNode.I trackerNode.h \
+    qptrackerNode.I qptrackerNode.h \
+    virtualMouse.h \
+    qpvirtualMouse.h
 
 
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
-    analogNode.cxx buttonNode.cxx clientAnalogDevice.cxx  \
+    analogNode.cxx \
+    qpanalogNode.cxx \
+    buttonNode.cxx \
+    qpbuttonNode.cxx \
+    clientAnalogDevice.cxx  \
     clientBase.cxx clientButtonDevice.cxx clientDevice.cxx  \
     clientBase.cxx clientButtonDevice.cxx clientDevice.cxx  \
     clientDialDevice.cxx clientTrackerDevice.cxx  \
     clientDialDevice.cxx clientTrackerDevice.cxx  \
-    config_device.cxx dialNode.cxx mouse.cxx \
+    config_device.cxx \
+    dialNode.cxx \
+    qpdialNode.cxx \
+    mouse.cxx \
     mouseAndKeyboard.cxx \
     mouseAndKeyboard.cxx \
-    trackerData.cxx  \
-    trackerNode.cxx virtualMouse.cxx
+    trackerData.cxx \
+    trackerNode.cxx \
+    qptrackerNode.cxx \
+    virtualMouse.cxx \
+    qpvirtualMouse.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     analogNode.I analogNode.h \
     analogNode.I analogNode.h \
+    qpanalogNode.I qpanalogNode.h \
     buttonNode.I buttonNode.h \
     buttonNode.I buttonNode.h \
+    qpbuttonNode.I qpbuttonNode.h \
     clientAnalogDevice.I clientAnalogDevice.h \
     clientAnalogDevice.I clientAnalogDevice.h \
     clientBase.I clientBase.h \
     clientBase.I clientBase.h \
     clientButtonDevice.I clientButtonDevice.h \
     clientButtonDevice.I clientButtonDevice.h \
@@ -40,9 +60,13 @@
     clientTrackerDevice.I clientTrackerDevice.h \
     clientTrackerDevice.I clientTrackerDevice.h \
     config_device.h mouse.h \
     config_device.h mouse.h \
     mouseAndKeyboard.h \
     mouseAndKeyboard.h \
-    dialNode.I dialNode.h \
+    dialNode.I dialNode.h  \
+    qpdialNode.I qpdialNode.h  \
     trackerData.I trackerData.h \
     trackerData.I trackerData.h \
-    trackerNode.I trackerNode.h virtualMouse.h
+    trackerNode.I trackerNode.h \
+    qptrackerNode.I qptrackerNode.h \
+    virtualMouse.h \
+    qpvirtualMouse.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 10 - 0
panda/src/device/config_device.cxx

@@ -19,7 +19,9 @@
 
 
 #include "config_device.h"
 #include "config_device.h"
 #include "analogNode.h"
 #include "analogNode.h"
+#include "qpanalogNode.h"
 #include "buttonNode.h"
 #include "buttonNode.h"
+#include "qpbuttonNode.h"
 #include "clientAnalogDevice.h"
 #include "clientAnalogDevice.h"
 #include "clientBase.h"
 #include "clientBase.h"
 #include "clientButtonDevice.h"
 #include "clientButtonDevice.h"
@@ -27,10 +29,13 @@
 #include "clientDialDevice.h"
 #include "clientDialDevice.h"
 #include "clientTrackerDevice.h"
 #include "clientTrackerDevice.h"
 #include "dialNode.h"
 #include "dialNode.h"
+#include "qpdialNode.h"
 #include "mouse.h"
 #include "mouse.h"
 #include "mouseAndKeyboard.h"
 #include "mouseAndKeyboard.h"
 #include "trackerNode.h"
 #include "trackerNode.h"
+#include "qptrackerNode.h"
 #include "virtualMouse.h"
 #include "virtualMouse.h"
+#include "qpvirtualMouse.h"
 
 
 #include <dconfig.h>
 #include <dconfig.h>
 
 
@@ -60,7 +65,9 @@ init_libdevice() {
   initialized = true;
   initialized = true;
 
 
   AnalogNode::init_type();
   AnalogNode::init_type();
+  qpAnalogNode::init_type();
   ButtonNode::init_type();
   ButtonNode::init_type();
+  qpButtonNode::init_type();
   ClientAnalogDevice::init_type();
   ClientAnalogDevice::init_type();
   ClientBase::init_type();
   ClientBase::init_type();
   ClientButtonDevice::init_type();
   ClientButtonDevice::init_type();
@@ -68,8 +75,11 @@ init_libdevice() {
   ClientDialDevice::init_type();
   ClientDialDevice::init_type();
   ClientTrackerDevice::init_type();
   ClientTrackerDevice::init_type();
   DialNode::init_type();
   DialNode::init_type();
+  qpDialNode::init_type();
   MouseAndKeyboard::init_type();
   MouseAndKeyboard::init_type();
   qpMouseAndKeyboard::init_type();
   qpMouseAndKeyboard::init_type();
   TrackerNode::init_type();
   TrackerNode::init_type();
+  qpTrackerNode::init_type();
   VirtualMouse::init_type();
   VirtualMouse::init_type();
+  qpVirtualMouse::init_type();
 }
 }

+ 5 - 0
panda/src/device/device_composite2.cxx

@@ -1,11 +1,16 @@
 
 
 #include "config_device.cxx"
 #include "config_device.cxx"
 #include "analogNode.cxx"
 #include "analogNode.cxx"
+#include "qpanalogNode.cxx"
 #include "buttonNode.cxx"
 #include "buttonNode.cxx"
+#include "qpbuttonNode.cxx"
 #include "dialNode.cxx"
 #include "dialNode.cxx"
+#include "qpdialNode.cxx"
 #include "mouse.cxx"
 #include "mouse.cxx"
 #include "mouseAndKeyboard.cxx"
 #include "mouseAndKeyboard.cxx"
 #include "trackerData.cxx"
 #include "trackerData.cxx"
 #include "trackerNode.cxx"
 #include "trackerNode.cxx"
+#include "qptrackerNode.cxx"
 #include "virtualMouse.cxx"
 #include "virtualMouse.cxx"
+#include "qpvirtualMouse.cxx"
 
 

+ 3 - 3
panda/src/device/mouseAndKeyboard.cxx

@@ -35,9 +35,9 @@ qpMouseAndKeyboard(GraphicsWindow *window, int device, const string &name) :
   _window(window),
   _window(window),
   _device(device)
   _device(device)
 {
 {
-  _pixel_xy_output = define_output("PixelXY", EventStoreVec2::get_class_type());
-  _xy_output = define_output("XY", EventStoreVec2::get_class_type());
-  _button_events_output = define_output("ButtonEvents", ButtonEventList::get_class_type());
+  _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
+  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
 
 
   _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
   _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
   _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
   _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));

+ 141 - 0
panda/src/device/qpanalogNode.I

@@ -0,0 +1,141 @@
+// Filename: qpanalogNode.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::OutputData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpAnalogNode::OutputData::
+OutputData() {
+  _index = -1;
+  _flip = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::is_valid
+//       Access: Public
+//  Description: Returns true if the qpAnalogNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpAnalogNode::
+is_valid() const {
+  return (_analog != (ClientAnalogDevice *)NULL) && _analog->is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::get_num_controls
+//       Access: Public
+//  Description: Returns the number of analog controls known to the
+//               qpAnalogNode.  This number may change as more controls
+//               are discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpAnalogNode::
+get_num_controls() const {
+  _analog->lock();
+  int result = _analog->get_num_controls();
+  _analog->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::get_control_state
+//       Access: Public
+//  Description: Returns the current position of indicated analog
+//               control identified by its index number, or 0.0 if
+//               the control is unknown.  The normal range of a single
+//               control is -1.0 to 1.0.
+////////////////////////////////////////////////////////////////////
+INLINE double qpAnalogNode::
+get_control_state(int index) const {
+  _analog->lock();
+  double result = _analog->get_control_state(index);
+  _analog->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::is_control_known
+//       Access: Public
+//  Description: Returns true if the state of the indicated analog
+//               control is known, or false if we have never heard
+//               anything about this particular control.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpAnalogNode::
+is_control_known(int index) const {
+  _analog->lock();
+  bool result = _analog->is_control_known(index);
+  _analog->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::set_output
+//       Access: Public
+//  Description: Causes a particular analog control to be placed in
+//               the data graph for the indicated channel.  Normally,
+//               a mouse uses channels 0 and 1 for the X and Y
+//               information, respectively; channels 0, 1, and 2 are
+//               available.  If flip is true, the analog control value
+//               will be reversed before outputting it.
+////////////////////////////////////////////////////////////////////
+INLINE void qpAnalogNode::
+set_output(int channel, int index, bool flip) {
+  nassertv(channel >= 0 && channel < max_outputs);
+  _outputs[channel]._index = index;
+  _outputs[channel]._flip = flip;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::clear_output
+//       Access: Public
+//  Description: Removes the output to the data graph associated with
+//               the indicated channel.  See set_output().
+////////////////////////////////////////////////////////////////////
+INLINE void qpAnalogNode::
+clear_output(int channel) {
+  nassertv(channel >= 0 && channel < max_outputs);
+  _outputs[channel]._index = -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::get_output
+//       Access: Public
+//  Description: Returns the analog control index that is output to
+//               the data graph on the indicated channel, or -1 if no
+//               control is output on that channel.  See set_output().
+////////////////////////////////////////////////////////////////////
+INLINE int qpAnalogNode::
+get_output(int channel) const {
+  nassertr(channel >= 0 && channel < max_outputs, -1);
+  return _outputs[channel]._index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::is_output_flipped
+//       Access: Public
+//  Description: Returns true if the analog control index that is
+//               output to the data graph on the indicated channel is
+//               flipped.  See set_output().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpAnalogNode::
+is_output_flipped(int channel) const {
+  nassertr(channel >= 0 && channel < max_outputs, false);
+  return _outputs[channel]._flip;
+}

+ 121 - 0
panda/src/device/qpanalogNode.cxx

@@ -0,0 +1,121 @@
+// Filename: qpanalogNode.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpanalogNode.h"
+#include "config_device.h"
+#include "dataNodeTransmit.h"
+
+
+TypeHandle qpAnalogNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpAnalogNode::
+qpAnalogNode(ClientBase *client, const string &device_name) :
+  qpDataNode(device_name)
+{
+  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
+  _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+
+  nassertv(client != (ClientBase *)NULL);
+  PT(ClientDevice) device =
+    client->get_device(ClientAnalogDevice::get_class_type(), device_name);
+
+  if (device == (ClientDevice *)NULL) {
+    device_cat.warning()
+      << "Unable to open analog device " << device_name << "\n";
+    return;
+  }
+
+  if (!device->is_of_type(ClientAnalogDevice::get_class_type())) {
+    device_cat.error()
+      << "Inappropriate device type " << device->get_type()
+      << " created; expected a ClientAnalogDevice.\n";
+    return;
+  }
+
+  _analog = DCAST(ClientAnalogDevice, device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpAnalogNode::
+~qpAnalogNode() {
+  // When the _analog pointer destructs, the ClientAnalogDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpAnalogNode::
+write(ostream &out, int indent_level) const {
+  qpDataNode::write(out, indent_level);
+
+  if (_analog != (ClientAnalogDevice *)NULL) {
+    _analog->lock();
+    _analog->write_controls(out, indent_level + 2);
+    _analog->unlock();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnalogNode::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpAnalogNode::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  if (is_valid()) {
+    _analog->poll();
+
+    LPoint2f out(0.0f, 0.0f);
+
+    _analog->lock();
+    for (int i = 0; i < max_outputs; i++) {
+      if (_outputs[i]._index >= 0 &&
+          _analog->is_control_known(_outputs[i]._index)) {
+        if (_outputs[i]._flip) {
+          out[i] = -_analog->get_control_state(_outputs[i]._index);
+        } else {
+          out[i] = _analog->get_control_state(_outputs[i]._index);
+        }
+      }
+    }
+    _analog->unlock();
+    _xy->set_value(out);
+    output.set_data(_xy_output, EventParameter(_xy));
+  }
+}

+ 113 - 0
panda/src/device/qpanalogNode.h

@@ -0,0 +1,113 @@
+// Filename: qpanalogNode.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpANALOGNODE_H
+#define qpANALOGNODE_H
+
+#include "pandabase.h"
+
+#include "clientBase.h"
+#include "clientAnalogDevice.h"
+#include "qpdataNode.h"
+#include "linmath_events.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpAnalogNode
+// Description : This is the primary interface to analog controls like
+//               sliders and joysticks associated with a ClientBase.
+//               This creates a node that connects to the named analog
+//               device, if it exists, and provides hooks to the user
+//               to read the state of any of the sequentially numbered
+//               controls associated with that device.
+//
+//               Each control can return a value ranging from -1 to 1,
+//               reflecting the current position of the control within
+//               its total range of motion.
+//
+//               The user may choose up to two analog controls to
+//               place on the data graph as the two channels of an
+//               xy datagram, similarly to the way a mouse places its
+//               position data.  In this way, an AnalogNode may be
+//               used in place of a mouse.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpAnalogNode : public qpDataNode {
+PUBLISHED:
+  qpAnalogNode(ClientBase *client, const string &device_name);
+  virtual ~qpAnalogNode();
+
+  INLINE bool is_valid() const;
+
+  INLINE int get_num_controls() const;
+
+  INLINE double get_control_state(int index) const;
+  INLINE bool is_control_known(int index) const;
+
+  INLINE void set_output(int channel, int index, bool flip);
+  INLINE void clear_output(int channel);
+  INLINE int get_output(int channel) const;
+  INLINE bool is_output_flipped(int channel) const;
+
+public:
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
+  class OutputData {
+  public:
+    INLINE OutputData();
+    int _index;
+    bool _flip;
+  };
+
+  enum { max_outputs = 2 };
+  OutputData _outputs[max_outputs];
+
+  PT(ClientAnalogDevice) _analog;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // outputs
+  int _xy_output;
+
+  PT(EventStoreVec2) _xy;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpAnalogNode",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpanalogNode.I"
+
+#endif

+ 114 - 0
panda/src/device/qpbuttonNode.I

@@ -0,0 +1,114 @@
+// Filename: qpbuttonNode.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::is_valid
+//       Access: Public
+//  Description: Returns true if the qpButtonNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpButtonNode::
+is_valid() const {
+  return (_button != (ClientButtonDevice *)NULL) && _button->is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::get_num_buttons
+//       Access: Public
+//  Description: Returns the number of buttons known to the
+//               qpButtonNode.  This includes those buttons whose state
+//               has been seen, as well as buttons that have been
+//               associated with a ButtonHandle even if their state is
+//               unknown.  This number may change as more buttons are
+//               discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpButtonNode::
+get_num_buttons() const {
+  _button->lock();
+  int result = _button->get_num_buttons();
+  _button->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::set_button_map
+//       Access: Public
+//  Description: Associates the indicated ButtonHandle with the button
+//               of the indicated index number.  When the given button
+//               index changes state, a corresponding ButtonEvent will
+//               be generated with the given ButtonHandle.  Pass
+//               ButtonHandle::none() to turn off any association.
+//
+//               It is not necessary to call this if you simply want
+//               to query the state of the various buttons by index
+//               number; this is only necessary in order to generate
+//               ButtonEvents when the buttons change state.
+////////////////////////////////////////////////////////////////////
+INLINE void qpButtonNode::
+set_button_map(int index, ButtonHandle button) {
+  _button->lock();
+  _button->set_button_map(index, button);
+  _button->unlock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::get_button_map
+//       Access: Public
+//  Description: Returns the ButtonHandle that was previously
+//               associated with the given index number by
+//               a call to set_button_map(), or ButtonHandle::none()
+//               if no button was associated.
+////////////////////////////////////////////////////////////////////
+INLINE ButtonHandle qpButtonNode::
+get_button_map(int index) const {
+  _button->lock();
+  ButtonHandle result = _button->get_button_map(index);
+  _button->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::get_button_state
+//       Access: Public
+//  Description: Returns true if the indicated button (identified by
+//               its index number) is currently known to be down, or
+//               false if it is up or unknown.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpButtonNode::
+get_button_state(int index) const {
+  _button->lock();
+  bool result = _button->get_button_state(index);
+  _button->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::is_button_known
+//       Access: Public
+//  Description: Returns true if the state of the indicated button is
+//               known, or false if we have never heard anything about
+//               this particular button.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpButtonNode::
+is_button_known(int index) const {
+  _button->lock();
+  bool result = _button->is_button_known(index);
+  _button->unlock();
+  return result;
+}

+ 131 - 0
panda/src/device/qpbuttonNode.cxx

@@ -0,0 +1,131 @@
+// Filename: qpbuttonNode.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpbuttonNode.h"
+#include "config_device.h"
+#include "dataNodeTransmit.h"
+#include "buttonEventList.h"
+
+TypeHandle qpButtonNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpButtonNode::
+qpButtonNode(ClientBase *client, const string &device_name) :
+  qpDataNode(device_name)
+{
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
+  _button_events = new ButtonEventList;
+
+  nassertv(client != (ClientBase *)NULL);
+  PT(ClientDevice) device =
+    client->get_device(ClientButtonDevice::get_class_type(), device_name);
+
+  if (device == (ClientDevice *)NULL) {
+    device_cat.warning()
+      << "Unable to open button device " << device_name << "\n";
+    return;
+  }
+
+  if (!device->is_of_type(ClientButtonDevice::get_class_type())) {
+    device_cat.error()
+      << "Inappropriate device type " << device->get_type()
+      << " created; expected a ClientButtonDevice.\n";
+    return;
+  }
+
+  _button = DCAST(ClientButtonDevice, device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpButtonNode::
+~qpButtonNode() {
+  // When the _button pointer destructs, the ClientButtonDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::output
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpButtonNode::
+output(ostream &out) const {
+  qpDataNode::output(out);
+
+  if (_button != (ClientButtonDevice *)NULL) {
+    out << " (";
+    _button->lock();
+    _button->output_buttons(out);
+    _button->unlock();
+    out << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpButtonNode::
+write(ostream &out, int indent_level) const {
+  qpDataNode::write(out, indent_level);
+
+  if (_button != (ClientButtonDevice *)NULL) {
+    _button->lock();
+    _button->write_buttons(out, indent_level + 2);
+    _button->unlock();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpButtonNode::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpButtonNode::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  if (is_valid()) {
+    _button->poll();
+    _button->lock();
+
+    // *** here we should copy the button events from the ClientButtonDevice.
+    //    (*_button_events) = (*_button->get_button_events());
+
+    _button->get_button_events()->clear();
+    _button->unlock();
+
+    output.set_data(_button_events_output, EventParameter(_button_events));
+  }
+}

+ 97 - 0
panda/src/device/qpbuttonNode.h

@@ -0,0 +1,97 @@
+// Filename: qpButtonNode.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpBUTTONNODE_H
+#define qpBUTTONNODE_H
+
+#include "pandabase.h"
+
+#include "clientBase.h"
+#include "clientButtonDevice.h"
+#include "qpdataNode.h"
+#include "buttonEventList.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpButtonNode
+// Description : This is the primary interface to on/off button
+//               devices associated with a ClientBase.  This creates a
+//               node that connects to the named button device, if it
+//               exists, and provides hooks to the user to read the
+//               state of any of the sequentially numbered buttons
+//               associated with that device.
+//
+//               It also can associate an arbitrary ButtonHandle with
+//               each button; when buttons are associated with
+//               ButtonHandles, this node will put appropriate up and
+//               down events on the data graph for each button state
+//               change.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpButtonNode : public qpDataNode {
+PUBLISHED:
+  qpButtonNode(ClientBase *client, const string &device_name);
+  virtual ~qpButtonNode();
+
+  INLINE bool is_valid() const;
+
+  INLINE int get_num_buttons() const;
+
+  INLINE void set_button_map(int index, ButtonHandle button);
+  INLINE ButtonHandle get_button_map(int index) const;
+
+  INLINE bool get_button_state(int index) const;
+  INLINE bool is_button_known(int index) const;
+
+public:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
+  PT(ClientButtonDevice) _button;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // outputs
+  int _button_events_output;
+  PT(ButtonEventList) _button_events;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpButtonNode",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpbuttonNode.I"
+
+#endif

+ 75 - 0
panda/src/device/qpdialNode.I

@@ -0,0 +1,75 @@
+// Filename: qpdialNode.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::is_valid
+//       Access: Public
+//  Description: Returns true if the qpDialNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpDialNode::
+is_valid() const {
+  return (_dial != (ClientDialDevice *)NULL) && _dial->is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::get_num_dials
+//       Access: Public
+//  Description: Returns the number of dial dials known to the
+//               qpDialNode.  This number may change as more dials
+//               are discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpDialNode::
+get_num_dials() const {
+  _dial->lock();
+  int result = _dial->get_num_dials();
+  _dial->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::read_dial
+//       Access: Public
+//  Description: Returns the number of complete revolutions of the
+//               dial since the last time read_dial() was called.
+//               This is a destructive operation; it is not possible
+//               to read the dial without resetting the counter.
+////////////////////////////////////////////////////////////////////
+INLINE double qpDialNode::
+read_dial(int index) {
+  _dial->lock();
+  double result = _dial->read_dial(index);
+  _dial->unlock();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::is_dial_known
+//       Access: Public
+//  Description: Returns true if the state of the indicated dial
+//               dial is known, or false if we have never heard
+//               anything about this particular dial.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpDialNode::
+is_dial_known(int index) const {
+  _dial->lock();
+  bool result = _dial->is_dial_known(index);
+  _dial->unlock();
+  return result;
+}

+ 86 - 0
panda/src/device/qpdialNode.cxx

@@ -0,0 +1,86 @@
+// Filename: qpdialNode.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpdialNode.h"
+#include "config_device.h"
+#include "dataNodeTransmit.h"
+
+TypeHandle qpDialNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpDialNode::
+qpDialNode(ClientBase *client, const string &device_name) :
+  qpDataNode(device_name)
+{
+  nassertv(client != (ClientBase *)NULL);
+  PT(ClientDevice) device =
+    client->get_device(ClientDialDevice::get_class_type(), device_name);
+
+  if (device == (ClientDevice *)NULL) {
+    device_cat.warning()
+      << "Unable to open dial device " << device_name << "\n";
+    return;
+  }
+
+  if (!device->is_of_type(ClientDialDevice::get_class_type())) {
+    device_cat.error()
+      << "Inappropriate device type " << device->get_type()
+      << " created; expected a ClientDialDevice.\n";
+    return;
+  }
+
+  _dial = DCAST(ClientDialDevice, device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpDialNode::
+~qpDialNode() {
+  // When the _dial pointer destructs, the ClientDialDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDialNode::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpDialNode::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  if (is_valid()) {
+    _dial->poll();
+
+    // Not clear yet what we should be transmitting.
+  }
+}

+ 86 - 0
panda/src/device/qpdialNode.h

@@ -0,0 +1,86 @@
+// Filename: qpdialNode.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpDIALNODE_H
+#define qpDIALNODE_H
+
+#include "pandabase.h"
+
+#include "clientBase.h"
+#include "clientDialDevice.h"
+#include "qpdataNode.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpDialNode
+// Description : This is the primary interface to infinite dial type
+//               devices associated with a ClientBase.  This creates a
+//               node that connects to the named dial device, if it
+//               exists, and provides hooks to the user to read the
+//               state of any of the sequentially numbered dial
+//               controls associated with that device.
+//
+//               A dial is a rotating device that does not have
+//               stops--it can keep rotating any number of times.
+//               Therefore it does not have a specific position at any
+//               given time, unlike an AnalogDevice.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpDialNode : public qpDataNode {
+PUBLISHED:
+  qpDialNode(ClientBase *client, const string &device_name);
+  virtual ~qpDialNode();
+
+  INLINE bool is_valid() const;
+
+  INLINE int get_num_dials() const;
+
+  INLINE double read_dial(int index);
+  INLINE bool is_dial_known(int index) const;
+
+private:
+  PT(ClientDialDevice) _dial;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // no inputs or outputs at the moment.
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpDialNode",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpdialNode.I"
+
+#endif

+ 118 - 0
panda/src/device/qptrackerNode.I

@@ -0,0 +1,118 @@
+// Filename: qptrackerNode.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::is_valid
+//       Access: Public
+//  Description: Returns true if the qpTrackerNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpTrackerNode::
+is_valid() const {
+  return (_tracker != (ClientTrackerDevice *)NULL) && _tracker->is_connected();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::get_pos
+//       Access: Public
+//  Description: Returns the current position of the tracker, if it is
+//               available.
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint3f &qpTrackerNode::
+get_pos() const {
+  return _data.get_pos();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::get_orient
+//       Access: Public
+//  Description: Returns the current orientation of the tracker, if it
+//               is available.
+////////////////////////////////////////////////////////////////////
+INLINE const LOrientationf &qpTrackerNode::
+get_orient() const {
+  return _data.get_orient();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::get_transform
+//       Access: Public
+//  Description: Returns the current position and orientation of the
+//               tracker, as a combined matrix.
+////////////////////////////////////////////////////////////////////
+INLINE const LMatrix4f &qpTrackerNode::
+get_transform() const {
+  return _mat;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::set_tracker_coordinate_system
+//       Access: Published
+//  Description: Specifies the coordinate system that the tracker
+//               associated with this node will operate in.  Normally,
+//               this is set from the ClientBase that's used to create
+//               the qpTrackerNode, so it should not need to be set on
+//               an individual tracker basis.
+////////////////////////////////////////////////////////////////////
+INLINE void qpTrackerNode::
+set_tracker_coordinate_system(CoordinateSystem cs) {
+  _tracker_cs = cs;
+  if (_tracker_cs == CS_default) {
+    _tracker_cs = default_coordinate_system;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::get_tracker_coordinate_system
+//       Access: Published
+//  Description: Returns the coordinate system that the tracker
+//               associated with this node will operate in.
+////////////////////////////////////////////////////////////////////
+INLINE CoordinateSystem qpTrackerNode::
+get_tracker_coordinate_system() const {
+  return _tracker_cs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::set_graph_coordinate_system
+//       Access: Published
+//  Description: Specifies the coordinate system that the qpTrackerNode
+//               will convert its transform into for passing down the
+//               data graph.  Normally, this is CS_default.
+////////////////////////////////////////////////////////////////////
+INLINE void qpTrackerNode::
+set_graph_coordinate_system(CoordinateSystem cs) {
+  _graph_cs = cs;
+  if (_graph_cs == CS_default) {
+    _graph_cs = default_coordinate_system;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::get_graph_coordinate_system
+//       Access: Published
+//  Description: Returns the coordinate system that the qpTrackerNode
+//               will convert its transform into for passing down the
+//               data graph.  Normally, this is CS_default.
+////////////////////////////////////////////////////////////////////
+INLINE CoordinateSystem qpTrackerNode::
+get_graph_coordinate_system() const {
+  return _graph_cs;
+}

+ 104 - 0
panda/src/device/qptrackerNode.cxx

@@ -0,0 +1,104 @@
+// Filename: qptrackerNode.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qptrackerNode.h"
+#include "config_device.h"
+
+TypeHandle qpTrackerNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpTrackerNode::
+qpTrackerNode(ClientBase *client, const string &device_name) :
+  qpDataNode(device_name)
+{
+  _transform_output = define_output("transform", EventStoreMat4::get_class_type());
+
+  _transform = new EventStoreMat4(LMatrix4f::ident_mat());
+
+  nassertv(client != (ClientBase *)NULL);
+  set_tracker_coordinate_system(client->get_coordinate_system());
+  set_graph_coordinate_system(CS_default);
+
+  PT(ClientDevice) device =
+    client->get_device(ClientTrackerDevice::get_class_type(), device_name);
+
+  if (device == (ClientDevice *)NULL) {
+    device_cat.warning()
+      << "Unable to open tracker device " << device_name << "\n";
+    return;
+  }
+
+  if (!device->is_of_type(ClientTrackerDevice::get_class_type())) {
+    device_cat.error()
+      << "Inappropriate device type " << device->get_type()
+      << " created; expected a ClientTrackerDevice.\n";
+    return;
+  }
+
+  _tracker = DCAST(ClientTrackerDevice, device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpTrackerNode::
+~qpTrackerNode() {
+  // When the _tracker pointer destructs, the ClientTrackerDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpTrackerNode::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpTrackerNode::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  if (is_valid()) {
+    _tracker->poll();
+    _tracker->lock();
+    _data = _tracker->get_data();
+    _tracker->unlock();
+
+    _data.get_orient().extract_to_matrix(_mat);
+    if (_tracker_cs != _graph_cs) {
+      // Convert the rotation for passing down the data graph.
+      _mat = _mat * LMatrix4f::convert_mat(_tracker_cs, _graph_cs);
+    }
+    _mat.set_row(3, _data.get_pos());
+
+    // Now send our matrix down the pipe.
+    _transform->set_value(_mat);
+    output.set_data(_transform_output, EventParameter(_transform));
+  }
+}

+ 94 - 0
panda/src/device/qptrackerNode.h

@@ -0,0 +1,94 @@
+// Filename: qptrackerNode.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpTRACKERNODE_H
+#define qpTRACKERNODE_H
+
+#include "pandabase.h"
+
+#include "clientBase.h"
+#include "trackerData.h"
+#include "clientTrackerDevice.h"
+#include "qpdataNode.h"
+#include "luse.h"
+#include "linmath_events.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TrackerNode
+// Description : This is the primary interface to a Tracker object
+//               associated with a ClientBase.  It reads the position
+//               and orientation information from the tracker and
+//               makes it available as a transformation on the data
+//               graph.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpTrackerNode : public qpDataNode {
+PUBLISHED:
+  qpTrackerNode(ClientBase *client, const string &device_name);
+  virtual ~qpTrackerNode();
+
+  INLINE bool is_valid() const;
+
+  INLINE const LPoint3f &get_pos() const;
+  INLINE const LOrientationf &get_orient() const;
+  INLINE const LMatrix4f &get_transform() const;
+
+  INLINE void set_tracker_coordinate_system(CoordinateSystem cs);
+  INLINE CoordinateSystem get_tracker_coordinate_system() const;
+
+  INLINE void set_graph_coordinate_system(CoordinateSystem cs);
+  INLINE CoordinateSystem get_graph_coordinate_system() const;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // outputs
+  int _transform_output;
+
+  PT(EventStoreMat4) _transform;
+
+private:
+  PT(ClientTrackerDevice) _tracker;
+  TrackerData _data;
+  LMatrix4f _mat;
+  CoordinateSystem _tracker_cs, _graph_cs;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpTrackerNode",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qptrackerNode.I"
+
+#endif

+ 144 - 0
panda/src/device/qpvirtualMouse.cxx

@@ -0,0 +1,144 @@
+// Filename: qpvirtualMouse.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpvirtualMouse.h"
+
+TypeHandle qpVirtualMouse::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpVirtualMouse::
+qpVirtualMouse(const string &name) :
+  qpDataNode(name)
+{
+  _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
+  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
+
+  _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _button_events = new ButtonEventList;
+  _next_button_events = new ButtonEventList;
+
+  _mouse_x = 0;
+  _mouse_y = 0;
+  _win_width = 100;
+  _win_height = 100;
+  _mouse_on = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::set_mouse_pos
+//       Access: Published
+//  Description: Sets the current mouse pixel location, where (0,0) is
+//               the upper left, and (width-1, height-1) is the lower
+//               right pixel of the virtual window.
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+set_mouse_pos(int x, int y) {
+  _mouse_x = x;
+  _mouse_y = y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::set_window_size
+//       Access: Published
+//  Description: Sets the size of the "window" in which the mouse
+//               rolls.  This changes the meaning of the values passed
+//               to set_mouse_pos().
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+set_window_size(int width, int height) {
+  _win_width = width;
+  _win_height = height;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::set_mouse_on
+//       Access: Published
+//  Description: Sets whether the mouse should appear to be within the
+//               window or not.  If this is true, the mouse is within
+//               the window; if false, the mouse is not within the
+//               window (and set_mouse_pos() means nothing).
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+set_mouse_on(bool flag) {
+  _mouse_on = flag;
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::press_button
+//       Access: Published
+//  Description: Simulates a mouse or keyboard button being depressed.
+//               This should be followed up by a call to
+//               release_button() sometime later (possibly
+//               immediately).
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+press_button(ButtonHandle button) {
+  _next_button_events->add_event(ButtonEvent(button, ButtonEvent::T_down));
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::release_button
+//       Access: Published
+//  Description: Simulates the button being released.  This should
+//               follow a previous call to press_button().
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+release_button(ButtonHandle button) {
+  _next_button_events->add_event(ButtonEvent(button, ButtonEvent::T_up));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpVirtualMouse::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpVirtualMouse::
+do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+  // Swap in the button events, and clear them for next time.
+  PT(ButtonEventList) events = _button_events;
+  _button_events = _next_button_events;
+  _next_button_events = events;
+  _next_button_events->clear();
+  output.set_data(_button_events_output, EventParameter(_button_events));
+
+  if (_mouse_on) {
+    // The mouse is within the window.
+    _pixel_xy->set_value(LPoint2f(_mouse_x, _mouse_y));
+    output.set_data(_pixel_xy_output, EventParameter(_pixel_xy));
+
+    // Normalize pixel motion to range [-1,1].
+    float xf = (2.0f * (float)_mouse_x) / (float)_win_width - 1.0f;
+    float yf = 1.0f - (2.0f * (float)_mouse_y) / (float)_win_height;
+    _xy->set_value(LPoint2f(xf, yf));
+    output.set_data(_xy_output, EventParameter(_xy));
+  }
+}

+ 89 - 0
panda/src/device/qpvirtualMouse.h

@@ -0,0 +1,89 @@
+// Filename: qpvirtualMouse.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpVIRTUALMOUSE_H
+#define qpVIRTUALMOUSE_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "buttonHandle.h"
+#include "buttonEvent.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpVirtualMouse
+// Description : Poses as a MouseAndKeyboard object in the datagraph,
+//               but accepts input from user calls, rather than
+//               reading the actual mouse and keyboard from an input
+//               device.  The user can write high-level code to put
+//               the mouse wherever he/she wants, and to insert
+//               keypresses on demand.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpVirtualMouse : public qpDataNode {
+PUBLISHED:
+  qpVirtualMouse(const string &name);
+
+  void set_mouse_pos(int x, int y);
+  void set_window_size(int width, int height);
+  void set_mouse_on(bool flag);
+  
+  void press_button(ButtonHandle button);
+  void release_button(ButtonHandle button);
+
+private:
+  int _mouse_x, _mouse_y;
+  int _win_width, _win_height;
+  bool _mouse_on;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // outputs
+  int _pixel_xy_output;
+  int _xy_output;
+  int _button_events_output;
+
+  PT(EventStoreVec2) _pixel_xy;
+  PT(EventStoreVec2) _xy;
+  PT(ButtonEventList) _button_events;
+  PT(ButtonEventList) _next_button_events;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpMouseAndKeyboard",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+

+ 49 - 40
panda/src/dgraph/qpdataGraphTraverser.cxx

@@ -75,52 +75,21 @@ traverse(PandaNode *node) {
     r_transmit(data_node, (DataNodeTransmit *)NULL);
     r_transmit(data_node, (DataNodeTransmit *)NULL);
 
 
   } else {
   } else {
-    r_traverse_children(node, DataNodeTransmit());
+    traverse_below(node, DataNodeTransmit());
   }
   }
 
 
-  // Now pick up any nodes that didn't get completely traversed.
-  // These must be nodes that have multiple parents, with at least one
-  // parent completely outside of the data graph.
-
-  while (!_multipass_data.empty()) {
-    MultipassData::iterator mi = _multipass_data.begin();
-    qpDataNode *data_node = (*mi).first;
-    const CollectedData &collected_data = (*mi).second;
-
-    dgraph_cat.warning()
-      << *data_node << " improperly parented partly outside of data graph.\n";
-
-    r_transmit(data_node, &collected_data._data[0]);
-    _multipass_data.erase(mi);
-  }  
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpDataGraphTraverser::r_transmit
-//       Access: Private
-//  Description: Part of the recursive implementation of traverse().
-//               This transmits the given data into the indicated
-//               DataNode, and then sends the output data to each of
-//               the node's children.
-////////////////////////////////////////////////////////////////////
-void qpDataGraphTraverser::
-r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]) {
-  DataNodeTransmit output;
-  output.reserve(data_node->get_num_outputs());
-  data_node->transmit_data(inputs, output);
-
-  r_traverse_children(data_node, output);
+  collect_leftovers();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: qpDataGraphTraverser::r_traverse_children
-//       Access: Private
-//  Description: Part of the recursive implementation of traverse().
-//               This visits each of the children of the indicated
-//               node, passing in the given data.
+//     Function: qpDataGraphTraverser::traverse_below
+//       Access: Public
+//  Description: Continues the traversal to all the children of the
+//               indicated node, passing in the given data, without
+//               actually calling transmit_data() on the given node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpDataGraphTraverser::
 void qpDataGraphTraverser::
-r_traverse_children(PandaNode *node, const DataNodeTransmit &output) {
+traverse_below(PandaNode *node, const DataNodeTransmit &output) {
   PandaNode::Children cr = node->get_children();
   PandaNode::Children cr = node->get_children();
   int num_children = cr.get_num_children();
   int num_children = cr.get_num_children();
 
 
@@ -161,7 +130,47 @@ r_traverse_children(PandaNode *node, const DataNodeTransmit &output) {
       // be passing the data through here, it doesn't do any good
       // be passing the data through here, it doesn't do any good
       // anyway, since the child nodes of this node will not know how
       // anyway, since the child nodes of this node will not know how
       // to interpret the data from a non-DataNode parent.)
       // to interpret the data from a non-DataNode parent.)
-      r_traverse_children(child_node, output);
+      traverse_below(child_node, output);
     }
     }
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::collect_leftovers
+//       Access: Public
+//  Description: Pick up any nodes that didn't get completely
+//               traversed.  These must be nodes that have multiple
+//               parents, with at least one parent completely outside
+//               of the data graph.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::
+collect_leftovers() {
+  while (!_multipass_data.empty()) {
+    MultipassData::iterator mi = _multipass_data.begin();
+    qpDataNode *data_node = (*mi).first;
+    const CollectedData &collected_data = (*mi).second;
+
+    dgraph_cat.warning()
+      << *data_node << " improperly parented partly outside of data graph.\n";
+
+    r_transmit(data_node, &collected_data._data[0]);
+    _multipass_data.erase(mi);
+  }  
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDataGraphTraverser::r_transmit
+//       Access: Private
+//  Description: Part of the recursive implementation of traverse().
+//               This transmits the given data into the indicated
+//               DataNode, and then sends the output data to each of
+//               the node's children.
+////////////////////////////////////////////////////////////////////
+void qpDataGraphTraverser::
+r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]) {
+  DataNodeTransmit output;
+  output.reserve(data_node->get_num_outputs());
+  data_node->transmit_data(inputs, output);
+
+  traverse_below(data_node, output);
+}

+ 2 - 1
panda/src/dgraph/qpdataGraphTraverser.h

@@ -42,10 +42,11 @@ PUBLISHED:
   ~qpDataGraphTraverser();
   ~qpDataGraphTraverser();
 
 
   void traverse(PandaNode *node);
   void traverse(PandaNode *node);
+  void traverse_below(PandaNode *node, const DataNodeTransmit &output);
+  void collect_leftovers();
 
 
 private:
 private:
   void r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]);
   void r_transmit(qpDataNode *data_node, const DataNodeTransmit inputs[]);
-  void r_traverse_children(PandaNode *node, const DataNodeTransmit &output);
 
 
   typedef pvector<DataNodeTransmit> DataVector;
   typedef pvector<DataNodeTransmit> DataVector;
 
 

+ 17 - 7
panda/src/tform/Sources.pp

@@ -12,11 +12,14 @@
   #define SOURCES  \
   #define SOURCES  \
     buttonThrower.h \
     buttonThrower.h \
     qpbuttonThrower.h \
     qpbuttonThrower.h \
-    config_tform.h dataValve.I dataValve.h  \
-    driveInterface.I driveInterface.h mouseWatcher.I  \
-    mouseWatcher.h mouseWatcherGroup.h \
-    mouseWatcherParameter.I mouseWatcherParameter.h  \
-    mouseWatcherRegion.I mouseWatcherRegion.h  \
+    config_tform.h dataValve.I dataValve.h \
+    driveInterface.I driveInterface.h \
+    qpdriveInterface.I qpdriveInterface.h \
+    mouseWatcher.I mouseWatcher.h \
+    qpmouseWatcher.I qpmouseWatcher.h \
+    mouseWatcherGroup.h \
+    mouseWatcherParameter.I mouseWatcherParameter.h \
+    mouseWatcherRegion.I mouseWatcherRegion.h \
     planarSlider.h trackball.h \
     planarSlider.h trackball.h \
     qptrackball.h \
     qptrackball.h \
     transform2sg.h \
     transform2sg.h \
@@ -26,7 +29,11 @@
     buttonThrower.cxx \
     buttonThrower.cxx \
     qpbuttonThrower.cxx \
     qpbuttonThrower.cxx \
     config_tform.cxx dataValve.cxx  \
     config_tform.cxx dataValve.cxx  \
-    driveInterface.cxx mouseWatcher.cxx mouseWatcherGroup.cxx \
+    driveInterface.cxx \
+    qpdriveInterface.cxx \
+    mouseWatcher.cxx \
+    qpmouseWatcher.cxx \
+    mouseWatcherGroup.cxx \
     mouseWatcherParameter.cxx mouseWatcherRegion.cxx  \
     mouseWatcherParameter.cxx mouseWatcherRegion.cxx  \
     planarSlider.cxx trackball.cxx \
     planarSlider.cxx trackball.cxx \
     qptrackball.cxx \
     qptrackball.cxx \
@@ -37,7 +44,10 @@
     buttonThrower.h \
     buttonThrower.h \
     qpbuttonThrower.h \
     qpbuttonThrower.h \
     dataValve.I dataValve.h \
     dataValve.I dataValve.h \
-    driveInterface.I driveInterface.h mouseWatcher.I mouseWatcher.h \
+    driveInterface.I driveInterface.h \
+    qpdriveInterface.I qpdriveInterface.h \
+    mouseWatcher.I mouseWatcher.h \
+    qpmouseWatcher.I qpmouseWatcher.h \
     mouseWatcherGroup.h \
     mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
     mouseWatcherRegion.I mouseWatcherRegion.h \
     mouseWatcherRegion.I mouseWatcherRegion.h \

+ 4 - 0
panda/src/tform/config_tform.cxx

@@ -20,9 +20,11 @@
 
 
 #include "dataValve.h"
 #include "dataValve.h"
 #include "driveInterface.h"
 #include "driveInterface.h"
+#include "qpdriveInterface.h"
 #include "buttonThrower.h"
 #include "buttonThrower.h"
 #include "qpbuttonThrower.h"
 #include "qpbuttonThrower.h"
 #include "mouseWatcher.h"
 #include "mouseWatcher.h"
+#include "qpmouseWatcher.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherGroup.h"
 #include "mouseWatcherRegion.h"
 #include "mouseWatcherRegion.h"
 #include "planarSlider.h"
 #include "planarSlider.h"
@@ -51,9 +53,11 @@ const double drive_horizontal_ramp_down_time = config_tform.GetDouble("drive-hor
 ConfigureFn(config_tform) {
 ConfigureFn(config_tform) {
   DataValve::init_type();
   DataValve::init_type();
   DriveInterface::init_type();
   DriveInterface::init_type();
+  qpDriveInterface::init_type();
   ButtonThrower::init_type();
   ButtonThrower::init_type();
   qpButtonThrower::init_type();
   qpButtonThrower::init_type();
   MouseWatcher::init_type();
   MouseWatcher::init_type();
+  qpMouseWatcher::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherRegion::init_type();
   MouseWatcherRegion::init_type();
   PlanarSlider::init_type();
   PlanarSlider::init_type();

+ 1 - 0
panda/src/tform/mouseWatcherGroup.h

@@ -58,6 +58,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class MouseWatcher;
   friend class MouseWatcher;
+  friend class qpMouseWatcher;
   friend class BlobWatcher;
   friend class BlobWatcher;
 };
 };
 
 

+ 2 - 2
panda/src/tform/qpbuttonThrower.cxx

@@ -36,8 +36,8 @@ qpButtonThrower::
 qpButtonThrower(const string &name) :
 qpButtonThrower(const string &name) :
   qpDataNode(name)
   qpDataNode(name)
 {
 {
-  _button_events_input = define_input("ButtonEvents", ButtonEventList::get_class_type());
-  _button_events_output = define_output("ButtonEvents", ButtonEventList::get_class_type());
+  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
 
 
   _button_events = new ButtonEventList;
   _button_events = new ButtonEventList;
 
 

+ 526 - 0
panda/src/tform/qpdriveInterface.I

@@ -0,0 +1,526 @@
+// Filename: qpdriveInterface.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_forward_speed
+//       Access: Published
+//  Description: Sets the speed of full forward motion, when the mouse
+//               is at the very top of the window.  This is in units
+//               (e.g. feet) per second.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_forward_speed(float speed) {
+  _forward_speed = speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_forward_speed
+//       Access: Published
+//  Description: Returns the speed of full forward motion, when the
+//               mouse is at the very top of the window.  This is in
+//               units (e.g. feet) per second.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_forward_speed() const {
+  return _forward_speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_reverse_speed
+//       Access: Published
+//  Description: Sets the speed of full reverse motion, when the mouse
+//               is at the very bottom of the window.  This is in
+//               units (e.g. feet) per second.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_reverse_speed(float speed) {
+  _reverse_speed = speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_reverse_speed
+//       Access: Published
+//  Description: Returns the speed of full reverse motion, when the
+//               mouse is at the very bottom of the window.  This is
+//               in units (e.g. feet) per second.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_reverse_speed() const {
+  return _reverse_speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_rotate_speed
+//       Access: Published
+//  Description: Sets the maximum rate at which the user can rotate
+//               left or right, when the mouse is at the very edge of
+//               the window.  This is in degrees per second.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_rotate_speed(float speed) {
+  _rotate_speed = speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_rotate_speed
+//       Access: Published
+//  Description: Returns the maximum rate at which the user can rotate
+//               left or right, when the mouse is at the very edge of
+//               the window.  This is in degrees per second.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_rotate_speed() const {
+  return _rotate_speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_vertical_dead_zone
+//       Access: Published
+//  Description: Sets the size of the horizontal bar in the center of
+//               the screen that represents the "dead zone" of
+//               vertical motion: the region in which the mouse does
+//               not report vertical motion.  This is in a fraction of
+//               the window height, so 0.5 will set a dead zone as
+//               large as half the screen.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_vertical_dead_zone(float speed) {
+  _vertical_dead_zone = speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_vertical_dead_zone
+//       Access: Published
+//  Description: Returns the size of the horizontal bar in the center
+//               of the screen that represents the "dead zone" of
+//               vertical motion: the region in which the mouse does
+//               not report vertical motion.  This is in a fraction of
+//               the window height, so 0.5 will set a dead zone as
+//               large as half the screen.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_vertical_dead_zone() const {
+  return _vertical_dead_zone;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_horizontal_dead_zone
+//       Access: Published
+//  Description: Sets the size of the vertical bar in the center of
+//               the screen that represents the "dead zone" of
+//               horizontal motion: the region in which the mouse does
+//               not report horizontal motion.  This is in a fraction of
+//               the window width, so 0.5 will set a dead zone as
+//               large as half the screen.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_horizontal_dead_zone(float speed) {
+  _horizontal_dead_zone = speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_horizontal_dead_zone
+//       Access: Published
+//  Description: Returns the size of the vertical bar in the center
+//               of the screen that represents the "dead zone" of
+//               horizontal motion: the region in which the mouse does
+//               not report horizontal motion.  This is in a fraction of
+//               the window width, so 0.5 will set a dead zone as
+//               large as half the screen.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_horizontal_dead_zone() const {
+  return _horizontal_dead_zone;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_vertical_ramp_up_time
+//       Access: Published
+//  Description: Sets the amount of time, in seconds, it takes between
+//               the time an up or down arrow key is pressed and the
+//               time it registers full forward or backward motion.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_vertical_ramp_up_time(float ramp_up_time) {
+  _vertical_ramp_up_time = ramp_up_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_vertical_ramp_up_time
+//       Access: Published
+//  Description: Returns the amount of time, in seconds, it takes
+//               between the time an up or down arrow key is pressed
+//               and the time it registers full forward or backward
+//               motion.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_vertical_ramp_up_time() const {
+  return _vertical_ramp_up_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_vertical_ramp_down_time
+//       Access: Published
+//  Description: Sets the amount of time, in seconds, it takes between
+//               the time an up or down arrow key is released and the
+//               time it registers no motion.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_vertical_ramp_down_time(float ramp_down_time) {
+  _vertical_ramp_down_time = ramp_down_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_vertical_ramp_down_time
+//       Access: Published
+//  Description: Returns the amount of time, in seconds, it takes
+//               between the time an up or down arrow key is released
+//               and the time it registers no motion.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_vertical_ramp_down_time() const {
+  return _vertical_ramp_down_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_horizontal_ramp_up_time
+//       Access: Published
+//  Description: Sets the amount of time, in seconds, it takes between
+//               the time a left or right arrow key is pressed and the
+//               time it registers full rotation.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_horizontal_ramp_up_time(float ramp_up_time) {
+  _horizontal_ramp_up_time = ramp_up_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_horizontal_ramp_up_time
+//       Access: Published
+//  Description: Returns the amount of time, in seconds, it takes
+//               between the time a left or right arrow key is pressed
+//               and the time it registers full rotation.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_horizontal_ramp_up_time() const {
+  return _horizontal_ramp_up_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_horizontal_ramp_down_time
+//       Access: Published
+//  Description: Sets the amount of time, in seconds, it takes between
+//               the time a left or right arrow key is released and the
+//               time it registers no motion.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_horizontal_ramp_down_time(float ramp_down_time) {
+  _horizontal_ramp_down_time = ramp_down_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_horizontal_ramp_down_time
+//       Access: Published
+//  Description: Returns the amount of time, in seconds, it takes
+//               between the time a left or right arrow key is released
+//               and the time it registers no motion.
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_horizontal_ramp_down_time() const {
+  return _horizontal_ramp_down_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_speed
+//       Access: Published
+//  Description: Returns the speed of the previous update in units/sec
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_speed() const {
+  return _speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_rot_speed
+//       Access: Published
+//  Description: Returns the rot_speed of the previous update in units/sec
+////////////////////////////////////////////////////////////////////
+INLINE float qpDriveInterface::
+get_rot_speed() const {
+  return _rot_speed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_pos
+//       Access: Published
+//  Description: Returns the driver's position.
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint3f &qpDriveInterface::
+get_pos() const {
+  return _xyz;
+}
+
+INLINE float qpDriveInterface::
+get_x() const {
+  return _xyz[0];
+}
+
+INLINE float qpDriveInterface::
+get_y() const {
+  return _xyz[1];
+}
+
+INLINE float qpDriveInterface::
+get_z() const {
+  return _xyz[2];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_pos
+//       Access: Published
+//  Description: Directly sets the driver's position.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_pos(const LVecBase3f &vec) {
+  _xyz = vec;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_pos(float x, float y, float z) {
+  _xyz.set(x, y, z);
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_x(float x) {
+  _xyz[0] = x;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_y(float y) {
+  _xyz[1] = y;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_z(float z) {
+  _xyz[2] = z;
+  recompute();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_hpr
+//       Access: Published
+//  Description: Returns the driver's orientation.
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase3f &qpDriveInterface::
+get_hpr() const {
+  return _hpr;
+}
+
+INLINE float qpDriveInterface::
+get_h() const {
+  return _hpr[0];
+}
+
+INLINE float qpDriveInterface::
+get_p() const {
+  return _hpr[1];
+}
+
+INLINE float qpDriveInterface::
+get_r() const {
+  return _hpr[2];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_hpr
+//       Access: Published
+//  Description: Directly sets the driver's orientation.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_hpr(const LVecBase3f &hpr) {
+  _hpr = hpr;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_hpr(float h, float p, float r) {
+  _hpr.set(h, p, r);
+  recompute();
+
+  if (_is_force_roll && r != _force_roll) {
+    reextract();
+  }
+}
+
+INLINE void qpDriveInterface::
+set_h(float h) {
+  _hpr[0] = h;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_p(float p) {
+  _hpr[1] = p;
+  recompute();
+}
+
+INLINE void qpDriveInterface::
+set_r(float r) {
+  _hpr[2] = r;
+  recompute();
+
+  if (_is_force_roll && r != _force_roll) {
+    reextract();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::is_force_roll
+//       Access: Published
+//  Description: Returns true if the force_roll state is in effect,
+//               e.g. because of a previous call to set_force_roll().
+//               In this state, the roll cannot be set to any value
+//               other than what the force_roll value indicates.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpDriveInterface::
+is_force_roll() const {
+  return _is_force_roll;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::clear_force_roll
+//       Access: Published
+//  Description: Disables the force_roll state.  See set_force_roll().
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+clear_force_roll() {
+  _is_force_roll = false;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_coordinate_system
+//       Access: Published
+//  Description: Sets the coordinate system of the qpDriveInterface.
+//               Normally, this is the default coordinate system.
+//               This changes the plane the user drives around in;
+//               it's normally the horizontal plane (e.g. the X-Y
+//               plane in a Z-up coordinate system, or the X-Z plane
+//               in a Y-up coordinate system).
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_coordinate_system(CoordinateSystem cs) {
+  _cs = cs;
+  recompute();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_coordinate_system
+//       Access: Published
+//  Description: Returns the coordinate system of the qpDriveInterface.
+//               See set_coordinate_system().
+////////////////////////////////////////////////////////////////////
+INLINE CoordinateSystem qpDriveInterface::
+get_coordinate_system() const {
+  return _cs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_ignore_mouse
+//       Access: Published
+//  Description: Changes the state of the ignore_mouse flag.  If this
+//               flag is true, the qpDriveInterface will ignore mouse
+//               down button events (but still recognize mouse up
+//               button events); the user will not be able to start
+//               the qpDriveInterface going again if it is stopped, but
+//               if the user is currently holding down a mouse button
+//               it will not stop immediately until the user
+//               eventually releases the button.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_ignore_mouse(bool ignore_mouse) {
+  _ignore_mouse = ignore_mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_ignore_mouse
+//       Access: Published
+//  Description: Returns the current setting of the ignore_mouse flag.
+//               See set_ignore_mouse().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpDriveInterface::
+get_ignore_mouse() const {
+  return _ignore_mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_force_mouse
+//       Access: Published
+//  Description: Changes the state of the force_mouse flag.  If this
+//               flag is true, the mouse button need not be held down
+//               in order to drive the avatar around.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_force_mouse(bool force_mouse) {
+  _force_mouse = force_mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_force_mouse
+//       Access: Published
+//  Description: Returns the current setting of the force_mouse flag.
+//               See set_force_mouse().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpDriveInterface::
+get_force_mouse() const {
+  return _force_mouse;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_mat
+//       Access: Published
+//  Description: Stores the indicated transform in the qpDriveInterface.
+//               This is a transform in global space, regardless of
+//               the rel_to node.
+////////////////////////////////////////////////////////////////////
+INLINE void qpDriveInterface::
+set_mat(const LMatrix4f &mat) {
+  _mat = mat;
+  reextract();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::get_mat
+//       Access: Published
+//  Description: Fills mat with the net transformation applied by the
+//               current state.
+////////////////////////////////////////////////////////////////////
+INLINE const LMatrix4f &qpDriveInterface::
+get_mat() const {
+  return _mat;
+}

+ 482 - 0
panda/src/tform/qpdriveInterface.cxx

@@ -0,0 +1,482 @@
+// Filename: qpdriveInterface.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "driveInterface.h"
+#include "config_tform.h"
+
+#include "compose_matrix.h"
+#include "mouse.h"
+#include "mouseData.h"
+#include "clockObject.h"
+#include "modifierButtons.h"
+#include "keyboardButton.h"
+#include "mouseButton.h"
+#include "buttonEventList.h"
+#include "dataNodeTransmit.h"
+#include "qpdataGraphTraverser.h"
+
+TypeHandle qpDriveInterface::_type_handle;
+
+qpDriveInterface::KeyHeld::
+KeyHeld() {
+  _down = false;
+  _changed_time = 0.0f;
+  _effect = 0.0f;
+  _effect_at_change = 0.0f;
+}
+
+float qpDriveInterface::KeyHeld::
+get_effect(float ramp_up_time, float ramp_down_time) {
+  double elapsed = ClockObject::get_global_clock()->get_frame_time() - _changed_time;
+
+  if (_down) {
+    // We are currently holding down the key.  That means we base our
+    // effect on the ramp_up_time.
+    if (ramp_up_time == 0.0f) {
+      _effect = 1.0f;
+
+    } else {
+      float change = elapsed / ramp_up_time;
+      _effect = min(_effect_at_change + change, 1.0f);
+    }
+
+  } else {
+    // We are *not* currently holding down the key.  That means we
+    // base our effect on the ramp_down_time.
+    if (ramp_down_time == 0.0f) {
+      _effect = 0.0f;
+
+    } else {
+      float change = elapsed / ramp_down_time;
+      _effect = max(_effect_at_change - change, 0.0f);
+    }
+  }
+
+  return _effect;
+}
+
+void qpDriveInterface::KeyHeld::
+set_key(bool down) {
+  if (_down != down) {
+    _down = down;
+    _changed_time = ClockObject::get_global_clock()->get_frame_time();
+    _effect_at_change = _effect;
+  }
+}
+
+void qpDriveInterface::KeyHeld::
+clear() {
+  _down = false;
+  _changed_time = 0.0f;
+  _effect = 0.0f;
+  _effect_at_change = 0.0f;
+}
+
+bool qpDriveInterface::KeyHeld::
+operator < (const qpDriveInterface::KeyHeld &other) const {
+  if (_down != other._down) {
+    // If one has the key held down and the other doesn't, the down
+    // key wins.
+    return _down;
+  }
+
+  // Otherwise, the most-recently changed key wins.
+  return _changed_time > other._changed_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpDriveInterface::
+qpDriveInterface(const string &name) : 
+  qpDataNode(name) 
+{
+  _xy_input = define_input("xy", EventStoreVec2::get_class_type());
+  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
+
+  _transform_output = define_output("transform", EventStoreMat4::get_class_type());
+
+  _transform = new EventStoreMat4(LMatrix4f::ident_mat());
+
+  _forward_speed = drive_forward_speed;
+  _reverse_speed = drive_reverse_speed;
+  _rotate_speed = drive_rotate_speed;
+  _vertical_dead_zone = drive_vertical_dead_zone;
+  _horizontal_dead_zone = drive_horizontal_dead_zone;
+  _vertical_center = drive_vertical_center;
+  _horizontal_center = drive_horizontal_center;
+
+  _vertical_ramp_up_time = drive_vertical_ramp_up_time;
+  _vertical_ramp_down_time = drive_vertical_ramp_down_time;
+  _horizontal_ramp_up_time = drive_horizontal_ramp_up_time;
+  _horizontal_ramp_down_time = drive_horizontal_ramp_down_time;
+
+  _speed = 0.0f;
+  _rot_speed = 0.0f;
+
+  _xyz.set(0.0f, 0.0f, 0.0f);
+  _hpr.set(0.0f, 0.0f, 0.0f);
+  _mat = LMatrix4f::ident_mat();
+  _force_roll = 0.0f;
+  _is_force_roll = true;
+
+  _cs = default_coordinate_system;
+  _ignore_mouse = false;
+  _force_mouse = false;
+
+  _mods.add_button(MouseButton::one());
+  _mods.add_button(MouseButton::two());
+  _mods.add_button(MouseButton::three());
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpDriveInterface::
+~qpDriveInterface() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::reset
+//       Access: Published
+//  Description: Reinitializes the driver to the origin and resets any
+//               knowledge about buttons being held down.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+reset() {
+  _xyz.set(0.0f, 0.0f, 0.0f);
+  _hpr.set(0.0f, 0.0f, 0.0f);
+  _force_roll = 0.0f;
+  _mat = LMatrix4f::ident_mat();
+  _up_arrow.clear();
+  _down_arrow.clear();
+  _left_arrow.clear();
+  _right_arrow.clear();
+
+  _mods.all_buttons_up();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::set_force_roll
+//       Access: Published
+//  Description: Sets the force_roll state.  In this state, roll is
+//               always fixed to a particular value (typically zero),
+//               regardless of what it is explicitly set to via
+//               set_hpr().
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+set_force_roll(float force_roll) {
+  if (_is_force_roll) {
+    // If we already had force_roll() in effect, we have to
+    // recompensate for it.
+    if (_force_roll != force_roll) {
+      _force_roll = force_roll;
+      reextract();
+    }
+
+  } else {
+    _force_roll = force_roll;
+    _is_force_roll = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::force_dgraph
+//       Access: Public
+//  Description: This is a special kludge for qpDriveInterface to allow
+//               us to avoid the one-frame latency after a collision.
+//               It forces an immediate partial data flow for all data
+//               graph nodes below this node, causing all data nodes
+//               that depend on this matrix to be updated immediately.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+force_dgraph() {
+  _transform->set_value(_mat);
+
+  DataNodeTransmit output;
+  output.reserve(get_num_outputs());
+  output.set_data(_transform_output, EventParameter(_transform));
+
+  qpDataGraphTraverser dg_trav;
+  dg_trav.traverse_below(this, output);
+  dg_trav.collect_leftovers();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::apply
+//       Access: Private
+//  Description: Applies the operation indicated by the user's mouse
+//               motion to the current state.  Returns the matrix
+//               indicating the new state.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+apply(double x, double y, bool any_button) {
+
+  // First reset the speeds
+  _speed = 0.0f;
+  _rot_speed = 0.0f;
+
+  if (any_button || _force_mouse) {
+    // If we're holding down any of the mouse buttons, do this
+    // computation based on the mouse position.
+
+    // Determine, based on the mouse's position and the amount of time
+    // elapsed since last frame, how far forward/backward we should
+    // move and how much we should rotate.
+
+    // First, how fast are we moving?  This is based on the mouse's
+    // vertical position.
+
+    float dead_zone_top = _vertical_center + _vertical_dead_zone;
+    float dead_zone_bottom = _vertical_center - _vertical_dead_zone;
+
+    if (y >= dead_zone_top) {
+      // Motion is forward.  Compute the throttle value: the ratio of
+      // the mouse pointer within the range of vertical movement.
+      float throttle =
+        // double 1.0, not 1.0f, is required here to satisfy min()
+        (min(y, 1.0) - dead_zone_top) /
+        (1.0f - dead_zone_top);
+      _speed = throttle * _forward_speed;
+
+    } else if (y <= dead_zone_bottom) {
+      // Motion is backward.
+      float throttle =
+        (max(y, -1.0) - dead_zone_bottom) /
+        (-1.0f - dead_zone_bottom);
+      _speed = -throttle * _reverse_speed;
+    }
+
+    // Now, what's our rotational velocity?  This is based on the
+    // mouse's horizontal position.
+
+    float dead_zone_right = _horizontal_center + _horizontal_dead_zone;
+    float dead_zone_left = _horizontal_center - _horizontal_dead_zone;
+
+    if (x >= dead_zone_right) {
+      // Rotation is to the right.  Compute the throttle value: the
+      // ratio of the mouse pointer within the range of horizontal
+      // movement.
+      float throttle =
+        (min(x, 1.0) - dead_zone_right) /
+        (1.0f - dead_zone_right);
+      _rot_speed = throttle * _rotate_speed;
+
+    } else if (x <= dead_zone_left) {
+      // Rotation is to the left.
+      float throttle =
+        (max(x, -1.0) - dead_zone_left) /
+        (-1.0f - dead_zone_left);
+      _rot_speed = -throttle * _rotate_speed;
+    }
+
+  } else {
+    // If we're not holding down any of the mouse buttons, do this
+    // computation based on the arrow keys.
+
+    // Which vertical arrow key changed state more recently?
+    float throttle;
+
+    if (_up_arrow < _down_arrow) {
+      throttle = _up_arrow.get_effect(_vertical_ramp_up_time,
+                                      _vertical_ramp_down_time);
+      _speed = throttle * _forward_speed;
+      _down_arrow._effect = 0.0f;
+
+    } else {
+      throttle = _down_arrow.get_effect(_vertical_ramp_up_time,
+                                        _vertical_ramp_down_time);
+      _speed = -throttle * _reverse_speed;
+      _up_arrow._effect = 0.0f;
+    }
+
+    // Which horizontal arrow key changed state more recently?
+    if (_right_arrow < _left_arrow) {
+      throttle = _right_arrow.get_effect(_horizontal_ramp_up_time,
+                                         _horizontal_ramp_down_time);
+      _rot_speed = throttle * _rotate_speed;
+      _left_arrow._effect = 0.0f;
+
+    } else {
+      throttle = _left_arrow.get_effect(_horizontal_ramp_up_time,
+                                        _horizontal_ramp_down_time);
+      _rot_speed = -throttle * _rotate_speed;
+      _right_arrow._effect = 0.0f;
+    }
+    _right_arrow._effect = throttle;
+    _left_arrow._effect = throttle;
+  }
+
+  if (_speed == 0.0f && _rot_speed == 0.0f) {
+    return;
+  }
+
+  // Now how far did we move based on the amount of time elapsed?
+  float distance = ClockObject::get_global_clock()->get_dt() * _speed;
+  float rotation = ClockObject::get_global_clock()->get_dt() * _rot_speed;
+
+  // Now apply the vectors.
+
+  // rot_mat is the rotation matrix corresponding to our previous
+  // heading.
+  LMatrix3f rot_mat =
+    LMatrix3f::rotate_mat_normaxis(_hpr[0], LVector3f::up(_cs), _cs);
+
+  // Take a step in the direction of our previous heading.
+  LVector3f step = distance * (LVector3f::forward(_cs) * rot_mat);
+
+  // To prevent upward drift due to numerical errors, force the
+  // vertical component of our step to zero (it should be pretty near
+  // zero anyway).
+  switch (_cs) {
+  case CS_zup_right:
+  case CS_zup_left:
+    step[2] = 0.0f;
+    break;
+
+  case CS_yup_right:
+  case CS_yup_left:
+    step[1] = 0.0f;
+    break;
+
+  default:
+    break;
+  }
+
+  _xyz += step;
+  _hpr[0] -= rotation;
+
+  recompute();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::reextract
+//       Access: Private
+//  Description: Given a correctly computed _mat matrix, rederive the
+//               translation and rotation elements.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+reextract() {
+  LVecBase3f scale;
+
+  if (_is_force_roll) {
+    // We strongly discourage a roll other than _force_roll.
+    decompose_matrix(_mat, scale, _hpr, _xyz, _force_roll);
+  } else {
+    decompose_matrix(_mat, scale, _hpr, _xyz);
+  }
+
+  if (tform_cat.is_debug()) {
+    tform_cat.debug()
+      << "Reextract " << _hpr << ", " << _xyz << " from:\n";
+    _mat.write(tform_cat.debug(false), 2);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::recompute
+//       Access: Private
+//  Description: Rebuilds the matrix according to the stored rotation
+//               and translation components.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+recompute() {
+  compose_matrix(_mat, LVecBase3f(1.0f, 1.0f, 1.0f), _hpr, _xyz, _cs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpDriveInterface::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpDriveInterface::
+do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  // Look for mouse activity.
+  double x = 0.0f;
+  double y = 0.0f;
+
+  bool got_mouse = false;
+
+  if (input.has_data(_xy_input)) {
+    const EventStoreVec2 *xy;
+    DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
+    const LVecBase2f &p = xy->get_value();
+    x = p[0];
+    y = p[1];
+
+    got_mouse = true;
+  }
+
+  // Look for keyboard events.
+  if (input.has_data(_button_events_input)) {
+    const ButtonEventList *button_events;
+    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+
+    int num_events = button_events->get_num_events();
+    for (int i = 0; i < num_events; i++) {
+      const ButtonEvent &be = button_events->get_event(i);
+      if (be._type != ButtonEvent::T_keystroke) {
+        bool down = (be._type == ButtonEvent::T_down);
+
+        if (down) {
+          // We only trap button down events if (a) the mouse is in the
+          // window, and (b) we aren't set to ignore the mouse.
+          if (got_mouse && !_ignore_mouse) {
+            _mods.add_event(be);
+          }
+        } else {
+          // However, we always trap button up events, so we don't get
+          // confused and believe a button is still being held down when
+          // it is not.
+          _mods.add_event(be);
+        }
+        
+        if (be._button == KeyboardButton::up()) {
+          _up_arrow.set_key(down);
+        } else if (be._button == KeyboardButton::down()) {
+          _down_arrow.set_key(down);
+        } else if (be._button == KeyboardButton::left()) {
+          _left_arrow.set_key(down);
+        } else if (be._button == KeyboardButton::right()) {
+          _right_arrow.set_key(down);
+        }
+      }
+    }
+  }
+
+  apply(x, y, _mods.is_any_down());
+  _transform->set_value(_mat);
+  output.set_data(_transform_output, EventParameter(_transform));
+}

+ 199 - 0
panda/src/tform/qpdriveInterface.h

@@ -0,0 +1,199 @@
+// Filename: qpdriveInterface.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpDRIVEINTERFACE_H
+#define qpDRIVEINTERFACE_H
+
+#include "pandabase.h"
+
+#include "qpdataNode.h"
+#include "modifierButtons.h"
+#include "luse.h"
+#include "linmath_events.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpDriveInterface
+// Description : This is a TFormer, similar to Trackball, that moves
+//               around a transform matrix in response to mouse input.
+//               The basic motion is on a horizontal plane, as if
+//               driving a vehicle.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpDriveInterface : public qpDataNode {
+PUBLISHED:
+  qpDriveInterface(const string &name = "");
+  ~qpDriveInterface();
+
+  INLINE void set_forward_speed(float speed);
+  INLINE float get_forward_speed() const;
+  INLINE void set_reverse_speed(float speed);
+  INLINE float get_reverse_speed() const;
+  INLINE void set_rotate_speed(float speed);
+  INLINE float get_rotate_speed() const;
+  INLINE void set_vertical_dead_zone(float zone);
+  INLINE float get_vertical_dead_zone() const;
+  INLINE void set_horizontal_dead_zone(float zone);
+  INLINE float get_horizontal_dead_zone() const;
+
+  INLINE void set_vertical_ramp_up_time(float ramp_up_time);
+  INLINE float get_vertical_ramp_up_time() const;
+  INLINE void set_vertical_ramp_down_time(float ramp_down_time);
+  INLINE float get_vertical_ramp_down_time() const;
+  INLINE void set_horizontal_ramp_up_time(float ramp_up_time);
+  INLINE float get_horizontal_ramp_up_time() const;
+  INLINE void set_horizontal_ramp_down_time(float ramp_down_time);
+  INLINE float get_horizontal_ramp_down_time() const;
+
+  INLINE float get_speed() const;
+  INLINE float get_rot_speed() const;
+
+  void reset();
+
+  /// **** Translation ****
+
+  INLINE const LPoint3f &get_pos() const;
+  INLINE float get_x() const;
+  INLINE float get_y() const;
+  INLINE float get_z() const;
+  INLINE void set_pos(const LVecBase3f &vec);
+  INLINE void set_pos(float x, float y, float z);
+  INLINE void set_x(float x);
+  INLINE void set_y(float y);
+  INLINE void set_z(float z);
+
+  /// **** Rotation ****
+
+  INLINE const LVecBase3f &get_hpr() const;
+  INLINE float get_h() const;
+  INLINE float get_p() const;
+  INLINE float get_r() const;
+  INLINE void set_hpr(const LVecBase3f &hpr);
+  INLINE void set_hpr(float h, float p, float r);
+  INLINE void set_h(float h);
+  INLINE void set_p(float p);
+  INLINE void set_r(float r);
+
+  void set_force_roll(float force_roll);
+  INLINE bool is_force_roll() const;
+  INLINE void clear_force_roll();
+
+  INLINE void set_coordinate_system(CoordinateSystem cs);
+  INLINE CoordinateSystem get_coordinate_system() const;
+
+  INLINE void set_ignore_mouse(bool ignore_mouse);
+  INLINE bool get_ignore_mouse() const;
+
+  INLINE void set_force_mouse(bool force_mouse);
+  INLINE bool get_force_mouse() const;
+
+  void set_mat(const LMatrix4f &mat);
+  const LMatrix4f &get_mat() const;
+
+  void force_dgraph();
+
+private:
+  void apply(double x, double y, bool any_button);
+
+  void reextract();
+  void recompute();
+
+  float _forward_speed;  // units / sec, mouse all the way up
+  float _reverse_speed;  // units / sec, mouse all the way down
+  float _rotate_speed;   // degrees / sec, mouse all the way over
+  float _vertical_dead_zone;    // fraction of window size
+  float _horizontal_dead_zone;  // fraction of window size
+  float _vertical_center;    // window units, 0 = center, -1 = bottom, 1 = top
+  float _horizontal_center;  // window units, 0 = center, -1 = left, 1 = right
+
+  // The time it takes to ramp up to full speed from a stop (or return
+  // to a stop from full speed) when using the keyboard.
+  float _vertical_ramp_up_time;
+  float _vertical_ramp_down_time;
+  float _horizontal_ramp_up_time;
+  float _horizontal_ramp_down_time;
+
+  float _speed; // instantaneous units / sec
+  float _rot_speed; // instantaneous rotational units / sec
+
+  LPoint3f _xyz;
+  LVecBase3f _hpr;
+  LMatrix4f _mat;
+  float _force_roll;
+  bool _is_force_roll;
+  CoordinateSystem _cs;
+  bool _ignore_mouse;
+  bool _force_mouse;
+
+  // Remember which mouse buttons are being held down.
+  ModifierButtons _mods;
+
+  // Remember which arrow keys are being held down and which aren't,
+  // and at what point they last changed state.
+  class KeyHeld {
+  public:
+    KeyHeld();
+    float get_effect(float ramp_up_time, float ramp_down_time);
+    void set_key(bool down);
+    void clear();
+    bool operator < (const KeyHeld &other) const;
+
+    float _effect;
+    bool _down;
+    double _changed_time;
+    float _effect_at_change;
+  };
+  KeyHeld _up_arrow, _down_arrow;
+  KeyHeld _left_arrow, _right_arrow;
+
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // inputs
+  int _xy_input;
+  int _button_events_input;
+
+  // outputs
+  int _transform_output;
+
+  PT(EventStoreMat4) _transform;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpDriveInterface",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpdriveInterface.I"
+
+#endif

+ 437 - 0
panda/src/tform/qpmouseWatcher.I

@@ -0,0 +1,437 @@
+// Filename: qpmouseWatcher.I
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::has_mouse
+//       Access: Published
+//  Description: Returns true if the mouse is anywhere within the
+//               window, false otherwise.  Also see is_mouse_open().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+has_mouse() const {
+  return _has_mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::is_mouse_open
+//       Access: Published
+//  Description: Returns true if the mouse is within the window and
+//               not over some particular MouseWatcherRegion that is
+//               marked to suppress mouse events; that is, that the
+//               mouse is in open space within the window.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+is_mouse_open() const {
+  return _has_mouse && (_suppress_flags & MouseWatcherRegion::SF_mouse_position) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_mouse
+//       Access: Published
+//  Description: It is only valid to call this if has_mouse() returns
+//               true.  If so, this returns the current position of
+//               the mouse within the window.
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint2f &qpMouseWatcher::
+get_mouse() const {
+#ifndef NDEBUG
+  static LPoint2f bogus_mouse(0.0f, 0.0f);
+  nassertr(_has_mouse, bogus_mouse);
+#endif
+  return _mouse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_mouse_x
+//       Access: Published
+//  Description: It is only valid to call this if has_mouse() returns
+//               true.  If so, this returns the current X position of
+//               the mouse within the window.
+////////////////////////////////////////////////////////////////////
+INLINE float qpMouseWatcher::
+get_mouse_x() const {
+  nassertr(_has_mouse, 0.0f);
+  return _mouse[0];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_mouse_y
+//       Access: Published
+//  Description: It is only valid to call this if has_mouse() returns
+//               true.  If so, this returns the current Y position of
+//               the mouse within the window.
+////////////////////////////////////////////////////////////////////
+INLINE float qpMouseWatcher::
+get_mouse_y() const {
+  nassertr(_has_mouse, 0.0f);
+  return _mouse[1];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::is_over_region
+//       Access: Published
+//  Description: Returns true if the mouse is over any rectangular
+//               region, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+is_over_region() const {
+  return get_over_region() != (MouseWatcherRegion *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::is_over_region
+//       Access: Published
+//  Description: Returns true if the mouse is over any rectangular
+//               region, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+is_over_region(float x, float y) const {
+  return get_over_region(x, y) != (MouseWatcherRegion *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::is_over_region
+//       Access: Published
+//  Description: Returns true if the mouse is over any rectangular
+//               region, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+is_over_region(const LPoint2f &pos) const {
+  return get_over_region(pos) != (MouseWatcherRegion *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_over_region
+//       Access: Published
+//  Description: Returns the smallest region the mouse is currently
+//               over, or NULL if it is over no region.
+////////////////////////////////////////////////////////////////////
+INLINE MouseWatcherRegion *qpMouseWatcher::
+get_over_region() const {
+  return _preferred_region;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_over_region
+//       Access: Published
+//  Description: Returns the smallest region the indicated point is
+//               over, or NULL if it is over no region.
+////////////////////////////////////////////////////////////////////
+INLINE MouseWatcherRegion *qpMouseWatcher::
+get_over_region(float x, float y) const {
+  return get_over_region(LPoint2f(x, y));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_button_down_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when a button is depressed.  This
+//               is a string that may contain any of the following:
+//
+//                  %r  - the name of the region the mouse is over
+//                  %b  - the name of the button pressed.
+//
+//               The event name will be based on the in_pattern
+//               string specified here, with all occurrences of the
+//               above strings replaced with the corresponding values.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_button_down_pattern(const string &pattern) {
+  _button_down_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_button_down_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when a button is depressed.  See
+//               set_button_down_pattern().
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_button_down_pattern() const {
+  return _button_down_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_button_up_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when a button is released.  See
+//               set_button_down_pattern().
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_button_up_pattern(const string &pattern) {
+  _button_up_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_button_up_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when a button is released.  See
+//               set_button_down_pattern().
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_button_up_pattern() const {
+  return _button_up_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_enter_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when the mouse enters a region.
+//               This is different from within_pattern, in that a
+//               mouse is only "entered" in the topmost region at a
+//               given time, while it might be "within" multiple
+//               nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_enter_pattern(const string &pattern) {
+  _enter_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_enter_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when the mouse enters a region.  This is
+//               different from within_pattern, in that a mouse is
+//               only "entered" in the topmost region at a given time,
+//               while it might be "within" multiple nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_enter_pattern() const {
+  return _enter_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_leave_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when the mouse leaves a region.
+//               This is different from without_pattern, in that a
+//               mouse is only "entered" in the topmost region at a
+//               given time, while it might be "within" multiple
+//               nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_leave_pattern(const string &pattern) {
+  _leave_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_leave_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when the mouse leaves a region.  This is
+//               different from without_pattern, in that a mouse is
+//               only "entered" in the topmost region at a given time,
+//               while it might be "within" multiple nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_leave_pattern() const {
+  return _leave_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_within_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when the mouse wanders over a
+//               region.  This is different from enter_pattern, in
+//               that a mouse is only "entered" in the topmost region
+//               at a given time, while it might be "within" multiple
+//               nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_within_pattern(const string &pattern) {
+  _within_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_within_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when the mouse wanders over a region.  This
+//               is different from enter_pattern, in that a mouse is
+//               only "entered" in the topmost region at a given time,
+//               while it might be "within" multiple nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_within_pattern() const {
+  return _within_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_without_pattern
+//       Access: Published
+//  Description: Sets the pattern string that indicates how the event
+//               names are generated when the mouse wanders out of a
+//               region.  This is different from leave_pattern, in
+//               that a mouse is only "entered" in the topmost region
+//               at a given time, while it might be "within" multiple
+//               nested regions.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_without_pattern(const string &pattern) {
+  _without_pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_without_pattern
+//       Access: Published
+//  Description: Returns the string that indicates how event names are
+//               generated when the mouse wanders out of a region.
+//               This is different from leave_pattern, in that a mouse
+//               is only "entered" in the topmost region at a given
+//               time, while it might be "within" multiple nested
+//               regions.
+////////////////////////////////////////////////////////////////////
+INLINE const string &qpMouseWatcher::
+get_without_pattern() const {
+  return _without_pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_geometry
+//       Access: Published
+//  Description: Sets the arc that will be transformed each frame by
+//               the mouse's coordinates.  It will also be hidden when
+//               the mouse goes outside the window.  This can be used
+//               to implement a software mouse pointer for when a
+//               hardware (or system) mouse pointer is unavailable.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_geometry(NodeRelation *arc) {
+  _geometry = arc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::has_geometry
+//       Access: Published
+//  Description: Returns true if a software mouse pointer has been
+//               setup via set_geometry(), or false otherwise.  See
+//               set_geometry().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpMouseWatcher::
+has_geometry() const {
+  return !_geometry.is_null();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_geometry
+//       Access: Published
+//  Description: Returns the arc that has been set as the software
+//               mouse pointer, or NULL if no arc has been set.  See
+//               has_geometry() and set_geometry().
+////////////////////////////////////////////////////////////////////
+INLINE NodeRelation *qpMouseWatcher::
+get_geometry() const {
+  return _geometry;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::clear_geometry
+//       Access: Published
+//  Description: Stops the use of the software cursor set up via
+//               set_geometry().
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+clear_geometry() {
+  _geometry.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_extra_handler
+//       Access: Published
+//  Description: As an optimization for the C++ Gui, an extra handler
+//               can be registered with a mouseWatcher so that events
+//               can be dealt with much sooner.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_extra_handler(EventHandler *eh) {
+  _eh = eh;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_extra_handler
+//       Access: Published
+//  Description: As an optimization for the C++ Gui, an extra handler
+//               can be registered with a mouseWatcher so that events
+//               can be dealt with much sooner.
+////////////////////////////////////////////////////////////////////
+INLINE EventHandler *qpMouseWatcher::
+get_extra_handler(void) const {
+  return _eh;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_modifier_buttons
+//       Access: Public
+//  Description: Sets the buttons that should be monitored as modifier
+//               buttons for generating events to the
+//               MouseWatcherRegions.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+set_modifier_buttons(const ModifierButtons &mods) {
+  _mods = mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_modifier_buttons
+//       Access: Published
+//  Description: Returns the set of buttons that are being monitored
+//               as modifier buttons, as well as their current state.
+////////////////////////////////////////////////////////////////////
+INLINE ModifierButtons qpMouseWatcher::
+get_modifier_buttons() const {
+  return _mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::within_region
+//       Access: Protected
+//  Description: Called internally to indicate the mouse pointer has
+//               moved within the indicated region's boundaries.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+within_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  region->within(param);
+  throw_event_pattern(_within_pattern, region, ButtonHandle::none());
+  if (_enter_multiple) {
+    enter_region(region, param);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::without_region
+//       Access: Protected
+//  Description: Called internally to indicate the mouse pointer has
+//               moved outside of the indicated region's boundaries.
+////////////////////////////////////////////////////////////////////
+INLINE void qpMouseWatcher::
+without_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  if (_enter_multiple) {
+    exit_region(region, param);
+  }
+  region->without(param);
+  throw_event_pattern(_without_pattern, region, ButtonHandle::none());
+}

+ 962 - 0
panda/src/tform/qpmouseWatcher.cxx

@@ -0,0 +1,962 @@
+// Filename: qpmouseWatcher.cxx
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpmouseWatcher.h"
+#include "config_tform.h"
+#include "mouseWatcherParameter.h"
+#include "mouse.h"
+#include "mouseData.h"
+#include "buttonEventList.h"
+#include "mouseButton.h"
+#include "throw_event.h"
+#include "eventParameter.h"
+#include "dataNodeTransmit.h"
+
+#include <algorithm>
+
+TypeHandle qpMouseWatcher::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpMouseWatcher::
+qpMouseWatcher(const string &name) : 
+  qpDataNode(name) 
+{
+  _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
+  _xy_input = define_input("xy", EventStoreVec2::get_class_type());
+  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
+
+  _pixel_xy_output = define_output("pixel_xy", EventStoreVec2::get_class_type());
+  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
+
+  _pixel_xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _xy = new EventStoreVec2(LPoint2f(0.0f, 0.0f));
+  _button_events = new ButtonEventList;
+
+  _has_mouse = false;
+  _suppress_flags = 0;
+  _preferred_region = (MouseWatcherRegion *)NULL;
+  _preferred_button_down_region = (MouseWatcherRegion *)NULL;
+  _button_down = false;
+  _eh = (EventHandler*)0L;
+
+  // When this flag is true, the mouse pointer is allowed to be
+  // "entered" into multiple regions simultaneously; when false, it
+  // will only be "within" multiple regions, but "entered" into the
+  // topmost of those.
+  _enter_multiple = false;
+
+  // When this flag is true, moving the pointer into a region is
+  // enough to click it.  The click is simulated with mouse button
+  // one.
+  _implicit_click = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpMouseWatcher::
+~qpMouseWatcher() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::remove_region
+//       Access: Published
+//  Description: Removes the indicated region from the group.
+//               Returns true if it was successfully removed, or false
+//               if it wasn't there in the first place.
+////////////////////////////////////////////////////////////////////
+bool qpMouseWatcher::
+remove_region(MouseWatcherRegion *region) {
+  remove_region_from(_current_regions, region);
+  if (region == _preferred_region) {
+    _preferred_region = (MouseWatcherRegion *)NULL;
+  }
+  if (region == _preferred_button_down_region) {
+    _preferred_button_down_region = (MouseWatcherRegion *)NULL;
+  }
+
+  return MouseWatcherGroup::remove_region(region);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_over_region
+//       Access: Published
+//  Description: Returns the preferred region the mouse is over.  In
+//               the case of overlapping regions, the region with the
+//               largest sort order is preferred; if two regions have
+//               the same sort order, then the smaller region is
+//               preferred.
+////////////////////////////////////////////////////////////////////
+MouseWatcherRegion *qpMouseWatcher::
+get_over_region(const LPoint2f &pos) const {
+  VRegions regions;
+  get_over_regions(regions, pos);
+  return get_preferred_region(regions);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::output
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+output(ostream &out) const {
+  qpDataNode::output(out);
+
+  int count = _regions.size();
+  Groups::const_iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    MouseWatcherGroup *group = (*gi);
+    count += group->_regions.size();
+  }
+
+  out << " (" << count << " regions)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "MouseWatcher " << get_name() << ":\n";
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+    region->write(out, indent_level + 2);
+  }
+
+  if (!_groups.empty()) {
+    Groups::const_iterator gi;
+    for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+      MouseWatcherGroup *group = (*gi);
+      indent(out, indent_level + 2)
+        << "Subgroup:\n";
+      for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
+        MouseWatcherRegion *region = (*ri);
+        region->write(out, indent_level + 4);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::add_group
+//       Access: Public
+//  Description: Adds the indicated group of regions to the set of
+//               regions the MouseWatcher will monitor each frame.
+//
+//               Since the MouseWatcher itself inherits from
+//               MouseWatcherGroup, this operation is normally not
+//               necessary--you can simply add the Regions you care
+//               about one at a time.  Adding a complete group is
+//               useful when you may want to explicitly remove the
+//               regions as a group later.
+//
+//               Returns true if the group was successfully added, or
+//               false if it was already on the list.
+////////////////////////////////////////////////////////////////////
+bool qpMouseWatcher::
+add_group(MouseWatcherGroup *group) {
+  // return _groups.insert(group).second;
+
+  // See if the group is in the set/vector already
+  PT(MouseWatcherGroup) pt = group;
+  Groups::const_iterator gi = 
+    find(_groups.begin(), _groups.end(), pt);
+  if (gi != _groups.end()) {
+    // Already in the set, return false
+    return false;
+  }
+
+  // Not in the set, add it and return true
+  _groups.push_back(pt);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::remove_group
+//       Access: Public
+//  Description: Removes the indicated group from the set of extra
+//               groups associated with the MouseWatcher.  Returns
+//               true if successful, or false if the group was already
+//               removed or was never added via add_group().
+////////////////////////////////////////////////////////////////////
+bool qpMouseWatcher::
+remove_group(MouseWatcherGroup *group) {
+  remove_regions_from(_current_regions, group);
+  if (group->has_region(_preferred_region)) {
+    _preferred_region = (MouseWatcherRegion *)NULL;
+  }
+  if (group->has_region(_preferred_button_down_region)) {
+    _preferred_button_down_region = (MouseWatcherRegion *)NULL;
+  }
+
+  // See if the group is in the set/vector
+  PT(MouseWatcherGroup) pt = group;
+  Groups::iterator gi = 
+    find(_groups.begin(), _groups.end(), pt);
+  if (gi != _groups.end()) {
+    // Found it, now erase it
+    _groups.erase(gi);
+    return true;
+  }
+
+  // Did not find the group to erase
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_over_regions
+//       Access: Protected
+//  Description: Fills up the "regions" list with the set of regions
+//               that the indicated point is over, sorted in order by
+//               pointer.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+get_over_regions(qpMouseWatcher::VRegions &regions, const LPoint2f &pos) const {
+  // Ensure the vector is empty before we begin.
+  regions.clear();
+
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+    const LVecBase4f &frame = region->get_frame();
+
+    if (region->get_active() &&
+        pos[0] >= frame[0] && pos[0] <= frame[1] &&
+        pos[1] >= frame[2] && pos[1] <= frame[3]) {
+
+      regions.push_back(region);
+    }
+  }
+
+  // Also check all of our sub-groups.
+  Groups::const_iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    MouseWatcherGroup *group = (*gi);
+    for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
+      MouseWatcherRegion *region = (*ri);
+      const LVecBase4f &frame = region->get_frame();
+      
+      if (region->get_active() &&
+          pos[0] >= frame[0] && pos[0] <= frame[1] &&
+          pos[1] >= frame[2] && pos[1] <= frame[3]) {
+        
+        regions.push_back(region);
+      }
+    }
+  }
+
+  // Now sort the regions by pointer.  By convention, the Regions
+  // vectors are always kept in order by pointer, so we can do easy
+  // linear comparison and intersection operations.
+  sort(regions.begin(), regions.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::get_preferred_region
+//       Access: Protected, Static
+//  Description: Returns the innermost region of all the regions
+//               indicated in the given vector (usually, the regions
+//               the mouse is over).  This is the "preferred" region
+//               that gets some special treatment.
+////////////////////////////////////////////////////////////////////
+MouseWatcherRegion *qpMouseWatcher::
+get_preferred_region(const qpMouseWatcher::VRegions &regions) {
+  if (regions.empty()) {
+    return (MouseWatcherRegion *)NULL;
+  }
+
+  VRegions::const_iterator ri;
+  ri = regions.begin();
+  MouseWatcherRegion *preferred = *ri;
+  ++ri;
+  while (ri != regions.end()) {
+    MouseWatcherRegion *region = *ri;
+
+    if (*region < *preferred) {
+      preferred = region;
+    }
+    ++ri;
+  }
+
+  return preferred;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::set_current_regions
+//       Access: Protected
+//  Description: Changes the "current" regions--the one we consider the
+//               mouse to be over--to the indicated list, and throws
+//               whatever events are appropriate because of that.
+//
+//               The list passed in is destroyed.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+set_current_regions(qpMouseWatcher::VRegions &regions) {
+  // Set up a parameter for passing through any change events.
+  MouseWatcherParameter param;
+  param.set_modifier_buttons(_mods);
+  param.set_mouse(_mouse);
+
+  // Now do a standard sorted comparison between the two vectors.
+  VRegions::const_iterator new_ri = regions.begin();
+  VRegions::const_iterator old_ri = _current_regions.begin();
+
+  // Queue up all the new regions so we can send the within patterns
+  // all at once, after all of the without patterns have been thrown.
+  vector<MouseWatcherRegion *> new_regions;
+
+  bool any_changes = false;
+  while (new_ri != regions.end() && old_ri != _current_regions.end()) {
+    if ((*new_ri) < (*old_ri)) {
+      // Here's a new region that we didn't have last frame.
+      MouseWatcherRegion *new_region = (*new_ri);
+      new_regions.push_back(new_region);
+      any_changes = true;
+      ++new_ri;
+
+    } else if ((*old_ri) < (*new_ri)) {
+      // Here's a region we don't have any more.
+      MouseWatcherRegion *old_region = (*old_ri);
+      without_region(old_region, param);
+      any_changes = true;
+      ++old_ri;
+
+    } else {
+      // Here's a region that hasn't changed.
+      ++new_ri;
+      ++old_ri;
+    }
+  }
+
+  while (new_ri != regions.end()) {
+    // Here's a new region that we didn't have last frame.
+    MouseWatcherRegion *new_region = (*new_ri);
+    new_regions.push_back(new_region);
+    any_changes = true;
+    ++new_ri;
+  }
+
+  while (old_ri != _current_regions.end()) {
+    // Here's a region we don't have any more.
+    MouseWatcherRegion *old_region = (*old_ri);
+    without_region(old_region, param);
+    any_changes = true;
+    ++old_ri;
+  }
+
+  if (any_changes) {
+    // Now that we've compared the two vectors, simply swap them to set
+    // the new vector.
+    _current_regions.swap(regions);
+
+    // And don't forget to throw all of the new regions' "within" events.
+    vector<MouseWatcherRegion *>::const_iterator ri;
+    for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) {
+      MouseWatcherRegion *new_region = (*ri);
+      within_region(new_region, param);
+    }
+  }
+
+  if (!_enter_multiple) {
+    // Determine which is the "preferred region", if any.  This is the
+    // topmost region that the mouse cursor is over, and the one that
+    // we are considered "entered" into.
+    MouseWatcherRegion *new_preferred_region = 
+      get_preferred_region(_current_regions);
+    
+    if (_button_down && new_preferred_region != _preferred_button_down_region) {
+      // If the button's being held down, we're only allowed to select
+      // the preferred button down region.
+      new_preferred_region = (MouseWatcherRegion *)NULL;
+    }
+
+    if (new_preferred_region != _preferred_region) {
+      if (_preferred_region != (MouseWatcherRegion *)NULL) {
+        exit_region(_preferred_region, param);
+      }
+      _preferred_region = new_preferred_region;
+      if (_preferred_region != (MouseWatcherRegion *)NULL) {
+        enter_region(_preferred_region, param);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::clear_current_regions
+//       Access: Protected
+//  Description: Empties the set of current regions.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+clear_current_regions() {
+  if (!_current_regions.empty()) {
+    // Set up a parameter for passing through any change events.
+    MouseWatcherParameter param;
+    param.set_modifier_buttons(_mods);
+    param.set_mouse(_mouse);
+    
+    VRegions::const_iterator old_ri = _current_regions.begin();
+    
+    while (old_ri != _current_regions.end()) {
+      // Here's a region we don't have any more.
+      MouseWatcherRegion *old_region = (*old_ri);
+      old_region->exit(param);
+      throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
+      ++old_ri;
+    }
+    
+    _current_regions.clear();
+
+    if (_preferred_region != (MouseWatcherRegion *)NULL) {
+      _preferred_region->exit(param);
+      throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
+      _preferred_region = (MouseWatcherRegion *)NULL;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::intersect_regions
+//       Access: Protected, Static
+//  Description: Sets result to be the intersection of the list of
+//               regions in regions_a and regions_b.  It is assumed
+//               that both vectors are already sorted in pointer
+//               order.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+intersect_regions(qpMouseWatcher::VRegions &result,
+                  const qpMouseWatcher::VRegions &regions_a,
+                  const qpMouseWatcher::VRegions &regions_b) {
+  // Get a temporary vector for storing the result in.  We don't use
+  // result directly, because it might be the same vector as one of a
+  // or b.
+  VRegions temp;
+
+  // Now do a standard sorted intersection between the two vectors.
+  VRegions::const_iterator a_ri = regions_a.begin();
+  VRegions::const_iterator b_ri = regions_b.begin();
+
+  while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
+    if ((*a_ri) < (*b_ri)) {
+      // Here's a region in a, not in b.
+      ++a_ri;
+
+    } else if ((*b_ri) < (*a_ri)) {
+      // Here's a region in b, not in a.
+      ++b_ri;
+
+    } else {
+      // Here's a region in both vectors.
+      temp.push_back(*a_ri);
+      ++a_ri;
+      ++b_ri;
+    }
+  }
+
+  // Now store the result!
+  result.swap(temp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::remove_region_from
+//       Access: Protected, Static
+//  Description: Removes the indicated region from the given vector.
+//               Assumes the vector is sorted in pointer order.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+remove_region_from(qpMouseWatcher::VRegions &regions,
+                   MouseWatcherRegion *region) {
+  VRegions::iterator ri = 
+    lower_bound(regions.begin(), regions.end(), region);
+  if (ri != regions.end() && (*ri) == region) {
+    // The region is in the vector.  Remove it.
+    regions.erase(ri);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::remove_regions_from
+//       Access: Protected, Static
+//  Description: Removes all the regions in the indicated group from
+//               the given vector.  Assumes the vector is sorted in
+//               pointer order.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+remove_regions_from(qpMouseWatcher::VRegions &regions,
+                    MouseWatcherGroup *group) {
+  // Since the group stores a set of regions, which are also sorted in
+  // pointer order, we can just do an intersection operation here.
+  VRegions temp;
+
+  VRegions::const_iterator a_ri = regions.begin();
+  MouseWatcherGroup::Regions::const_iterator b_ri = group->_regions.begin();
+
+  while (a_ri != regions.end() && b_ri != group->_regions.end()) {
+    if ((*a_ri) < (*b_ri)) {
+      // Here's a region in the group, not in regions.
+      ++a_ri;
+
+    } else if ((*b_ri) < (*a_ri)) {
+      // Here's a region in regions, not in the group.
+      temp.push_back(*b_ri);
+      ++b_ri;
+
+    } else {
+      // Here's a region in the group and in regions.
+      ++a_ri;
+      ++b_ri;
+    }
+  }
+
+  // Now store the result!
+  regions.swap(temp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::throw_event_for
+//       Access: Protected
+//  Description: Throws an event associated with the indicated region,
+//               using the given pattern.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+throw_event_pattern(const string &pattern, const MouseWatcherRegion *region,
+                    const ButtonHandle &button) {
+  if (pattern.empty()) {
+    return;
+  }
+#ifndef NDEBUG
+  if (region != (MouseWatcherRegion *)NULL) {
+    region->test_ref_count_integrity();
+  }
+#endif
+
+  string button_name;
+  if (button != ButtonHandle::none()) {
+    if (!_mods.has_button(button)) {
+      // We only prepend modifier names for buttons which are not
+      // themselves modifiers.
+      button_name = _mods.get_prefix();
+    }
+    button_name += button.get_name();
+  }
+
+  string event;
+  for (size_t p = 0; p < pattern.size(); ++p) {
+    if (pattern[p] == '%') {
+      string cmd = pattern.substr(p + 1, 1);
+      p++;
+      if (cmd == "r") {
+        if (region != (MouseWatcherRegion *)NULL) {
+          event += button_name;
+        }
+
+      } else if (cmd == "b") {
+        event += button.get_name();
+
+      } else {
+        tform_cat.error()
+          << "Invalid symbol in event_pattern: %" << cmd << "\n";
+      }
+    } else {
+      event += pattern[p];
+    }
+  }
+
+  if (!event.empty()) {
+    throw_event(event, EventParameter(region), EventParameter(button_name));
+    if (_eh != (EventHandler*)0L)
+      throw_event_directly(*_eh, event, EventParameter(region),
+                           EventParameter(button_name));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::press
+//       Access: Protected
+//  Description: Records the indicated mouse or keyboard button as
+//               being depressed.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+press(ButtonHandle button) {
+  MouseWatcherParameter param;
+  param.set_button(button);
+  param.set_modifier_buttons(_mods);
+  param.set_mouse(_mouse);
+
+  if (MouseButton::is_mouse_button(button)) {
+    // Mouse buttons are inextricably linked to the mouse position.
+    
+    if (!_button_down) {
+      _preferred_button_down_region = _preferred_region;
+    }
+    _button_down = true;
+
+    if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
+      _preferred_button_down_region->press(param);
+      throw_event_pattern(_button_down_pattern,
+                          _preferred_button_down_region, button);
+    }
+    
+  } else {
+    // It's a keyboard button; therefore, send the event to every
+    // region that wants keyboard buttons, regardless of the mouse
+    // position.
+    if (_preferred_region != (MouseWatcherRegion *)NULL) {
+      // Our current region, the one under the mouse, always get
+      // all the keyboard events, even if it doesn't set its
+      // keyboard flag.
+      _preferred_region->press(param);
+    }
+
+    if ((_suppress_flags & MouseWatcherRegion::SF_other_button) == 0) {
+      // All the other regions only get the keyboard events if they
+      // set their global keyboard flag, *and* the current region does
+      // not suppress keyboard buttons.
+      param.set_outside(true);
+      global_keyboard_press(param);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::release
+//       Access: Protected
+//  Description: Records the indicated mouse or keyboard button as
+//               being released.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+release(ButtonHandle button) {
+  MouseWatcherParameter param;
+  param.set_button(button);
+  param.set_modifier_buttons(_mods);
+  param.set_mouse(_mouse);
+
+  if (MouseButton::is_mouse_button(button)) {
+    // Button up.  Send the up event associated with the region(s) we
+    // were over when the button went down.
+    
+    // There is some danger of losing button-up events here.  If
+    // more than one button goes down together, we won't detect
+    // both of the button-up events properly.
+    if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
+      param.set_outside(_preferred_button_down_region != _preferred_region);
+      _preferred_button_down_region->release(param);
+      throw_event_pattern(_button_up_pattern,
+                          _preferred_button_down_region, button);
+    }
+
+    _button_down = false;
+    _preferred_button_down_region = (MouseWatcherRegion *)NULL;
+    
+  } else {
+    // It's a keyboard button; therefore, send the event to every
+    // region that wants keyboard buttons, regardless of the mouse
+    // position.
+    if (_preferred_region != (MouseWatcherRegion *)NULL) {
+      _preferred_region->release(param);
+    }
+    
+    param.set_outside(true);
+    global_keyboard_release(param);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::keystroke
+//       Access: Protected
+//  Description: Records that the indicated keystroke has been
+//               generated.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+keystroke(int keycode) {
+  MouseWatcherParameter param;
+  param.set_keycode(keycode);
+  param.set_modifier_buttons(_mods);
+  param.set_mouse(_mouse);
+
+  // Keystrokes go to all those regions that want keyboard events,
+  // regardless of which is the "preferred" region (that is, without
+  // respect to the mouse position).  However, we do set the outside
+  // flag according to whether the given region is preferred region or
+  // not.
+
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+
+    if (region->get_keyboard()) {
+      param.set_outside(region != _preferred_region);
+      region->keystroke(param);
+    }
+  }
+
+  // Also check all of our sub-groups.
+  Groups::const_iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    MouseWatcherGroup *group = (*gi);
+    for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
+      MouseWatcherRegion *region = (*ri);
+
+      if (region->get_keyboard()) {
+        param.set_outside(region != _preferred_region);
+        region->keystroke(param);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::global_keyboard_press
+//       Access: Protected
+//  Description: Calls press() on all regions that are interested in
+//               receiving global keyboard events, except for the
+//               current region (which already received this one).
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+global_keyboard_press(const MouseWatcherParameter &param) {
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+
+    if (region != _preferred_region && region->get_keyboard()) {
+      region->press(param);
+    }
+  }
+
+  // Also check all of our sub-groups.
+  Groups::const_iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    MouseWatcherGroup *group = (*gi);
+    for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
+      MouseWatcherRegion *region = (*ri);
+
+      if (region != _preferred_region && region->get_keyboard()) {
+        region->press(param);
+      }
+    }
+  }
+}
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::global_keyboard_release
+//       Access: Protected
+//  Description: Calls release() on all regions that are interested in
+//               receiving global keyboard events, except for the
+//               current region (which already received this one).
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+global_keyboard_release(const MouseWatcherParameter &param) {
+  Regions::const_iterator ri;
+  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
+    MouseWatcherRegion *region = (*ri);
+
+    if (region != _preferred_region && region->get_keyboard()) {
+      region->release(param);
+    }
+  }
+
+  // Also check all of our sub-groups.
+  Groups::const_iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    MouseWatcherGroup *group = (*gi);
+    for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
+      MouseWatcherRegion *region = (*ri);
+
+      if (region != _preferred_region && region->get_keyboard()) {
+        region->release(param);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::enter_region
+//       Access: Protected
+//  Description: Called internally to indicate the mouse pointer is no
+//               longer favoring the indicated region.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  region->enter(param);
+  throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
+  if (_implicit_click) {
+    MouseWatcherParameter param1(param);
+    param1.set_button(MouseButton::one());
+    region->press(param1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::exit_region
+//       Access: Protected
+//  Description: Called internally to indicate the mouse pointer is no
+//               longer favoring the indicated region.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param) {
+  if (_implicit_click) {
+    MouseWatcherParameter param1(param);
+    param1.set_button(MouseButton::one());
+    region->release(param1);
+  }
+  region->exit(param);
+  throw_event_pattern(_leave_pattern, region, ButtonHandle::none());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpMouseWatcher::do_transmit_data
+//       Access: Protected, Virtual
+//  Description: The virtual implementation of transmit_data().  This
+//               function receives an array of input parameters and
+//               should generate an array of output parameters.  The
+//               input parameters may be accessed with the index
+//               numbers returned by the define_input() calls that
+//               were made earlier (presumably in the constructor);
+//               likewise, the output parameters should be set with
+//               the index numbers returned by the define_output()
+//               calls.
+////////////////////////////////////////////////////////////////////
+void qpMouseWatcher::
+do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  if (!input.has_data(_pixel_xy_input)) {
+    // No mouse in the window.
+
+    if (_has_mouse) {
+      // Hide the mouse pointer.
+      if (!_geometry.is_null()) {
+        _geometry->set_transition(new PruneTransition);
+      }
+    }
+
+    _has_mouse = false;
+    // If the mouse is outside the window, do nothing; let all the
+    // events continue down the pipe unmolested.
+    clear_current_regions();
+    return;
+  }
+
+  // The mouse is within the window.  Get the current mouse position.
+  const EventStoreVec2 *pixel_xy;
+  DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
+  const LVecBase2f &p = pixel_xy->get_value();
+  _mouse.set(p[0], p[1]);
+
+  if (!_geometry.is_null()) {
+    // Transform the mouse pointer.
+    LMatrix4f mat = LMatrix4f::translate_mat(p[0], 0, p[1]);
+    _geometry->set_transition(new TransformTransition(mat));
+    if (!_has_mouse) {
+      // Show the mouse pointer.
+      _geometry->clear_transition(PruneTransition::get_class_type());
+    }
+  }
+
+  _has_mouse = true;
+
+  VRegions regions;
+  get_over_regions(regions, _mouse);
+  set_current_regions(regions);
+
+  _suppress_flags = 0;
+  if (_preferred_region != (MouseWatcherRegion *)NULL) {
+    _suppress_flags = _preferred_region->get_suppress_flags();
+  }
+
+  // Look for button events.
+  if (input.has_data(_button_events_input)) {
+    const ButtonEventList *button_events;
+    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+    int num_events = button_events->get_num_events();
+    for (int i = 0; i < num_events; i++) {
+      const ButtonEvent &be = button_events->get_event(i);
+      _mods.add_event(be);
+
+      switch (be._type) {
+      case ButtonEvent::T_down:
+        press(be._button);
+        break;
+
+      case ButtonEvent::T_up:
+        release(be._button);
+        break;
+
+      case ButtonEvent::T_keystroke:
+        keystroke(be._keycode);
+        break;
+      }
+    }
+  }
+
+  if ((_suppress_flags & MouseWatcherRegion::SF_mouse_position) == 0) {
+    // Transmit the mouse position.
+    output.set_data(_xy_output, input.get_data(_xy_input));
+    output.set_data(_pixel_xy_output, input.get_data(_pixel_xy_input));
+  }
+
+  int suppress_buttons = (_suppress_flags & MouseWatcherRegion::SF_any_button);
+  if (suppress_buttons == MouseWatcherRegion::SF_any_button) {
+    // Suppress all buttons.
+
+  } else if (suppress_buttons != 0) {
+    // Suppress some buttons.
+    _button_events->clear();
+
+    if (input.has_data(_button_events_input)) {
+      const ButtonEventList *button_events;
+      DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+      int num_events = button_events->get_num_events();
+      for (int i = 0; i < num_events; i++) {
+        const ButtonEvent &be = button_events->get_event(i);
+        bool suppress = true;
+        
+        if (be._type != ButtonEvent::T_keystroke && 
+            MouseButton::is_mouse_button(be._button)) {
+          suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
+        } else {
+          suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
+        }
+
+        if (!suppress) {
+          // Don't suppress this button event; pass it through.
+          _button_events->add_event(be);
+        }
+      }
+    }
+
+    if (_button_events->get_num_events() != 0) {
+      output.set_data(_button_events_output, EventParameter(_button_events));
+    }
+
+  } else {
+    // Transmit all buttons.
+    output.set_data(_button_events_output, input.get_data(_button_events_input));
+  }
+}
+

+ 214 - 0
panda/src/tform/qpmouseWatcher.h

@@ -0,0 +1,214 @@
+// Filename: qpmouseWatcher.h
+// Created by:  drose (12Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpMOUSEWATCHER_H
+#define qpMOUSEWATCHER_H
+
+#include "pandabase.h"
+
+#include "mouseWatcherRegion.h"
+#include "mouseWatcherGroup.h"
+#include "qpdataNode.h"
+#include "luse.h"
+#include "pointerTo.h"
+#include "eventHandler.h"
+#include "modifierButtons.h"
+#include "buttonHandle.h"
+#include "buttonEventList.h"
+#include "linmath_events.h"
+#include "pvector.h"
+
+class MouseWatcherParameter;
+
+////////////////////////////////////////////////////////////////////
+//       Class : MouseWatcher
+// Description : This TFormer maintains a list of rectangular regions
+//               on the screen that are considered special mouse
+//               regions; typically these will be click buttons.  When
+//               the mouse passes in or out of one of these regions,
+//               or when a button is clicked while the mouse is in one
+//               of these regions, an event is thrown.
+//
+//               Mouse events may also be suppressed from the rest of
+//               the datagraph in these special regions.
+//
+//               This class can also implement a software mouse
+//               pointer by automatically generating a transform to
+//               apply to a piece of geometry placed under the 2-d
+//               scene graph.  It will move the geometry around
+//               according to the mouse's known position.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpMouseWatcher : public qpDataNode, public MouseWatcherGroup {
+PUBLISHED:
+  qpMouseWatcher(const string &name = "");
+  ~qpMouseWatcher();
+
+  bool remove_region(MouseWatcherRegion *region);
+
+  INLINE bool has_mouse() const;
+  INLINE bool is_mouse_open() const;
+  INLINE const LPoint2f &get_mouse() const;
+  INLINE float get_mouse_x() const;
+  INLINE float get_mouse_y() const;
+
+  INLINE bool is_over_region() const;
+  INLINE bool is_over_region(float x, float y) const;
+  INLINE bool is_over_region(const LPoint2f &pos) const;
+
+  INLINE MouseWatcherRegion *get_over_region() const;
+  INLINE MouseWatcherRegion *get_over_region(float x, float y) const;
+  MouseWatcherRegion *get_over_region(const LPoint2f &pos) const;
+
+  INLINE void set_button_down_pattern(const string &pattern);
+  INLINE const string &get_button_down_pattern() const;
+
+  INLINE void set_button_up_pattern(const string &pattern);
+  INLINE const string &get_button_up_pattern() const;
+
+  INLINE void set_enter_pattern(const string &pattern);
+  INLINE const string &get_enter_pattern() const;
+
+  INLINE void set_leave_pattern(const string &pattern);
+  INLINE const string &get_leave_pattern() const;
+
+  INLINE void set_within_pattern(const string &pattern);
+  INLINE const string &get_within_pattern() const;
+
+  INLINE void set_without_pattern(const string &pattern);
+  INLINE const string &get_without_pattern() const;
+
+  INLINE void set_geometry(NodeRelation *arc);
+  INLINE bool has_geometry() const;
+  INLINE NodeRelation *get_geometry() const;
+  INLINE void clear_geometry();
+
+  INLINE void set_extra_handler(EventHandler *eh);
+  INLINE EventHandler *get_extra_handler(void) const;
+
+  INLINE void set_modifier_buttons(const ModifierButtons &mods);
+  INLINE ModifierButtons get_modifier_buttons() const;
+
+public:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+  bool add_group(MouseWatcherGroup *group);
+  bool remove_group(MouseWatcherGroup *group);
+
+protected:
+  typedef pvector< PT(MouseWatcherRegion) > VRegions;
+  void get_over_regions(VRegions &regions, const LPoint2f &pos) const;
+  static MouseWatcherRegion *get_preferred_region(const VRegions &regions);
+
+  void set_current_regions(VRegions &regions);
+  void clear_current_regions();
+  static void intersect_regions(MouseWatcher::VRegions &result,
+                                const MouseWatcher::VRegions &regions_a,
+                                const MouseWatcher::VRegions &regions_b);
+  static void remove_region_from(MouseWatcher::VRegions &regions,
+                                 MouseWatcherRegion *region);
+  static void remove_regions_from(MouseWatcher::VRegions &regions,
+                                  MouseWatcherGroup *group);
+
+    
+  void throw_event_pattern(const string &pattern,
+                           const MouseWatcherRegion *region,
+                           const ButtonHandle &button);
+
+  void press(ButtonHandle button);
+  void release(ButtonHandle button);
+  void keystroke(int keycode);
+  void global_keyboard_press(const MouseWatcherParameter &param);
+  void global_keyboard_release(const MouseWatcherParameter &param);
+
+  INLINE void within_region(MouseWatcherRegion *region, const MouseWatcherParameter &param);
+  INLINE void without_region(MouseWatcherRegion *region, const MouseWatcherParameter &param);
+  void enter_region(MouseWatcherRegion *region, const MouseWatcherParameter &param);
+  void exit_region(MouseWatcherRegion *region, const MouseWatcherParameter &param);
+
+  // This wants to be a set, but because you cannot export sets across
+  // dlls in windows, we will make it a vector instead
+  typedef pvector< PT(MouseWatcherGroup) > Groups;
+  Groups _groups;
+
+  bool _has_mouse;
+  int _suppress_flags;
+  LPoint2f _mouse;
+
+  VRegions _current_regions;
+  PT(MouseWatcherRegion) _preferred_region;
+  PT(MouseWatcherRegion) _preferred_button_down_region;
+  bool _button_down;
+
+  bool _enter_multiple;
+  bool _implicit_click;
+
+  string _button_down_pattern;
+  string _button_up_pattern;
+  string _enter_pattern;
+  string _leave_pattern;
+  string _within_pattern;
+  string _without_pattern;
+
+  PT_NodeRelation _geometry;
+
+  EventHandler *_eh;
+
+  ModifierButtons _mods;
+
+protected:
+  // Inherited from DataNode
+  virtual void do_transmit_data(const DataNodeTransmit &input,
+                                DataNodeTransmit &output);
+
+private:
+  // inputs
+  int _pixel_xy_input;
+  int _xy_input;
+  int _button_events_input;
+
+  // outputs
+  int _pixel_xy_output;
+  int _xy_output;
+  int _button_events_output;
+
+  PT(EventStoreVec2) _pixel_xy;
+  PT(EventStoreVec2) _xy;
+  PT(ButtonEventList) _button_events;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpDataNode::init_type();
+    register_type(_type_handle, "qpMouseWatcher",
+                  qpDataNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpmouseWatcher.I"
+
+#endif

+ 3 - 3
panda/src/tform/qptrackball.cxx

@@ -40,10 +40,10 @@ qpTrackball::
 qpTrackball(const string &name) :
 qpTrackball(const string &name) :
   qpDataNode(name)
   qpDataNode(name)
 {
 {
-  _pixel_xy_input = define_input("PixelXY", EventStoreVec2::get_class_type());
-  _button_events_input = define_input("ButtonEvents", ButtonEventList::get_class_type());
+  _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
+  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
 
 
-  _transform_output = define_output("Transform", EventStoreMat4::get_class_type());
+  _transform_output = define_output("transform", EventStoreMat4::get_class_type());
 
 
   _transform = new EventStoreMat4(LMatrix4f::ident_mat());
   _transform = new EventStoreMat4(LMatrix4f::ident_mat());
 
 

+ 1 - 1
panda/src/tform/qptransform2sg.cxx

@@ -31,7 +31,7 @@ qpTransform2SG::
 qpTransform2SG(const string &name) :
 qpTransform2SG(const string &name) :
   qpDataNode(name)
   qpDataNode(name)
 {
 {
-  _transform_input = define_input("Transform", EventStoreMat4::get_class_type());
+  _transform_input = define_input("transform", EventStoreMat4::get_class_type());
 
 
   _node = NULL;
   _node = NULL;
 }
 }

+ 1 - 0
panda/src/tform/tform_composite1.cxx

@@ -4,5 +4,6 @@
 #include "config_tform.cxx"
 #include "config_tform.cxx"
 #include "dataValve.cxx"
 #include "dataValve.cxx"
 #include "driveInterface.cxx"
 #include "driveInterface.cxx"
+#include "qpdriveInterface.cxx"
 #include "planarSlider.cxx"
 #include "planarSlider.cxx"
 
 

+ 1 - 0
panda/src/tform/tform_composite2.cxx

@@ -1,5 +1,6 @@
 
 
 #include "mouseWatcher.cxx"
 #include "mouseWatcher.cxx"
+#include "qpmouseWatcher.cxx"
 #include "mouseWatcherGroup.cxx"
 #include "mouseWatcherGroup.cxx"
 #include "mouseWatcherParameter.cxx"
 #include "mouseWatcherParameter.cxx"
 #include "mouseWatcherRegion.cxx"
 #include "mouseWatcherRegion.cxx"