Browse Source

define mouseInterfaceNode

David Rose 22 years ago
parent
commit
081122fe3b

+ 33 - 0
panda/src/putil/modifierButtons.I

@@ -66,6 +66,39 @@ operator < (const ModifierButtons &other) const {
   return _state < other._state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::operator &
+//       Access: Published
+//  Description: Returns a new ModifierButtons object for which
+//               is_down() will be true only if it is true on both
+//               source objects.  The set of buttons reported by
+//               has_button() is not completely defined if both source
+//               objects have a different set.
+////////////////////////////////////////////////////////////////////
+INLINE ModifierButtons ModifierButtons::
+operator & (const ModifierButtons &other) const {
+  ModifierButtons result = *this;
+  result &= other;
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::operator |
+//       Access: Published
+//  Description: Returns a new ModifierButtons object for which
+//               is_down() will be true if it is true on either of the
+//               source objects.  The set of buttons reported by
+//               has_button() is not completely defined if both source
+//               objects have a different set.
+////////////////////////////////////////////////////////////////////
+INLINE ModifierButtons ModifierButtons::
+operator | (const ModifierButtons &other) const {
+  ModifierButtons result = *this;
+  result |= other;
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ModifierButtons::get_num_buttons
 //       Access: Published

+ 92 - 1
panda/src/putil/modifierButtons.cxx

@@ -29,7 +29,7 @@ ModifierButtons::
 ModifierButtons() :
   _state(0)
 {
-   _button_list= PTA(ButtonHandle)::empty_array(0);
+   _button_list = PTA(ButtonHandle)::empty_array(0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -53,6 +53,97 @@ ModifierButtons::
 ~ModifierButtons() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::operator &=
+//       Access: Published
+//  Description: Sets is_down() true for any button that is already
+//               true for this object and the other object.
+////////////////////////////////////////////////////////////////////
+void ModifierButtons::
+operator &= (const ModifierButtons &other) {
+  if (_button_list == other._button_list) {
+    // Trivially easy case: if the button lists are the same, we can
+    // do this using a bitmask operation.
+    _state &= other._state;
+
+  } else {
+    // More complicated case: if the button lists are different, we
+    // have to iterate through the buttons and compare them
+    // case-by-case.  This becomes an n^2 operation, but fortunately
+    // there won't be more than a handful of buttons.
+    int num_buttons = get_num_buttons();
+    for (int i = 0; i < num_buttons; i++) {
+      if (is_down(i) && !other.is_down(get_button(i))) {
+        _state &= ~((BitmaskType)1 << i);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::operator |=
+//       Access: Published
+//  Description: Sets is_down() true for any button that is already
+//               true for this object and the other object.  Adds
+//               whatever buttons are necessary to the list to make
+//               this so
+////////////////////////////////////////////////////////////////////
+void ModifierButtons::
+operator |= (const ModifierButtons &other) {
+  if (_button_list == other._button_list) {
+    // Trivially easy case: if the button lists are the same, we can
+    // do this using a bitmask operation.
+    _state |= other._state;
+
+  } else {
+    // More complicated case: if the button lists are different, we
+    // have to iterate through the buttons and compare them
+    // case-by-case.  This becomes an n^2 operation, but fortunately
+    // there won't be more than a handful of buttons.
+    int num_buttons = other.get_num_buttons();
+    for (int i = 0; i < num_buttons; i++) {
+      if (other.is_down(i)) {
+        add_button(other.get_button(i));
+        button_down(other.get_button(i));
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModifierButtons::set_button_list
+//       Access: Published
+//  Description: Sets the list of buttons to watch to be the same as
+//               that of the other ModifierButtons object.  This makes
+//               the lists pointer equivalent (until one or the other
+//               is later modified).
+//
+//               This will preserve the state of any button that was
+//               on the original list and is also on the new lists.
+//               Any other buttons will get reset to the default state
+//               of "up".
+////////////////////////////////////////////////////////////////////
+void ModifierButtons::
+set_button_list(const ModifierButtons &other) {
+  if (_button_list != other._button_list) {
+    if (_state != 0) {
+      // If we have some buttons already down, we have to copy them to
+      // the new state.
+      BitmaskType new_state = 0;
+      int num_buttons = other.get_num_buttons();
+      for (int i = 0; i < num_buttons; i++) {
+        if (is_down(other.get_button(i))) {
+          new_state |= ((BitmaskType)1 << i);
+        }
+      }
+    
+      _state = new_state;
+    }
+
+    _button_list = other._button_list;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ModifierButtons::matches
 //       Access: Published

+ 8 - 0
panda/src/putil/modifierButtons.h

@@ -41,6 +41,14 @@ PUBLISHED:
   INLINE bool operator != (const ModifierButtons &other) const;
   INLINE bool operator < (const ModifierButtons &other) const;
 
+  INLINE ModifierButtons operator & (const ModifierButtons &other) const;
+  INLINE ModifierButtons operator | (const ModifierButtons &other) const;
+
+  void operator &= (const ModifierButtons &other);
+  void operator |= (const ModifierButtons &other);
+
+  void set_button_list(const ModifierButtons &other);
+
   bool matches(const ModifierButtons &other) const;
 
   bool add_button(ButtonHandle button);

+ 3 - 0
panda/src/tform/Sources.pp

@@ -13,6 +13,7 @@
     buttonThrower.I buttonThrower.h \
     config_tform.h \
     driveInterface.I driveInterface.h \
+    mouseInterfaceNode.I mouseInterfaceNode.h \
     mouseWatcher.I mouseWatcher.h \
     mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \
@@ -24,6 +25,7 @@
     buttonThrower.cxx \
     config_tform.cxx \
     driveInterface.cxx \
+    mouseInterfaceNode.cxx \
     mouseWatcher.cxx \
     mouseWatcherGroup.cxx \
     mouseWatcherParameter.cxx mouseWatcherRegion.cxx  \
@@ -33,6 +35,7 @@
   #define INSTALL_HEADERS \
     buttonThrower.I buttonThrower.h \
     driveInterface.I driveInterface.h \
+    mouseInterfaceNode.I mouseInterfaceNode.h \
     mouseWatcher.I mouseWatcher.h \
     mouseWatcherGroup.h \
     mouseWatcherParameter.I mouseWatcherParameter.h \

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

@@ -46,6 +46,7 @@ const double drive_horizontal_ramp_down_time = config_tform.GetDouble("drive-hor
 ConfigureFn(config_tform) {
   DriveInterface::init_type();
   ButtonThrower::init_type();
+  MouseInterfaceNode::init_type();
   MouseWatcher::init_type();
   MouseWatcherGroup::init_type();
   MouseWatcherRegion::init_type();

+ 9 - 24
panda/src/tform/driveInterface.cxx

@@ -104,7 +104,7 @@ operator < (const DriveInterface::KeyHeld &other) const {
 ////////////////////////////////////////////////////////////////////
 DriveInterface::
 DriveInterface(const string &name) : 
-  DataNode(name) 
+  MouseInterfaceNode(name) 
 {
   _xy_input = define_input("xy", EventStoreVec2::get_class_type());
   _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
@@ -138,9 +138,7 @@ DriveInterface(const string &name) :
   _force_mouse = false;
   _stop_this_frame = false;
 
-  _mods.add_button(MouseButton::one());
-  _mods.add_button(MouseButton::two());
-  _mods.add_button(MouseButton::three());
+  watch_button(MouseButton::one());
 }
 
 
@@ -168,8 +166,6 @@ reset() {
   _down_arrow.clear();
   _left_arrow.clear();
   _right_arrow.clear();
-
-  _mods.all_buttons_up();
 }
 
 
@@ -398,13 +394,17 @@ apply(double x, double y, bool any_button) {
 ////////////////////////////////////////////////////////////////////
 void DriveInterface::
 do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+  // First, update our modifier buttons.
+  bool required_buttons_match;
+  const ButtonEventList *button_events = check_button_events(input, required_buttons_match);
+
   // Look for mouse activity.
   double x = 0.0f;
   double y = 0.0f;
 
   bool got_mouse = false;
 
-  if (input.has_data(_xy_input)) {
+  if (required_buttons_match && input.has_data(_xy_input)) {
     const EventStoreVec2 *xy;
     DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr());
     const LVecBase2f &p = xy->get_value();
@@ -415,28 +415,13 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
   }
 
   // Look for keyboard events.
-  if (input.has_data(_button_events_input)) {
-    const ButtonEventList *button_events;
-    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
+  if (required_buttons_match && button_events != (const ButtonEventList *)NULL) {
 
     int num_events = button_events->get_num_events();
     for (int i = 0; i < num_events; i++) {
       const ButtonEvent &be = button_events->get_event(i);
       if (be._type != ButtonEvent::T_keystroke) {
         bool down = (be._type == ButtonEvent::T_down);
-
-        if (down) {
-          // We only trap button down events if (a) the mouse is in the
-          // window, and (b) we aren't set to ignore the mouse.
-          if (got_mouse && !_ignore_mouse) {
-            be.update_mods(_mods);
-          }
-        } else {
-          // However, we always trap button up events, so we don't get
-          // confused and believe a button is still being held down when
-          // it is not.
-          be.update_mods(_mods);
-        }
         
         if (be._button == KeyboardButton::up()) {
           _up_arrow.set_key(down);
@@ -451,7 +436,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
     }
   }
 
-  apply(x, y, _mods.is_any_down());
+  apply(x, y, !_ignore_mouse && is_down(MouseButton::one()));
   _transform = TransformState::make_pos_hpr(_xyz, _hpr);
   _velocity->set_value(_vel);
   output.set_data(_transform_output, EventParameter(_transform));

+ 4 - 7
panda/src/tform/driveInterface.h

@@ -21,7 +21,7 @@
 
 #include "pandabase.h"
 
-#include "dataNode.h"
+#include "mouseInterfaceNode.h"
 #include "modifierButtons.h"
 #include "luse.h"
 #include "linmath_events.h"
@@ -35,7 +35,7 @@
 //               The basic motion is on a horizontal plane, as if
 //               driving a vehicle.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DriveInterface : public DataNode {
+class EXPCL_PANDA DriveInterface : public MouseInterfaceNode {
 PUBLISHED:
   DriveInterface(const string &name = "");
   ~DriveInterface();
@@ -136,9 +136,6 @@ private:
   // This is only used to return a temporary value in get_mat().
   LMatrix4f _mat;
 
-  // Remember which mouse buttons are being held down.
-  ModifierButtons _mods;
-
   // Remember which arrow keys are being held down and which aren't,
   // and at what point they last changed state.
   class KeyHeld {
@@ -187,9 +184,9 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    DataNode::init_type();
+    MouseInterfaceNode::init_type();
     register_type(_type_handle, "DriveInterface",
-                  DataNode::get_class_type());
+                  MouseInterfaceNode::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 30 - 0
panda/src/tform/mouseInterfaceNode.I

@@ -0,0 +1,30 @@
+// Filename: mouseInterfaceNode.I
+// Created by:  drose (11Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::is_down
+//       Access: Protected
+//  Description: Returns true if the indicated button (which must have
+//               been specified in a previous call to watch_button())
+//               is known to be held down, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool MouseInterfaceNode::
+is_down(ButtonHandle button) const {
+  return _current_button_state.is_down(button);
+}

+ 155 - 0
panda/src/tform/mouseInterfaceNode.cxx

@@ -0,0 +1,155 @@
+// Filename: mouseInterfaceNode.cxx
+// Created by:  drose (11Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#include "trackball.h"
+#include "buttonEvent.h"
+#include "buttonEventList.h"
+#include "dataNodeTransmit.h"
+#include "mouseData.h"
+
+TypeHandle MouseInterfaceNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+MouseInterfaceNode::
+MouseInterfaceNode(const string &name) :
+  DataNode(name)
+{
+  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+MouseInterfaceNode::
+~MouseInterfaceNode() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::require_button
+//       Access: Published
+//  Description: Indicates that the indicated button must be in the
+//               required state (either up or down) in order for this
+//               particular MouseInterfaceNode to do anything.  For
+//               instance, this may be called to make a Trackball
+//               object respect mouse input only when the control key
+//               is held down.
+////////////////////////////////////////////////////////////////////
+void MouseInterfaceNode::
+require_button(const ButtonHandle &button, bool is_down) {
+  _required_buttons_mask.add_button(button);
+  _required_buttons_state.set_button_list(_required_buttons_mask);
+  _current_button_state.set_button_list(_required_buttons_mask);
+
+  _required_buttons_mask.button_down(button);
+  if (is_down) {
+    _required_buttons_state.button_down(button);
+  } else {
+    _required_buttons_state.button_up(button);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::clear_button
+//       Access: Published
+//  Description: Removes any requirement on the indicated button set
+//               by an earlier call to require_button().
+////////////////////////////////////////////////////////////////////
+void MouseInterfaceNode::
+clear_button(const ButtonHandle &button) {
+  _required_buttons_mask.button_up(button);
+  _required_buttons_state.button_up(button);
+
+  // The _required_buttons_mask and state must always keep the buttons
+  // that are listed in _watched_buttons.
+
+  if (!_watched_buttons.has_button(button)) {
+    _required_buttons_mask.remove_button(button);
+    _required_buttons_state.set_button_list(_required_buttons_mask);
+    _current_button_state.set_button_list(_required_buttons_mask);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::clear_all_button
+//       Access: Published
+//  Description: Removes all requirements on buttons set by an earlier
+//               call to require_button().
+////////////////////////////////////////////////////////////////////
+void MouseInterfaceNode::
+clear_all_buttons() {
+  _required_buttons_mask.all_buttons_up();
+  _required_buttons_state.all_buttons_up();
+
+  _required_buttons_mask.set_button_list(_watched_buttons);
+  _required_buttons_state.set_button_list(_watched_buttons);
+  _current_button_state.set_button_list(_watched_buttons);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::watch_button
+//       Access: Protected
+//  Description: Indicates that the derived class would like to know
+//               the state of the given button.
+////////////////////////////////////////////////////////////////////
+void MouseInterfaceNode::
+watch_button(const ButtonHandle &button) {
+  _watched_buttons.add_button(button);
+
+  // We also add the button to _required_buttons_mask and
+  // _required_buttons_state, but it's left 'up' in these two.
+  _required_buttons_mask.add_button(button);
+  _required_buttons_state.set_button_list(_required_buttons_mask);
+  _current_button_state.set_button_list(_required_buttons_mask);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseInterfaceNode::check_button_events
+//       Access: Protected
+//  Description: Gets the button events from the data graph and
+//               updates the ModifierButtons objects appropriately.
+//
+//               Sets required_buttons_match to true if the required
+//               combination of buttons are being held down, or false
+//               otherwise.
+//
+//               The return value is the list of button events
+//               processed this frame, or NULL if there are no button
+//               events.
+////////////////////////////////////////////////////////////////////
+const ButtonEventList *MouseInterfaceNode::
+check_button_events(const DataNodeTransmit &input,
+                    bool &required_buttons_match) {
+  const ButtonEventList *button_events = NULL;
+
+  if (input.has_data(_button_events_input)) {
+    DCAST_INTO_R(button_events, input.get_data(_button_events_input).get_ptr(), false);
+    button_events->update_mods(_current_button_state);
+  }
+
+  required_buttons_match = 
+    (_current_button_state & _required_buttons_mask) == _required_buttons_state;
+
+  return button_events;
+}

+ 84 - 0
panda/src/tform/mouseInterfaceNode.h

@@ -0,0 +1,84 @@
+// Filename: mouseInterfaceNode.h
+// Created by:  drose (11Jun04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MOUSEINTERFACENODE_H
+#define MOUSEINTERFACENODE_H
+
+#include "pandabase.h"
+
+#include "dataNode.h"
+#include "modifierButtons.h"
+
+class ButtonEventList;
+
+////////////////////////////////////////////////////////////////////
+//       Class : MouseInterfaceNode
+// Description : This is the base class for some classes that monitor
+//               the mouse and keyboard input and perform some action
+//               due to their state.
+//
+//               It collects together some common interface; in
+//               particular, the require_button() and related methods.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA MouseInterfaceNode : public DataNode {
+public:
+  MouseInterfaceNode(const string &name);
+  virtual ~MouseInterfaceNode();
+
+PUBLISHED:
+  void require_button(const ButtonHandle &button, bool is_down);
+  void clear_button(const ButtonHandle &button);
+  void clear_all_buttons();
+
+protected:
+  void watch_button(const ButtonHandle &button);
+  const ButtonEventList *check_button_events(const DataNodeTransmit &input,
+                                             bool &required_buttons_match);
+
+  INLINE bool is_down(ButtonHandle button) const;
+
+private:
+  ModifierButtons _current_button_state;
+  ModifierButtons _watched_buttons;
+  ModifierButtons _required_buttons_mask;
+  ModifierButtons _required_buttons_state;
+
+private:
+  int _button_events_input;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    DataNode::init_type();
+    register_type(_type_handle, "MouseInterfaceNode",
+                  DataNode::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 "mouseInterfaceNode.I"
+
+#endif

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

@@ -1,4 +1,6 @@
 #include "buttonThrower.cxx"
 #include "config_tform.cxx"
 #include "driveInterface.cxx"
+#include "mouseInterfaceNode.cxx"
+
 

+ 15 - 18
panda/src/tform/trackball.cxx

@@ -38,10 +38,9 @@ TypeHandle Trackball::_type_handle;
 ////////////////////////////////////////////////////////////////////
 Trackball::
 Trackball(const string &name) :
-  DataNode(name)
+  MouseInterfaceNode(name)
 {
   _pixel_xy_input = define_input("pixel_xy", EventStoreVec2::get_class_type());
-  _button_events_input = define_input("button_events", ButtonEventList::get_class_type());
 
   _transform_output = define_output("transform", TransformState::get_class_type());
 
@@ -59,9 +58,10 @@ Trackball(const string &name) :
   _invert = true;
   _cs = default_coordinate_system;
 
-  _mods.add_button(MouseButton::one());
-  _mods.add_button(MouseButton::two());
-  _mods.add_button(MouseButton::three());
+  // We want to track the state of these buttons.
+  watch_button(MouseButton::one());
+  watch_button(MouseButton::two());
+  watch_button(MouseButton::three());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -535,36 +535,33 @@ recompute() {
 void Trackball::
 do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
   // First, update our modifier buttons.
-  if (input.has_data(_button_events_input)) {
-    const ButtonEventList *button_events;
-    DCAST_INTO_V(button_events, input.get_data(_button_events_input).get_ptr());
-    button_events->update_mods(_mods);
-  }
+  bool required_buttons_match;
+  check_button_events(input, required_buttons_match);
 
   // Now, check for mouse motion.
-  if (input.has_data(_pixel_xy_input)) {
+  if (required_buttons_match && input.has_data(_pixel_xy_input)) {
     const EventStoreVec2 *pixel_xy;
     DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr());
     const LVecBase2f &p = pixel_xy->get_value();
     float this_x = p[0];
     float this_y = p[1];
     int this_button = 0;
-
-    if (_mods.is_down(MouseButton::one())) {
+    
+    if (is_down(MouseButton::one())) {
       this_button |= B1_MASK;
     }
-    if (_mods.is_down(MouseButton::two())) {
+    if (is_down(MouseButton::two())) {
       this_button |= B2_MASK;
     }
-    if (_mods.is_down(MouseButton::three())) {
+    if (is_down(MouseButton::three())) {
       this_button |= B3_MASK;
     }
-
+    
     float x = this_x - _lastx;
     float y = this_y - _lasty;
-
+    
     apply(x, y, this_button);
-
+    
     _lastx = this_x;
     _lasty = this_y;
   }

+ 5 - 10
panda/src/tform/trackball.h

@@ -21,7 +21,7 @@
 
 #include "pandabase.h"
 
-#include "dataNode.h"
+#include "mouseInterfaceNode.h"
 #include "nodePath.h"
 #include "modifierButtons.h"
 #include "luse.h"
@@ -41,7 +41,7 @@
 //               it to actually transform objects (or cameras) in the
 //               world.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA Trackball : public DataNode {
+class EXPCL_PANDA Trackball : public MouseInterfaceNode {
 PUBLISHED:
   Trackball(const string &name);
   ~Trackball();
@@ -88,7 +88,7 @@ PUBLISHED:
   void set_invert(bool flag);
   bool get_invert() const;
 
-  void set_rel_to(const NodePath &_rel_to);
+  void set_rel_to(const NodePath &rel_to);
   const NodePath &get_rel_to() const;
 
   void set_coordinate_system(CoordinateSystem cs);
@@ -118,10 +118,6 @@ private:
   NodePath _rel_to;
   CoordinateSystem _cs;
 
-  // Remember which mouse buttons are being held down.
-  ModifierButtons _mods;
-
-
 protected:
   // Inherited from DataNode
   virtual void do_transmit_data(const DataNodeTransmit &input,
@@ -130,7 +126,6 @@ protected:
 private:
   // inputs
   int _pixel_xy_input;
-  int _button_events_input;
 
   // outputs
   int _transform_output;
@@ -142,9 +137,9 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    DataNode::init_type();
+    MouseInterfaceNode::init_type();
     register_type(_type_handle, "Trackball",
-                  DataNode::get_class_type());
+                  MouseInterfaceNode::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();