Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
867fa370f4
63 changed files with 4033 additions and 1153 deletions
  1. 21 7
      panda/src/device/Sources.pp
  2. 0 124
      panda/src/device/adinputNode.cxx
  3. 0 78
      panda/src/device/adinputNode.h
  4. 0 62
      panda/src/device/analogData.I
  5. 0 9
      panda/src/device/analogData.cxx
  6. 0 37
      panda/src/device/analogData.h
  7. 119 0
      panda/src/device/analogNode.I
  8. 116 0
      panda/src/device/analogNode.cxx
  9. 101 0
      panda/src/device/analogNode.h
  10. 0 49
      panda/src/device/buttonData.I
  11. 0 9
      panda/src/device/buttonData.cxx
  12. 0 29
      panda/src/device/buttonData.h
  13. 87 0
      panda/src/device/buttonNode.I
  14. 132 0
      panda/src/device/buttonNode.cxx
  15. 84 0
      panda/src/device/buttonNode.h
  16. 90 0
      panda/src/device/clientAnalogDevice.I
  17. 66 0
      panda/src/device/clientAnalogDevice.cxx
  18. 75 0
      panda/src/device/clientAnalogDevice.h
  19. 49 0
      panda/src/device/clientBase.I
  20. 134 358
      panda/src/device/clientBase.cxx
  21. 56 99
      panda/src/device/clientBase.h
  22. 115 0
      panda/src/device/clientButtonDevice.I
  23. 153 0
      panda/src/device/clientButtonDevice.cxx
  24. 92 0
      panda/src/device/clientButtonDevice.h
  25. 85 0
      panda/src/device/clientDevice.I
  26. 104 0
      panda/src/device/clientDevice.cxx
  27. 85 0
      panda/src/device/clientDevice.h
  28. 28 0
      panda/src/device/clientTrackerDevice.I
  29. 8 0
      panda/src/device/clientTrackerDevice.cxx
  30. 50 0
      panda/src/device/clientTrackerDevice.h
  31. 14 4
      panda/src/device/config_device.cxx
  32. 164 23
      panda/src/device/trackerData.I
  33. 14 2
      panda/src/device/trackerData.cxx
  34. 35 17
      panda/src/device/trackerData.h
  35. 51 0
      panda/src/device/trackerNode.I
  36. 54 106
      panda/src/device/trackerNode.cxx
  37. 40 52
      panda/src/device/trackerNode.h
  38. 1 1
      panda/src/sgraph/geomNode.h
  39. 2 2
      panda/src/tform/mouseWatcher.h
  40. 10 2
      panda/src/vrpn/Sources.pp
  41. 6 0
      panda/src/vrpn/config_vrpn.cxx
  42. 38 0
      panda/src/vrpn/vrpnAnalog.I
  43. 121 0
      panda/src/vrpn/vrpnAnalog.cxx
  44. 68 0
      panda/src/vrpn/vrpnAnalog.h
  45. 17 0
      panda/src/vrpn/vrpnAnalogDevice.I
  46. 32 0
      panda/src/vrpn/vrpnAnalogDevice.cxx
  47. 57 0
      panda/src/vrpn/vrpnAnalogDevice.h
  48. 38 0
      panda/src/vrpn/vrpnButton.I
  49. 117 0
      panda/src/vrpn/vrpnButton.cxx
  50. 68 0
      panda/src/vrpn/vrpnButton.h
  51. 17 0
      panda/src/vrpn/vrpnButtonDevice.I
  52. 32 0
      panda/src/vrpn/vrpnButtonDevice.cxx
  53. 57 0
      panda/src/vrpn/vrpnButtonDevice.h
  54. 52 9
      panda/src/vrpn/vrpnClient.I
  55. 541 0
      panda/src/vrpn/vrpnClient.cxx
  56. 63 74
      panda/src/vrpn/vrpnClient.h
  57. 38 0
      panda/src/vrpn/vrpnTracker.I
  58. 186 0
      panda/src/vrpn/vrpnTracker.cxx
  59. 72 0
      panda/src/vrpn/vrpnTracker.h
  60. 42 0
      panda/src/vrpn/vrpnTrackerDevice.I
  61. 35 0
      panda/src/vrpn/vrpnTrackerDevice.cxx
  62. 73 0
      panda/src/vrpn/vrpnTrackerDevice.h
  63. 28 0
      panda/src/vrpn/vrpn_interface.h

+ 21 - 7
panda/src/device/Sources.pp

@@ -7,16 +7,30 @@
     dgraph display gobj sgraph graph gsgbase ipc mathutil linmath putil
 
   #define SOURCES \
-    adinputNode.cxx adinputNode.h analogData.I analogData.cxx \
-    analogData.h buttonData.I buttonData.cxx buttonData.h \
-    clientBase.cxx clientBase.h config_device.cxx config_device.h \
+    analogData.I analogData.cxx analogData.h \
+    analogNode.I analogNode.cxx analogNode.h \
+    buttonNode.I buttonNode.h buttonNode.cxx \
+    clientAnalogDevice.I clientAnalogDevice.cxx clientAnalogDevice.h \
+    clientBase.I clientBase.cxx clientBase.h \
+    clientButtonDevice.I clientButtonDevice.cxx clientButtonDevice.h \
+    clientDevice.I clientDevice.cxx clientDevice.h \
+    clientTrackerDevice.I clientTrackerDevice.cxx clientTrackerDevice.h \
+    config_device.cxx config_device.h \
     dialData.I dialData.cxx dialData.h mouse.cxx mouse.h trackerData.I \
-    trackerData.cxx trackerData.h trackerNode.cxx trackerNode.h
+    trackerData.cxx trackerData.h \
+    trackerNode.I trackerNode.cxx trackerNode.h
 
   #define INSTALL_HEADERS \
-    adinputNode.h analogData.I analogData.h buttonData.I buttonData.h \
-    clientBase.h config_device.h dialData.I dialData.h mouse.h \
-    trackerData.I trackerData.h trackerNode.h
+    analogData.I analogData.h \
+    analogNode.I analogNode.h \
+    buttonNode.I buttonNode.h \
+    clientAnalogDevice.I clientAnalogDevice.h \
+    clientBase.I clientBase.h \
+    clientButtonDevice.I clientButtonDevice.h \
+    clientDevice.I clientDevice.h \
+    clientTrackerDevice.I clientTrackerDevice.h \
+    config_device.h dialData.I dialData.h mouse.h \
+    trackerData.I trackerData.h trackerNode.I trackerNode.h
 
   #define IGATESCAN all
 

+ 0 - 124
panda/src/device/adinputNode.cxx

@@ -1,124 +0,0 @@
-// Filename: adinputNode.cxx
-// Created by:  jason (08Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "adinputNode.h"
-#include "config_device.h"
-
-////////////////////////////////////////////////////////////////////
-// Static variables
-////////////////////////////////////////////////////////////////////
-TypeHandle ADInputNode::_type_handle;
-
-TypeHandle ADInputNode::_dtime_type;
-TypeHandle ADInputNode::_dial_id_type;
-TypeHandle ADInputNode::_change_type;
-
-TypeHandle ADInputNode::_btime_type;
-TypeHandle ADInputNode::_button_id_type;
-TypeHandle ADInputNode::_state_type;
-
-TypeHandle ADInputNode::_atime_type;
-TypeHandle ADInputNode::_channels_type;
-
-  
-////////////////////////////////////////////////////////////////////
-//     Function: ADInputNode::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-ADInputNode::
-ADInputNode(PT(ClientBase) client, const string &adinput) :
-  DataNode(adinput), _client(client), _adinput(adinput)
-{
-  _client->add_remote_analog(_adinput);
-  _client->add_remote_button(_adinput);
-  _client->add_remote_dial(_adinput);
-  
-  _dtime = new DoubleDataAttribute();
-  _dial_id = new IntDataAttribute();
-  _change = new DoubleDataAttribute();
-  
-  _btime = new DoubleDataAttribute();
-  _button_id = new IntDataAttribute();
-  _state = new IntDataAttribute();
-
-  _atime = new DoubleDataAttribute();
-  _channels = new DoublePtrDataAttribute();
-
-  _adinput_attrib.set_attribute(_dtime_type, _dtime);
-  _adinput_attrib.set_attribute(_dial_id_type, _dial_id);
-  _adinput_attrib.set_attribute(_change_type, _change);
-
-  _adinput_attrib.set_attribute(_btime_type, _btime);
-  _adinput_attrib.set_attribute(_button_id_type, _button_id);
-  _adinput_attrib.set_attribute(_state_type, _state);
-
-  _adinput_attrib.set_attribute(_atime_type, _atime);
-  _adinput_attrib.set_attribute(_channels_type, _channels);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ADInputNode::transmit_data
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-void ADInputNode::
-transmit_data(NodeAttributes &data) {
-  AnalogData new_analog = _client->get_analog_data(_adinput);
-  ButtonData new_button = _client->get_button_data(_adinput);
-  DialData new_dial = _client->get_dial_data(_adinput);
-
-  _dtime->set_value(new_dial.dtime);
-  _dial_id->set_value(new_dial.dial_id);
-  _change->set_value(new_dial.change);
-
-  _btime->set_value(new_button.btime);
-  _button_id->set_value(new_button.button_id);
-  _state->set_value(new_button.state);
-
-  _atime->set_value(new_analog.atime);
-  _channels->set_value((double*)new_analog.channels);
-
-  data = _adinput_attrib;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ADInputNode::init_type
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void ADInputNode::
-init_type() {
-  DataNode::init_type();
-  register_type(_type_handle, "ADInputNode",
-		DataNode::get_class_type());
-
-  DoubleDataTransition::init_type();
-  IntDataTransition::init_type();
-  DoublePtrDataTransition::init_type();
-
-  register_data_transition(_dtime_type, "Dial Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_dial_id_type, "Dial ID",
-			   IntDataTransition::get_class_type());
-  register_data_transition(_change_type, "Dial Change",
-			   DoubleDataTransition::get_class_type());
-
-  register_data_transition(_btime_type, "Button Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_button_id_type, "Button ID",
-			   IntDataTransition::get_class_type());
-  register_data_transition(_state_type, "Button State",
-			   IntDataTransition::get_class_type());
-
-  register_data_transition(_atime_type, "Analog Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_channels_type, "Analog Channels",
-			   DoublePtrDataTransition::get_class_type());
-}
-
-
-
-

+ 0 - 78
panda/src/device/adinputNode.h

@@ -1,78 +0,0 @@
-// Filename: cerealboxNode.h
-// Created by:  jason (08Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef _ADINPUT_NODE
-#define _ADINPUT_NODE
-
-#include <pandabase.h>
-
-#include <dataNode.h>
-#include <nodeAttributes.h>
-#include <doubleDataAttribute.h>
-#include <doubleDataTransition.h>
-#include <intDataAttribute.h>
-#include <intDataTransition.h>
-#include <doublePtrDataAttribute.h>
-#include <doublePtrDataTransition.h>
-
-#include <pointerTo.h>
-#include "clientBase.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : ADInputNode
-// Description : Reads the analog, buttons and dials from a adinput
-//               and sends it down the DataGraph
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ADInputNode : public DataNode {
-PUBLISHED:
-  ADInputNode(PT(ClientBase) client, const string &adinput);
-
-public:
-  virtual void transmit_data(NodeAttributes &data);
-
-public:
-  NodeAttributes _adinput_attrib;
-
-  PT(DoubleDataAttribute) _btime;
-  PT(IntDataAttribute) _button_id;
-  PT(IntDataAttribute) _state;
-  
-  PT(DoubleDataAttribute) _dtime;
-  PT(IntDataAttribute) _dial_id;
-  PT(DoubleDataAttribute) _change;
-  
-  PT(DoubleDataAttribute) _atime;
-  PT(DoublePtrDataAttribute) _channels;
-
-  static TypeHandle _btime_type;
-  static TypeHandle _button_id_type;
-  static TypeHandle _state_type;
-
-  static TypeHandle _dtime_type;
-  static TypeHandle _dial_id_type;
-  static TypeHandle _change_type;
-
-  static TypeHandle _atime_type;
-  static TypeHandle _channels_type;
-  
-protected:
-  PT(ClientBase) _client;
-  string _adinput;
-
-public:
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type();
-
-private:
-  static TypeHandle _type_handle;
-};
-
-#endif

+ 0 - 62
panda/src/device/analogData.I

@@ -1,62 +0,0 @@
-// Filename: analogData.I
-// Created by:  jason (04Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////
-//     Function: AnalogData::Default Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE AnalogData::
-AnalogData() :
-  atime(0)
-{
-  channels = new vector_double;
-  stored_channels = new vector_double;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: AnalogData::Default Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE AnalogData::
-~AnalogData() {
-  delete channels;
-  delete stored_channels;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: AnalogData::Copy Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE AnalogData::
-AnalogData(const AnalogData &copy) {
-  (*this) = copy;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: AnalogData::Copy Assignment Operator
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE AnalogData &AnalogData::
-operator = (const AnalogData &copy) {
-  atime = copy.atime;
-  channels = new vector_double(*copy.channels);
-  stored_channels = new vector_double(*copy.stored_channels);
-
-  return *this;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: AnalogData::none
-//       Access: Public, Static
-//  Description: Returns an empty AnalogData object
-////////////////////////////////////////////////////////////////////
-INLINE const AnalogData &AnalogData::
-none() {
- return _none; 
-}

+ 0 - 9
panda/src/device/analogData.cxx

@@ -1,9 +0,0 @@
-// Filename: analogData.cxx
-// Created by:  jason (04Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "analogData.h"
-
-// This is initialized to zero by static initialization.
-AnalogData AnalogData::_none;

+ 0 - 37
panda/src/device/analogData.h

@@ -1,37 +0,0 @@
-// Filename: analogData.h
-// Created by:  jason (04Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef ANALOG_DATA
-#define ANALOG_DATA
-
-#include <pandabase.h>
-#include <vector_double.h>
-
-class EXPCL_PANDA AnalogData {
-public:
-  INLINE AnalogData();
-  INLINE ~AnalogData();
-  INLINE AnalogData(const AnalogData &copy);
-  INLINE AnalogData &operator = (const AnalogData &copy);
-
-  INLINE static const AnalogData &none();
-
-  double atime;
-  //These two pointers are so that we can return a reference to the
-  //current channel values, but not incure the overhead of copying all
-  //those values everywhere.  We need two so that we can implement a
-  //rotating pointer scheme to ensure that there are no collisions
-  //between writing and reading.
-  vector_double *channels;  
-private:
-  vector_double *stored_channels;  
-  static AnalogData _none;
-
-  friend class ClientBase;
-};
-
-#include "analogData.I"
-
-#endif

+ 119 - 0
panda/src/device/analogNode.I

@@ -0,0 +1,119 @@
+// Filename: analogNode.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::OutputData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE AnalogNode::OutputData::
+OutputData() {
+  _index = -1;
+  _flip = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::is_valid
+//       Access: Public
+//  Description: Returns true if the AnalogNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool AnalogNode::
+is_valid() const {
+  return (_analog != (ClientAnalogDevice *)NULL) && _analog->is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::get_num_controls
+//       Access: Public
+//  Description: Returns the number of analog controls known to the
+//               AnalogNode.  This number may change as more controls
+//               are discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int AnalogNode::
+get_num_controls() const {
+  return _analog->get_num_controls();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::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 AnalogNode::
+get_control_state(int index) const {
+  return _analog->get_control_state(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::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 AnalogNode::
+is_control_known(int index) const {
+  return _analog->is_control_known(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::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 AnalogNode::
+set_output(int channel, int index, bool flip) {
+  nassertv(channel >= 0 && channel < max_outputs);
+  _outputs[channel]._index = index;
+  _outputs[channel]._flip = flip;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::clear_output
+//       Access: Public
+//  Description: Removes the output to the data graph associated with
+//               the indicated channel.  See set_output().
+////////////////////////////////////////////////////////////////////
+INLINE void AnalogNode::
+clear_output(int channel) {
+  nassertv(channel >= 0 && channel < max_outputs);
+  _outputs[channel]._index = -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::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 AnalogNode::
+get_output(int channel) const {
+  nassertr(channel >= 0 && channel < max_outputs, -1);
+  return _outputs[channel]._index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::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 AnalogNode::
+is_output_flipped(int channel) const {
+  nassertr(channel >= 0 && channel < max_outputs, false);
+  return _outputs[channel]._flip;
+}

+ 116 - 0
panda/src/device/analogNode.cxx

@@ -0,0 +1,116 @@
+// Filename: analogNode.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "analogNode.h"
+#include "config_device.h"
+
+////////////////////////////////////////////////////////////////////
+// Static variables
+////////////////////////////////////////////////////////////////////
+TypeHandle AnalogNode::_type_handle;
+
+TypeHandle AnalogNode::_xyz_type;
+  
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AnalogNode::
+AnalogNode(ClientBase *client, const string &device_name) :
+  DataNode(device_name)
+{
+  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);
+
+  _xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0));
+  _attrib.set_attribute(_xyz_type, _xyz);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+AnalogNode::
+~AnalogNode() {
+  // When the _analog pointer destructs, the ClientAnalogDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void AnalogNode::
+write(ostream &out, int indent_level) const {
+  DataNode::write(out, indent_level);
+
+  if (_analog != (ClientAnalogDevice *)NULL) {
+    _analog->write_controls(out, indent_level + 2);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::transmit_data
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void AnalogNode::
+transmit_data(NodeAttributes &data) {
+  if (is_valid()) {
+    _analog->poll();
+
+    LPoint3f out(0.0, 0.0, 0.0);
+
+    for (int i = 0; i < max_outputs; i++) {
+      if (_outputs[i]._index >= 0 && is_control_known(_outputs[i]._index)) {
+	if (_outputs[i]._flip) {
+	  out[i] = -get_control_state(_outputs[i]._index);
+	} else {
+	  out[i] = get_control_state(_outputs[i]._index);
+	}
+      }
+    }
+    _xyz->set_value(out);
+  }
+
+  data = _attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::init_type
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void AnalogNode::
+init_type() {
+  DataNode::init_type();
+  register_type(_type_handle, "AnalogNode",
+		DataNode::get_class_type());
+
+  Vec3DataTransition::init_type();
+  register_data_transition(_xyz_type, "XYZ",
+			   Vec3DataTransition::get_class_type());
+}
+

+ 101 - 0
panda/src/device/analogNode.h

@@ -0,0 +1,101 @@
+// Filename: analogNode.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef ANALOGNODE_H
+#define ANALOGNODE_H
+
+#include <pandabase.h>
+
+#include "clientBase.h"
+#include "clientAnalogDevice.h"
+
+#include <dataNode.h>
+#include <nodeAttributes.h>
+#include <vec3DataTransition.h>
+#include <vec3DataAttribute.h>
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : AnalogNode
+// 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 three analog controls to
+//               place on the data graph as the three channels of an
+//               XYZ 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 AnalogNode : public DataNode {
+PUBLISHED:
+  AnalogNode(ClientBase *client, const string &device_name);
+  virtual ~AnalogNode();
+
+  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 = 3 };
+  OutputData _outputs[max_outputs];
+
+////////////////////////////////////////////////////////////////////
+// From parent class DataNode
+////////////////////////////////////////////////////////////////////
+public:
+  virtual void
+  transmit_data(NodeAttributes &data);
+
+  NodeAttributes _attrib;
+  PT(Vec3DataAttribute) _xyz;
+
+  static TypeHandle _xyz_type;
+
+private:
+  PT(ClientAnalogDevice) _analog;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type();
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "analogNode.I"
+
+#endif

+ 0 - 49
panda/src/device/buttonData.I

@@ -1,49 +0,0 @@
-// Filename: buttonData.I
-// Created by:  jason (07Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonData::Default Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ButtonData::
-ButtonData() :
-  btime(0), button_id(0), state(0)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonData::Copy Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ButtonData::
-ButtonData(const ButtonData &copy) {
-  (*this) = copy;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonData::Copy Assignment Operator
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE ButtonData &ButtonData::
-operator = (const ButtonData &copy) {
-  btime = copy.btime;
-  button_id = copy.button_id;
-  state = copy.state;
-
-  return *this;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonData::none
-//       Access: Public, Static
-//  Description: Returns an empty ButtonData object
-////////////////////////////////////////////////////////////////////
-INLINE const ButtonData &ButtonData::
-none() {
- return _none; 
-}

+ 0 - 9
panda/src/device/buttonData.cxx

@@ -1,9 +0,0 @@
-// Filename: buttonData.cxx
-// Created by:  jason (04Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "buttonData.h"
-
-// This is initialized to zero by static initialization.
-ButtonData ButtonData::_none;

+ 0 - 29
panda/src/device/buttonData.h

@@ -1,29 +0,0 @@
-// Filename: buttonData.h
-// Created by:  jason (07Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef BUTTON_DATA
-#define BUTTON_DATA
-
-#include <pandabase.h>
-#include <vector_float.h>
-
-class EXPCL_PANDA ButtonData {
-public:
-  INLINE ButtonData();
-  INLINE ButtonData(const ButtonData &copy);
-  INLINE ButtonData &operator = (const ButtonData &copy);
-
-  INLINE static const ButtonData &none();
-
-  double btime;
-  int button_id;
-  int state;
-private:
-  static ButtonData _none;
-};
-
-#include "buttonData.I"
-
-#endif

+ 87 - 0
panda/src/device/buttonNode.I

@@ -0,0 +1,87 @@
+// Filename: buttonNode.I
+// Created by:  drose (31Dec69)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::is_valid
+//       Access: Public
+//  Description: Returns true if the ButtonNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ButtonNode::
+is_valid() const {
+  return (_button != (ClientButtonDevice *)NULL) && _button->is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::get_num_buttons
+//       Access: Public
+//  Description: Returns the number of buttons known to the
+//               ButtonNode.  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 ButtonNode::
+get_num_buttons() const {
+  return _button->get_num_buttons();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::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 ButtonNode::
+set_button_map(int index, ButtonHandle button) {
+  _button->set_button_map(index, button);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::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 ButtonNode::
+get_button_map(int index) const {
+  return _button->get_button_map(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::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 ButtonNode::
+get_button_state(int index) const {
+  return _button->get_button_state(index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::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 ButtonNode::
+is_button_known(int index) const {
+  return _button->is_button_known(index);
+}

+ 132 - 0
panda/src/device/buttonNode.cxx

@@ -0,0 +1,132 @@
+// Filename: buttonNode.cxx
+// Created by:  drose (31Dec69)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "buttonNode.h"
+#include "config_device.h"
+
+#include <buttonEventDataTransition.h>
+
+////////////////////////////////////////////////////////////////////
+// Static variables
+////////////////////////////////////////////////////////////////////
+TypeHandle ButtonNode::_type_handle;
+
+TypeHandle ButtonNode::_button_events_type;
+  
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+ButtonNode::
+ButtonNode(ClientBase *client, const string &device_name) :
+  DataNode(device_name)
+{
+  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);
+
+  if (_button != (ClientButtonDevice *)NULL) {
+    _button_events = new ButtonEventDataAttribute();
+    _attrib.set_attribute(_button_events_type, _button_events);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+ButtonNode::
+~ButtonNode() {
+  // When the _button pointer destructs, the ClientButtonDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ButtonNode::
+output(ostream &out) const {
+  DataNode::output(out);
+
+  if (_button != (ClientButtonDevice *)NULL) {
+    out << " (";
+    _button->output_buttons(out);
+    out << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ButtonNode::
+write(ostream &out, int indent_level) const {
+  DataNode::write(out, indent_level);
+
+  if (_button != (ClientButtonDevice *)NULL) {
+    _button->write_buttons(out, indent_level + 2);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::transmit_data
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void ButtonNode::
+transmit_data(NodeAttributes &data) {
+  if (is_valid()) {
+    _button->poll();
+    _button->lock();
+    (*_button_events) = (*_button->get_button_events());
+    _button->get_button_events()->clear();
+    _button->unlock();
+
+    if (device_cat.is_debug()) {
+      device_cat.debug() << "ButtonNode:attributes" << endl;
+      _attrib.write(device_cat.debug(false), 3);
+    }
+  }
+
+  data = _attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::init_type
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void ButtonNode::
+init_type() {
+  DataNode::init_type();
+  register_type(_type_handle, "ButtonNode",
+		DataNode::get_class_type());
+
+  ButtonEventDataTransition::init_type();
+  register_data_transition(_button_events_type, "ButtonEvents",
+			   ButtonEventDataTransition::get_class_type());
+}
+

+ 84 - 0
panda/src/device/buttonNode.h

@@ -0,0 +1,84 @@
+// Filename: buttonNode.h
+// Created by:  drose (31Dec69)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef BUTTONNODE_H
+#define BUTTONNODE_H
+
+#include <pandabase.h>
+
+#include "clientBase.h"
+#include "clientButtonDevice.h"
+
+#include <dataNode.h>
+#include <nodeAttributes.h>
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : ButtonNode
+// 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 ButtonNode : public DataNode {
+PUBLISHED:
+  ButtonNode(ClientBase *client, const string &device_name);
+  virtual ~ButtonNode();
+
+  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;
+
+////////////////////////////////////////////////////////////////////
+// From parent class DataNode
+////////////////////////////////////////////////////////////////////
+public:
+  virtual void
+  transmit_data(NodeAttributes &data);
+
+  NodeAttributes _attrib;
+  PT(ButtonEventDataAttribute) _button_events;
+
+  // outputs 
+  static TypeHandle _button_events_type;
+
+private:
+  PT(ClientButtonDevice) _button;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type();
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "buttonNode.I"
+
+#endif

+ 90 - 0
panda/src/device/clientAnalogDevice.I

@@ -0,0 +1,90 @@
+// Filename: clientAnalogDevice.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::AnalogState::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ClientAnalogDevice::AnalogState::
+AnalogState() :
+  _state(0.0),
+  _known(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ClientAnalogDevice::
+ClientAnalogDevice(ClientBase *client, const string &device_name):
+  ClientDevice(client, get_class_type(), device_name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::get_num_controls
+//       Access: Public
+//  Description: Returns the number of analog controls known to the
+//               ClientAnalogDevice.  This number may change as
+//               more controls are discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int ClientAnalogDevice::
+get_num_controls() const {
+  return _controls.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::set_control_state
+//       Access: Public
+//  Description: Sets the state of the indicated analog index.  The
+//               caller should ensure that lock() is in effect while
+//               this call is made.  This should be a number in the
+//               range -1.0 to 1.0, representing the current position
+//               of the control within its total range of movement.
+////////////////////////////////////////////////////////////////////
+INLINE void ClientAnalogDevice::
+set_control_state(int index, double state) {
+  ensure_control_index(index);
+  nassertv(index >= 0 && index < (int)_controls.size());
+  _controls[index]._state = state;
+  _controls[index]._known = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::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 ClientAnalogDevice::
+get_control_state(int index) const {
+  if (index >= 0 && index < (int)_controls.size()) {
+    return _controls[index]._state;
+  } else {
+    return 0.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::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 ClientAnalogDevice::
+is_control_known(int index) const {
+  if (index >= 0 && index < (int)_controls.size()) {
+    return _controls[index]._known;
+  } else {
+    return false;
+  }
+}

+ 66 - 0
panda/src/device/clientAnalogDevice.cxx

@@ -0,0 +1,66 @@
+// Filename: clientAnalogDevice.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "clientAnalogDevice.h"
+
+#include <indent.h>
+
+TypeHandle ClientAnalogDevice::_type_handle;
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::ensure_control_index
+//       Access: Private
+//  Description: Guarantees that there is a slot in the array for the
+//               indicated index number, by filling the array up to
+//               that index if necessary.
+////////////////////////////////////////////////////////////////////
+void ClientAnalogDevice::
+ensure_control_index(int index) {
+  nassertv(index >= 0);
+
+  _controls.reserve(index + 1);
+  while ((int)_controls.size() <= index) {
+    _controls.push_back(AnalogState());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClientAnalogDevice::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n";
+  write_controls(out, indent_level + 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientAnalogDevice::write_analogs
+//       Access: Public
+//  Description: Writes a multi-line description of the current analog
+//               control states.
+////////////////////////////////////////////////////////////////////
+void ClientAnalogDevice::
+write_controls(ostream &out, int indent_level) const {
+  bool any_controls = false;
+  Controls::const_iterator ai;
+  for (ai = _controls.begin(); ai != _controls.end(); ++ai) {
+    const AnalogState &state = (*ai);
+    if (state._known) {
+      any_controls = true;
+
+      indent(out, indent_level)
+	<< (int)(ai - _controls.begin()) << ". " << state._state << "\n";
+    }
+  }
+
+  if (!any_controls) {
+    indent(out, indent_level)
+      << "(no known analog controls)\n";
+  }
+}

+ 75 - 0
panda/src/device/clientAnalogDevice.h

@@ -0,0 +1,75 @@
+// Filename: clientAnalogDevice.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLIENTANALOGDEVICE_H
+#define CLIENTANALOGDEVICE_H
+
+#include <pandabase.h>
+
+#include "clientDevice.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ClientAnalogDevice
+// Description : A device, attached to the ClientBase by a
+//               AnalogNode, that records the data from a single
+//               named analog device.  The named device can contain
+//               any number of analog controls, numbered in
+//               sequence beginning at zero.
+//
+//               Each analog control returns a value ranging from -1
+//               to 1, reflecting the current position of the control
+//               within its total range of motion.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClientAnalogDevice : public ClientDevice {
+protected:
+  INLINE ClientAnalogDevice(ClientBase *client, const string &device_name);
+
+public:
+  INLINE int get_num_controls() const;
+
+  INLINE void set_control_state(int index, double state);
+  INLINE double get_control_state(int index) const;
+  INLINE bool is_control_known(int index) const;
+
+  virtual void write(ostream &out, int indent_level = 0) const;
+  void write_controls(ostream &out, int indent_level) const;
+
+private:
+  void ensure_control_index(int index);
+
+protected:
+  class AnalogState {
+  public:
+    INLINE AnalogState();
+
+    double _state;
+    bool _known;
+  };
+
+  typedef vector<AnalogState> Controls;
+  Controls _controls;
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientDevice::init_type();
+    register_type(_type_handle, "ClientAnalogDevice",
+                  ClientDevice::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 "clientAnalogDevice.I"
+
+#endif

+ 49 - 0
panda/src/device/clientBase.I

@@ -0,0 +1,49 @@
+// Filename: clientBase.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientBase::is_forked
+//       Access: Public
+//  Description: Returns true if the ClientBase has been forked (and,
+//               therefore, poll() does not need to be called), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClientBase::
+is_forked() const {
+  return _forked;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientBase::poll
+//       Access: Public
+//  Description: Initiates a poll of the client devices, if we are not
+//               forked and if we have not already polled this frame.
+//               Returns true if the poll occurred, or false if it did
+//               not.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClientBase::
+poll() {
+  if (_forked || 
+      _last_poll_frame == ClockObject::get_global_clock()->get_frame_count()) {
+    return false;
+  }
+
+  do_poll();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientBase::get_last_poll_time
+//       Access: Public
+//  Description: Returns the time (according to the global
+//               ClockObject's get_real_time() method) of the last
+//               device poll.
+////////////////////////////////////////////////////////////////////
+INLINE double ClientBase::
+get_last_poll_time() const {
+  return _last_poll_time;
+}

+ 134 - 358
panda/src/device/clientBase.cxx

@@ -8,18 +8,19 @@
 
 TypeHandle ClientBase::_type_handle;
 
-#include <algorithm>
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ClientBase::constructor
-//       Access: Public
+//       Access: Protected
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ClientBase::
-ClientBase(const string &server) :
-  _sleep_time(1000000/60), _server(server), _forked(false)
-{
+ClientBase() {
+  _forked = false;
+  _last_poll_time = 0.0;
+  _last_poll_frame = 0;
+
 #ifdef HAVE_IPC
+  _client_thread = (thread *)NULL;
   _shutdown = false;
 #endif
 }
@@ -31,20 +32,22 @@ ClientBase(const string &server) :
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ClientBase::
-~ClientBase()
-{
-#ifdef HAVE_IPC
-  if (asynchronous_clients && _forked == true) {
-    {
-      //Make sure that you grab all locks before setting shutdown to
-      //true.  This ensures that all polling actions on the thread
-      //serving the devices have finished before we tell it to
-      //shutdown
-      mutex_lock t_lock(_tracker_lock);
-      mutex_lock a_lock(_analog_lock);
-      mutex_lock b_lock(_button_lock);
-      _shutdown = true;
+~ClientBase() {
+  // We have to disconnect all of our devices before destructing.
+  Devices::iterator di;
+  Devices devices_copy = _devices;
+  for (di = devices_copy.begin(); di != devices_copy.end(); ++di) {
+    DevicesByName &dbn = (*di).second;
+    DevicesByName::iterator dbni;
+    for (dbni = dbn.begin(); dbni != dbn.end(); ++dbni) {
+      ClientDevice *device = (*dbni).second;
+      device->disconnect();
     }
+  }
+
+#ifdef HAVE_IPC
+  if (_forked) {
+    _shutdown = true;
 
     // Join the loader thread - calling process blocks until the loader
     // thread returns.
@@ -53,15 +56,29 @@ ClientBase::
   }
 #endif
 }
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ClientBase::fork_asynchronous_thread
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-fork_asynchronous_thread(void) {
+//  Description: Forks a separate thread to do all the polling of
+//               connected devices.  The forked thread will poll after
+//               every poll_time seconds has elapsed.  Returns true if
+//               the fork was successful, or false otherwise (for
+//               instance, because we were already forked, or because
+//               asynchronous threads are disabled).
+////////////////////////////////////////////////////////////////////
+bool ClientBase::
+fork_asynchronous_thread(double poll_time) {
 #ifdef HAVE_IPC
+  if (_forked) {
+    device_cat.error()
+      << "Attempt to fork client thread twice.\n";
+    return false;
+  }
+
   if (asynchronous_clients) {
+    _sleep_time = (int)(1000000 * poll_time);
+
     _client_thread = thread::create(&st_callback, this);
     _forked = true;
     if (device_cat.is_debug()) {
@@ -69,22 +86,107 @@ fork_asynchronous_thread(void) {
 	<< "fork_asynchronous_thread() - forking client thread"
 	<< endl;
     }
+    return true;
   }
 #endif
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::set_poll_time
+//     Function: ClientBase::get_device
 //       Access: Public
-//  Description: Sets the time between polls.  The poll time is assumed
-//               to be in seconds
+//  Description: Returns a ClientDevice pointer that corresponds to
+//               the named device of the indicated device type.  The
+//               device_type should be one of ClientTrackerDevice,
+//               ClientAnalogDevice, etc.; the device_name is
+//               implementation defined.
+//
+//               Normally, the user does not need to call this
+//               function directly; it is called automatically by
+//               creating a TrackerNode or AnalogNode or some such
+//               data graph node.
+//
+//               The return value is the pointer to the created device
+//               (which might be the same pointer returned by a
+//               previous call to this function with the same
+//               parameters).  When the pointer destructs (i.e. its
+//               reference count reaches zero) it will automatically
+//               be disconnected.
+//
+//               If the named device does not exist or cannot be
+//               connected for some reason, NULL is returned.
+////////////////////////////////////////////////////////////////////
+PT(ClientDevice) ClientBase::
+get_device(TypeHandle device_type, const string &device_name) {
+  DevicesByName &dbn = _devices[device_type];
+
+  DevicesByName::iterator dbni;
+  dbni = dbn.find(device_name);
+  if (dbni != dbn.end()) {
+    // This device was previously connected.  Return it again.
+    return (*dbni).second;
+  }
+
+  // We need to create a new device for this name.
+  PT(ClientDevice) device = make_device(device_type, device_name);
+
+  if (device != (ClientDevice *)NULL) {
+    dbn.insert(DevicesByName::value_type(device_name, device));
+    device->_is_connected = true;
+  }
+
+  return device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientBase::disconnect_device
+//       Access: Protected, Virtual
+//  Description: Removes the device, which is presumably about to
+//               destruct, from the list of connected devices, and
+//               frees any data required to support it.  This device
+//               will no longer receive automatic updates with each
+//               poll.
+//
+//               The return value is true if the device was
+//               disconnected, or false if it was unknown (e.g. it was
+//               disconnected previously).
+////////////////////////////////////////////////////////////////////
+bool ClientBase::
+disconnect_device(TypeHandle device_type, const string &device_name,
+		  ClientDevice *device) {
+  DevicesByName &dbn = _devices[device_type];
+
+  DevicesByName::iterator dbni;
+  dbni = dbn.find(device_name);
+  if (dbni != dbn.end()) {
+    if ((*dbni).second == device) {
+      // We found it!
+      dbn.erase(dbni);
+      return true;
+    }
+  }
+
+  // The device was unknown.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientBase::do_poll
+//       Access: Protected, Virtual
+//  Description: Implements the polling and updating of connected
+//               devices, if the ClientBase requires this.  This may
+//               be called in a sub-thread if
+//               fork_asynchronous_thread() was called; otherwise, it
+//               will be called once per frame.
 ////////////////////////////////////////////////////////////////////
 void ClientBase::
-set_poll_time(float poll_time) {
-  _sleep_time = (int)(1000000 * poll_time);
+do_poll() {
+  ClockObject *global_clock = ClockObject::get_global_clock();
+  _last_poll_frame = global_clock->get_frame_count();
+  _last_poll_time = global_clock->get_time();
 }
 
-
 #ifdef HAVE_IPC
 ////////////////////////////////////////////////////////////////////
 //     Function: ClientBase::st_callback
@@ -113,339 +215,13 @@ st_callback(void *arg) {
 //               being watched
 ////////////////////////////////////////////////////////////////////
 void ClientBase::
-callback(void) {
-  while(true) {
+callback() {
+  while (true) {
     if (_shutdown) {
       break;
     }
-    poll_trackers();
-    poll_analogs();
-    poll_buttons();
+    do_poll();
     ipc_traits::sleep(0, _sleep_time);
   }
 }
 #endif // HAVE_IPC
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::get_tracker_data
-//       Access: Public, Virtual
-//  Description: Returns the current data for the sensor of a particular
-//               tracker
-////////////////////////////////////////////////////////////////////
-const TrackerData& ClientBase::
-get_tracker_data(const string &tracker, int sensor) {
-
-  //Very important that this check and associated call are made before
-  //the mutex lock, otherwise if we are not forked and we lock before
-  //doing poll we will get into a deadlock condition when the
-  //associated push for this function is called
-  if (!_forked) {
-    poll_tracker(tracker);
-  }
-
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_tracker_lock);
-#endif
-
-  if ((find(_trackers.begin(), _trackers.end(), tracker) != _trackers.end()) ||
-      (find(_sensors[tracker].begin(), _sensors[tracker].end(), sensor) 
-                                                != _sensors[tracker].end())) {
-    if (_tracker_datas.find(tracker) != _tracker_datas.end()) {
-      if (_tracker_datas[tracker].find(sensor) != _tracker_datas[tracker].end()) {
-	return _tracker_datas[tracker][sensor];
-      }
-    }
-  }
-  else {
-    device_cat.error() << "Request for either unknown sensor " << sensor 
-		       <<  " or unknown tracker " << tracker <<  " made" << endl;
-  }
-
-  return TrackerData::none();
-
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::get_analog_data
-//       Access: Public, Virtual
-//  Description: Returns the current data for the sensor of a particular
-//               analog
-////////////////////////////////////////////////////////////////////
-const AnalogData& ClientBase::
-get_analog_data(const string &analog) {
-
-  //Very important that this check and associated call are made before
-  //the mutex lock, otherwise if we are not forked and we lock before
-  //doing poll we will get into a deadlock condition when the
-  //associated push for this function is called
-  if (!_forked) {
-    poll_analog(analog);
-  }
-
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_analog_lock);
-#endif
-
-  if (find(_analogs.begin(), _analogs.end(), analog) != _analogs.end()) {
-    if (_analog_datas.find(analog) != _analog_datas.end()) {
-      //Make sure to rotate the arrays to ensure access safety
-      swap(_analog_datas[analog].channels, _analog_datas[analog].stored_channels);
-      return _analog_datas[analog];
-    }
-  }
-  else {
-    device_cat.error() << "Request for unknown analog " << analog << " made" << endl;
-  }
-
-  return AnalogData::none();
-
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::get_button_data
-//       Access: Public, Virtual
-//  Description: Returns the current data for the sensor of a particular
-//               button
-////////////////////////////////////////////////////////////////////
-const ButtonData& ClientBase::
-get_button_data(const string &button) {
-
-  //Very important that this check and associated call are made before
-  //the mutex lock, otherwise if we are not forked and we lock before
-  //doing poll we will get into a deadlock condition when the
-  //associated push for this function is called
-  if (!_forked) {
-    poll_button(button);
-  }
-
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_button_lock);
-#endif
-
-  if (find(_buttons.begin(), _buttons.end(), button) != _buttons.end()) {
-    if (_button_datas.find(button) != _button_datas.end()) {
-      return _button_datas[button];
-    }
-  }
-  else {
-    device_cat.error() << "Request for unknown button " << button << " made" << endl;
-  }
-
-  return ButtonData::none();
-
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::get_dial_data
-//       Access: Public, Virtual
-//  Description: Returns the current data for the sensor of a particular
-//               dial
-////////////////////////////////////////////////////////////////////
-const DialData& ClientBase::
-get_dial_data(const string &dial) {
-
-  //Very important that this check and associated call are made before
-  //the mutex lock, otherwise if we are not forked and we lock before
-  //doing poll we will get into a deadlock condition when the
-  //associated push for this function is called
-  if (!_forked) {
-    poll_dial(dial);
-  }
-
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_dial_lock);
-#endif
-
-  if (find(_dials.begin(), _dials.end(), dial) != _dials.end()) {
-    if (_dial_datas.find(dial) != _dial_datas.end()) {
-      return _dial_datas[dial];
-    }
-  }
-  else {
-    device_cat.error() << "Request for unknown dial " << dial << " made" << endl;
-  }
-
-  return DialData::none();
-
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_tracker_position
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               tracker data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_tracker_position(const string &tracker, const int &sensor, const double &ptime,
-		      const LPoint3f &pos, const LVector4f &pquat) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_tracker_lock);  
-#endif
-
-  _tracker_datas[tracker][sensor].ptime = ptime;
-  _tracker_datas[tracker][sensor].position = pos;
-  _tracker_datas[tracker][sensor].pquat = pquat;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_tracker_position
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               tracker data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_tracker_velocity(const string &tracker, int sensor, const double &vtime, 
-		      const LPoint3f &vel, const LVector4f &vquat, const float &dt) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_tracker_lock);  
-#endif
-
-  _tracker_datas[tracker][sensor].vtime = vtime;
-  _tracker_datas[tracker][sensor].velocity = vel;
-  _tracker_datas[tracker][sensor].vquat = vquat;
-  _tracker_datas[tracker][sensor].vquat_dt = dt;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_tracker_position
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               tracker data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_tracker_acceleration(const string &tracker, const int &sensor, const double &atime, 
-			  const LPoint3f &acc, const LVector4f &aquat, const float &dt) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_tracker_lock);  
-#endif
-
-  _tracker_datas[tracker][sensor].atime = atime;
-  _tracker_datas[tracker][sensor].acceleration = acc;
-  _tracker_datas[tracker][sensor].aquat = aquat;
-  _tracker_datas[tracker][sensor].aquat_dt = dt;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_analog
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               analog data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_analog(const string &analog, const float &atime, 
-	    const double *channels, int num_channels) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_analog_lock);  
-#endif
-
-  _analog_datas[analog].atime = atime;
-
-  const double *head = channels;
-  const double *end = head + num_channels;
-  _analog_datas[analog].stored_channels->clear();
-  vector_double::iterator start = _analog_datas[analog].stored_channels->begin();
-  _analog_datas[analog].stored_channels->insert(start, head, end);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_button
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               button data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_button(const string &button, const float &btime, const int &button_id, 
-	    const int &state) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_button_lock);  
-#endif
-
-  _button_datas[button].btime = btime;
-  _button_datas[button].button_id = button_id;
-  _button_datas[button].state = state;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::push_dial
-//       Access: Protected, Virtual
-//  Description: Use this function in the children class to fill in
-//               dial data
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-push_dial(const string &dial, const float &dtime, const int &dial_id, 
-	    const float &change) {
-  //Make sure to prevent simultaneous write and read of device
-#ifdef HAVE_IPC
-  mutex_lock lock(_dial_lock);  
-#endif
-
-  _dial_datas[dial].dtime = dtime;
-  _dial_datas[dial].dial_id = dial_id;
-  _dial_datas[dial].change = change;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::poll_trackers()
-//       Access: Private
-//  Description: Polls all current trackers
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-poll_trackers() {
-  Trackers::iterator ti = _trackers.begin();
-  for(; ti != _trackers.end(); ti++) {
-    poll_tracker((*ti));
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::poll_analogs()
-//       Access: Private
-//  Description: Polls all current analogs
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-poll_analogs() {
-  Analogs::iterator ti = _analogs.begin();
-  for(; ti != _analogs.end(); ti++) {
-    poll_analog((*ti));
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::poll_buttons()
-//       Access: Private
-//  Description: Polls all current buttons
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-poll_buttons() {
-  Buttons::iterator ti = _buttons.begin();
-  for(; ti != _buttons.end(); ti++) {
-    poll_button((*ti));
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientBase::poll_dials()
-//       Access: Private
-//  Description: Polls all current dials
-////////////////////////////////////////////////////////////////////
-void ClientBase::
-poll_dials() {
-  Dials::iterator ti = _dials.begin();
-  for(; ti != _dials.end(); ti++) {
-    poll_dial((*ti));
-  }
-}
-
-
-
-

+ 56 - 99
panda/src/device/clientBase.h

@@ -3,145 +3,102 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-#ifndef _CLIENT_BASE
-#define _CLIENT_BASE
+#ifndef CLIENTBASE_H
+#define CLIENTBASE_H
 
 #include <pandabase.h>
 
-#include "trackerData.h"
-#include "analogData.h"
-#include "buttonData.h"
-#include "dialData.h"
+#include "clientDevice.h"
 
 #include <typedReferenceCount.h>
 #include <luse.h>
 #include <vector_string.h>
 #include <vector_int.h>
+#include <clockObject.h>
+#include <pointerTo.h>
 
 #ifdef HAVE_IPC
-#include <ipc_mutex.h>
-#include <ipc_condition.h>
 #include <ipc_thread.h>
 #endif
 
 #include <map>
 
+////////////////////////////////////////////////////////////////////
+//       Class : ClientBase
+// Description : An abstract base class for a family of of client
+//               device interfaces--including trackers, buttons,
+//               dials, and other analog inputs.
+//
+//               This provides a common interface to connect to such
+//               devices and extract their data; it is used by
+//               TrackerNode etc. to put these devices in the data
+//               graph.
+////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA ClientBase : public TypedReferenceCount {
+protected:
+  ClientBase();
+
 PUBLISHED:
-  ClientBase(const string &server);
   ~ClientBase();
-  void fork_asynchronous_thread(void);
-  void set_poll_time(float poll_time);
 
-  //ADD FUNCTIONS
-  virtual bool add_remote_tracker(const string &tracker, int sensor) = 0;
-  virtual bool add_remote_analog(const string &analog) = 0;
-  virtual bool add_remote_button(const string &button) = 0;
-  virtual bool add_remote_dial(const string &dial) = 0;
+  bool fork_asynchronous_thread(double poll_time);
+  INLINE bool is_forked() const;
+  INLINE bool poll();
+  INLINE double get_last_poll_time() const;
 
 public:
-  //GET FUNCTIONS
-  virtual const TrackerData &get_tracker_data(const string &tracker, int sensor);
-  virtual const AnalogData &get_analog_data(const string &analog);
-  virtual const ButtonData &get_button_data(const string &button);
-  virtual const DialData &get_dial_data(const string &dial);
-
+  PT(ClientDevice) get_device(TypeHandle device_type,
+			      const string &device_name);
+  
 protected:
-  void push_tracker_position(const string &tracker, const int &sensor, const double &ptime, 
-			     const LPoint3f &pos, const LVector4f &pquat);
-  void push_tracker_velocity(const string &tracker, int sensor, const double &vtime, 
-			     const LPoint3f &vel, const LVector4f &vquat, const float &dt);
-  void push_tracker_acceleration(const string &tracker, const int &sensor, const double &atime, 
-			     const LPoint3f &acc, const LVector4f &aquat, const float &dt);
-
-  void push_analog(const string &analog, const float &atime, 
-		   const double *channels, int num_channels);
-  void push_button(const string &button, const float &btime, const int &button_id, 
-		   const int &state);
-  void push_dial(const string &dial, const float &dtime, const int &dial_id, 
-		   const float &change);
+  virtual PT(ClientDevice) make_device(TypeHandle device_type,
+				       const string &device_name)=0;
+
+  virtual bool disconnect_device(TypeHandle device_type, 
+				 const string &device_name,
+				 ClientDevice *device);
+
+  virtual void do_poll();
 
 private:
-  int _sleep_time;
-  const string _server;
+  typedef map<string, ClientDevice *> DevicesByName;
+  typedef map<TypeHandle, DevicesByName> Devices;
+  Devices _devices;
+
   bool _forked;
+  double _last_poll_time;
+  int _last_poll_frame;
 
 #ifdef HAVE_IPC
-  //Device locks and conditionals
-  mutex _tracker_lock; 
-  mutex _analog_lock; 
-  mutex _button_lock; 
-  mutex _dial_lock; 
-
-  //Thread variables and functions
+  int _sleep_time;
   thread *_client_thread;
   bool _shutdown;
 
   static void* st_callback(void *arg);
-  void callback(void);
+  void callback();
 #endif
 
-protected:
- //Device polling functions
-  void poll_trackers();
-  virtual void poll_tracker(const string &tracker) = 0;
-  
-  void poll_analogs();
-  virtual void poll_analog(const string &analog) = 0;
-  virtual int max_analog_channels(void) = 0;
-  
-  void poll_buttons();
-  virtual void poll_button(const string &button) = 0;
-  
-  void poll_dials();
-  virtual void poll_dial(const string &dial) = 0;
-
-
-protected:
-  typedef map< int, TrackerData > SensorDatas;
-  typedef map< const string, SensorDatas > TrackerDatas;
-  typedef map< const string, AnalogData > AnalogDatas;
-  typedef map< const string, ButtonData > ButtonDatas;
-  typedef map< const string, DialData > DialDatas;
-
-  typedef vector_string Trackers;
-  typedef vector_int Sensors;
-  typedef map< string, Sensors > TrackerSensors;
-  typedef vector_string Analogs;
-  typedef vector_string Buttons;
-  typedef vector_string Dials;
-
-  TrackerDatas _tracker_datas;
-  Trackers _trackers;
-  TrackerSensors _sensors;
-
-  AnalogDatas _analog_datas;
-  Analogs _analogs;
-
-  ButtonDatas _button_datas;
-  Buttons _buttons;
-
-  DialDatas _dial_datas;
-  Dials _dials;
 
 public:
-  static TypeHandle get_class_type( void ) {
-      return _type_handle;
+  static TypeHandle get_class_type() {
+    return _type_handle;
   }
-  static void init_type( void ) {
+  static void init_type() {
     TypedReferenceCount::init_type();
-    register_type( _type_handle, "ClientBase",
-		   TypedReferenceCount::get_class_type() );
-  }
-  virtual TypeHandle get_type( void ) const {
-    return get_class_type();
+    register_type(_type_handle, "ClientBase",
+                  TypedReferenceCount::get_class_type());
   }
-  virtual TypeHandle force_init_type() {
-    init_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;
+  static TypeHandle _type_handle;
+
+  friend class ClientDevice;
 };
+
+#include "clientBase.I"
+
 #endif

+ 115 - 0
panda/src/device/clientButtonDevice.I

@@ -0,0 +1,115 @@
+// Filename: clientButtonDevice.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::ButtonState::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ClientButtonDevice::ButtonState::
+ButtonState() :
+  _handle(ButtonHandle::none()),
+  _state(S_unknown)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::get_num_buttons
+//       Access: Public
+//  Description: Returns the number of buttons known to the
+//               ClientButtonDevice.  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 ClientButtonDevice::
+get_num_buttons() const {
+  return _buttons.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::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 ClientButtonDevice::
+set_button_map(int index, ButtonHandle button) {
+  ensure_button_index(index);
+  nassertv(index >= 0 && index < (int)_buttons.size());
+  _buttons[index]._handle = button;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::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 ClientButtonDevice::
+get_button_map(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return _buttons[index]._handle;
+  } else {
+    return ButtonHandle::none();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::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 ClientButtonDevice::
+get_button_state(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return (_buttons[index]._state == S_down);
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::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 ClientButtonDevice::
+is_button_known(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return _buttons[index]._state != S_unknown;
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::get_button_events
+//       Access: Public
+//  Description: Returns the list of recently-generated ButtonEvents.
+//               This must be periodically cleared, or the buttons
+//               will accumulate.
+////////////////////////////////////////////////////////////////////
+INLINE ButtonEventDataAttribute *ClientButtonDevice::
+get_button_events() const {
+  return _button_events;
+}

+ 153 - 0
panda/src/device/clientButtonDevice.cxx

@@ -0,0 +1,153 @@
+// Filename: clientButtonDevice.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "clientButtonDevice.h"
+
+#include <indent.h>
+
+TypeHandle ClientButtonDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ClientButtonDevice::
+ClientButtonDevice(ClientBase *client, const string &device_name):
+  ClientDevice(client, get_class_type(), device_name)
+{
+  _button_events = new ButtonEventDataAttribute();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::set_button_state
+//       Access: Public
+//  Description: Sets the state of the indicated button index, where
+//               true indicates down, and false indicates up.  This
+//               may generate a ButtonEvent if the button has an
+//               associated ButtonHandle.  The caller should ensure
+//               that lock() is in effect while this call is made.
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+set_button_state(int index, bool down) {
+  ensure_button_index(index);
+  nassertv(index >= 0 && index < (int)_buttons.size());
+  _buttons[index]._state = down ? S_down : S_up;
+
+  ButtonHandle handle = _buttons[index]._handle;
+  if (handle != ButtonHandle::none()) {
+    _button_events->push_back(ButtonEvent(handle, down));
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::ensure_button_index
+//       Access: Private
+//  Description: Guarantees that there is a slot in the array for the
+//               indicated index number, by filling the array up to
+//               that index if necessary.
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+ensure_button_index(int index) {
+  nassertv(index >= 0);
+
+  _buttons.reserve(index + 1);
+  while ((int)_buttons.size() <= index) {
+    _buttons.push_back(ButtonState());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+output(ostream &out) const {
+  out << get_type() << " " << get_device_name() << " (";
+  output_buttons(out);
+  out << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n";
+  write_buttons(out, indent_level + 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::output_buttons
+//       Access: Public
+//  Description: Writes a one-line string of all of the current button
+//               states.
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+output_buttons(ostream &out) const {
+  bool any_buttons = false;
+  Buttons::const_iterator bi;
+  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
+    const ButtonState &state = (*bi);
+    if (state._state != S_unknown) {
+      if (any_buttons) {
+	out << ", ";
+      }
+      any_buttons = true;
+      out << (int)(bi - _buttons.begin()) << "=";
+      if (state._state == S_up) {
+	out << "up";
+      } else {
+	out << "down";
+      }
+    }
+  }
+
+  if (!any_buttons) {
+    out << "no known buttons";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientButtonDevice::write_buttons
+//       Access: Public
+//  Description: Writes a multi-line description of the current button
+//               states.
+////////////////////////////////////////////////////////////////////
+void ClientButtonDevice::
+write_buttons(ostream &out, int indent_level) const {
+  bool any_buttons = false;
+  Buttons::const_iterator bi;
+  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
+    const ButtonState &state = (*bi);
+    if (state._state != S_unknown) {
+      any_buttons = true;
+
+      indent(out, indent_level)
+	<< (int)(bi - _buttons.begin()) << ". ";
+
+      if (state._handle != ButtonHandle::none()) {
+	out << "(" << state._handle << ") ";
+      }
+
+      if (state._state == S_up) {
+	out << "up";
+      } else {
+	out << "down";
+      }
+      out << "\n";
+    }
+  }
+
+  if (!any_buttons) {
+    indent(out, indent_level)
+      << "(no known buttons)\n";
+  }
+}

+ 92 - 0
panda/src/device/clientButtonDevice.h

@@ -0,0 +1,92 @@
+// Filename: clientButtonDevice.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLIENTBUTTONDEVICE_H
+#define CLIENTBUTTONDEVICE_H
+
+#include <pandabase.h>
+
+#include "clientDevice.h"
+
+#include <buttonHandle.h>
+#include <buttonEvent.h>
+#include <buttonEventDataAttribute.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : ClientButtonDevice
+// Description : A device, attached to the ClientBase by a
+//               ButtonNode, that records the data from a single
+//               named button device.  The named device can contain
+//               any number of up/down style buttons, numbered in
+//               sequence beginning at zero; these are mapped by this
+//               class to a sequence of ButtonHandles specified by the
+//               user.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClientButtonDevice : public ClientDevice {
+protected:
+  ClientButtonDevice(ClientBase *client, const string &device_name);
+
+public:
+  INLINE int get_num_buttons() const;
+
+  INLINE void set_button_map(int index, ButtonHandle button);
+  INLINE ButtonHandle get_button_map(int index) const;
+
+  void set_button_state(int index, bool down);
+  INLINE bool get_button_state(int index) const;
+  INLINE bool is_button_known(int index) const;
+
+  INLINE ButtonEventDataAttribute *get_button_events() const;
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+  void output_buttons(ostream &out) const;
+  void write_buttons(ostream &out, int indent_level) const;
+
+private:
+  void ensure_button_index(int index);
+
+protected:
+  enum State {
+    S_unknown,
+    S_up,
+    S_down
+  };
+
+  class ButtonState {
+  public:
+    INLINE ButtonState();
+
+    ButtonHandle _handle;
+    State _state;
+  };
+
+  typedef vector<ButtonState> Buttons;
+  Buttons _buttons;
+
+  PT(ButtonEventDataAttribute) _button_events;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientDevice::init_type();
+    register_type(_type_handle, "ClientButtonDevice",
+                  ClientDevice::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 "clientButtonDevice.I"
+
+#endif

+ 85 - 0
panda/src/device/clientDevice.I

@@ -0,0 +1,85 @@
+// Filename: clientDevice.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::get_client
+//       Access: Public
+//  Description: Returns the ClientBase this device is associated
+//               with.
+////////////////////////////////////////////////////////////////////
+INLINE ClientBase *ClientDevice::
+get_client() const {
+  return _client;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::is_connected
+//       Access: Public
+//  Description: Returns true if the device is still connected to its
+//               ClientBase, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClientDevice::
+is_connected() const {
+  return _is_connected;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::get_device_type
+//       Access: Public
+//  Description: Returns the type of device this is considered to be
+//               to the ClientBase: a ClientTrackerDevice,
+//               ClientAnalogDevice, or what have you.  This is not
+//               exactly the same thing as get_type(), because it does
+//               not return the exact type of the ClientDevice
+//               (e.g. it reports ClientTrackerDevice, not
+//               VrpnTrackerDevice).
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle ClientDevice::
+get_device_type() const {
+  return _device_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::get_device_name
+//       Access: Public
+//  Description: Returns the device name reported to the ClientBase.
+//               This has some implementation-defined meaning to
+//               identify particular devices.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ClientDevice::
+get_device_name() const {
+  return _device_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::lock
+//       Access: Public
+//  Description: Grabs the mutex associated with this particular
+//               device.  The device will not update asynchronously
+//               while the mutex is held, allowing the user to copy
+//               the data out without fear of getting a partial update
+//               during the copy.
+////////////////////////////////////////////////////////////////////
+INLINE void ClientDevice::
+lock() {
+#ifdef HAVE_IPC
+  _lock.lock();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::unlock
+//       Access: Public
+//  Description: Releases the mutex associated with this particular
+//               device.  This should be called after all the data has
+//               been successfully copied out.  See lock().
+////////////////////////////////////////////////////////////////////
+INLINE void ClientDevice::
+unlock() {
+#ifdef HAVE_IPC
+  _lock.unlock();
+#endif
+}

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

@@ -0,0 +1,104 @@
+// Filename: clientDevice.cxx
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "clientDevice.h"
+#include "clientBase.h"
+
+#include <indent.h>
+
+TypeHandle ClientDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ClientDevice::
+ClientDevice(ClientBase *client, TypeHandle device_type, 
+	     const string &device_name) :
+  _client(client),
+  _device_type(device_type),
+  _device_name(device_name)
+{
+  // We have to explicitly ref the client pointer, since we can't use
+  // a PT(ClientBase) for circular include reasons.
+  _client->ref();
+  _is_connected = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::Destructor
+//       Access: Public, Virtual
+//  Description: We don't actually call disconnect() at the
+//               ClientDevice level destructor, because by the time
+//               we get here we're already partly destructed.
+//               Instead, we should call disconnect() from each
+//               specific kind of derived class.
+////////////////////////////////////////////////////////////////////
+ClientDevice::
+~ClientDevice() {
+  nassertv(!_is_connected);
+
+  // And now we explicitly unref the client pointer.
+  unref_delete(_client);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::disconnect
+//       Access: Public
+//  Description: Disconnects the ClientDevice from its ClientBase
+//               object.  The device will stop receiving
+//               updates.  
+//
+//               Normally, you should not need to call this explicitly
+//               (and it is probably a mistake to do so); it will
+//               automatically be called when the ClientDevice object
+//               destructs.
+////////////////////////////////////////////////////////////////////
+void ClientDevice::
+disconnect() {
+  if (_is_connected) {
+    bool disconnected =
+      _client->disconnect_device(_device_type, _device_name, this);
+    _is_connected = false;
+    nassertv(disconnected);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::poll
+//       Access: Public
+//  Description: Causes the connected ClientBase to poll all of its
+//               clients, if necessary.  This will be a no-op if the
+//               client is running in forked mode, or if it has
+//               already polled everything this frame.
+//
+//               This should generally be called before accessing the
+//               data in this ClientDevice to ensure that it is fresh.
+////////////////////////////////////////////////////////////////////
+void ClientDevice::
+poll() {
+  _client->poll();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClientDevice::
+output(ostream &out) const {
+  out << get_type() << " " << get_device_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientDevice::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClientDevice::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}

+ 85 - 0
panda/src/device/clientDevice.h

@@ -0,0 +1,85 @@
+// Filename: clientDevice.h
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLIENTDEVICE_H
+#define CLIENTDEVICE_H
+
+#include <pandabase.h>
+
+#include <typedReferenceCount.h>
+
+#ifdef HAVE_IPC
+#include <ipc_mutex.h>
+#endif
+
+class ClientBase;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ClientDevice
+// Description : Any of a number of different devices that might be
+//               attached to a ClientBase, including trackers, etc.
+//               This is an abstract interface; the actual
+//               implementations are in ClientTrackerDevice, etc.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClientDevice : public TypedReferenceCount {
+protected:
+  ClientDevice(ClientBase *client, TypeHandle device_type, 
+	       const string &device_name);
+
+public:
+  virtual ~ClientDevice();
+
+  INLINE ClientBase *get_client() const;
+  INLINE TypeHandle get_device_type() const;
+  INLINE const string &get_device_name() const;
+
+  INLINE bool is_connected() const;
+  void disconnect();
+
+  void poll();
+  INLINE void lock();
+  INLINE void unlock();
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+private:
+  ClientBase *_client;
+  TypeHandle _device_type;
+  string _device_name;
+  bool _is_connected;
+
+#ifdef HAVE_IPC
+  mutex _lock;
+#endif
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "ClientDevice",
+                  TypedReferenceCount::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;
+
+  friend class ClientBase;
+};
+
+INLINE ostream &operator <<(ostream &out, const ClientDevice &device) {
+  device.output(out);
+  return out;
+}
+
+#include "clientDevice.I"
+
+#endif

+ 28 - 0
panda/src/device/clientTrackerDevice.I

@@ -0,0 +1,28 @@
+// Filename: clientTrackerDevice.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientTrackerDevice::Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ClientTrackerDevice::
+ClientTrackerDevice(ClientBase *client, const string &device_name):
+  ClientDevice(client, get_class_type(), device_name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClientTrackerDevice::get_data
+//       Access: Public
+//  Description: Returns the TrackerData that this device is
+//               reporting.
+////////////////////////////////////////////////////////////////////
+INLINE const TrackerData &ClientTrackerDevice::
+get_data() const {
+  return _data;
+}
+

+ 8 - 0
panda/src/device/clientTrackerDevice.cxx

@@ -0,0 +1,8 @@
+// Filename: clientTrackerDevice.cxx
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "clientTrackerDevice.h"
+
+TypeHandle ClientTrackerDevice::_type_handle;

+ 50 - 0
panda/src/device/clientTrackerDevice.h

@@ -0,0 +1,50 @@
+// Filename: clientTrackerDevice.h
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLIENTTRACKERDEVICE_H
+#define CLIENTTRACKERDEVICE_H
+
+#include <pandabase.h>
+
+#include "clientDevice.h"
+#include "trackerData.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ClientTrackerDevice
+// Description : A device, attached to the ClientBase by a
+//               TrackerNode, that records the data from a single
+//               tracker device.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClientTrackerDevice : public ClientDevice {
+protected:
+  INLINE ClientTrackerDevice(ClientBase *client, const string &device_name);
+
+public:
+  INLINE const TrackerData &get_data() const;
+
+protected:
+  TrackerData _data;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientDevice::init_type();
+    register_type(_type_handle, "ClientTrackerDevice",
+                  ClientDevice::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 "clientTrackerDevice.I"
+
+#endif

+ 14 - 4
panda/src/device/config_device.cxx

@@ -4,10 +4,15 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_device.h"
-#include "mouse.h"
+#include "analogNode.h"
+#include "buttonNode.h"
+#include "clientAnalogDevice.h"
 #include "clientBase.h"
+#include "clientButtonDevice.h"
+#include "clientDevice.h"
+#include "clientTrackerDevice.h"
+#include "mouse.h"
 #include "trackerNode.h"
-#include "adinputNode.h"
 
 #include <dconfig.h>
 
@@ -36,8 +41,13 @@ init_libdevice() {
   }
   initialized = true;
 
+  AnalogNode::init_type();
+  ButtonNode::init_type();
+  ClientAnalogDevice::init_type();
+  ClientBase::init_type();
+  ClientButtonDevice::init_type();
+  ClientDevice::init_type();
+  ClientTrackerDevice::init_type();
   MouseAndKeyboard::init_type();
   TrackerNode::init_type();
-  ADInputNode::init_type();
-  ClientBase::init_type();
 }

+ 164 - 23
panda/src/device/trackerData.I

@@ -10,7 +10,7 @@
 ////////////////////////////////////////////////////////////////////
 INLINE TrackerData::
 TrackerData() :
-  ptime(0), vtime(0), vquat_dt(0), atime(0), aquat_dt(0)
+  _flags(0)
 {
 }
 
@@ -25,35 +25,176 @@ TrackerData(const TrackerData &copy) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TrackerData::Copy Assignment Operator
+//     Function: TrackerData::clear
 //       Access: Public
-//  Description: 
+//  Description: Removes all data from the structure.
+////////////////////////////////////////////////////////////////////
+INLINE void TrackerData::
+clear() {
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::set_time
+//       Access: Public
+//  Description: Indicates the time at which the position information
+//               (pos and orient) are effective.  This is a time
+//               elapsed in seconds since some undefined epoch; it may
+//               or may not correspond to the clock time indicated in
+//               the global ClockObject.
+////////////////////////////////////////////////////////////////////
+INLINE void TrackerData::
+set_time(double time) {
+  _time = time;
+  _flags |= F_has_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::has_time
+//       Access: Public
+//  Description: Returns true if the position information time is
+//               available.  See set_time().
+////////////////////////////////////////////////////////////////////
+INLINE bool TrackerData::
+has_time() const {
+  return (_flags & F_has_time) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::get_time
+//       Access: Public
+//  Description: Returns the time at which the position information
+//               (pos and orient) are effective.  It is an error to
+//               call this if has_time() does not return true.  See
+//               set_time().
+////////////////////////////////////////////////////////////////////
+INLINE double TrackerData::
+get_time() const {
+  nassertr(has_time(), 0.0);
+  return _time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::set_pos
+//       Access: Public
+//  Description: Indicates the current position of the tracker sensor
+//               in space.  The coordinate system of this position is
+//               defined by the tracker.
+////////////////////////////////////////////////////////////////////
+INLINE void TrackerData::
+set_pos(const LPoint3f &pos) {
+  _pos = pos;
+  _flags |= F_has_pos;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::has_pos
+//       Access: Public
+//  Description: Returns true if the current position is available.
+//               See set_pos().
+////////////////////////////////////////////////////////////////////
+INLINE bool TrackerData::
+has_pos() const {
+  return (_flags & F_has_pos) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::get_pos
+//       Access: Public
+//  Description: Returns the current position of the tracker.  It is
+//               legal to call this if has_pos() returns false; in
+//               this case, the position will always be (0, 0, 0).
 ////////////////////////////////////////////////////////////////////
-INLINE TrackerData &TrackerData::
-operator = (const TrackerData &copy) {
-  ptime = copy.ptime;
-  position = copy.position;
-  pquat = copy.pquat;
+INLINE const LPoint3f &TrackerData::
+get_pos() const {
+  if (has_pos()) {
+    return _pos;
+  } else {
+    static LPoint3f zero(0.0, 0.0, 0.0);
+    return zero;
+  }
+}
 
-  vtime = copy.vtime;
-  velocity = copy.velocity;
-  vquat = copy.vquat;
-  vquat_dt = copy.vquat_dt;
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::set_orient
+//       Access: Public
+//  Description: Indicates the current orientation of the tracker
+//               sensor in space.  The coordinate system of this
+//               orientation is defined by the tracker, but should be
+//               the same coordinate system as that reflected by
+//               set_pos().
+////////////////////////////////////////////////////////////////////
+INLINE void TrackerData::
+set_orient(const LOrientationf &orient) {
+  _orient = orient;
+  _flags |= F_has_orient;
+}
 
-  atime = copy.atime;
-  acceleration = copy.acceleration;
-  aquat = copy.aquat;
-  aquat_dt = copy.aquat_dt;
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::has_orient
+//       Access: Public
+//  Description: Returns true if the current orientation is available.
+//               See set_orient().
+////////////////////////////////////////////////////////////////////
+INLINE bool TrackerData::
+has_orient() const {
+  return (_flags & F_has_orient) != 0;
+}
 
-  return *this;
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::get_orient
+//       Access: Public
+//  Description: Returns the current orientation of the tracker.  It
+//               is legal to call this if has_orient() returns false;
+//               in this case, the result is always the identity
+//               orientation.
+////////////////////////////////////////////////////////////////////
+INLINE const LOrientationf &TrackerData::
+get_orient() const {
+  if (has_orient()) {
+    return _orient;
+  } else {
+    static LOrientationf ident = LOrientationf::ident_quat();
+    return ident;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::set_dt
+//       Access: Public
+//  Description: Indicates the amount of elapsed time over which which
+//               the information (pos and orient) were computed.  This
+//               only makes sense if the information represents
+//               velocity or acceleration, rather than position.  This
+//               is an elapsed time in seconds.
+////////////////////////////////////////////////////////////////////
+INLINE void TrackerData::
+set_dt(double dt) {
+  _dt = dt;
+  _flags |= F_has_dt;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TrackerData::none
-//       Access: Public, Static
-//  Description: Returns an empty TrackerData object
+//     Function: TrackerData::has_dt
+//       Access: Public
+//  Description: Returns true if the computed elapsed time is
+//               available.  See set_dt().
+////////////////////////////////////////////////////////////////////
+INLINE bool TrackerData::
+has_dt() const {
+  return (_flags & F_has_dt) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::get_dt
+//       Access: Public
+//  Description: Returns the amount of elapsed time over which the
+//               information (pos and orient) were computed.  It
+//               is an error to call this if has_dt() does not return
+//               true.  See set_dt().
 ////////////////////////////////////////////////////////////////////
-INLINE const TrackerData &TrackerData::
-none() {
- return _none; 
+INLINE double TrackerData::
+get_dt() const {
+  nassertr(has_dt(), 0.0);
+  return _dt;
 }

+ 14 - 2
panda/src/device/trackerData.cxx

@@ -5,5 +5,17 @@
 
 #include "trackerData.h"
 
-// This is initialized to zero by static initialization.
-TrackerData TrackerData::_none;
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerData::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TrackerData::
+operator = (const TrackerData &copy) {
+  _flags = copy._flags;
+
+  _time = copy._time;
+  _pos = copy._pos;
+  _orient = copy._orient;
+  _dt = copy._dt;
+}

+ 35 - 17
panda/src/device/trackerData.h

@@ -3,36 +3,54 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-#ifndef TRACKER_DATA
-#define TRACKER_DATA
+#ifndef TRACKERDATA_H
+#define TRACKERDATA_H
 
 #include <pandabase.h>
 #include <luse.h>
 
+////////////////////////////////////////////////////////////////////
+//       Class : TrackerData
+// Description : Stores the kinds of data that a tracker might output.
+////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA TrackerData {
 public:
   INLINE TrackerData();
   INLINE TrackerData(const TrackerData &copy);
-  INLINE TrackerData &operator = (const TrackerData &copy);
+  void operator = (const TrackerData &copy);
+
+  INLINE void clear();
+
+  INLINE void set_time(double time);
+  INLINE bool has_time() const;
+  INLINE double get_time() const;
 
-  INLINE static const TrackerData &none();
+  INLINE void set_pos(const LPoint3f &pos);
+  INLINE bool has_pos() const;
+  INLINE const LPoint3f &get_pos() const;
 
-  double ptime;
-  LPoint3f position;
-  LVector4f pquat;
-  
-  double vtime;
-  LVector3f velocity;
-  LVector4f vquat;
-  float vquat_dt;
+  INLINE void set_orient(const LOrientationf &orient);
+  INLINE bool has_orient() const;
+  INLINE const LOrientationf &get_orient() const;
 
-  double atime;
-  LVector3f acceleration;
-  LVector4f aquat;
-  float aquat_dt;
+  INLINE void set_dt(double dt);
+  INLINE bool has_dt() const;
+  INLINE double get_dt() const;
 
 private:
-  static TrackerData _none;
+  enum Flags {
+    F_has_time    = 0x0001,
+    F_has_pos     = 0x0002,
+    F_has_orient  = 0x0004,
+    F_has_dt      = 0x0008,
+  };
+
+  int _flags;
+
+  double _time;
+  LPoint3f _pos;
+  LOrientationf _orient;
+  double _dt;
 };
 
 #include "trackerData.I"

+ 51 - 0
panda/src/device/trackerNode.I

@@ -0,0 +1,51 @@
+// Filename: trackerNode.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerNode::is_valid
+//       Access: Public
+//  Description: Returns true if the TrackerNode is valid and
+//               connected to a server, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool TrackerNode::
+is_valid() const {
+  return (_tracker != (ClientTrackerDevice *)NULL) && _tracker->is_connected();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerNode::get_pos
+//       Access: Public
+//  Description: Returns the current position of the tracker, if it is
+//               available.
+////////////////////////////////////////////////////////////////////
+const LPoint3f &TrackerNode::
+get_pos() const {
+  return _data.get_pos();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerNode::get_orient
+//       Access: Public
+//  Description: Returns the current orientation of the tracker, if it
+//               is available.
+////////////////////////////////////////////////////////////////////
+const LOrientationf &TrackerNode::
+get_orient() const {
+  return _data.get_orient();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerNode::get_transform
+//       Access: Public
+//  Description: Returns the current position and orientation of the
+//               tracker, as a combined matrix.
+////////////////////////////////////////////////////////////////////
+const LMatrix4f &TrackerNode::
+get_transform() const {
+  return _transform;
+}
+

+ 54 - 106
panda/src/device/trackerNode.cxx

@@ -11,19 +11,7 @@
 ////////////////////////////////////////////////////////////////////
 TypeHandle TrackerNode::_type_handle;
 
-TypeHandle TrackerNode::_ptime_type;
-TypeHandle TrackerNode::_position_type;
-TypeHandle TrackerNode::_pquat_type;
-
-TypeHandle TrackerNode::_vtime_type;
-TypeHandle TrackerNode::_velocity_type;
-TypeHandle TrackerNode::_vquat_type;
-TypeHandle TrackerNode::_vquat_dt_type;
-
-TypeHandle TrackerNode::_atime_type;
-TypeHandle TrackerNode::_acceleration_type;
-TypeHandle TrackerNode::_aquat_type;
-TypeHandle TrackerNode::_aquat_dt_type;
+TypeHandle TrackerNode::_transform_type;
   
 ////////////////////////////////////////////////////////////////////
 //     Function: TrackerNode::Constructor
@@ -31,39 +19,44 @@ TypeHandle TrackerNode::_aquat_dt_type;
 //  Description:
 ////////////////////////////////////////////////////////////////////
 TrackerNode::
-TrackerNode(PT(ClientBase) client, const string &tracker, 
-	    int sensor) :
-  DataNode(tracker), _client(client), _tracker(tracker), _sensor(sensor)
+TrackerNode(ClientBase *client, const string &device_name) :
+  DataNode(device_name)
 {
-  _client->add_remote_tracker(_tracker, _sensor);
-  
-  _ptime = new DoubleDataAttribute();
-  _position = new Vec3DataAttribute(LPoint3f(0,0,0));
-  _pquat = new Vec4DataAttribute(LVector4f(0,0,0,0));
-
-  _vtime = new DoubleDataAttribute();
-  _velocity = new Vec3DataAttribute(LPoint3f(0,0,0));
-  _vquat = new Vec4DataAttribute(LVector4f(0,0,0,0));
-  _vquat_dt = new DoubleDataAttribute();
-
-  _atime = new DoubleDataAttribute();
-  _acceleration = new Vec3DataAttribute(LPoint3f(0,0,0));
-  _aquat = new Vec4DataAttribute(LVector4f(0,0,0,0));
-  _aquat_dt = new DoubleDataAttribute();
-
-  _tracker_attrib.set_attribute(_ptime_type, _ptime);
-  _tracker_attrib.set_attribute(_position_type, _position);
-  _tracker_attrib.set_attribute(_pquat_type, _pquat);
-
-  _tracker_attrib.set_attribute(_vtime_type, _vtime);
-  _tracker_attrib.set_attribute(_velocity_type, _velocity);
-  _tracker_attrib.set_attribute(_vquat_type, _vquat);
-  _tracker_attrib.set_attribute(_vquat_dt_type, _vquat_dt);
-
-  _tracker_attrib.set_attribute(_atime_type, _atime);
-  _tracker_attrib.set_attribute(_acceleration_type, _acceleration);
-  _tracker_attrib.set_attribute(_aquat_type, _aquat);
-  _tracker_attrib.set_attribute(_aquat_dt_type, _aquat_dt);
+  nassertv(client != (ClientBase *)NULL);
+  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);
+
+  if (_tracker != (ClientTrackerDevice *)NULL) {
+    _transform_attrib = new MatrixDataAttribute;
+    _attrib.set_attribute(_transform_type, _transform_attrib);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrackerNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+TrackerNode::
+~TrackerNode() {
+  // When the _tracker pointer destructs, the ClientTrackerDevice
+  // disconnects itself from the ClientBase, and everything that needs
+  // to get turned off does.  Magic.
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -73,32 +66,24 @@ TrackerNode(PT(ClientBase) client, const string &tracker,
 ////////////////////////////////////////////////////////////////////
 void TrackerNode::
 transmit_data(NodeAttributes &data) {
-  TrackerData new_data = _client->get_tracker_data(_tracker, _sensor);
+  if (is_valid()) {
+    _tracker->poll();
+    _tracker->lock();
+    _data = _tracker->get_data();
+    _tracker->unlock();
 
-  if (device_cat.is_debug()) {
-    device_cat.debug() << "TrackerNode:transmit_data" << endl;
-  }
+    _data.get_orient().extract_to_matrix(_transform);
+    _transform.set_row(3, _data.get_pos());
 
-  _ptime->set_value(new_data.ptime);
-  _position->set_value(new_data.position);
-  _pquat->set_value(new_data.pquat);
+    _transform_attrib->set_value(_transform);
 
-  _vtime->set_value(new_data.vtime);
-  _velocity->set_value(new_data.velocity);
-  _vquat->set_value(new_data.vquat);
-  _vquat_dt->set_value(new_data.vquat_dt);
-
-  _atime->set_value(new_data.atime);
-  _acceleration->set_value(new_data.acceleration);
-  _aquat->set_value(new_data.aquat);
-  _aquat_dt->set_value(new_data.aquat_dt);
-
-  if (device_cat.is_debug()) {
-    device_cat.debug() << "TrackerNode:attributes" << endl;
-    _tracker_attrib.write(device_cat.debug(false), 3);
+    if (device_cat.is_debug()) {
+      device_cat.debug() << "TrackerNode:attributes" << endl;
+      _attrib.write(device_cat.debug(false), 3);
+    }
   }
 
-  data = _tracker_attrib;
+  data = _attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -112,45 +97,8 @@ init_type() {
   register_type(_type_handle, "TrackerNode",
 		DataNode::get_class_type());
 
-  DoubleDataTransition::init_type();
-  Vec3DataTransition::init_type();
-  Vec4DataTransition::init_type();
-
-  register_data_transition(_ptime_type, "Position Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_position_type, "Position",
-			   Vec3DataTransition::get_class_type());
-  register_data_transition(_pquat_type, "Position Quat",
-			   Vec4DataTransition::get_class_type());
-
-  register_data_transition(_vtime_type, "Velocity Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_velocity_type, "Velocity",
-			   Vec3DataTransition::get_class_type());
-  register_data_transition(_vquat_type, "Velocity Quat",
-			   Vec4DataTransition::get_class_type());
-  register_data_transition(_vquat_dt_type, "Velocity Quat dt",
-			   DoubleDataTransition::get_class_type());
-
-  register_data_transition(_atime_type, "Acceleration Time",
-			   DoubleDataTransition::get_class_type());
-  register_data_transition(_acceleration_type, "Acceleration",
-			   Vec3DataTransition::get_class_type());
-  register_data_transition(_aquat_type, "Acceleration Quat",
-			   Vec4DataTransition::get_class_type());
-  register_data_transition(_aquat_dt_type, "Acceleration Quat dt",
-			   DoubleDataTransition::get_class_type());
+  MatrixDataTransition::init_type();
+  register_data_transition(_transform_type, "Transform",
+			   MatrixDataTransition::get_class_type());
 }
 
-
-
-
-
-
-
-
-
-
-
-
-

+ 40 - 52
panda/src/device/trackerNode.h

@@ -3,73 +3,59 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-#ifndef _TRACKER_NODE
-#define _TRACKER_NODE
+#ifndef TRACKERNODE_H
+#define TRACKERNODE_H
 
 #include <pandabase.h>
 
+#include "clientBase.h"
+#include "trackerData.h"
+#include "clientTrackerDevice.h"
+
 #include <dataNode.h>
+#include <matrixDataTransition.h>
+#include <matrixDataAttribute.h>
 #include <nodeAttributes.h>
-#include <doubleDataAttribute.h>
-#include <doubleDataTransition.h>
-#include <vec3DataAttribute.h>
-#include <vec3DataTransition.h>
-#include <vec3DataAttribute.h>
-#include <vec4DataTransition.h>
-#include <vec4DataAttribute.h>
-
+#include <luse.h>
+#include <lmatrix.h>
 #include <pointerTo.h>
-#include "clientBase.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : TrackerNode
-// Description : Reads the position, velocity and acceleration
-//               information from one sensor on a tracker and sends it
-//               down the DataGraph
+// Description : Reads the data associated with a single tracker
+//               sensor accessed on some ClientBase, and makes the
+//               data available to user code.  Also places the current
+//               tracker's transform on the data graph.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA TrackerNode : public DataNode {
 PUBLISHED:
-  TrackerNode(PT(ClientBase) client, const string &tracker, 
-	      int sensor);
+  TrackerNode(ClientBase *client, const string &device_name);
+  virtual ~TrackerNode();
+
+  INLINE bool is_valid() const;
+
+  INLINE const LPoint3f &get_pos() const;
+  INLINE const LOrientationf &get_orient() const;
+  INLINE const LMatrix4f &get_transform() const;
 
-public:
-  virtual void transmit_data(NodeAttributes &data);
 
+////////////////////////////////////////////////////////////////////
+// From parent class DataNode
+////////////////////////////////////////////////////////////////////
 public:
-  NodeAttributes _tracker_attrib;
-
-  PT(DoubleDataAttribute) _ptime;
-  PT(Vec3DataAttribute) _position;
-  PT(Vec4DataAttribute) _pquat;
-  
-  PT(DoubleDataAttribute) _vtime;
-  PT(Vec3DataAttribute) _velocity;
-  PT(Vec4DataAttribute) _vquat;
-  PT(DoubleDataAttribute) _vquat_dt;
-
-  PT(DoubleDataAttribute) _atime;
-  PT(Vec3DataAttribute) _acceleration;
-  PT(Vec4DataAttribute) _aquat;
-  PT(DoubleDataAttribute) _aquat_dt;
-
-  static TypeHandle _ptime_type;
-  static TypeHandle _position_type;
-  static TypeHandle _pquat_type;
-
-  static TypeHandle _vtime_type;
-  static TypeHandle _velocity_type;
-  static TypeHandle _vquat_type;
-  static TypeHandle _vquat_dt_type;
-
-  static TypeHandle _atime_type;
-  static TypeHandle _acceleration_type;
-  static TypeHandle _aquat_type;
-  static TypeHandle _aquat_dt_type;
-  
-protected:
-  PT(ClientBase) _client;
-  string _tracker;
-  int _sensor;
+  virtual void
+  transmit_data(NodeAttributes &data);
+
+  NodeAttributes _attrib;
+  PT(MatrixDataAttribute) _transform_attrib;
+
+  // outputs
+  static TypeHandle _transform_type;
+
+private:
+  PT(ClientTrackerDevice) _tracker;
+  TrackerData _data;
+  LMatrix4f _transform;
 
 public:
   virtual TypeHandle get_type() const {
@@ -85,4 +71,6 @@ private:
   static TypeHandle _type_handle;
 };
 
+#include "trackerNode.I"
+
 #endif

+ 1 - 1
panda/src/sgraph/geomNode.h

@@ -37,7 +37,7 @@ public:
   virtual void xform(const LMatrix4f &mat);
 
 PUBLISHED:
-  void write(ostream &out, int indent_level = 0) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
   void write_verbose(ostream &out, int indent_level) const;
 
 public:

+ 2 - 2
panda/src/tform/mouseWatcher.h

@@ -76,8 +76,8 @@ PUBLISHED:
   INLINE NodeRelation *get_geometry() const;
   INLINE void clear_geometry();
 
-  void output(ostream &out) const;
-  void write(ostream &out, int indent_level = 0) const;
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
 
 private:
   void set_current_region(MouseWatcherRegion *region);

+ 10 - 2
panda/src/vrpn/Sources.pp

@@ -11,10 +11,18 @@
 
   #define SOURCES \
     config_vrpn.cxx config_vrpn.h vrpnClient.I vrpnClient.cxx \
-    vrpnClient.h
+    vrpnAnalog.I vrpnAnalog.cxx vrpnAnalog.h \
+    vrpnAnalogDevice.I vrpnAnalogDevice.cxx vrpnAnalogDevice.h \
+    vrpnButton.I vrpnButton.cxx vrpnButton.h \
+    vrpnButtonDevice.I vrpnButtonDevice.cxx vrpnButtonDevice.h \
+    vrpnClient.h \
+    vrpnTracker.I vrpnTracker.cxx vrpnTracker.h \
+    vrpnTrackerDevice.I vrpnTrackerDevice.cxx vrpnTrackerDevice.h \
+    vrpn_interface.h
 
   #define INSTALL_HEADERS \
-    config_vrpn.h vrpnClient.I vrpnClient.h
+    config_vrpn.h \
+    vrpnClient.I vrpnClient.h
 
   #define IGATESCAN all
 

+ 6 - 0
panda/src/vrpn/config_vrpn.cxx

@@ -4,7 +4,10 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_vrpn.h"
+#include "vrpnAnalogDevice.h"
+#include "vrpnButtonDevice.h"
 #include "vrpnClient.h"
+#include "vrpnTrackerDevice.h"
 
 #include <dconfig.h>
 
@@ -13,5 +16,8 @@ NotifyCategoryDef(vrpn, "");
 
 
 ConfigureFn(config_vrpn) {
+  VrpnAnalogDevice::init_type();
+  VrpnButtonDevice::init_type();
   VrpnClient::init_type();
+  VrpnTrackerDevice::init_type();
 }

+ 38 - 0
panda/src/vrpn/vrpnAnalog.I

@@ -0,0 +1,38 @@
+// Filename: vrpnAnalog.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::get_analog_name
+//       Access: Public
+//  Description: Returns the name of the analog device that was used
+//               to create this VrpnAnalog.
+////////////////////////////////////////////////////////////////////
+INLINE const string &VrpnAnalog::
+get_analog_name() const {
+  return _analog_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::is_empty
+//       Access: Public
+//  Description: Returns true if no VrpnAnalogDevices reference this
+//               VrpnAnalog, or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool VrpnAnalog::
+is_empty() const {
+  return _devices.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::poll
+//       Access: Public
+//  Description: Polls the connected device.  Normally you should not
+//               call this directly; this will be called by the
+//               VrpnClient.
+////////////////////////////////////////////////////////////////////
+INLINE void VrpnAnalog::
+poll() {
+  _analog->mainloop();
+}

+ 121 - 0
panda/src/vrpn/vrpnAnalog.cxx

@@ -0,0 +1,121 @@
+// Filename: vrpnAnalog.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnAnalog.h"
+#include "vrpnAnalogDevice.h"
+#include "vrpnClient.h"
+#include "config_vrpn.h"
+
+#include <indent.h>
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnAnalog::
+VrpnAnalog(const string &analog_name, vrpn_Connection *connection) :
+  _analog_name(analog_name)
+{
+  _analog = new vrpn_Analog_Remote(_analog_name.c_str(), connection);
+
+  _analog->register_change_handler((void*)this, &vrpn_analog_callback);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnAnalog::
+~VrpnAnalog() {
+  delete _analog;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::mark
+//       Access: Public
+//  Description: Adds the indicated VrpnAnalogDevice to the list of
+//               devices that are sharing this VrpnAnalog.
+////////////////////////////////////////////////////////////////////
+void VrpnAnalog::
+mark(VrpnAnalogDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " marking " << *device << "\n";
+  }
+  _devices.push_back(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::unmark
+//       Access: Public
+//  Description: Removes the indicated VrpnAnalogDevice from the list
+//               of devices that are sharing this VrpnAnalog.
+////////////////////////////////////////////////////////////////////
+void VrpnAnalog::
+unmark(VrpnAnalogDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " unmarking " << *device << "\n";
+  }
+
+  Devices::iterator di = 
+    find(_devices.begin(), _devices.end(), device);
+
+  if (di != _devices.end()) {
+    _devices.erase(di);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnAnalog::
+output(ostream &out) const {
+  out << _analog_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnAnalog::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) 
+    << get_analog_name() << " ("
+    << _devices.size() << " devices)\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalog::vrpn_analog_callback
+//       Access: Private, Static
+//  Description: Receives the analog event data from the VRPN
+//               code and sends it to any interested
+//               VrpnAnalogDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnAnalog::
+vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info) {
+  VrpnAnalog *self = (VrpnAnalog *)userdata;
+
+  Devices::iterator di;
+  for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
+    VrpnAnalogDevice *device = (*di);
+    device->lock();
+    for (int i = 0; i < info.num_channel; i++) {
+      if (vrpn_cat.is_debug()) {
+	if (device->get_control_state(i) != info.channel[i]) {
+	  vrpn_cat.debug()
+	    << *self << " got analog " << i << " = " << info.channel[i] << "\n";
+	}
+      }
+      device->set_control_state(i, info.channel[i]);
+    }
+    device->unlock();
+  }
+}

+ 68 - 0
panda/src/vrpn/vrpnAnalog.h

@@ -0,0 +1,68 @@
+// Filename: vrpnAnalog.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNANALOG_H
+#define VRPNANALOG_H
+
+#include <pandabase.h>
+
+#include "vrpn_interface.h"
+
+#include <vector>
+
+class VrpnAnalogDevice;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnAnalog
+// Description : This is the actual interface to a particular VRPN
+//               analog device, and all of its numbered controls.  A
+//               pointer to this object is stored in the VrpnClient
+//               class for each differently-named VRPN analog device
+//               we connect to.
+//
+//               The VRPN callbacks go here, which in turn get
+//               vectored out to any VrpnAnalogDevice objects that
+//               register with this.  When the last VrpnAnalogDevice
+//               object unregisters, the VrpnAnalog will be deleted
+//               by the VrpnClient.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnAnalog {
+public:
+  VrpnAnalog(const string &analog_name, vrpn_Connection *connection);
+  ~VrpnAnalog();
+
+  INLINE const string &get_analog_name() const;
+  INLINE bool is_empty() const;
+
+  void mark(VrpnAnalogDevice *device);
+  void unmark(VrpnAnalogDevice *device);
+
+  INLINE void poll();
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+private:
+  static void
+  vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info);
+
+private:
+  string _analog_name;
+  vrpn_Analog_Remote *_analog;
+
+  typedef vector<VrpnAnalogDevice *> Devices;
+  Devices _devices;
+};
+
+INLINE ostream &operator << (ostream &out, const VrpnAnalog &analog) {
+  analog.output(out);
+  return out;
+}
+
+#include "vrpnAnalog.I"
+
+#endif

+ 17 - 0
panda/src/vrpn/vrpnAnalogDevice.I

@@ -0,0 +1,17 @@
+// Filename: vrpnAnalogDevice.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalogDevice::get_vrpn_analog
+//       Access: Public
+//  Description: Returns a pointer to the particular VrpnAnalog this
+//               device gets its data from.  This pointer may be
+//               shared with other VrpnAnalogDevice objects.
+////////////////////////////////////////////////////////////////////
+INLINE VrpnAnalog *VrpnAnalogDevice::
+get_vrpn_analog() const {
+  return _vrpn_analog;
+}

+ 32 - 0
panda/src/vrpn/vrpnAnalogDevice.cxx

@@ -0,0 +1,32 @@
+// Filename: vrpnAnalogDevice.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnAnalogDevice.h"
+#include "vrpnClient.h"
+
+TypeHandle VrpnAnalogDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalogDevice::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnAnalogDevice::
+VrpnAnalogDevice(VrpnClient *client, const string &device_name,
+		 VrpnAnalog *vrpn_analog) :
+  ClientAnalogDevice(client, device_name),
+  _vrpn_analog(vrpn_analog)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnAnalogDevice::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnAnalogDevice::
+~VrpnAnalogDevice() {
+  disconnect();
+}

+ 57 - 0
panda/src/vrpn/vrpnAnalogDevice.h

@@ -0,0 +1,57 @@
+// Filename: vrpnAnalogDevice.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNANALOGDEVICE_H
+#define VRPNANALOGDEVICE_H
+
+#include <pandabase.h>
+
+#include <clientAnalogDevice.h>
+
+class VrpnClient;
+class VrpnAnalog;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnAnalogDevice
+// Description : The Panda interface to a VRPN analog device.  This
+//               object will be returned by VrpnClient::make_device(),
+//               for attaching to a AnalogNode.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnAnalogDevice : public ClientAnalogDevice {
+public:
+  VrpnAnalogDevice(VrpnClient *client, const string &device_name,
+		   VrpnAnalog *vrpn_analog);
+  virtual ~VrpnAnalogDevice();
+
+  INLINE VrpnAnalog *get_vrpn_analog() const;
+
+private:
+  VrpnAnalog *_vrpn_analog;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientAnalogDevice::init_type();
+    register_type(_type_handle, "VrpnAnalogDevice",
+                  ClientAnalogDevice::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;
+
+  friend class VrpnAnalog;
+};
+
+#include "vrpnAnalogDevice.I"
+
+#endif

+ 38 - 0
panda/src/vrpn/vrpnButton.I

@@ -0,0 +1,38 @@
+// Filename: vrpnButton.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::get_button_name
+//       Access: Public
+//  Description: Returns the name of the button device that was used
+//               to create this VrpnButton.
+////////////////////////////////////////////////////////////////////
+INLINE const string &VrpnButton::
+get_button_name() const {
+  return _button_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::is_empty
+//       Access: Public
+//  Description: Returns true if no VrpnButtonDevices reference this
+//               VrpnButton, or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool VrpnButton::
+is_empty() const {
+  return _devices.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::poll
+//       Access: Public
+//  Description: Polls the connected device.  Normally you should not
+//               call this directly; this will be called by the
+//               VrpnClient.
+////////////////////////////////////////////////////////////////////
+INLINE void VrpnButton::
+poll() {
+  _button->mainloop();
+}

+ 117 - 0
panda/src/vrpn/vrpnButton.cxx

@@ -0,0 +1,117 @@
+// Filename: vrpnButton.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnButton.h"
+#include "vrpnButtonDevice.h"
+#include "vrpnClient.h"
+#include "config_vrpn.h"
+
+#include <indent.h>
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnButton::
+VrpnButton(const string &button_name, vrpn_Connection *connection) :
+  _button_name(button_name)
+{
+  _button = new vrpn_Button_Remote(_button_name.c_str(), connection);
+
+  _button->register_change_handler((void*)this, &vrpn_button_callback);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnButton::
+~VrpnButton() {
+  delete _button;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::mark
+//       Access: Public
+//  Description: Adds the indicated VrpnButtonDevice to the list of
+//               devices that are sharing this VrpnButton.
+////////////////////////////////////////////////////////////////////
+void VrpnButton::
+mark(VrpnButtonDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " marking " << *device << "\n";
+  }
+  _devices.push_back(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::unmark
+//       Access: Public
+//  Description: Removes the indicated VrpnButtonDevice from the list
+//               of devices that are sharing this VrpnButton.
+////////////////////////////////////////////////////////////////////
+void VrpnButton::
+unmark(VrpnButtonDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " unmarking " << *device << "\n";
+  }
+
+  Devices::iterator di = 
+    find(_devices.begin(), _devices.end(), device);
+
+  if (di != _devices.end()) {
+    _devices.erase(di);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnButton::
+output(ostream &out) const {
+  out << _button_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnButton::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) 
+    << get_button_name() << " ("
+    << _devices.size() << " devices)\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButton::vrpn_button_callback
+//       Access: Private, Static
+//  Description: Receives the button event data from the VRPN
+//               code and sends it to any interested
+//               VrpnButtonDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnButton::
+vrpn_button_callback(void *userdata, const vrpn_BUTTONCB info) {
+  VrpnButton *self = (VrpnButton *)userdata;
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << *self << " got button " << info.button << " = " << info.state << "\n";
+  }
+
+  Devices::iterator di;
+  for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
+    VrpnButtonDevice *device = (*di);
+    device->lock();
+    device->set_button_state(info.button, info.state != 0);
+    device->unlock();
+  }
+}

+ 68 - 0
panda/src/vrpn/vrpnButton.h

@@ -0,0 +1,68 @@
+// Filename: vrpnButton.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNBUTTON_H
+#define VRPNBUTTON_H
+
+#include <pandabase.h>
+
+#include "vrpn_interface.h"
+
+#include <vector>
+
+class VrpnButtonDevice;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnButton
+// Description : This is the actual interface to a particular VRPN
+//               button device, and all of its numbered buttons.  A
+//               pointer to this object is stored in the VrpnClient
+//               class for each differently-named VRPN button device
+//               we connect to.
+//
+//               The VRPN callbacks go here, which in turn get
+//               vectored out to any VrpnButtonDevice objects that
+//               register with this.  When the last VrpnButtonDevice
+//               object unregisters, the VrpnButton will be deleted
+//               by the VrpnClient.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnButton {
+public:
+  VrpnButton(const string &button_name, vrpn_Connection *connection);
+  ~VrpnButton();
+
+  INLINE const string &get_button_name() const;
+  INLINE bool is_empty() const;
+
+  void mark(VrpnButtonDevice *device);
+  void unmark(VrpnButtonDevice *device);
+
+  INLINE void poll();
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+private:
+  static void
+  vrpn_button_callback(void *userdata, const vrpn_BUTTONCB info);
+
+private:
+  string _button_name;
+  vrpn_Button_Remote *_button;
+
+  typedef vector<VrpnButtonDevice *> Devices;
+  Devices _devices;
+};
+
+INLINE ostream &operator << (ostream &out, const VrpnButton &button) {
+  button.output(out);
+  return out;
+}
+
+#include "vrpnButton.I"
+
+#endif

+ 17 - 0
panda/src/vrpn/vrpnButtonDevice.I

@@ -0,0 +1,17 @@
+// Filename: vrpnButtonDevice.I
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButtonDevice::get_vrpn_button
+//       Access: Public
+//  Description: Returns a pointer to the particular VrpnButton this
+//               device gets its data from.  This pointer may be
+//               shared with other VrpnButtonDevice objects.
+////////////////////////////////////////////////////////////////////
+INLINE VrpnButton *VrpnButtonDevice::
+get_vrpn_button() const {
+  return _vrpn_button;
+}

+ 32 - 0
panda/src/vrpn/vrpnButtonDevice.cxx

@@ -0,0 +1,32 @@
+// Filename: vrpnButtonDevice.cxx
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnButtonDevice.h"
+#include "vrpnClient.h"
+
+TypeHandle VrpnButtonDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButtonDevice::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnButtonDevice::
+VrpnButtonDevice(VrpnClient *client, const string &device_name,
+		 VrpnButton *vrpn_button) :
+  ClientButtonDevice(client, device_name),
+  _vrpn_button(vrpn_button)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnButtonDevice::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnButtonDevice::
+~VrpnButtonDevice() {
+  disconnect();
+}

+ 57 - 0
panda/src/vrpn/vrpnButtonDevice.h

@@ -0,0 +1,57 @@
+// Filename: vrpnButtonDevice.h
+// Created by:  drose (26Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNBUTTONDEVICE_H
+#define VRPNBUTTONDEVICE_H
+
+#include <pandabase.h>
+
+#include <clientButtonDevice.h>
+
+class VrpnClient;
+class VrpnButton;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnButtonDevice
+// Description : The Panda interface to a VRPN button.  This object
+//               will be returned by VrpnClient::make_device(), for
+//               attaching to a ButtonNode.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnButtonDevice : public ClientButtonDevice {
+public:
+  VrpnButtonDevice(VrpnClient *client, const string &device_name,
+		   VrpnButton *vrpn_button);
+  virtual ~VrpnButtonDevice();
+
+  INLINE VrpnButton *get_vrpn_button() const;
+
+private:
+  VrpnButton *_vrpn_button;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientButtonDevice::init_type();
+    register_type(_type_handle, "VrpnButtonDevice",
+                  ClientButtonDevice::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;
+
+  friend class VrpnButton;
+};
+
+#include "vrpnButtonDevice.I"
+
+#endif

+ 52 - 9
panda/src/vrpn/vrpnClient.I

@@ -5,6 +5,57 @@
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::get_server_name
+//       Access: Public
+//  Description: Returns the name of the server as passed to the
+//               VrpnClient constructor.
+////////////////////////////////////////////////////////////////////
+INLINE const string &VrpnClient::
+get_server_name() const {
+  return _server_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::is_valid
+//       Access: Public
+//  Description: Returns true if everything seems to be kosher with
+//               the server (even if there is no connection), or false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool VrpnClient::
+is_valid() const {
+  return _connection->doing_okay();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::is_connected
+//       Access: Public
+//  Description: Returns true if the connection is established
+//               succesfully, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool VrpnClient::
+is_connected() const {
+  return _connection->connected();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::convert_to_secs
+//       Access: Public, Static
+//  Description: Little inline function to convert a struct timeval
+//               to only seconds
+////////////////////////////////////////////////////////////////////
+INLINE double VrpnClient::
+convert_to_secs(struct timeval msg_time) {
+  return (double)(msg_time.tv_sec) + (double)msg_time.tv_usec * 0.000001;
+}
+
+
+
+
+#if 0
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VrpnClient::constructor
 //       Access: Public
@@ -107,12 +158,4 @@ dial(const string &dial, const vrpn_DIALCB info) {
   push_dial(dial, dtime, info.dial, info.change);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: convert_to_secs
-//       Access: Public
-//  Description: Little inline function to convert a struct timeval
-//               to only seconds
-////////////////////////////////////////////////////////////////////
-INLINE double convert_to_secs(struct timeval msg_time) {
-  return (double)(msg_time.tv_sec) + (double)msg_time.tv_usec * 0.000001;
-}
+#endif

+ 541 - 0
panda/src/vrpn/vrpnClient.cxx

@@ -4,9 +4,548 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "vrpnClient.h"
+#include "vrpnTracker.h"
+#include "vrpnTrackerDevice.h"
+#include "vrpnButton.h"
+#include "vrpnButtonDevice.h"
+#include "vrpnAnalog.h"
+#include "vrpnAnalogDevice.h"
+#include "config_vrpn.h"
+
+#include <string_utils.h>
+#include <indent.h>
 
 TypeHandle VrpnClient::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnClient::
+VrpnClient(const string &server_name) :
+  _server_name(server_name)
+{
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Attempting to connect to VRPN server " << _server_name
+      << "\n";
+  }
+  _connection = vrpn_get_connection_by_name(_server_name.c_str());
+  nassertv(_connection != (vrpn_Connection *)NULL);
+
+  if (!is_valid()) {
+    vrpn_cat.warning()
+      << "Unable to establish connection to VRPN server " << _server_name
+      << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnClient::
+~VrpnClient() {
+  delete _connection;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::write
+//       Access: Public
+//  Description: Writes a list of the active devices that the
+//               VrpnClient is currently polling each frame.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "VrpnClient, server " << _server_name << "\n";
+
+  if (!is_valid()) {
+    indent(out, indent_level + 2)
+      << "(error)\n";
+  } else if (!is_connected()) {
+    indent(out, indent_level + 2)
+      << "(no connection)\n";
+  }
+
+  if (!_trackers.empty()) {
+    indent(out, indent_level + 2)
+      << _trackers.size() << " trackers:\n";
+    Trackers::const_iterator ti;
+    for (ti = _trackers.begin(); ti != _trackers.end(); ++ti) {
+      VrpnTracker *vrpn_tracker = (*ti).second;
+      vrpn_tracker->write(out, indent_level + 4);
+    }
+  }
+
+  if (!_buttons.empty()) {
+    indent(out, indent_level + 2)
+      << _buttons.size() << " buttons:\n";
+    Buttons::const_iterator bi;
+    for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
+      VrpnButton *vrpn_button = (*bi).second;
+      vrpn_button->write(out, indent_level + 4);
+    }
+  }
+
+  if (!_analogs.empty()) {
+    indent(out, indent_level + 2)
+      << _analogs.size() << " analogs:\n";
+    Analogs::const_iterator ai;
+    for (ai = _analogs.begin(); ai != _analogs.end(); ++ai) {
+      VrpnAnalog *vrpn_analog = (*ai).second;
+      vrpn_analog->write(out, indent_level + 4);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::make_device
+//       Access: Protected, Virtual
+//  Description: Creates and returns a new ClientDevice of the
+//               appropriate type, according to the requested
+//               device_type and device_name.  Returns NULL if a
+//               matching device cannot be found.
+//
+//               This is guaranteed not to be called twice for a given
+//               device_type/device_name combination (unless
+//               disconnect_device() has already been called for the
+//               same device_type/device_name).
+////////////////////////////////////////////////////////////////////
+PT(ClientDevice) VrpnClient::
+make_device(TypeHandle device_type, const string &device_name) {
+  if (device_type == ClientTrackerDevice::get_class_type()) {
+    return make_tracker_device(device_name);
+
+  } else if (device_type == ClientButtonDevice::get_class_type()) {
+    return make_button_device(device_name);
+
+  } else if (device_type == ClientAnalogDevice::get_class_type()) {
+    return make_analog_device(device_name);
+
+  } else {
+    return NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::disconnect_device
+//       Access: Protected, Virtual
+//  Description: Removes the device, which is presumably about to
+//               destruct, from the list of connected devices, and
+//               frees any data required to support it.  This device
+//               will no longer receive automatic updates with each
+//               poll.
+//
+//               The return value is true if the device was
+//               disconnected, or false if it was unknown (e.g. it was
+//               disconnected previously).
+////////////////////////////////////////////////////////////////////
+bool VrpnClient::
+disconnect_device(TypeHandle device_type, const string &device_name,
+		  ClientDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Disconnecting device " << *device << "\n";
+  }
+
+  if (ClientBase::disconnect_device(device_type, device_name, device)) {
+    if (device->is_of_type(VrpnTrackerDevice::get_class_type())) {
+      disconnect_tracker_device(DCAST(VrpnTrackerDevice, device));
+
+    } else if (device->is_of_type(VrpnButtonDevice::get_class_type())) {
+      disconnect_button_device(DCAST(VrpnButtonDevice, device));
+
+    } else if (device->is_of_type(VrpnAnalogDevice::get_class_type())) {
+      disconnect_analog_device(DCAST(VrpnAnalogDevice, device));
+
+    }
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::do_poll
+//       Access: Protected, Virtual
+//  Description: Implements the polling and updating of connected
+//               devices, if the ClientBase requires this.  This may
+//               be called in a sub-thread if
+//               fork_asynchronous_thread() was called; otherwise, it
+//               will be called once per frame.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+do_poll() {
+  ClientBase::do_poll();
+
+  if (vrpn_cat.is_spam()) {
+    vrpn_cat.spam()
+      << "VrpnClient " << _server_name << " polling " 
+      << _trackers.size() + _buttons.size() + _analogs.size()
+      << " devices.\n";
+  }
+
+  Trackers::iterator ti;
+  for (ti = _trackers.begin(); ti != _trackers.end(); ++ti) {
+    VrpnTracker *vrpn_tracker = (*ti).second;
+    vrpn_tracker->poll();
+  }
+
+  Buttons::iterator bi;
+  for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
+    VrpnButton *vrpn_button = (*bi).second;
+    vrpn_button->poll();
+  }
+
+  Analogs::iterator ai;
+  for (ai = _analogs.begin(); ai != _analogs.end(); ++ai) {
+    VrpnAnalog *vrpn_analog = (*ai).second;
+    vrpn_analog->poll();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::make_tracker_device
+//       Access: Private
+//  Description: Creates a new tracker device.  The device_name is
+//               parsed for sensor and data_type information.
+//
+//               The device_name may be one of the following:
+//
+//                    tracker_name
+//                    tracker_name:N
+//                    tracker_name:N[pva]
+//
+//               Where N is an integer sensor number, and [pva] is one
+//               of the lowercase letters p, v, or a.
+//
+//               In the first form, the device connects to the
+//               indicated tracker, and reports position information
+//               on sensor number 0.
+//
+//               In the second form, the device connects to the
+//               indicated tracker, and reports position information
+//               on the indicated sensor number.
+//
+//               In the third form, the device connects to the
+//               indicated tracker, and reports either position,
+//               velocity, or acceleration information on the
+//               indicated sensor number.
+////////////////////////////////////////////////////////////////////
+PT(ClientDevice) VrpnClient::
+make_tracker_device(const string &device_name) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Making tracker device for " << device_name << "\n";
+  }
+
+  string tracker_name = device_name;
+  int sensor = 0;
+  VrpnTrackerDevice::DataType data_type = VrpnTrackerDevice::DT_position;
+  
+  size_t colon = device_name.rfind(':');
+  if (colon != string::npos && colon + 1 < device_name.length()) {
+    size_t begin = colon + 1;
+    size_t end = device_name.length();
+    VrpnTrackerDevice::DataType maybe_data_type = data_type;
+
+    switch (device_name[end - 1]) {
+    case 'p':
+      maybe_data_type = VrpnTrackerDevice::DT_position;
+      end--;
+      break;
+
+    case 'v':
+      maybe_data_type = VrpnTrackerDevice::DT_velocity;
+      end--;
+      break;
+
+    case 'a':
+      maybe_data_type = VrpnTrackerDevice::DT_acceleration;
+      end--;
+      break;
+    }
+    int maybe_sensor;
+    if (string_to_int(device_name.substr(begin, end - begin), maybe_sensor)) {
+      // It seems to be a legitimate integer!
+      sensor = maybe_sensor;
+      data_type = maybe_data_type;
+      tracker_name = device_name.substr(0, colon);
+    }
+  }
+
+  VrpnTracker *tracker = get_tracker(tracker_name);
+
+  VrpnTrackerDevice *device = 
+    new VrpnTrackerDevice(this, device_name, sensor, data_type, tracker);
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating " << *device << "\n";
+  }
+
+  tracker->mark(device);
+  return device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::make_button_device
+//       Access: Private
+//  Description: Creates a new button device.  The device_name is sent
+//               verbatim to the VRPN library.
+////////////////////////////////////////////////////////////////////
+PT(ClientDevice) VrpnClient::
+make_button_device(const string &device_name) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Making button device for " << device_name << "\n";
+  }
+
+  VrpnButton *button = get_button(device_name);
+
+  VrpnButtonDevice *device = 
+    new VrpnButtonDevice(this, device_name, button);
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating " << *device << "\n";
+  }
+
+  button->mark(device);
+  return device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::make_analog_device
+//       Access: Private
+//  Description: Creates a new analog device.  The device_name is sent
+//               verbatim to the VRPN library.
+////////////////////////////////////////////////////////////////////
+PT(ClientDevice) VrpnClient::
+make_analog_device(const string &device_name) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Making analog device for " << device_name << "\n";
+  }
+
+  VrpnAnalog *analog = get_analog(device_name);
+
+  VrpnAnalogDevice *device = 
+    new VrpnAnalogDevice(this, device_name, analog);
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating " << *device << "\n";
+  }
+
+  analog->mark(device);
+  return device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::disconnect_tracker_device
+//       Access: Private
+//  Description: Removes the tracker device from the list of things to
+//               be updated.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+disconnect_tracker_device(VrpnTrackerDevice *device) {
+  VrpnTracker *vrpn_tracker = device->get_vrpn_tracker();
+  vrpn_tracker->unmark(device);
+  if (vrpn_tracker->is_empty()) {
+    free_tracker(vrpn_tracker);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::disconnect_button_device
+//       Access: Private
+//  Description: Removes the button device from the list of things to
+//               be updated.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+disconnect_button_device(VrpnButtonDevice *device) {
+  VrpnButton *vrpn_button = device->get_vrpn_button();
+  vrpn_button->unmark(device);
+  if (vrpn_button->is_empty()) {
+    free_button(vrpn_button);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::disconnect_analog_device
+//       Access: Private
+//  Description: Removes the analog device from the list of things to
+//               be updated.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+disconnect_analog_device(VrpnAnalogDevice *device) {
+  VrpnAnalog *vrpn_analog = device->get_vrpn_analog();
+  vrpn_analog->unmark(device);
+  if (vrpn_analog->is_empty()) {
+    free_analog(vrpn_analog);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::get_tracker
+//       Access: Private
+//  Description: Finds a VrpnTracker of the indicated name, and
+//               returns it if one already exists, or creates a new
+//               one if it does not.
+////////////////////////////////////////////////////////////////////
+VrpnTracker *VrpnClient::
+get_tracker(const string &tracker_name) {
+  Trackers::iterator ti;
+  ti = _trackers.find(tracker_name);
+
+  if (ti != _trackers.end()) {
+    return (*ti).second;
+  }
+
+  VrpnTracker *vrpn_tracker = new VrpnTracker(tracker_name, _connection);
+  _trackers.insert(Trackers::value_type(tracker_name, vrpn_tracker));
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating tracker " << *vrpn_tracker << "\n";
+  }
+
+  return vrpn_tracker;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::free_tracker
+//       Access: Private
+//  Description: Removes and deletes the indicated VrpnTracker, which
+//               is no longer referenced by any VrpnTrackerDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+free_tracker(VrpnTracker *vrpn_tracker) {
+  nassertv(vrpn_tracker->is_empty());
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Deleting tracker " << *vrpn_tracker << "\n";
+  }
+
+  Trackers::iterator ti;
+  ti = _trackers.find(vrpn_tracker->get_tracker_name());
+  nassertv(ti != _trackers.end());
+  nassertv((*ti).second == vrpn_tracker);
+
+  _trackers.erase(ti);
+  delete vrpn_tracker;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::get_button
+//       Access: Private
+//  Description: Finds a VrpnButton of the indicated name, and
+//               returns it if one already exists, or creates a new
+//               one if it does not.
+////////////////////////////////////////////////////////////////////
+VrpnButton *VrpnClient::
+get_button(const string &button_name) {
+  Buttons::iterator bi;
+  bi = _buttons.find(button_name);
+
+  if (bi != _buttons.end()) {
+    return (*bi).second;
+  }
+
+  VrpnButton *vrpn_button = new VrpnButton(button_name, _connection);
+  _buttons.insert(Buttons::value_type(button_name, vrpn_button));
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating button " << *vrpn_button << "\n";
+  }
+
+  return vrpn_button;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::free_button
+//       Access: Private
+//  Description: Removes and deletes the indicated VrpnButton, which
+//               is no longer referenced by any VrpnButtonDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+free_button(VrpnButton *vrpn_button) {
+  nassertv(vrpn_button->is_empty());
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Deleting button " << *vrpn_button << "\n";
+  }
+
+  Buttons::iterator bi;
+  bi = _buttons.find(vrpn_button->get_button_name());
+  nassertv(bi != _buttons.end());
+  nassertv((*bi).second == vrpn_button);
+
+  _buttons.erase(bi);
+  delete vrpn_button;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::get_analog
+//       Access: Private
+//  Description: Finds a VrpnAnalog of the indicated name, and
+//               returns it if one already exists, or creates a new
+//               one if it does not.
+////////////////////////////////////////////////////////////////////
+VrpnAnalog *VrpnClient::
+get_analog(const string &analog_name) {
+  Analogs::iterator ai;
+  ai = _analogs.find(analog_name);
+
+  if (ai != _analogs.end()) {
+    return (*ai).second;
+  }
+
+  VrpnAnalog *vrpn_analog = new VrpnAnalog(analog_name, _connection);
+  _analogs.insert(Analogs::value_type(analog_name, vrpn_analog));
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Creating analog " << *vrpn_analog << "\n";
+  }
+
+  return vrpn_analog;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnClient::free_analog
+//       Access: Private
+//  Description: Removes and deletes the indicated VrpnAnalog, which
+//               is no longer referenced by any VrpnAnalogDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnClient::
+free_analog(VrpnAnalog *vrpn_analog) {
+  nassertv(vrpn_analog->is_empty());
+
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug()
+      << "Deleting analog " << *vrpn_analog << "\n";
+  }
+
+  Analogs::iterator ai;
+  ai = _analogs.find(vrpn_analog->get_analog_name());
+  nassertv(ai != _analogs.end());
+  nassertv((*ai).second == vrpn_analog);
+
+  _analogs.erase(ai);
+  delete vrpn_analog;
+}
+
+
+#if 0
+
 #include <datagram.h>
 #include <datagramIterator.h>
 
@@ -261,3 +800,5 @@ st_dial(void *userdata, const vrpn_DIALCB info) {
   VrpnClientInfo *data = (VrpnClientInfo *)userdata;
   ((VrpnClient *)data->self)->dial(data->device_name, info);
 }
+
+#endif

+ 63 - 74
panda/src/vrpn/vrpnClient.h

@@ -3,110 +3,99 @@
 // 
 ////////////////////////////////////////////////////////////////////
 
-#ifndef VRPN_CLIENT
-#define VRPN_CLIENT
+#ifndef VRPNCLIENT_H
+#define VRPNCLIENT_H
 
 #include <pandabase.h>
 #include <clientBase.h>
 
-#ifdef CPPPARSER
-  // For correct interrogate parsing of UNC's vrpn library.
-  #ifdef WIN32_VC
-    #define _WIN32
-    #define SOCKET int
-  #else
-    #define linux
-    typedef struct timeval timeval;
-  #endif
-#endif
+#include "vrpn_interface.h"
 
-#include <vrpn_Connection.h>
-#include <vrpn_Tracker.h>
-#include <vrpn_Analog.h>
-#include <vrpn_Button.h>
-#include <vrpn_Dial.h>
+class VrpnTracker;
+class VrpnTrackerDevice;
+class VrpnButton;
+class VrpnButtonDevice;
+class VrpnAnalog;
+class VrpnAnalogDevice;
 
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnClient
+// Description : A specific ClientBase that connects to a VRPN server
+//               and records information on the connected VRPN
+//               devices.
+////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA VrpnClient : public ClientBase {
 PUBLISHED:
-  INLINE VrpnClient(const string &server);
+  VrpnClient(const string &server_name);
+  ~VrpnClient();
 
-public:
-  //ADD FUNCTIONS
-  virtual bool add_remote_tracker(const string &tracker, int sensor);
-  virtual bool add_remote_analog(const string &analog);
-  virtual bool add_remote_button(const string &button);
-  virtual bool add_remote_dial(const string &dial);
+  INLINE const string &get_server_name() const;
+  INLINE bool is_valid() const;
+  INLINE bool is_connected() const;
 
+  void write(ostream &out, int indent_level = 0) const;
+
+public:
+  INLINE static double convert_to_secs(struct timeval msg_time);
+  
 protected:
-  virtual int max_analog_channels();
+  virtual PT(ClientDevice) make_device(TypeHandle device_type,
+				       const string &device_name);
 
-  //Device polling functions
-  virtual void poll_tracker(const string &tracker);
-  virtual void poll_analog(const string &analog);
-  virtual void poll_button(const string &button);
-  virtual void poll_dial(const string &dial);
+  virtual bool disconnect_device(TypeHandle device_type, 
+				 const string &device_name,
+				 ClientDevice *device);
 
-private:
-  //Private VRPN objects
-  typedef map <string, vrpn_Tracker*> VrpnTrackers;
-  typedef map <string, vrpn_Analog*> VrpnAnalogs;
-  typedef map <string, vrpn_Button*> VrpnButtons;
-  typedef map <string, vrpn_Dial*> VrpnDials;
+  virtual void do_poll();
 
-  vrpn_Connection *_connection;
-  VrpnTrackers _vrpn_trackers;
-  VrpnAnalogs _vrpn_analogs;
-  VrpnButtons _vrpn_buttons;
-  VrpnDials _vrpn_dials;
-
-  //VRPN Callback functions.  Each callback actually needs to be a
-  //pair of two functions, one a static and one not.  The static is
-  //needed because we can't set member functions as callbacks (due to
-  //the implicity this pointer) and the non-static function is needed
-  //so that we can set the non-static data storage variables
-  //appropriately
 private:
-  static void st_tracker_position(void *userdata, const vrpn_TRACKERCB info);
-  INLINE void tracker_position(const string &tracker, const vrpn_TRACKERCB info);
+  PT(ClientDevice) make_tracker_device(const string &device_name);
+  PT(ClientDevice) make_button_device(const string &device_name);
+  PT(ClientDevice) make_analog_device(const string &device_name);
+  void disconnect_tracker_device(VrpnTrackerDevice *device);
+  void disconnect_button_device(VrpnButtonDevice *device);
+  void disconnect_analog_device(VrpnAnalogDevice *device);
 
-  static void st_tracker_velocity(void *userdata, const vrpn_TRACKERVELCB info);
-  INLINE void tracker_velocity(const string &tracker, const vrpn_TRACKERVELCB info);
+  VrpnTracker *get_tracker(const string &tracker_name);
+  void free_tracker(VrpnTracker *vrpn_tracker);
 
-  static void st_tracker_acceleration(void *userdata, const vrpn_TRACKERACCCB info);
-  INLINE void tracker_acceleration(const string &tracker, const vrpn_TRACKERACCCB info);
+  VrpnButton *get_button(const string &button_name);
+  void free_button(VrpnButton *vrpn_button);
 
-  static void st_analog(void *userdata, const vrpn_ANALOGCB info);
-  INLINE void analog(const string &analog, const vrpn_ANALOGCB info);
+  VrpnAnalog *get_analog(const string &analog_name);
+  void free_analog(VrpnAnalog *vrpn_analog);
 
-  static void st_button(void *userdata, const vrpn_BUTTONCB info);
-  INLINE void button(const string &button, const vrpn_BUTTONCB info);
+private:
+  string _server_name;
+  vrpn_Connection *_connection;
+
+  typedef map<string, VrpnTracker *> Trackers;
+  typedef map<string, VrpnButton *> Buttons;
+  typedef map<string, VrpnAnalog *> Analogs;
+
+  Trackers _trackers;
+  Buttons _buttons;
+  Analogs _analogs;
 
-  static void st_dial(void *userdata, const vrpn_DIALCB info);
-  INLINE void dial(const string &dial, const vrpn_DIALCB info);
 
 public:
-  static TypeHandle get_class_type( void ) {
-      return _type_handle;
+  static TypeHandle get_class_type() {
+    return _type_handle;
   }
-  static void init_type( void ) {
+  static void init_type() {
     ClientBase::init_type();
-    register_type( _type_handle, "VrpnClient",
-		   ClientBase::get_class_type() );
+    register_type(_type_handle, "VrpnClient",
+                  ClientBase::get_class_type());
   }
-  virtual TypeHandle get_type( void ) const {
+  virtual TypeHandle get_type() const {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() {
-    init_type(); 
-    return get_class_type();
-  }
-
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+ 
 private:
-  static TypeHandle		_type_handle;
+  static TypeHandle _type_handle;
 };
 
-INLINE double convert_to_secs(struct timeval msg_time);
-
 #include "vrpnClient.I"
 
 #endif

+ 38 - 0
panda/src/vrpn/vrpnTracker.I

@@ -0,0 +1,38 @@
+// Filename: vrpnTracker.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::get_tracker_name
+//       Access: Public
+//  Description: Returns the name of the tracker device that was used
+//               to create this VrpnTracker.
+////////////////////////////////////////////////////////////////////
+INLINE const string &VrpnTracker::
+get_tracker_name() const {
+  return _tracker_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::is_empty
+//       Access: Public
+//  Description: Returns true if no VrpnTrackerDevices reference this
+//               VrpnTracker, or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool VrpnTracker::
+is_empty() const {
+  return _devices.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::poll
+//       Access: Public
+//  Description: Polls the connected device.  Normally you should not
+//               call this directly; this will be called by the
+//               VrpnClient.
+////////////////////////////////////////////////////////////////////
+INLINE void VrpnTracker::
+poll() {
+  _tracker->mainloop();
+}

+ 186 - 0
panda/src/vrpn/vrpnTracker.cxx

@@ -0,0 +1,186 @@
+// Filename: vrpnTracker.cxx
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnTracker.h"
+#include "vrpnTrackerDevice.h"
+#include "vrpnClient.h"
+#include "config_vrpn.h"
+
+#include <indent.h>
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnTracker::
+VrpnTracker(const string &tracker_name, vrpn_Connection *connection) :
+  _tracker_name(tracker_name)
+{
+  _tracker = new vrpn_Tracker_Remote(_tracker_name.c_str(), connection);
+
+  _tracker->register_change_handler((void*)this, &vrpn_position_callback);
+  _tracker->register_change_handler((void*)this, &vrpn_velocity_callback);
+  _tracker->register_change_handler((void*)this, &vrpn_acceleration_callback);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnTracker::
+~VrpnTracker() {
+  delete _tracker;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::mark
+//       Access: Public
+//  Description: Adds the indicated VrpnTrackerDevice to the list of
+//               devices that are sharing this VrpnTracker.
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+mark(VrpnTrackerDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " marking " << *device << "\n";
+  }
+  _devices.push_back(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::unmark
+//       Access: Public
+//  Description: Removes the indicated VrpnTrackerDevice from the list
+//               of devices that are sharing this VrpnTracker.
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+unmark(VrpnTrackerDevice *device) {
+  if (vrpn_cat.is_debug()) {
+    vrpn_cat.debug() << *this << " unmarking " << *device << "\n";
+  }
+
+  Devices::iterator di = 
+    find(_devices.begin(), _devices.end(), device);
+
+  if (di != _devices.end()) {
+    _devices.erase(di);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+output(ostream &out) const {
+  out << _tracker_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) 
+    << get_tracker_name() << " ("
+    << _devices.size() << " devices)\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::vrpn_position_callback
+//       Access: Private, Static
+//  Description: Receives the tracker positional data from the VRPN
+//               code and sends it to any interested
+//               VrpnTrackerDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+vrpn_position_callback(void *userdata, const vrpn_TRACKERCB info) {
+  VrpnTracker *self = (VrpnTracker *)userdata;
+  if (vrpn_cat.is_spam()) {
+    vrpn_cat.spam()
+      << *self << " position_callback\n";
+  }
+
+  Devices::iterator di;
+  for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
+    VrpnTrackerDevice *device = (*di);
+    if (device->get_sensor() == info.sensor &&
+	device->get_data_type() == VrpnTrackerDevice::DT_position) {
+      device->lock();
+      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
+      device->_data.set_pos(LPoint3f(info.pos[0], info.pos[1], info.pos[2]));
+      device->_data.set_orient(LOrientationf(info.quat[0], info.quat[1], info.quat[2], info.quat[3]));
+      device->unlock();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::vrpn_velocity_callback
+//       Access: Private, Static
+//  Description: Receives the tracker velocity data from the VRPN
+//               code and sends it to any interested
+//               VrpnTrackerDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+vrpn_velocity_callback(void *userdata, const vrpn_TRACKERVELCB info) {
+  VrpnTracker *self = (VrpnTracker *)userdata;
+  if (vrpn_cat.is_spam()) {
+    vrpn_cat.spam()
+      << *self << " velocity_callback\n";
+  }
+
+  Devices::iterator di;
+  for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
+    VrpnTrackerDevice *device = (*di);
+    if (device->get_sensor() == info.sensor &&
+	device->get_data_type() == VrpnTrackerDevice::DT_velocity) {
+      device->lock();
+      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
+      device->_data.set_pos(LPoint3f(info.vel[0], info.vel[1], info.vel[2]));
+      device->_data.set_orient(LOrientationf(info.vel_quat[0], info.vel_quat[1], 
+					     info.vel_quat[2], info.vel_quat[3]));
+      device->_data.set_dt(info.vel_quat_dt);
+      device->unlock();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTracker::vrpn_acceleration_callback
+//       Access: Private, Static
+//  Description: Receives the tracker acceleration data from the VRPN
+//               code and sends it to any interested
+//               VrpnTrackerDevices.
+////////////////////////////////////////////////////////////////////
+void VrpnTracker::
+vrpn_acceleration_callback(void *userdata, const vrpn_TRACKERACCCB info) {
+  VrpnTracker *self = (VrpnTracker *)userdata;
+  if (vrpn_cat.is_spam()) {
+    vrpn_cat.spam()
+      << *self << " acceleration_callback\n";
+  }
+
+  Devices::iterator di;
+  for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
+    VrpnTrackerDevice *device = (*di);
+    if (device->get_sensor() == info.sensor &&
+	device->get_data_type() == VrpnTrackerDevice::DT_acceleration) {
+      device->lock();
+      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
+      device->_data.set_pos(LPoint3f(info.acc[0], info.acc[1], info.acc[2]));
+      device->_data.set_orient(LOrientationf(info.acc_quat[0], info.acc_quat[1], 
+					     info.acc_quat[2], info.acc_quat[3]));
+      device->_data.set_dt(info.acc_quat_dt);
+      device->unlock();
+    }
+  }
+}

+ 72 - 0
panda/src/vrpn/vrpnTracker.h

@@ -0,0 +1,72 @@
+// Filename: vrpnTracker.h
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNTRACKER_H
+#define VRPNTRACKER_H
+
+#include <pandabase.h>
+
+#include "vrpn_interface.h"
+
+#include <vector>
+
+class VrpnTrackerDevice;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnTracker
+// Description : This is the actual interface to a particular VRPN
+//               tracker object, and all of its sensors.  A pointer to
+//               this object is stored in the VrpnClient class for
+//               each differently-named VRPN tracker we connect to.
+//
+//               The VRPN callbacks go here, which in turn get
+//               vectored out to any VrpnTrackerDevice objects that
+//               register with this.  When the last VrpnTrackerDevice
+//               object unregisters, the VrpnTracker will be deleted
+//               by the VrpnClient.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnTracker {
+public:
+  VrpnTracker(const string &tracker_name, vrpn_Connection *connection);
+  ~VrpnTracker();
+
+  INLINE const string &get_tracker_name() const;
+  INLINE bool is_empty() const;
+
+  void mark(VrpnTrackerDevice *device);
+  void unmark(VrpnTrackerDevice *device);
+
+  INLINE void poll();
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
+private:
+  static void
+  vrpn_position_callback(void *userdata, const vrpn_TRACKERCB info);
+  static void
+  vrpn_velocity_callback(void *userdata, const vrpn_TRACKERVELCB info);
+  static void
+  vrpn_acceleration_callback(void *userdata, const vrpn_TRACKERACCCB info);
+
+private:
+  string _tracker_name;
+  vrpn_Tracker_Remote *_tracker;
+
+  typedef vector<VrpnTrackerDevice *> Devices;
+  Devices _devices;
+};
+
+INLINE ostream &operator << (ostream &out, const VrpnTracker &tracker) {
+  tracker.output(out);
+  return out;
+}
+
+#include "vrpnTracker.I"
+
+#endif
+

+ 42 - 0
panda/src/vrpn/vrpnTrackerDevice.I

@@ -0,0 +1,42 @@
+// Filename: vrpnTrackerDevice.I
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTrackerDevice::get_sensor
+//       Access: Public
+//  Description: Returns the particular sensor index that this device
+//               wants to hear about from the VrpnTracker.
+////////////////////////////////////////////////////////////////////
+INLINE int VrpnTrackerDevice::
+get_sensor() const {
+  return _sensor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTrackerDevice::get_data_type
+//       Access: Public
+//  Description: Returns the type of data this device represents from
+//               the VrpnTracker.  This may be position, velocity, or
+//               acceleration.
+////////////////////////////////////////////////////////////////////
+INLINE VrpnTrackerDevice::DataType VrpnTrackerDevice::
+get_data_type() const {
+  return _data_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTrackerDevice::get_vrpn_tracker
+//       Access: Public
+//  Description: Returns a pointer to the particular VrpnTracker this
+//               device gets its data from.  This pointer may be
+//               shared with other VrpnTrackerDevice objects (each
+//               representing a different portion of the tracker
+//               data).
+////////////////////////////////////////////////////////////////////
+INLINE VrpnTracker *VrpnTrackerDevice::
+get_vrpn_tracker() const {
+  return _vrpn_tracker;
+}

+ 35 - 0
panda/src/vrpn/vrpnTrackerDevice.cxx

@@ -0,0 +1,35 @@
+// Filename: vrpnTrackerDevice.cxx
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "vrpnTrackerDevice.h"
+#include "vrpnClient.h"
+
+TypeHandle VrpnTrackerDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTrackerDevice::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnTrackerDevice::
+VrpnTrackerDevice(VrpnClient *client, const string &device_name,
+		  int sensor, VrpnTrackerDevice::DataType data_type,
+		  VrpnTracker *vrpn_tracker) :
+  ClientTrackerDevice(client, device_name),
+  _sensor(sensor),
+  _data_type(data_type),
+  _vrpn_tracker(vrpn_tracker)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VrpnTrackerDevice::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+VrpnTrackerDevice::
+~VrpnTrackerDevice() {
+  disconnect();
+}

+ 73 - 0
panda/src/vrpn/vrpnTrackerDevice.h

@@ -0,0 +1,73 @@
+// Filename: vrpnTrackerDevice.h
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPNTRACKERDEVICE_H
+#define VRPNTRACKERDEVICE_H
+
+#include <pandabase.h>
+
+#include <clientTrackerDevice.h>
+
+class VrpnClient;
+class VrpnTracker;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VrpnTrackerDevice
+// Description : The Panda interface to a VRPN tracker.  This object
+//               will be returned by VrpnClient::make_device(), for
+//               attaching to a TrackerNode.
+//
+//               It represents the data from just one particular
+//               sensor of a named VRPN tracker, and may reflect
+//               either the sensor's position, its velocity, or its
+//               acceleration.
+//
+//               This class does not need to be exported from the DLL.
+////////////////////////////////////////////////////////////////////
+class VrpnTrackerDevice : public ClientTrackerDevice {
+public:
+  enum DataType {
+    DT_position,
+    DT_velocity,
+    DT_acceleration
+  };
+
+  VrpnTrackerDevice(VrpnClient *client, const string &device_name,
+		    int sensor, DataType data_type,
+		    VrpnTracker *vrpn_tracker);
+  virtual ~VrpnTrackerDevice();
+
+  INLINE int get_sensor() const;
+  INLINE DataType get_data_type() const;
+  INLINE VrpnTracker *get_vrpn_tracker() const;
+
+private:
+  int _sensor;
+  DataType _data_type;
+  VrpnTracker *_vrpn_tracker;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ClientTrackerDevice::init_type();
+    register_type(_type_handle, "VrpnTrackerDevice",
+                  ClientTrackerDevice::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;
+
+  friend class VrpnTracker;
+};
+
+#include "vrpnTrackerDevice.I"
+
+#endif

+ 28 - 0
panda/src/vrpn/vrpn_interface.h

@@ -0,0 +1,28 @@
+// Filename: vrpn_interface.h
+// Created by:  drose (25Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef VRPN_INTERFACE_H
+#define VRPN_INTERFACE_H
+
+#include <pandabase.h>
+
+#ifdef CPPPARSER
+  // For correct interrogate parsing of UNC's vrpn library.
+  #ifdef WIN32_VC
+    #define _WIN32
+    #define SOCKET int
+  #else
+    #define linux
+    typedef struct timeval timeval;
+  #endif
+#endif
+
+#include <vrpn_Connection.h>
+#include <vrpn_Tracker.h>
+#include <vrpn_Analog.h>
+#include <vrpn_Button.h>
+#include <vrpn_Dial.h>
+
+#endif